Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions packages/admin-ui/src/lib/core/src/data/server-config.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Injector } from '@angular/core';
import { DEFAULT_CHANNEL_CODE } from '@vendure/common/lib/shared-constants';
import { of, throwError } from 'rxjs';

import { LocalStorageService } from '../providers/local-storage/local-storage.service';

import { GET_CURRENT_USER } from './definitions/auth-definitions';
import { GET_SERVER_CONFIG } from './definitions/settings-definitions';
import { BaseDataService } from './providers/base-data.service';
import { ServerConfigService } from './server-config';

describe('ServerConfigService', () => {
const serverConfigResult = {
globalSettings: {
serverConfig: {
entityCustomFields: [],
},
},
};

function createService(
query: jasmine.Spy,
storedValues: Record<string, any> = {},
): {
service: ServerConfigService;
localStorageService: jasmine.SpyObj<LocalStorageService>;
} {
const localStorageService = jasmine.createSpyObj<LocalStorageService>('LocalStorageService', [
'get',
'set',
]);
localStorageService.get.and.callFake((key: string) => storedValues[key] ?? null);
localStorageService.set.and.callFake((key: string, value: any) => {
storedValues[key] = value;
});

const injector = jasmine.createSpyObj<Injector>('Injector', ['get']);
injector.get.withArgs(BaseDataService).and.returnValue({ query });

return {
service: new ServerConfigService(injector, localStorageService),
localStorageService,
};
}

it('sets the active channel token before fetching the server config', async () => {
const query = jasmine.createSpy('query').and.callFake(document => {
if (document === GET_CURRENT_USER) {
return {
single$: of({
me: {
channels: [
{ code: 'secondary', token: 'secondary-token' },
{ code: DEFAULT_CHANNEL_CODE, token: 'default-token' },
],
},
}),
};
}
if (document === GET_SERVER_CONFIG) {
return { single$: of(serverConfigResult) };
}
});
const { service, localStorageService } = createService(query);

await service.getServerConfig();

expect(query.calls.allArgs().map(args => args[0])).toEqual([GET_CURRENT_USER, GET_SERVER_CONFIG]);
expect(localStorageService.set).toHaveBeenCalledWith('activeChannelToken', 'default-token');
});

it('does not fetch the current user when an active channel token already exists', async () => {
const query = jasmine.createSpy('query').and.returnValue({ single$: of(serverConfigResult) });
const { service } = createService(query, { activeChannelToken: 'stored-token' });

await service.getServerConfig();

expect(query.calls.allArgs().map(args => args[0])).toEqual([GET_SERVER_CONFIG]);
});

it('still fetches the server config when current user lookup fails', async () => {
const query = jasmine.createSpy('query').and.callFake(document => {
if (document === GET_CURRENT_USER) {
return { single$: throwError(() => new Error('Not authenticated')) };
}
if (document === GET_SERVER_CONFIG) {
return { single$: of(serverConfigResult) };
}
});
const { service } = createService(query);

await service.getServerConfig();

expect(query.calls.allArgs().map(args => args[0])).toEqual([GET_CURRENT_USER, GET_SERVER_CONFIG]);
});
});
62 changes: 52 additions & 10 deletions packages/admin-ui/src/lib/core/src/data/server-config.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { Injectable, Injector } from '@angular/core';
import { DEFAULT_CHANNEL_CODE } from '@vendure/common/lib/shared-constants';
import { lastValueFrom } from 'rxjs';

import {
CurrentUserFragment,
CustomFieldConfig,
CustomFields,
GetCurrentUserQuery,
GetGlobalSettingsQuery,
GetServerConfigQuery,
OrderProcessState,
PermissionDefinition,
} from '../common/generated-types';
import { LocalStorageService } from '../providers/local-storage/local-storage.service';

import { GET_CURRENT_USER } from './definitions/auth-definitions';
import { GET_GLOBAL_SETTINGS, GET_SERVER_CONFIG } from './definitions/settings-definitions';
import { BaseDataService } from './providers/base-data.service';

Expand All @@ -29,7 +34,10 @@ export class ServerConfigService {
return this.injector.get<BaseDataService>(BaseDataService);
}

constructor(private injector: Injector) {}
constructor(
private injector: Injector,
private localStorageService: LocalStorageService,
) {}

/**
* Fetches the ServerConfig. Should be run as part of the app bootstrap process by attaching
Expand All @@ -43,21 +51,55 @@ export class ServerConfigService {
* Fetch the ServerConfig. Should be run on app init (in case user is already logged in) and on successful login.
*/
getServerConfig() {
return lastValueFrom(
this.baseDataService.query<GetServerConfigQuery>(GET_SERVER_CONFIG).single$,
).then(
result => {
this._serverConfig = result.globalSettings.serverConfig;
for (const entityCustomFields of this._serverConfig.entityCustomFields) {
this.customFieldsMap.set(entityCustomFields.entityName, entityCustomFields.customFields);
return this.ensureActiveChannelToken().then(() =>
lastValueFrom(this.baseDataService.query<GetServerConfigQuery>(GET_SERVER_CONFIG).single$).then(
result => {
this._serverConfig = result.globalSettings.serverConfig;
for (const entityCustomFields of this._serverConfig.entityCustomFields) {
this.customFieldsMap.set(
entityCustomFields.entityName,
entityCustomFields.customFields,
);
}
},
err => {
// Let the error fall through to be caught by the http interceptor.
},
),
);
}

private ensureActiveChannelToken(): Promise<void> {
if (this.localStorageService.get('activeChannelToken')) {
return Promise.resolve();
}
return lastValueFrom(this.baseDataService.query<GetCurrentUserQuery>(GET_CURRENT_USER).single$).then(
({ me }) => {
if (me?.channels.length) {
this.localStorageService.set(
'activeChannelToken',
this.getActiveChannel(me.channels).token,
);
}
},
err => {
// Let the error fall through to be caught by the http interceptor.
() => {
// Ignore unauthenticated app initialization; the main server config request handles errors.
},
);
}

private getActiveChannel(userChannels: CurrentUserFragment['channels']) {
const lastActiveChannelToken = this.localStorageService.get('activeChannelToken');
if (lastActiveChannelToken) {
const lastActiveChannel = userChannels.find(c => c.token === lastActiveChannelToken);
if (lastActiveChannel) {
return lastActiveChannel;
}
}
const defaultChannel = userChannels.find(c => c.code === DEFAULT_CHANNEL_CODE);
return defaultChannel || userChannels[0];
}

getAvailableLanguages() {
return this.baseDataService
.query<GetGlobalSettingsQuery>(GET_GLOBAL_SETTINGS, {}, 'cache-first')
Expand Down
Loading