Browse Source

Merge pull request #17 from ckeditor/t/16

Internal: Enhanced docs for the tokenUrl config. Closes #16. Closes #20.
Piotr Jasiun 7 years ago
parent
commit
066a675c39

+ 1 - 0
packages/ckeditor5-cloud-services/package.json

@@ -14,6 +14,7 @@
     "@ckeditor/ckeditor5-utils": "^10.2.0"
   },
   "devDependencies": {
+    "@ckeditor/ckeditor5-editor-classic": "^11.0.0",
     "eslint": "^5.5.0",
     "eslint-config-ckeditor5": "^1.0.7",
     "husky": "^0.14.3",

+ 57 - 4
packages/ckeditor5-cloud-services/src/cloudservices.js

@@ -20,6 +20,13 @@ import Token from '@ckeditor/ckeditor-cloud-services-core/src/token/token';
  */
 export default class CloudServices extends Plugin {
 	/**
+	 * @inheritdoc
+	 */
+	static get pluginName() {
+		return 'CloudServices';
+	}
+
+	/**
 	 * @inheritDoc
 	 */
 	init() {
@@ -33,10 +40,11 @@ export default class CloudServices extends Plugin {
 		}
 
 		/**
-		 * The authentication token URL for CKEditor Cloud Services.
+		 * The authentication token URL for CKEditor Cloud Services or a callback to the token value promise. See the
+		 * {@link module:cloud-services/cloudservices~CloudServicesConfig#tokenUrl} for more details.
 		 *
 		 * @readonly
-		 * @member {String|undefined} #tokenUrl
+		 * @member {String|Function|undefined} #tokenUrl
 		 */
 
 		/**
@@ -95,17 +103,62 @@ CloudServices.Token = Token;
  */
 
 /**
- * The URL to the security token endpoint in your application. The role of this endpoint is to securely authorize the
+ * A token URL or a token request function.
+ *
+ * As a string it should be a URL to the security token endpoint in your application. The role of this endpoint is to securely authorize the
  * end users of your application to use [CKEditor Cloud Services](https://ckeditor.com/ckeditor-cloud-services), only
  * if they should have access e.g. to upload files with Easy Image or to access the Collaboraation service.
  *
+ *		ClassicEditor
+ *			.create( document.querySelector( '#editor' ), {
+ *				cloudServices: {
+ *					tokenUrl: 'https://example.com/cs-token-endpoint',
+ *					...
+ *				}
+ *			} )
+ *			.then( ... )
+ *			.catch( ... );
+ *
+ * As a function it should provide a promise to the token value, so you can highly customize the token and provide your token URL endpoint.
+ * By using that approach you can set your own headers to the request.
+ *
+ * 		ClassicEditor
+ *			.create( document.querySelector( '#editor' ), {
+ *				cloudServices: {
+ *					tokenUrl: () => new Promise( ( resolve, reject ) => {
+ *						const xhr = new XMLHttpRequest();
+ *
+ *						xhr.open( 'GET', 'https://example.com/cs-token-endpoint' );
+ *
+ *						xhr.addEventListener( 'load', () => {
+ *							const statusCode = xhr.status;
+ *							const xhrResponse = xhr.response;
+ *
+ *							if ( statusCode < 200 || statusCode > 299 ) {
+ *								return reject( new Error( 'Cannot download new token!' ) );
+ *							}
+ *
+ *							return resolve( xhrResponse );
+ *						} );
+ *
+ *						xhr.addEventListener( 'error', () => reject( new Error( 'Network Error' ) ) );
+ *						xhr.addEventListener( 'abort', () => reject( new Error( 'Abort' ) ) );
+ *
+ *						xhr.setRequestHeader( customHeader, customValue );
+ *
+ *						xhr.send();
+ *					} ),
+ *					...
+ *				}
+ *			} )
+ *
  * You can find more information about token endpoints in the
  * {@glink @cs guides/easy-image/quick-start#create-token-endpoint Cloud Services - Quick start}
  * and {@glink @cs guides/token-endpoints/tokenendpoint Cloud Services - Creating token endpoint} documentation.
  *
  * Without a properly working token endpoint (token URL) CKEditor plugins will not be able to connect to CKEditor Cloud Services.
  *
- * @member {String} module:cloud-services/cloudservices~CloudServicesConfig#tokenUrl
+ * @member {String|Function} module:cloud-services/cloudservices~CloudServicesConfig#tokenUrl
  */
 
 /**

+ 11 - 0
packages/ckeditor5-cloud-services/tests/cloudservices.js

@@ -46,6 +46,17 @@ describe( 'CloudServices', () => {
 				} );
 		} );
 
+		it( 'should be able to get by its plugin name', () => {
+			return ClassicTestEditor
+				.create( element, {
+					plugins: [ CloudServices ]
+				} )
+				.then( editor => {
+					const cloudServicesPlugin = editor.plugins.get( 'CloudServices' );
+					expect( cloudServicesPlugin ).to.be.instanceOf( CloudServices );
+				} );
+		} );
+
 		it( 'should not throw an error when no config is provided', () => {
 			return ClassicTestEditor
 				.create( element, {

+ 17 - 0
packages/ckeditor5-cloud-services/tests/manual/token.html

@@ -0,0 +1,17 @@
+<div id="editor">
+	<h2>Token URL callback example</h2>
+</div>
+
+<style>
+	pre {
+		white-space: pre-wrap;
+		white-space: -moz-pre-wrap;
+		white-space: -pre-wrap;
+		white-space: -o-pre-wrap;
+		word-wrap: break-word;
+	}
+</style>
+
+<div id="request"></div>
+
+<pre id="output"></pre>

+ 74 - 0
packages/ckeditor5-cloud-services/tests/manual/token.js

@@ -0,0 +1,74 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals console, window, document, XMLHttpRequest */
+
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
+import CloudServices from '../../src/cloudservices';
+
+import { TOKEN_URL, UPLOAD_URL } from '../_utils/cloud-services-config';
+
+const output = document.getElementById( 'output' );
+const requestOutput = document.getElementById( 'request' );
+
+ClassicEditor
+	.create( document.getElementById( 'editor' ), {
+		cloudServices: {
+			tokenUrl: getToken,
+			uploadUrl: UPLOAD_URL
+		},
+		plugins: [ ArticlePluginSet, CloudServices ],
+		toolbar: [ 'heading', '|', 'undo', 'redo' ]
+	} )
+	.then( editor => {
+		window.editor = editor;
+	} )
+	.catch( err => {
+		output.innerText = err.stack;
+		console.error( err.stack );
+	} );
+
+function handleRequest( xhr, resolve, reject ) {
+	requestOutput.innerHTML = `
+		<div>XHR request: <pre class='xhr-data'></pre></div>
+		<button class="resolve">Resolve with the xhr response</button>
+		<button class="reject">Reject with an error</button>
+	`;
+
+	const xhrSpan = requestOutput.querySelector( '.xhr-data' );
+	const xhrData = {
+		status: xhr.status,
+		response: xhr.response,
+	};
+	xhrSpan.innerText = JSON.stringify( xhrData, null, 2 );
+
+	const resolveButton = requestOutput.querySelector( '.resolve' );
+	resolveButton.addEventListener( 'click', () => resolve( xhr.response ) );
+
+	const rejectButton = requestOutput.querySelector( '.reject' );
+	rejectButton.addEventListener( 'click', () => reject( new Error( 'Cannot download new token!' ) ) );
+}
+
+function getToken() {
+	return new Promise( ( resolve, reject ) => {
+		const xhr = new XMLHttpRequest();
+
+		xhr.open( 'GET', TOKEN_URL );
+
+		xhr.addEventListener( 'load', () => {
+			handleRequest( xhr, resolve, reject );
+		} );
+
+		xhr.addEventListener( 'error', () => reject( new Error( 'Network Error' ) ) );
+		xhr.addEventListener( 'abort', () => reject( new Error( 'Abort' ) ) );
+
+		xhr.send();
+	} ).then( response => {
+		output.innerText = `Response: ${ response }`;
+
+		return response;
+	} );
+}

+ 22 - 0
packages/ckeditor5-cloud-services/tests/manual/token.md

@@ -0,0 +1,22 @@
+## Token sample
+
+### Scenario 1
+
+**Actions**
+1. Click `Resolve` button to resolve the pending promise with the XHR response.
+
+**Expected result**
+* Editor isn't initialized at the start of the scenario.
+* After clicking the button the editor initializes with no error in the console.
+* The token used in the editor should be visible in the output.
+
+### Scenario 2
+
+**Actions**
+1. Restart the page with <kbd>CMD</kbd> + <kbd>R</kbd>
+1. Click `Reject` button to reject the pending promise with an error.
+
+**Expected result**
+* Editor isn't initialized at the start of the scenario.
+* The token is different than the previous token.
+* After clicking the button the editor doesn't initialize and there is an error in the output - `Error: Cannot download new token!`.