Răsfoiți Sursa

feat(core): Implement `deleteAdministrator` mutation

Relates to #384

BREAKING CHANGE: The Administrator entity has a new `deletedAt` field, which will require a non-destructive database migration.
Michael Bromley 5 ani în urmă
părinte
comite
dc82b2c239

+ 7 - 0
packages/admin-ui/src/lib/core/src/common/generated-types.ts

@@ -1827,6 +1827,8 @@ export type Mutation = {
   createTaxRate: TaxRate;
   /** Create a new Zone */
   createZone: Zone;
+  /** Delete an Administrator */
+  deleteAdministrator: DeletionResponse;
   /** Delete an Asset */
   deleteAsset: DeletionResponse;
   /** Delete a Channel */
@@ -2089,6 +2091,11 @@ export type MutationCreateZoneArgs = {
 };
 
 
+export type MutationDeleteAdministratorArgs = {
+  id: Scalars['ID'];
+};
+
+
 export type MutationDeleteAssetArgs = {
   id: Scalars['ID'];
   force?: Maybe<Scalars['Boolean']>;

+ 6 - 0
packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts

@@ -1771,6 +1771,8 @@ export type Mutation = {
     createAdministrator: Administrator;
     /** Update an existing Administrator */
     updateAdministrator: Administrator;
+    /** Delete an Administrator */
+    deleteAdministrator: DeletionResponse;
     /** Assign a Role to an Administrator */
     assignRoleToAdministrator: Administrator;
     /** Create a new Asset */
@@ -1930,6 +1932,10 @@ export type MutationUpdateAdministratorArgs = {
     input: UpdateAdministratorInput;
 };
 
+export type MutationDeleteAdministratorArgs = {
+    id: Scalars['ID'];
+};
+
 export type MutationAssignRoleToAdministratorArgs = {
     administratorId: Scalars['ID'];
     roleId: Scalars['ID'];

+ 0 - 5
packages/common/src/generated-shop-types.ts

@@ -97,7 +97,6 @@ export enum AssetType {
 
 export type AuthenticationInput = {
     native?: Maybe<NativeAuthInput>;
-    google?: Maybe<GoogleAuthInput>;
 };
 
 export type AuthenticationMethod = Node & {
@@ -896,10 +895,6 @@ export type GlobalSettings = {
     customFields?: Maybe<Scalars['JSON']>;
 };
 
-export type GoogleAuthInput = {
-    token: Scalars['String'];
-};
-
 export type HistoryEntry = Node & {
     __typename?: 'HistoryEntry';
     id: Scalars['ID'];

+ 7 - 0
packages/common/src/generated-types.ts

@@ -1770,6 +1770,8 @@ export type Mutation = {
   createAdministrator: Administrator;
   /** Update an existing Administrator */
   updateAdministrator: Administrator;
+  /** Delete an Administrator */
+  deleteAdministrator: DeletionResponse;
   /** Assign a Role to an Administrator */
   assignRoleToAdministrator: Administrator;
   /** Create a new Asset */
@@ -1932,6 +1934,11 @@ export type MutationUpdateAdministratorArgs = {
 };
 
 
+export type MutationDeleteAdministratorArgs = {
+  id: Scalars['ID'];
+};
+
+
 export type MutationAssignRoleToAdministratorArgs = {
   administratorId: Scalars['ID'];
   roleId: Scalars['ID'];

+ 45 - 0
packages/core/e2e/administrator.e2e-spec.ts

@@ -9,6 +9,8 @@ import { ADMINISTRATOR_FRAGMENT } from './graphql/fragments';
 import {
     Administrator,
     CreateAdministrator,
+    DeleteAdministrator,
+    DeletionResult,
     GetAdministrator,
     GetAdministrators,
     UpdateAdministrator,
@@ -121,6 +123,40 @@ describe('Administrator resolver', () => {
             `No Role with the id '999' could be found`,
         ),
     );
+
+    it('deleteAdministrator', async () => {
+        const { administrators: before } = await adminClient.query<
+            GetAdministrators.Query,
+            GetAdministrators.Variables
+        >(GET_ADMINISTRATORS);
+        expect(before.totalItems).toBe(2);
+
+        const { deleteAdministrator } = await adminClient.query<
+            DeleteAdministrator.Mutation,
+            DeleteAdministrator.Variables
+        >(DELETE_ADMINISTRATOR, {
+            id: createdAdmin.id,
+        });
+
+        expect(deleteAdministrator.result).toBe(DeletionResult.DELETED);
+
+        const { administrators: after } = await adminClient.query<
+            GetAdministrators.Query,
+            GetAdministrators.Variables
+        >(GET_ADMINISTRATORS);
+        expect(after.totalItems).toBe(1);
+    });
+
+    it('cannot query a deleted Administrator', async () => {
+        const { administrator } = await adminClient.query<GetAdministrator.Query, GetAdministrator.Variables>(
+            GET_ADMINISTRATOR,
+            {
+                id: createdAdmin.id,
+            },
+        );
+
+        expect(administrator).toBeNull();
+    });
 });
 
 export const GET_ADMINISTRATORS = gql`
@@ -152,3 +188,12 @@ export const UPDATE_ADMINISTRATOR = gql`
     }
     ${ADMINISTRATOR_FRAGMENT}
 `;
+
+export const DELETE_ADMINISTRATOR = gql`
+    mutation DeleteAdministrator($id: ID!) {
+        deleteAdministrator(id: $id) {
+            message
+            result
+        }
+    }
+`;

+ 20 - 0
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -1771,6 +1771,8 @@ export type Mutation = {
     createAdministrator: Administrator;
     /** Update an existing Administrator */
     updateAdministrator: Administrator;
+    /** Delete an Administrator */
+    deleteAdministrator: DeletionResponse;
     /** Assign a Role to an Administrator */
     assignRoleToAdministrator: Administrator;
     /** Create a new Asset */
@@ -1930,6 +1932,10 @@ export type MutationUpdateAdministratorArgs = {
     input: UpdateAdministratorInput;
 };
 
+export type MutationDeleteAdministratorArgs = {
+    id: Scalars['ID'];
+};
+
 export type MutationAssignRoleToAdministratorArgs = {
     administratorId: Scalars['ID'];
     roleId: Scalars['ID'];
@@ -3653,6 +3659,14 @@ export type UpdateAdministratorMutation = { __typename?: 'Mutation' } & {
     updateAdministrator: { __typename?: 'Administrator' } & AdministratorFragment;
 };
 
+export type DeleteAdministratorMutationVariables = {
+    id: Scalars['ID'];
+};
+
+export type DeleteAdministratorMutation = { __typename?: 'Mutation' } & {
+    deleteAdministrator: { __typename?: 'DeletionResponse' } & Pick<DeletionResponse, 'message' | 'result'>;
+};
+
 export type Q1QueryVariables = {};
 
 export type Q1Query = { __typename?: 'Query' } & {
@@ -5765,6 +5779,12 @@ export namespace UpdateAdministrator {
     export type UpdateAdministrator = AdministratorFragment;
 }
 
+export namespace DeleteAdministrator {
+    export type Variables = DeleteAdministratorMutationVariables;
+    export type Mutation = DeleteAdministratorMutation;
+    export type DeleteAdministrator = DeleteAdministratorMutation['deleteAdministrator'];
+}
+
 export namespace Q1 {
     export type Variables = Q1QueryVariables;
     export type Query = Q1Query;

+ 0 - 5
packages/core/e2e/graphql/generated-e2e-shop-types.ts

@@ -97,7 +97,6 @@ export enum AssetType {
 
 export type AuthenticationInput = {
     native?: Maybe<NativeAuthInput>;
-    google?: Maybe<GoogleAuthInput>;
 };
 
 export type AuthenticationMethod = Node & {
@@ -896,10 +895,6 @@ export type GlobalSettings = {
     customFields?: Maybe<Scalars['JSON']>;
 };
 
-export type GoogleAuthInput = {
-    token: Scalars['String'];
-};
-
 export type HistoryEntry = Node & {
     __typename?: 'HistoryEntry';
     id: Scalars['ID'];

+ 9 - 0
packages/core/src/api/resolvers/admin/administrator.resolver.ts

@@ -1,7 +1,9 @@
 import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
 import {
+    DeletionResponse,
     MutationAssignRoleToAdministratorArgs,
     MutationCreateAdministratorArgs,
+    MutationDeleteAdministratorArgs,
     MutationUpdateAdministratorArgs,
     Permission,
     QueryAdministratorArgs,
@@ -48,4 +50,11 @@ export class AdministratorResolver {
     assignRoleToAdministrator(@Args() args: MutationAssignRoleToAdministratorArgs): Promise<Administrator> {
         return this.administratorService.assignRole(args.administratorId, args.roleId);
     }
+
+    @Mutation()
+    @Allow(Permission.DeleteAdministrator)
+    deleteAdministrator(@Args() args: MutationDeleteAdministratorArgs): Promise<DeletionResponse> {
+        const { id } = args;
+        return this.administratorService.softDelete(id);
+    }
 }

+ 2 - 0
packages/core/src/api/schema/admin-api/administrator.api.graphql

@@ -8,6 +8,8 @@ type Mutation {
     createAdministrator(input: CreateAdministratorInput!): Administrator!
     "Update an existing Administrator"
     updateAdministrator(input: UpdateAdministratorInput!): Administrator!
+    "Delete an Administrator"
+    deleteAdministrator(id: ID!): DeletionResponse!
     "Assign a Role to an Administrator"
     assignRoleToAdministrator(administratorId: ID!, roleId: ID!): Administrator!
 }

+ 6 - 2
packages/core/src/entity/administrator/administrator.entity.ts

@@ -1,6 +1,7 @@
 import { DeepPartial } from '@vendure/common/lib/shared-types';
 import { Column, Entity, JoinColumn, OneToOne } from 'typeorm';
 
+import { SoftDeletable } from '../../common/types/common-types';
 import { VendureEntity } from '../base/base.entity';
 import { User } from '../user/user.entity';
 
@@ -11,11 +12,14 @@ import { User } from '../user/user.entity';
  * @docsCategory entities
  */
 @Entity()
-export class Administrator extends VendureEntity {
+export class Administrator extends VendureEntity implements SoftDeletable {
     constructor(input?: DeepPartial<Administrator>) {
         super(input);
     }
 
+    @Column({ type: Date, nullable: true })
+    deletedAt: Date | null;
+
     @Column() firstName: string;
 
     @Column() lastName: string;
@@ -23,7 +27,7 @@ export class Administrator extends VendureEntity {
     @Column({ unique: true })
     emailAddress: string;
 
-    @OneToOne(type => User)
+    @OneToOne((type) => User)
     @JoinColumn()
     user: User;
 }

+ 22 - 3
packages/core/src/service/services/administrator.service.ts

@@ -1,6 +1,10 @@
 import { Injectable } from '@nestjs/common';
 import { InjectConnection } from '@nestjs/typeorm';
-import { CreateAdministratorInput, UpdateAdministratorInput } from '@vendure/common/lib/generated-types';
+import {
+    CreateAdministratorInput,
+    DeletionResult,
+    UpdateAdministratorInput,
+} from '@vendure/common/lib/generated-types';
 import { ID, PaginatedList } from '@vendure/common/lib/shared-types';
 import { Connection } from 'typeorm';
 
@@ -8,10 +12,10 @@ import { EntityNotFoundError } from '../../common/error/errors';
 import { ListQueryOptions } from '../../common/types/common-types';
 import { ConfigService } from '../../config';
 import { Administrator } from '../../entity/administrator/administrator.entity';
-import { NativeAuthenticationMethod } from '../../entity/authentication-method/native-authentication-method.entity';
 import { User } from '../../entity/user/user.entity';
 import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
 import { PasswordCiper } from '../helpers/password-cipher/password-ciper';
+import { getEntityOrThrow } from '../helpers/utils/get-entity-or-throw';
 import { patchEntity } from '../helpers/utils/patch-entity';
 
 import { RoleService } from './role.service';
@@ -34,7 +38,7 @@ export class AdministratorService {
 
     findAll(options?: ListQueryOptions<Administrator>): Promise<PaginatedList<Administrator>> {
         return this.listQueryBuilder
-            .build(Administrator, options, { relations: ['user', 'user.roles'] })
+            .build(Administrator, options, { relations: ['user', 'user.roles'], where: { deletedAt: null } })
             .getManyAndCount()
             .then(([items, totalItems]) => ({
                 items,
@@ -45,6 +49,9 @@ export class AdministratorService {
     findOne(administratorId: ID): Promise<Administrator | undefined> {
         return this.connection.manager.findOne(Administrator, administratorId, {
             relations: ['user', 'user.roles'],
+            where: {
+                deletedAt: null,
+            },
         });
     }
 
@@ -110,6 +117,18 @@ export class AdministratorService {
         return administrator;
     }
 
+    async softDelete(id: ID) {
+        const administrator = await getEntityOrThrow(this.connection, Administrator, id, {
+            relations: ['user'],
+        });
+        await this.connection.getRepository(Administrator).update({ id }, { deletedAt: new Date() });
+        // tslint:disable-next-line:no-non-null-assertion
+        await this.userService.softDelete(administrator.user!.id);
+        return {
+            result: DeletionResult.DELETED,
+        };
+    }
+
     /**
      * There must always exist a SuperAdmin, otherwise full administration via API will
      * no longer be possible.

+ 6 - 0
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts

@@ -1771,6 +1771,8 @@ export type Mutation = {
     createAdministrator: Administrator;
     /** Update an existing Administrator */
     updateAdministrator: Administrator;
+    /** Delete an Administrator */
+    deleteAdministrator: DeletionResponse;
     /** Assign a Role to an Administrator */
     assignRoleToAdministrator: Administrator;
     /** Create a new Asset */
@@ -1930,6 +1932,10 @@ export type MutationUpdateAdministratorArgs = {
     input: UpdateAdministratorInput;
 };
 
+export type MutationDeleteAdministratorArgs = {
+    id: Scalars['ID'];
+};
+
 export type MutationAssignRoleToAdministratorArgs = {
     administratorId: Scalars['ID'];
     roleId: Scalars['ID'];

Fișier diff suprimat deoarece este prea mare
+ 0 - 0
schema-admin.json


Fișier diff suprimat deoarece este prea mare
+ 0 - 0
schema-shop.json


Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff