feature(home-plugin): Adds user scope to storage calls
This patch adds the userEntityRef to the storage key ensuring they are scoped per user. Signed-off-by: Renan Mendes Carvalho <aitherios@gmail.com>
This commit is contained in:
committed by
Camila Belo
parent
c485469098
commit
6cfcf8a4a7
@@ -14,6 +14,7 @@ import { CardSettings as CardSettings_2 } from '@backstage/plugin-home-react';
|
||||
import { ComponentParts as ComponentParts_2 } from '@backstage/plugin-home-react';
|
||||
import { ComponentRenderer as ComponentRenderer_2 } from '@backstage/plugin-home-react';
|
||||
import { createCardExtension as createCardExtension_2 } from '@backstage/plugin-home-react';
|
||||
import { IdentityApi } from '@backstage/core-plugin-api';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { default as React_2 } from 'react';
|
||||
import { ReactElement } from 'react';
|
||||
@@ -79,12 +80,14 @@ export const ComponentTabs: (props: {
|
||||
export class CoreStorageVisitsApi extends VisitsApiFactory {
|
||||
constructor({
|
||||
storageApi,
|
||||
identityApi,
|
||||
randomUUID,
|
||||
limit,
|
||||
}: {
|
||||
storageApi: StorageApi;
|
||||
randomUUID?: Window['crypto']['randomUUID'];
|
||||
limit?: number;
|
||||
identityApi: IdentityApi;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -181,10 +184,12 @@ export class LocalStorageVisitsApi extends VisitsApiFactory {
|
||||
localStorage,
|
||||
randomUUID,
|
||||
limit,
|
||||
}?: {
|
||||
identityApi,
|
||||
}: {
|
||||
localStorage?: Window['localStorage'];
|
||||
randomUUID?: Window['crypto']['randomUUID'];
|
||||
limit?: number;
|
||||
identityApi: IdentityApi;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BackstageUserIdentity, IdentityApi } from '@backstage/core-plugin-api';
|
||||
import { CoreStorageVisitsApi } from './CoreStorageVisitsApi';
|
||||
import { MockStorageApi } from '@backstage/test-utils';
|
||||
|
||||
@@ -24,6 +25,14 @@ describe('new CoreStorageVisitsApi({ storageApi: MockStorageApi.create() })', ()
|
||||
() => Math.floor(Math.random() * 16).toString(16), // 0x0 to 0xf
|
||||
) as `${string}-${string}-${string}-${string}-${string}`;
|
||||
|
||||
const mockIdentityApi: IdentityApi = {
|
||||
signOut: jest.fn(),
|
||||
getProfileInfo: jest.fn(),
|
||||
getBackstageIdentity: async () =>
|
||||
({ userEntityRef: 'user:default/guest' } as BackstageUserIdentity),
|
||||
getCredentials: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
window.crypto.randomUUID = mockRandomUUID;
|
||||
});
|
||||
@@ -32,9 +41,10 @@ describe('new CoreStorageVisitsApi({ storageApi: MockStorageApi.create() })', ()
|
||||
window.localStorage.clear();
|
||||
});
|
||||
|
||||
it('instantiates with no configuration', () => {
|
||||
it('instantiates', () => {
|
||||
const api = new CoreStorageVisitsApi({
|
||||
storageApi: MockStorageApi.create(),
|
||||
identityApi: mockIdentityApi,
|
||||
});
|
||||
expect(api).toBeTruthy();
|
||||
});
|
||||
@@ -42,6 +52,7 @@ describe('new CoreStorageVisitsApi({ storageApi: MockStorageApi.create() })', ()
|
||||
it('saves a visit', async () => {
|
||||
const api = new CoreStorageVisitsApi({
|
||||
storageApi: MockStorageApi.create(),
|
||||
identityApi: mockIdentityApi,
|
||||
});
|
||||
const visit = {
|
||||
pathname: '/catalog/default/component/playback-order',
|
||||
@@ -58,6 +69,7 @@ describe('new CoreStorageVisitsApi({ storageApi: MockStorageApi.create() })', ()
|
||||
it('retrieves visits', async () => {
|
||||
const api = new CoreStorageVisitsApi({
|
||||
storageApi: MockStorageApi.create(),
|
||||
identityApi: mockIdentityApi,
|
||||
});
|
||||
const visit = {
|
||||
pathname: '/catalog/default/component/playback-order',
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { StorageApi } from '@backstage/core-plugin-api';
|
||||
import { IdentityApi, StorageApi } from '@backstage/core-plugin-api';
|
||||
import { Visit } from './VisitsApi';
|
||||
import { VisitsApiFactory } from './VisitsApiFactory';
|
||||
|
||||
@@ -23,30 +23,40 @@ import { VisitsApiFactory } from './VisitsApiFactory';
|
||||
*/
|
||||
export class CoreStorageVisitsApi extends VisitsApiFactory {
|
||||
private readonly storageApi: StorageApi;
|
||||
private readonly storageKey = '@backstage/plugin-home:visits';
|
||||
private readonly storageKeyPrefix = '@backstage/plugin-home:visits';
|
||||
private readonly identityApi: IdentityApi;
|
||||
|
||||
constructor({
|
||||
storageApi,
|
||||
identityApi,
|
||||
randomUUID = window?.crypto?.randomUUID,
|
||||
limit = 100,
|
||||
}: {
|
||||
storageApi: StorageApi;
|
||||
randomUUID?: Window['crypto']['randomUUID'];
|
||||
limit?: number;
|
||||
identityApi: IdentityApi;
|
||||
}) {
|
||||
super({ randomUUID, limit });
|
||||
this.storageApi = storageApi;
|
||||
this.identityApi = identityApi;
|
||||
this.retrieveAll = async (): Promise<Array<Visit>> => {
|
||||
let visits: Array<Visit>;
|
||||
const { userEntityRef } = await this.identityApi.getBackstageIdentity();
|
||||
const storageKey = `${this.storageKeyPrefix}:${userEntityRef}`;
|
||||
|
||||
try {
|
||||
visits =
|
||||
this.storageApi.snapshot<Array<Visit>>(this.storageKey).value ?? [];
|
||||
visits = this.storageApi.snapshot<Array<Visit>>(storageKey).value ?? [];
|
||||
} catch {
|
||||
visits = [];
|
||||
}
|
||||
return visits;
|
||||
};
|
||||
this.persistAll = async (visits: Array<Visit>) =>
|
||||
this.storageApi.set<Array<Visit>>(this.storageKey, visits);
|
||||
this.persistAll = async (visits: Array<Visit>) => {
|
||||
const { userEntityRef } = await this.identityApi.getBackstageIdentity();
|
||||
const storageKey = `${this.storageKeyPrefix}:${userEntityRef}`;
|
||||
|
||||
return this.storageApi.set<Array<Visit>>(storageKey, visits);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BackstageUserIdentity, IdentityApi } from '@backstage/core-plugin-api';
|
||||
import { LocalStorageVisitsApi } from './LocalStorageVisitsApi';
|
||||
|
||||
describe('new LocalStorageVisitsApi()', () => {
|
||||
@@ -23,21 +24,30 @@ describe('new LocalStorageVisitsApi()', () => {
|
||||
() => Math.floor(Math.random() * 16).toString(16), // 0x0 to 0xf
|
||||
) as `${string}-${string}-${string}-${string}-${string}`;
|
||||
|
||||
const mockIdentityApi: IdentityApi = {
|
||||
signOut: jest.fn(),
|
||||
getProfileInfo: jest.fn(),
|
||||
getBackstageIdentity: async () =>
|
||||
({ userEntityRef: 'user:default/guest' } as BackstageUserIdentity),
|
||||
getCredentials: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
window.crypto.randomUUID = mockRandomUUID;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.localStorage.clear();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('instantiates with no configuration', () => {
|
||||
const api = new LocalStorageVisitsApi();
|
||||
it('instantiates with only identitiyApi', () => {
|
||||
const api = new LocalStorageVisitsApi({ identityApi: mockIdentityApi });
|
||||
expect(api).toBeTruthy();
|
||||
});
|
||||
|
||||
it('saves a visit', async () => {
|
||||
const api = new LocalStorageVisitsApi();
|
||||
const api = new LocalStorageVisitsApi({ identityApi: mockIdentityApi });
|
||||
const visit = {
|
||||
pathname: '/catalog/default/component/playback-order',
|
||||
entityRef: 'component:default/playback-order',
|
||||
@@ -51,7 +61,7 @@ describe('new LocalStorageVisitsApi()', () => {
|
||||
});
|
||||
|
||||
it('retrieves visits', async () => {
|
||||
const api = new LocalStorageVisitsApi();
|
||||
const api = new LocalStorageVisitsApi({ identityApi: mockIdentityApi });
|
||||
const visit = {
|
||||
pathname: '/catalog/default/component/playback-order',
|
||||
entityRef: 'component:default/playback-order',
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { IdentityApi } from '@backstage/core-plugin-api';
|
||||
import { Visit } from './VisitsApi';
|
||||
import { VisitsApiFactory } from './VisitsApiFactory';
|
||||
|
||||
@@ -22,31 +23,41 @@ import { VisitsApiFactory } from './VisitsApiFactory';
|
||||
*/
|
||||
export class LocalStorageVisitsApi extends VisitsApiFactory {
|
||||
private readonly localStorage: Window['localStorage'];
|
||||
private readonly storageKey = '@backstage/plugin-home:visits';
|
||||
private readonly storageKeyPrefix = '@backstage/plugin-home:visits';
|
||||
private readonly identityApi: IdentityApi;
|
||||
|
||||
constructor({
|
||||
localStorage = window?.localStorage,
|
||||
randomUUID = window?.crypto?.randomUUID,
|
||||
limit = 100,
|
||||
identityApi,
|
||||
}: {
|
||||
localStorage?: Window['localStorage'];
|
||||
randomUUID?: Window['crypto']['randomUUID'];
|
||||
limit?: number;
|
||||
} = {}) {
|
||||
identityApi: IdentityApi;
|
||||
}) {
|
||||
super({ randomUUID, limit });
|
||||
this.localStorage = localStorage;
|
||||
this.identityApi = identityApi;
|
||||
this.retrieveAll = async (): Promise<Array<Visit>> => {
|
||||
let visits: Array<Visit>;
|
||||
const { userEntityRef } = await this.identityApi.getBackstageIdentity();
|
||||
const storageKey = `${this.storageKeyPrefix}:${userEntityRef}`;
|
||||
|
||||
try {
|
||||
visits = JSON.parse(this.localStorage.getItem(this.storageKey) ?? '[]');
|
||||
visits = JSON.parse(this.localStorage.getItem(storageKey) ?? '[]');
|
||||
} catch {
|
||||
visits = [];
|
||||
}
|
||||
return visits;
|
||||
};
|
||||
this.persistAll = async (visits: Array<Visit>) => {
|
||||
this.localStorage.setItem(
|
||||
this.storageKey,
|
||||
const { userEntityRef } = await this.identityApi.getBackstageIdentity();
|
||||
const storageKey = `${this.storageKeyPrefix}:${userEntityRef}`;
|
||||
|
||||
return this.localStorage.setItem(
|
||||
storageKey,
|
||||
JSON.stringify(visits.splice(0, this.limit)),
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user