Skip to content

Commit

Permalink
Merge pull request #16694 from ckeditor/ck/lazy-token-in-ckbox-poc
Browse files Browse the repository at this point in the history
Other (ckbox): The plugin no longer slows down the editor startup because it fetches the token in the background instead of during the editor’s initialization. Closes #16760

MINOR BREAKING CHANGE (ckbox): The `CKBoxUtils#getWorkspaceId` and `CKBoxUtils#getToken` methods now return a promise instead of a resolved value.
  • Loading branch information
Mati365 authored Jul 23, 2024
2 parents 498f6f2 + b70d6a3 commit e326f48
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ export default class CKBoxImageEditCommand extends Command {
const response: CKBoxRawAssetDataDefinition = await sendHttpRequest( {
url,
signal,
authorization: ckboxUtils.getToken().value
authorization: ( await ckboxUtils.getToken() ).value
} );
const status = response.metadata!.metadataProcessingStatus;

Expand Down
6 changes: 3 additions & 3 deletions packages/ckeditor5-ckbox/src/ckboxuploadadapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class Adapter implements UploadAdapter {
/**
* CKEditor Cloud Services access token.
*/
public token: InitializedToken;
public token: Promise<InitializedToken>;

/**
* The editor instance.
Expand Down Expand Up @@ -148,7 +148,7 @@ class Adapter implements UploadAdapter {
const uploadUrl = new URL( 'assets', this.serviceOrigin );
const formData = new FormData();

uploadUrl.searchParams.set( 'workspaceId', ckboxUtils.getWorkspaceId() );
uploadUrl.searchParams.set( 'workspaceId', await ckboxUtils.getWorkspaceId() );

formData.append( 'categoryId', category );
formData.append( 'file', file );
Expand All @@ -165,7 +165,7 @@ class Adapter implements UploadAdapter {
}
},
signal: this.controller.signal,
authorization: this.token.value
authorization: ( await this.token ).value
} as const;

return sendHttpRequest( requestConfig )
Expand Down
20 changes: 10 additions & 10 deletions packages/ckeditor5-ckbox/src/ckboxutils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default class CKBoxUtils extends Plugin {
/**
* CKEditor Cloud Services access token.
*/
private _token!: InitializedToken;
private _token!: Promise<InitializedToken>;

/**
* @inheritDoc
Expand All @@ -48,7 +48,7 @@ export default class CKBoxUtils extends Plugin {
/**
* @inheritDoc
*/
public async init(): Promise<void> {
public init(): void {
const editor = this.editor;
const hasConfiguration = !!editor.config.get( 'ckbox' );
const isLibraryLoaded = !!window.CKBox;
Expand Down Expand Up @@ -94,27 +94,27 @@ export default class CKBoxUtils extends Plugin {
}

if ( ckboxTokenUrl == cloudServicesTokenUrl ) {
this._token = cloudServices.token!;
this._token = Promise.resolve( cloudServices.token! );
} else {
this._token = await cloudServices.registerTokenUrl( ckboxTokenUrl );
this._token = cloudServices.registerTokenUrl( ckboxTokenUrl );
}
}

/**
* Returns a token used by the CKBox plugin for communication with the CKBox service.
*/
public getToken(): InitializedToken {
public getToken(): Promise<InitializedToken> {
return this._token;
}

/**
* The ID of workspace to use when uploading an image.
*/
public getWorkspaceId(): string {
public async getWorkspaceId(): Promise<string> {
const t = this.editor.t;
const cannotAccessDefaultWorkspaceError = t( 'Cannot access default workspace.' );
const defaultWorkspaceId = this.editor.config.get( 'ckbox.defaultUploadWorkspaceId' );
const workspaceId = getWorkspaceId( this._token, defaultWorkspaceId );
const workspaceId = getWorkspaceId( await this._token, defaultWorkspaceId );

if ( workspaceId == null ) {
/**
Expand Down Expand Up @@ -192,7 +192,7 @@ export default class CKBoxUtils extends Plugin {
const token = this._token;
const { signal } = options;
const serviceOrigin = editor.config.get( 'ckbox.serviceOrigin' )!;
const workspaceId = this.getWorkspaceId();
const workspaceId = await this.getWorkspaceId();

try {
const result: Array<AvailableCategory> = [];
Expand Down Expand Up @@ -222,7 +222,7 @@ export default class CKBoxUtils extends Plugin {
return undefined;
}

function fetchCategories( offset: number ): Promise<{ totalCount: number; items: Array<AvailableCategory> }> {
async function fetchCategories( offset: number ): Promise<{ totalCount: number; items: Array<AvailableCategory> }> {
const categoryUrl = new URL( 'categories', serviceOrigin );

categoryUrl.searchParams.set( 'limit', String( ITEMS_PER_REQUEST ) );
Expand All @@ -232,7 +232,7 @@ export default class CKBoxUtils extends Plugin {
return sendHttpRequest( {
url: categoryUrl,
signal,
authorization: token.value
authorization: ( await token ).value
} );
}
}
Expand Down
14 changes: 7 additions & 7 deletions packages/ckeditor5-ckbox/tests/ckboxuploadadapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,7 @@ describe( 'CKBoxUploadAdapter', () => {
for ( const { testName, workspaceId, tokenClaims } of testData ) {
it( testName, async () => {
TokenMock.initialToken = createToken( tokenClaims );
ckboxUtils._token.refreshToken();
( await ckboxUtils._token ).refreshToken();

sinonXHR.respondWith( 'GET', /\/categories/, [
200,
Expand Down Expand Up @@ -1035,9 +1035,9 @@ describe( 'CKBoxUploadAdapter', () => {
} );

describe( 'defaultUploadWorkspaceId is defined', () => {
it( 'should use the default workspace', () => {
it( 'should use the default workspace', async () => {
TokenMock.initialToken = createToken( { auth: { ckbox: { workspaces: [ 'workspace1', 'workspace2' ] } } } );
ckboxUtils._token.refreshToken();
( await ckboxUtils._token ).refreshToken();

sinonXHR.respondWith( 'GET', /\/categories/, [
200,
Expand Down Expand Up @@ -1075,9 +1075,9 @@ describe( 'CKBoxUploadAdapter', () => {
} );
} );

it( 'should use the default workspace when the user is superadmin', () => {
it( 'should use the default workspace when the user is superadmin', async () => {
TokenMock.initialToken = createToken( { auth: { ckbox: { role: 'superadmin' } } } );
ckboxUtils._token.refreshToken();
( await ckboxUtils._token ).refreshToken();

sinonXHR.respondWith( 'GET', /\/categories/, [
200,
Expand Down Expand Up @@ -1115,11 +1115,11 @@ describe( 'CKBoxUploadAdapter', () => {
} );
} );

it( 'should throw an error when default workspace is not listed in the token', () => {
it( 'should throw an error when default workspace is not listed in the token', async () => {
sinon.stub( console, 'error' );

TokenMock.initialToken = createToken( { auth: { ckbox: { workspaces: [ 'workspace1', 'workspace2' ] } } } );
ckboxUtils._token.refreshToken();
( await ckboxUtils._token ).refreshToken();

sinonXHR.respondWith( 'GET', /\/categories/, [
200,
Expand Down
88 changes: 77 additions & 11 deletions packages/ckeditor5-ckbox/tests/ckboxutils.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,62 @@ describe( 'CKBoxUtils', () => {
} );

describe( 'getToken()', () => {
it( 'should return an instance of token', () => {
expect( ckboxUtils.getToken() ).to.be.instanceOf( Token );
it( 'should return an instance of token', async () => {
expect( await ckboxUtils.getToken() ).to.be.instanceOf( Token );
} );
} );

describe( 'init()', () => {
it( 'should not block initialization of plugin while fetching token', async () => {
const defer = createDefer();
const slowToken = createToken( { auth: { ckbox: { workspaces: [ 'workspace1' ] } } } );

await editor.destroy();

class SlowCloudServices extends CloudServices {
async registerTokenUrl() {
await defer.promise;
return slowToken;
}
}

editor = await VirtualTestEditor.create( {
plugins: [
ImageBlockEditing,
ImageInlineEditing,
ImageCaptionEditing,
LinkEditing,
LinkImageEditing,
PictureEditing,
ImageUploadEditing,
ImageUploadProgress,
SlowCloudServices,
CKBoxUploadAdapter,
CKBoxEditing
],
substitutePlugins: [
CloudServicesCoreMock
],
ckbox: {
tokenUrl: 'http://cs.example.com',
serviceOrigin: CKBOX_API_URL
}
} );

ckboxUtils = editor.plugins.get( CKBoxUtils );
expect( ckboxUtils.getToken() ).to.be.instanceOf( Promise );

defer.resolve();
expect( await ckboxUtils.getToken() ).to.be.equal( slowToken );
} );
} );

describe( 'fetching token', () => {
it( 'should create an instance of Token class which is ready to use (specified ckbox.tokenUrl)', () => {
expect( ckboxUtils.getToken() ).to.be.instanceOf( Token );
expect( ckboxUtils.getToken().value ).to.equal( token );
it( 'should create an instance of Token class which is ready to use (specified ckbox.tokenUrl)', async () => {
const resolvedToken = await ckboxUtils.getToken();

expect( resolvedToken ).to.be.instanceOf( Token );
expect( resolvedToken.value ).to.equal( token );
expect( editor.plugins.get( 'CloudServicesCore' ).tokenUrl ).to.equal( 'http://cs.example.com' );
} );

Expand Down Expand Up @@ -105,8 +152,10 @@ describe( 'CKBoxUtils', () => {
} );

const ckboxUtils = editor.plugins.get( CKBoxUtils );
expect( ckboxUtils.getToken() ).to.be.instanceOf( Token );
expect( ckboxUtils.getToken().value ).to.equal( token );
const resolvedToken = await ckboxUtils.getToken();

expect( resolvedToken ).to.be.instanceOf( Token );
expect( resolvedToken.value ).to.equal( token );
expect( editor.plugins.get( 'CloudServicesCore' ).tokenUrl ).to.equal( 'http://cs.example.com' );

editorElement.remove();
Expand Down Expand Up @@ -142,8 +191,10 @@ describe( 'CKBoxUtils', () => {
} );

const ckboxUtils = editor.plugins.get( CKBoxUtils );
expect( ckboxUtils.getToken() ).to.be.instanceOf( Token );
expect( ckboxUtils.getToken().value ).to.equal( token );
const resolvedToken = await ckboxUtils.getToken();

expect( resolvedToken ).to.be.instanceOf( Token );
expect( resolvedToken.value ).to.equal( token );
expect( editor.plugins.get( 'CloudServicesCore' ).tokenUrl ).to.equal( 'http://ckbox.example.com' );

editorElement.remove();
Expand Down Expand Up @@ -179,8 +230,10 @@ describe( 'CKBoxUtils', () => {
} );

const ckboxUtils = editor.plugins.get( CKBoxUtils );
expect( ckboxUtils.getToken() ).to.be.instanceOf( Token );
expect( ckboxUtils.getToken().value ).to.equal( token );
const resolvedToken = await ckboxUtils.getToken();

expect( resolvedToken ).to.be.instanceOf( Token );
expect( resolvedToken.value ).to.equal( token );
expect( editor.plugins.get( 'CloudServicesCore' ).tokenUrl ).to.equal( 'http://example.com' );

editorElement.remove();
Expand Down Expand Up @@ -721,3 +774,16 @@ function createToken( tokenClaims ) {
'signature'
].join( '.' );
}

function createDefer() {
const deferred = {
resolve: ( ) => {},
promise: Promise.resolve( null )
};

deferred.promise = new Promise( resolve => {
deferred.resolve = resolve;
} );

return deferred;
}

0 comments on commit e326f48

Please sign in to comment.