Browse Source

refactor(admin-ui): Keep active channels in local client state

Michael Bromley 6 years ago
parent
commit
ed9c09f280

+ 4 - 2
packages/admin-ui/src/app/catalog/components/collection-tree/collection-tree-node.component.ts

@@ -4,6 +4,8 @@ import { DataService } from '@vendure/admin-ui/src/app/data/providers/data.servi
 import { Observable } from 'rxjs';
 import { map, shareReplay } from 'rxjs/operators';
 
+import { Permission } from '../../../common/generated-types';
+
 import { RootNode, TreeNode } from './array-to-tree';
 import { CollectionPartial, CollectionTreeComponent } from './collection-tree.component';
 
@@ -38,8 +40,8 @@ export class CollectionTreeNodeComponent implements OnInit {
             .userStatus()
             .mapStream(data => data.userStatus.permissions)
             .pipe(shareReplay(1));
-        this.hasUpdatePermission$ = permissions$.pipe(map(perms => perms.includes('UpdateCatalog')));
-        this.hasDeletePermission$ = permissions$.pipe(map(perms => perms.includes('DeleteCatalog')));
+        this.hasUpdatePermission$ = permissions$.pipe(map(perms => perms.includes(Permission.UpdateCatalog)));
+        this.hasDeletePermission$ = permissions$.pipe(map(perms => perms.includes(Permission.DeleteCatalog)));
     }
 
     trackByFn(index: number, item: CollectionPartial) {

+ 35 - 14
packages/admin-ui/src/app/common/generated-types.ts

@@ -534,6 +534,7 @@ export type CreatePromotionInput = {
 };
 
 export type CreateRoleInput = {
+  channelIds?: Maybe<Array<Scalars['ID']>>,
   code: Scalars['String'],
   description: Scalars['String'],
   permissions: Array<Permission>,
@@ -895,6 +896,14 @@ export type CurrentUser = {
 
 export type CurrentUserChannel = {
   __typename?: 'CurrentUserChannel',
+  id: Scalars['ID'],
+  token: Scalars['String'],
+  code: Scalars['String'],
+  permissions: Array<Permission>,
+};
+
+export type CurrentUserChannelInput = {
+  id: Scalars['ID'],
   token: Scalars['String'],
   code: Scalars['String'],
   permissions: Array<Permission>,
@@ -2150,9 +2159,7 @@ export type MutationRemoveMembersFromZoneArgs = {
 
 
 export type MutationSetAsLoggedInArgs = {
-  username: Scalars['String'],
-  loginTime: Scalars['String'],
-  permissions: Array<Scalars['String']>
+  input: UserStatusInput
 };
 
 
@@ -3491,7 +3498,16 @@ export type UserStatus = {
   username: Scalars['String'],
   isLoggedIn: Scalars['Boolean'],
   loginTime: Scalars['String'],
-  permissions: Array<Scalars['String']>,
+  permissions: Array<Permission>,
+  activeChannelId?: Maybe<Scalars['ID']>,
+  channels: Array<CurrentUserChannel>,
+};
+
+export type UserStatusInput = {
+  username: Scalars['String'],
+  loginTime: Scalars['String'],
+  activeChannelId: Scalars['ID'],
+  channels: Array<CurrentUserChannelInput>,
 };
 
 export type Zone = Node & {
@@ -3570,7 +3586,7 @@ export type AssignRoleToAdministratorMutationVariables = {
 
 export type AssignRoleToAdministratorMutation = ({ __typename?: 'Mutation' } & { assignRoleToAdministrator: ({ __typename?: 'Administrator' } & AdministratorFragment) });
 
-export type CurrentUserFragment = ({ __typename?: 'CurrentUser' } & Pick<CurrentUser, 'id' | 'identifier'> & { channels: Array<({ __typename?: 'CurrentUserChannel' } & Pick<CurrentUserChannel, 'code' | 'token' | 'permissions'>)> });
+export type CurrentUserFragment = ({ __typename?: 'CurrentUser' } & Pick<CurrentUser, 'id' | 'identifier'> & { channels: Array<({ __typename?: 'CurrentUserChannel' } & Pick<CurrentUserChannel, 'id' | 'code' | 'token' | 'permissions'>)> });
 
 export type AttemptLoginMutationVariables = {
   username: Scalars['String'],
@@ -3601,19 +3617,19 @@ export type RequestCompletedMutationVariables = {};
 
 export type RequestCompletedMutation = ({ __typename?: 'Mutation' } & Pick<Mutation, 'requestCompleted'>);
 
+export type UserStatusFragment = ({ __typename?: 'UserStatus' } & Pick<UserStatus, 'username' | 'isLoggedIn' | 'loginTime' | 'activeChannelId' | 'permissions'> & { channels: Array<({ __typename?: 'CurrentUserChannel' } & Pick<CurrentUserChannel, 'id' | 'code' | 'token' | 'permissions'>)> });
+
 export type SetAsLoggedInMutationVariables = {
-  username: Scalars['String'],
-  loginTime: Scalars['String'],
-  permissions: Array<Scalars['String']>
+  input: UserStatusInput
 };
 
 
-export type SetAsLoggedInMutation = ({ __typename?: 'Mutation' } & { setAsLoggedIn: ({ __typename?: 'UserStatus' } & Pick<UserStatus, 'username' | 'isLoggedIn' | 'loginTime' | 'permissions'>) });
+export type SetAsLoggedInMutation = ({ __typename?: 'Mutation' } & { setAsLoggedIn: ({ __typename?: 'UserStatus' } & UserStatusFragment) });
 
 export type SetAsLoggedOutMutationVariables = {};
 
 
-export type SetAsLoggedOutMutation = ({ __typename?: 'Mutation' } & { setAsLoggedOut: ({ __typename?: 'UserStatus' } & Pick<UserStatus, 'username' | 'isLoggedIn' | 'loginTime' | 'permissions'>) });
+export type SetAsLoggedOutMutation = ({ __typename?: 'Mutation' } & { setAsLoggedOut: ({ __typename?: 'UserStatus' } & UserStatusFragment) });
 
 export type SetUiLanguageMutationVariables = {
   languageCode: LanguageCode
@@ -3630,7 +3646,7 @@ export type GetNetworkStatusQuery = ({ __typename?: 'Query' } & { networkStatus:
 export type GetUserStatusQueryVariables = {};
 
 
-export type GetUserStatusQuery = ({ __typename?: 'Query' } & { userStatus: ({ __typename?: 'UserStatus' } & Pick<UserStatus, 'username' | 'isLoggedIn' | 'loginTime' | 'permissions'>) });
+export type GetUserStatusQuery = ({ __typename?: 'Query' } & { userStatus: ({ __typename?: 'UserStatus' } & UserStatusFragment) });
 
 export type GetUiStateQueryVariables = {};
 
@@ -4493,16 +4509,21 @@ export namespace RequestCompleted {
   export type Mutation = RequestCompletedMutation;
 }
 
+export namespace UserStatus {
+  export type Fragment = UserStatusFragment;
+  export type Channels = (NonNullable<UserStatusFragment['channels'][0]>);
+}
+
 export namespace SetAsLoggedIn {
   export type Variables = SetAsLoggedInMutationVariables;
   export type Mutation = SetAsLoggedInMutation;
-  export type SetAsLoggedIn = SetAsLoggedInMutation['setAsLoggedIn'];
+  export type SetAsLoggedIn = UserStatusFragment;
 }
 
 export namespace SetAsLoggedOut {
   export type Variables = SetAsLoggedOutMutationVariables;
   export type Mutation = SetAsLoggedOutMutation;
-  export type SetAsLoggedOut = SetAsLoggedOutMutation['setAsLoggedOut'];
+  export type SetAsLoggedOut = UserStatusFragment;
 }
 
 export namespace SetUiLanguage {
@@ -4519,7 +4540,7 @@ export namespace GetNetworkStatus {
 export namespace GetUserStatus {
   export type Variables = GetUserStatusQueryVariables;
   export type Query = GetUserStatusQuery;
-  export type UserStatus = GetUserStatusQuery['userStatus'];
+  export type UserStatus = UserStatusFragment;
 }
 
 export namespace GetUiState {

+ 5 - 6
packages/admin-ui/src/app/core/providers/auth/auth.service.ts

@@ -30,8 +30,8 @@ export class AuthService {
                 return this.serverConfigService.getServerConfig().then(() => response.login.user);
             }),
             switchMap(user => {
-                const { permissions } = this.getActiveChannel(user.channels);
-                return this.dataService.client.loginSuccess(username, permissions);
+                const { id } = this.getActiveChannel(user.channels);
+                return this.dataService.client.loginSuccess(username, id, user.channels);
             }),
         );
     }
@@ -75,14 +75,14 @@ export class AuthService {
      * that token against the API.
      */
     validateAuthToken(): Observable<boolean> {
-        return this.dataService.auth.checkLoggedIn().single$.pipe(
+        return this.dataService.auth.currentUser().single$.pipe(
             mergeMap(result => {
                 if (!result.me) {
                     return of(false) as any;
                 }
                 this.setChannelToken(result.me.channels);
-                const { permissions } = this.getActiveChannel(result.me.channels);
-                return this.dataService.client.loginSuccess(result.me.identifier, permissions);
+                const { id } = this.getActiveChannel(result.me.channels);
+                return this.dataService.client.loginSuccess(result.me.identifier, id, result.me.channels);
             }),
             mapTo(true),
             catchError(err => of(false)),
@@ -95,7 +95,6 @@ export class AuthService {
     }
 
     private setChannelToken(userChannels: CurrentUserFragment['channels']) {
-        const defaultChannel = userChannels.find(c => c.code === DEFAULT_CHANNEL_CODE);
         this.localStorageService.set('activeChannelToken', this.getActiveChannel(userChannels).token);
     }
 }

+ 2 - 0
packages/admin-ui/src/app/data/client-state/client-defaults.ts

@@ -10,7 +10,9 @@ export const clientDefaults = {
         username: '',
         isLoggedIn: false,
         loginTime: '',
+        activeChannelId: null,
         permissions: [],
+        channels: [],
         __typename: 'UserStatus',
     } as GetUserStatus.UserStatus,
     uiState: {

+ 11 - 2
packages/admin-ui/src/app/data/client-state/client-resolvers.ts

@@ -8,6 +8,7 @@ import {
     LanguageCode,
     SetAsLoggedIn,
     SetUiLanguage,
+    UserStatus,
 } from '../../common/generated-types';
 import { GET_NEWTORK_STATUS } from '../definitions/client-definitions';
 
@@ -32,14 +33,20 @@ export const clientResolvers: ResolverDefinition = {
             return updateRequestsInFlight(cache, -1);
         },
         setAsLoggedIn: (_, args: SetAsLoggedIn.Variables, { cache }): GetUserStatus.UserStatus => {
-            const { username, loginTime, permissions } = args;
-            const data: GetUserStatus.Query = {
+            const {
+                input: { username, loginTime, channels, activeChannelId },
+            } = args;
+            // tslint:disable-next-line:no-non-null-assertion
+            const permissions = channels.find(c => c.id === activeChannelId)!.permissions;
+            const data: { userStatus: UserStatus } = {
                 userStatus: {
                     __typename: 'UserStatus',
                     username,
                     loginTime,
                     isLoggedIn: true,
                     permissions,
+                    channels,
+                    activeChannelId,
                 },
             };
             cache.writeData({ data });
@@ -53,6 +60,8 @@ export const clientResolvers: ResolverDefinition = {
                     loginTime: '',
                     isLoggedIn: false,
                     permissions: [],
+                    channels: [],
+                    activeChannelId: null,
                 },
             };
             cache.writeData({ data });

+ 18 - 2
packages/admin-ui/src/app/data/client-state/client-types.graphql

@@ -7,7 +7,7 @@ type Query {
 type Mutation {
     requestStarted: Int!
     requestCompleted: Int!
-    setAsLoggedIn(username: String!, loginTime: String!, permissions: [String!]!): UserStatus!
+    setAsLoggedIn(input: UserStatusInput!): UserStatus!
     setAsLoggedOut: UserStatus!
     setUiLanguage(languageCode: LanguageCode): LanguageCode
 }
@@ -20,9 +20,25 @@ type UserStatus {
     username: String!
     isLoggedIn: Boolean!
     loginTime: String!
-    permissions: [String!]!
+    permissions: [Permission!]!
+    activeChannelId: ID
+    channels: [CurrentUserChannel!]!
 }
 
 type UiState {
     language: LanguageCode!
 }
+
+input CurrentUserChannelInput {
+    id: ID!
+    token: String!
+    code: String!
+    permissions: [Permission!]!
+}
+
+input UserStatusInput {
+    username: String!
+    loginTime: String!
+    activeChannelId: ID!
+    channels: [CurrentUserChannelInput!]!
+}

+ 1 - 0
packages/admin-ui/src/app/data/definitions/auth-definitions.ts

@@ -5,6 +5,7 @@ export const CURRENT_USER_FRAGMENT = gql`
         id
         identifier
         channels {
+            id
             code
             token
             permissions

+ 24 - 14
packages/admin-ui/src/app/data/definitions/client-definitions.ts

@@ -12,26 +12,38 @@ export const REQUEST_COMPLETED = gql`
     }
 `;
 
-export const SET_AS_LOGGED_IN = gql`
-    mutation SetAsLoggedIn($username: String!, $loginTime: String!, $permissions: [String!]!) {
-        setAsLoggedIn(username: $username, loginTime: $loginTime, permissions: $permissions) @client {
-            username
-            isLoggedIn
-            loginTime
+export const USER_STATUS_FRAGMENT = gql`
+    fragment UserStatus on UserStatus {
+        username
+        isLoggedIn
+        loginTime
+        activeChannelId
+        permissions
+        channels {
+            id
+            code
+            token
             permissions
         }
     }
 `;
 
+export const SET_AS_LOGGED_IN = gql`
+    mutation SetAsLoggedIn($input: UserStatusInput!) {
+        setAsLoggedIn(input: $input) @client {
+            ...UserStatus
+        }
+    }
+    ${USER_STATUS_FRAGMENT}
+`;
+
 export const SET_AS_LOGGED_OUT = gql`
     mutation SetAsLoggedOut {
         setAsLoggedOut @client {
-            username
-            isLoggedIn
-            loginTime
-            permissions
+            ...UserStatus
         }
     }
+    ${USER_STATUS_FRAGMENT}
 `;
 
 export const SET_UI_LANGUAGE = gql`
@@ -51,12 +63,10 @@ export const GET_NEWTORK_STATUS = gql`
 export const GET_USER_STATUS = gql`
     query GetUserStatus {
         userStatus @client {
-            username
-            isLoggedIn
-            loginTime
-            permissions
+            ...UserStatus
         }
     }
+    ${USER_STATUS_FRAGMENT}
 `;
 
 export const GET_UI_STATE = gql`

+ 1 - 1
packages/admin-ui/src/app/data/providers/auth-data.service.ts

@@ -6,7 +6,7 @@ import { BaseDataService } from './base-data.service';
 export class AuthDataService {
     constructor(private baseDataService: BaseDataService) {}
 
-    checkLoggedIn() {
+    currentUser() {
         return this.baseDataService.query<GetCurrentUser.Query>(GET_CURRENT_USER);
     }
 

+ 8 - 4
packages/admin-ui/src/app/data/providers/client-data.service.ts

@@ -1,4 +1,5 @@
 import {
+    CurrentUserChannel,
     GetNetworkStatus,
     GetUiState,
     GetUserStatus,
@@ -40,13 +41,16 @@ export class ClientDataService {
         return this.baseDataService.query<GetNetworkStatus.Query>(GET_NEWTORK_STATUS, {}, 'cache-first');
     }
 
-    loginSuccess(username: string, permissions: string[]) {
+    loginSuccess(username: string, activeChannelId: string, channels: CurrentUserChannel[]) {
         return this.baseDataService.mutate<SetAsLoggedIn.Mutation, SetAsLoggedIn.Variables>(
             SET_AS_LOGGED_IN,
             {
-                username,
-                loginTime: Date.now().toString(),
-                permissions,
+                input: {
+                    username,
+                    loginTime: Date.now().toString(),
+                    activeChannelId,
+                    channels,
+                },
             },
         );
     }

+ 1 - 1
packages/admin-ui/src/app/settings/components/admin-detail/admin-detail.component.ts

@@ -55,7 +55,7 @@ export class AdminDetailComponent extends BaseDetailComponent<Administrator> imp
         this.administrator$ = this.entity$;
         this.allRoles$ = this.dataService.administrator.getRoles(99999).mapStream(item => item.roles.items);
         this.dataService.client.userStatus().single$.subscribe(({ userStatus }) => {
-            if (!userStatus.permissions.includes('UpdateAdministrator')) {
+            if (!userStatus.permissions.includes(Permission.UpdateAdministrator)) {
                 const rolesSelect = this.detailForm.get('roles');
                 if (rolesSelect) {
                     rolesSelect.disable();

+ 2 - 2
packages/admin-ui/src/app/settings/components/global-settings/global-settings.component.ts

@@ -4,7 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router';
 import { switchMap } from 'rxjs/operators';
 
 import { BaseDetailComponent } from '../../../common/base-detail.component';
-import { CustomFieldConfig, GlobalSettings, LanguageCode } from '../../../common/generated-types';
+import { CustomFieldConfig, GlobalSettings, LanguageCode, Permission } from '../../../common/generated-types';
 import { _ } from '../../../core/providers/i18n/mark-for-extraction';
 import { NotificationService } from '../../../core/providers/notification/notification.service';
 import { DataService } from '../../../data/providers/data.service';
@@ -44,7 +44,7 @@ export class GlobalSettingsComponent extends BaseDetailComponent<GlobalSettings>
     ngOnInit(): void {
         this.init();
         this.dataService.client.userStatus().single$.subscribe(({ userStatus }) => {
-            if (!userStatus.permissions.includes('UpdateSettings')) {
+            if (!userStatus.permissions.includes(Permission.UpdateSettings)) {
                 const languagesSelect = this.detailForm.get('availableLanguages');
                 if (languagesSelect) {
                     languagesSelect.disable();

+ 5 - 3
packages/admin-ui/src/app/shared/directives/if-permissions.directive.ts

@@ -1,6 +1,8 @@
 import { Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef } from '@angular/core';
 import { DataService } from '@vendure/admin-ui/src/app/data/providers/data.service';
 
+import { Permission } from '../../common/generated-types';
+
 /**
  * Conditionally shows/hides templates based on the current active user having the specified permission.
  * Based on the ngIf source. Also support "else" templates:
@@ -35,7 +37,7 @@ export class IfPermissionsDirective {
     @Input()
     set vdrIfPermissions(permission: string | null) {
         this.permissionToCheck = permission;
-        this._updateView(permission);
+        this._updateView(permission as Permission);
     }
 
     /**
@@ -46,10 +48,10 @@ export class IfPermissionsDirective {
         assertTemplate('vdrIfPermissionsElse', templateRef);
         this._elseTemplateRef = templateRef;
         this._elseViewRef = null; // clear previous view if any.
-        this._updateView(this.permissionToCheck);
+        this._updateView(this.permissionToCheck as Permission);
     }
 
-    private _updateView(permission: string | null) {
+    private _updateView(permission: Permission | null) {
         if (!permission) {
             this.showThen();
             return;

+ 5 - 1
scripts/codegen/client-schema.ts

@@ -4,6 +4,8 @@ import path from 'path';
 
 const CLIENT_SCHEMA_FILE = '../../packages/admin-ui/src/app/data/client-state/client-types.graphql';
 const LANGUAGE_CODE_FILE = '../../packages/core/src/api/schema/common/language-code.graphql';
+const AUTH_TYPE_FILE = '../../packages/core/src/api/schema/type/auth.type.graphql';
+const PERMISSION_TYPE_FILE = '../../packages/core/src/api/schema/common/permission.graphql';
 
 function loadGraphQL(file: string): string {
     const filePath = path.join(__dirname, file);
@@ -21,8 +23,10 @@ function getClientSchema() {
     `;
     const clientSchemaString = loadGraphQL(CLIENT_SCHEMA_FILE);
     const languageCodeString = loadGraphQL(LANGUAGE_CODE_FILE);
+    const authTypeString = loadGraphQL(AUTH_TYPE_FILE);
+    const permissionTypeString = loadGraphQL(PERMISSION_TYPE_FILE);
     const schema = makeExecutableSchema({
-        typeDefs: [clientSchemaString, clientDirective, languageCodeString],
+        typeDefs: [clientSchemaString, clientDirective, languageCodeString, authTypeString, permissionTypeString],
     });
     return schema;
 }