From daa8d8e054c309f4150195e4dab0d108350cc0d3 Mon Sep 17 00:00:00 2001 From: Sasho Mihajlov Date: Fri, 29 Sep 2023 10:28:05 +0200 Subject: [PATCH 1/3] wip: basic methods for crud ans storage operations --- .../src/crud/createContentReviewMethods.ts | 2 + .../src/crud/createReviewersGroupMethods.ts | 44 +++++++++++- .../api-apw/src/storageOperations/index.ts | 8 +++ .../reviewsGroupStorageOperations.ts | 36 ++++++++++ .../api-apw/src/storageOperations/types.ts | 16 +++-- packages/api-apw/src/types.ts | 70 +++++++++++++++++-- 6 files changed, 165 insertions(+), 11 deletions(-) create mode 100644 packages/api-apw/src/storageOperations/reviewsGroupStorageOperations.ts diff --git a/packages/api-apw/src/crud/createContentReviewMethods.ts b/packages/api-apw/src/crud/createContentReviewMethods.ts index ce27eedba5d..7f13ffc8db8 100644 --- a/packages/api-apw/src/crud/createContentReviewMethods.ts +++ b/packages/api-apw/src/crud/createContentReviewMethods.ts @@ -8,6 +8,7 @@ import { ApwContentReviewStatus, ApwContentReviewStepStatus, ApwReviewerCrud, + ApwReviewsGroupCrud, ApwScheduleActionData, ApwWorkflowStepTypes, CreateApwContentReviewParams, @@ -41,6 +42,7 @@ import { PluginsContainer } from "@webiny/plugins"; export interface CreateContentReviewMethodsParams extends CreateApwParams { getReviewer: ApwReviewerCrud["get"]; + getReviewersGroup: ApwReviewsGroupCrud["get"]; getContentGetter: AdvancedPublishingWorkflow["getContentGetter"]; getContentPublisher: AdvancedPublishingWorkflow["getContentPublisher"]; getContentUnPublisher: AdvancedPublishingWorkflow["getContentUnPublisher"]; diff --git a/packages/api-apw/src/crud/createReviewersGroupMethods.ts b/packages/api-apw/src/crud/createReviewersGroupMethods.ts index 573958474af..20368de8814 100644 --- a/packages/api-apw/src/crud/createReviewersGroupMethods.ts +++ b/packages/api-apw/src/crud/createReviewersGroupMethods.ts @@ -1,3 +1,43 @@ -export function createReviewersGroupMethods(): Record { - return {}; +import { + ApwReviewersGroup, + ApwReviewsGroupCrud, + CreateApwParams, + CreateApwReviewsGroupParams, + OnReviewerGroupAfterCreateTopicParams, + OnReviewerGroupBeforeCreateTopicParams, + UpdateApwReviewsGroupParams +} from "~/types"; +import { createTopic } from "@webiny/pubsub"; + +export function createReviewersGroupMethods({ + storageOperations +}: CreateApwParams): ApwReviewsGroupCrud { + // create + const onReviewersGroupBeforeCreate = createTopic( + "apw.onReviewersGroupBeforeCreate" + ); + const onReviewersGroupAfterCreate = createTopic( + "apw.onReviewersGroupAfterCreate" + ); + + return { + onReviewersGroupBeforeCreate, + onReviewersGroupAfterCreate, + async create(data: CreateApwReviewsGroupParams): Promise { + await onReviewersGroupBeforeCreate.publish(data); + + const reviewersGroup = await storageOperations.createReviewersGroup({ data }); + + await onReviewersGroupAfterCreate.publish({ group: reviewersGroup }); + + return reviewersGroup; + }, + async get(id: string): Promise { + return new Promise(); + }, + async update(id: string, data: UpdateApwReviewsGroupParams): Promise { + return null; + }, + async delete(id: string): Promise {} + }; } diff --git a/packages/api-apw/src/storageOperations/index.ts b/packages/api-apw/src/storageOperations/index.ts index 05f28fce9f9..302c452b9a1 100644 --- a/packages/api-apw/src/storageOperations/index.ts +++ b/packages/api-apw/src/storageOperations/index.ts @@ -8,6 +8,7 @@ import { createChangeRequestStorageOperations } from "./changeRequestStorageOper import { createCommentStorageOperations } from "~/storageOperations/commentStorageOperations"; import { createApwModels } from "./models"; import { Security } from "@webiny/api-security/types"; +import { createReviewersGroupStorageOperations } from "~/storageOperations/reviewsGroupStorageOperations"; export interface CreateApwStorageOperationsParams { cms: HeadlessCms; @@ -15,6 +16,12 @@ export interface CreateApwStorageOperationsParams { getCmsContext: () => CmsContext; } +export interface CreateApwReviewersGroupStorageOperationsParams { + cms: HeadlessCms; + security: Security; + getCmsContext: () => CmsContext; +} + /** * Using any because value can be a lot of types. * TODO @ts-refactor figure out correct types. @@ -36,6 +43,7 @@ export const createStorageOperations = ( return { ...createReviewerStorageOperations(params), + ...createReviewersGroupStorageOperations(params), ...createWorkflowStorageOperations(params), ...createContentReviewStorageOperations(params), ...createChangeRequestStorageOperations(params), diff --git a/packages/api-apw/src/storageOperations/reviewsGroupStorageOperations.ts b/packages/api-apw/src/storageOperations/reviewsGroupStorageOperations.ts new file mode 100644 index 00000000000..61f1b05f3a6 --- /dev/null +++ b/packages/api-apw/src/storageOperations/reviewsGroupStorageOperations.ts @@ -0,0 +1,36 @@ +import { ApwReviewersGroupStorageOperations } from "./types"; +import { + baseFields, + CreateApwReviewersGroupStorageOperationsParams, + getFieldValues +} from "~/storageOperations/index"; +import WebinyError from "@webiny/error"; + +export const createReviewersGroupStorageOperations = ({ + cms, + security +}: CreateApwReviewersGroupStorageOperationsParams): ApwReviewersGroupStorageOperations => { + const getReviewersGroupModel = async () => { + const model = await security.withoutAuthorization(async () => { + return cms.getModel("apwReviewersGroupModelDefinition"); + }); + if (!model) { + throw new WebinyError( + "Could not find `apwReviewersGroupModelDefinition` model.", + "MODEL_NOT_FOUND_ERROR" + ); + } + return model; + }; + + return { + getReviewersGroupModel, + async createReviewersGroup(params) { + const model = await getReviewersGroupModel(); + const entry = await security.withoutAuthorization(async () => { + return cms.createEntry(model, params.data); + }); + return getFieldValues(entry, baseFields); + } + }; +}; diff --git a/packages/api-apw/src/storageOperations/types.ts b/packages/api-apw/src/storageOperations/types.ts index 17f6a8dcfc8..b1774497949 100644 --- a/packages/api-apw/src/storageOperations/types.ts +++ b/packages/api-apw/src/storageOperations/types.ts @@ -1,10 +1,11 @@ import { CmsModel } from "@webiny/api-headless-cms/types"; import { - ApwReviewerStorageOperations as BaseApwReviewerStorageOperations, - ApwWorkflowStorageOperations as BaseApwWorkflowStorageOperations, - ApwContentReviewStorageOperations as BaseApwContentReviewStorageOperations, ApwChangeRequestStorageOperations as BaseApwChangeRequestStorageOperations, - ApwCommentStorageOperations as BaseApwCommentStorageOperations + ApwCommentStorageOperations as BaseApwCommentStorageOperations, + ApwContentReviewStorageOperations as BaseApwContentReviewStorageOperations, + ApwReviewersGroupStorageOperations as BaseApwReviewersGroupStorageOperations, + ApwReviewerStorageOperations as BaseApwReviewerStorageOperations, + ApwWorkflowStorageOperations as BaseApwWorkflowStorageOperations } from "~/types"; export interface ApwCommentStorageOperations extends BaseApwCommentStorageOperations { @@ -21,6 +22,13 @@ export interface ApwReviewerStorageOperations extends BaseApwReviewerStorageOper getReviewerModel(): Promise; } +export interface ApwReviewersGroupStorageOperations extends BaseApwReviewersGroupStorageOperations { + /** + * @internal + */ + getReviewersGroupModel(): Promise; +} + export interface ApwWorkflowStorageOperations extends BaseApwWorkflowStorageOperations { /** * @internal diff --git a/packages/api-apw/src/types.ts b/packages/api-apw/src/types.ts index ba6dd054849..74d5ff8c35a 100644 --- a/packages/api-apw/src/types.ts +++ b/packages/api-apw/src/types.ts @@ -1,15 +1,15 @@ import { CmsEntry as BaseCmsEntry, - OnEntryBeforePublishTopicParams, OnEntryAfterPublishTopicParams, - OnEntryAfterUnpublishTopicParams + OnEntryAfterUnpublishTopicParams, + OnEntryBeforePublishTopicParams } from "@webiny/api-headless-cms/types"; import { - Page, - OnPageBeforeCreateTopicParams, OnPageBeforeCreateFromTopicParams, - OnPageBeforeUpdateTopicParams, + OnPageBeforeCreateTopicParams, OnPageBeforePublishTopicParams, + OnPageBeforeUpdateTopicParams, + Page, PageSettings } from "@webiny/api-page-builder/types"; import { Context } from "@webiny/api/types"; @@ -163,6 +163,11 @@ export interface ApwReviewerWithEmail extends Omit { email: string; } +export interface ApwReviewersGroup extends ApwBaseFields { + displayName: string | null; + reviewers: ApwReviewer[]; +} + export interface ApwComment extends ApwBaseFields { body: Record; changeRequest: string; @@ -334,6 +339,17 @@ export interface UpdateApwContentReviewParams { content?: ApwContentReviewContent; } +export interface CreateApwReviewsGroupParams { + displayName: string; + reviewers: ApwReviewer[]; +} + +export interface UpdateApwReviewsGroupParams { + groupId: string; + displayName: string; + reviewers: ApwReviewer[]; +} + interface BaseApwCrud { get(id: string): Promise; @@ -465,6 +481,19 @@ export interface ApwContentReviewCrud onContentReviewBeforeList: Topic; } +export interface ApwReviewsGroupCrud + extends BaseApwCrud< + ApwReviewersGroup, + CreateApwReviewsGroupParams, + any //UpdateApwReviewsGroupParams + > { + /** + * Lifecycle events + */ + onReviewersGroupBeforeCreate: Topic; + onReviewersGroupAfterCreate: Topic; +} + export type ContentGetter = ( id: string, settings: { modelId?: string } @@ -552,6 +581,15 @@ interface StorageOperationsDeleteReviewerParams { id: string; } +interface CreateApwReviewersGroupData { + displayName: string; + reviewers: ApwReviewer[]; +} + +interface StorageOperationsCreateReviewersGroupParams { + data: CreateApwReviewersGroupData; +} + interface StorageOperationsGetParams { id: string; } @@ -642,6 +680,12 @@ export interface ApwReviewerStorageOperations { deleteReviewer(params: StorageOperationsDeleteReviewerParams): Promise; } +export interface ApwReviewersGroupStorageOperations { + createReviewersGroup( + params: StorageOperationsCreateReviewersGroupParams + ): Promise; +} + export interface ApwWorkflowStorageOperations { /* * Workflow methods @@ -716,6 +760,7 @@ export interface ApwCommentStorageOperations { export interface ApwStorageOperations extends ApwReviewerStorageOperations, + ApwReviewersGroupStorageOperations, ApwWorkflowStorageOperations, ApwContentReviewStorageOperations, ApwChangeRequestStorageOperations, @@ -959,6 +1004,21 @@ export interface OnWorkflowAfterDeleteTopicParams { export type WorkflowModelDefinition = Omit; +/** + * @category Lifecycle events + */ +export interface OnReviewerGroupBeforeCreateTopicParams { + displayName: string; + reviewers: ApwReviewer[]; +} + +/** + * @category Lifecycle events + */ +export interface OnReviewerGroupAfterCreateTopicParams { + group: ApwReviewersGroup; +} + /** * Headless CMS */ From da73af5129bf949ddc7eefcbd3093d08f6d04afa Mon Sep 17 00:00:00 2001 From: Sasho Mihajlov Date: Sat, 30 Sep 2023 21:16:12 +0200 Subject: [PATCH 2/3] wip: stup test --- .../graphql/reviewersGroup.crud.test.ts | 487 ++++++++++++++++++ 1 file changed, 487 insertions(+) create mode 100644 packages/api-apw/__tests__/graphql/reviewersGroup.crud.test.ts diff --git a/packages/api-apw/__tests__/graphql/reviewersGroup.crud.test.ts b/packages/api-apw/__tests__/graphql/reviewersGroup.crud.test.ts new file mode 100644 index 00000000000..ecdeacea530 --- /dev/null +++ b/packages/api-apw/__tests__/graphql/reviewersGroup.crud.test.ts @@ -0,0 +1,487 @@ +import { useGraphQlHandler } from "~tests/utils/useGraphQlHandler"; +import { defaultIdentity } from "../utils/defaultIdentity"; + +const identityRoot = { + id: "root", + displayName: "root", + type: "admin", + email: "root@webiny.com" +}; +const updatedDisplayName = "Robert Downey"; + +describe("Reviewer crud test", () => { + const { securityIdentity, reviewer, until } = useGraphQlHandler({ + path: "/graphql", + plugins: [defaultIdentity()] + }); + + const { securityIdentity: securityIdentityRoot } = useGraphQlHandler({ + path: "/graphql", + plugins: [defaultIdentity()], + identity: identityRoot + }); + + const { securityIdentity: securityIdentityRootUpdated } = useGraphQlHandler({ + path: "/graphql", + plugins: [defaultIdentity()], + identity: { + ...identityRoot, + displayName: updatedDisplayName + } + }); + + it("should be able to hook on to after login", async () => { + const [response] = await securityIdentity.login(); + expect(response).toMatchObject({ + data: { + security: { + login: { + data: { + id: "12345678" + }, + error: null + } + } + } + }); + + await until( + () => reviewer.listReviewersQuery({}).then(([data]) => data), + (response: any) => { + const list = response.data.apw.listReviewers.data; + return list.length === 1; + }, + { + name: "Wait for listReviewers query" + } + ); + + /** + * Should created a reviewer entry after login. + */ + const [listReviewersResponse] = await reviewer.listReviewersQuery({}); + expect(listReviewersResponse).toEqual({ + data: { + apw: { + listReviewers: { + data: [ + { + id: expect.any(String), + createdOn: expect.stringMatching(/^20/), + savedOn: expect.stringMatching(/^20/), + createdBy: { + id: "12345678", + displayName: "John Doe", + type: "admin" + }, + identityId: "12345678", + displayName: "John Doe", + type: "admin", + email: "testing@webiny.com" + } + ], + error: null, + meta: { + hasMoreItems: false, + cursor: null, + totalCount: 1 + } + } + } + } + }); + /* + * Login with another identity. + */ + await securityIdentityRoot.login(); + + await until( + () => reviewer.listReviewersQuery({}).then(([data]) => data), + (response: any) => { + const list = response.data.apw.listReviewers.data; + return list.length === 2; + }, + { + name: "Wait for listReviewers query" + } + ); + + /** + * Should now have 2 reviewers. + */ + const [listReviewersAgainResponse] = await reviewer.listReviewersQuery({}); + expect(listReviewersAgainResponse).toEqual({ + data: { + apw: { + listReviewers: { + data: [ + { + id: expect.any(String), + createdOn: expect.stringMatching(/^20/), + savedOn: expect.stringMatching(/^20/), + createdBy: { + id: identityRoot.id, + displayName: identityRoot.displayName, + type: identityRoot.type + }, + identityId: identityRoot.id, + displayName: identityRoot.displayName, + type: "admin", + email: identityRoot.email + }, + { + id: expect.any(String), + createdOn: expect.stringMatching(/^20/), + savedOn: expect.stringMatching(/^20/), + createdBy: { + id: "12345678", + displayName: "John Doe", + type: "admin" + }, + identityId: "12345678", + displayName: "John Doe", + type: "admin", + email: "testing@webiny.com" + } + ], + error: null, + meta: { + hasMoreItems: false, + cursor: null, + totalCount: 2 + } + } + } + } + }); + }); + + it("should not create more than one entry due to multiple login", async () => { + const [response] = await securityIdentity.login(); + expect(response).toMatchObject({ + data: { + security: { + login: { + data: { + id: "12345678" + }, + error: null + } + } + } + }); + + await until( + () => reviewer.listReviewersQuery({}).then(([data]) => data), + (response: any) => { + const list = response.data.apw.listReviewers.data; + return list.length === 1; + }, + { + name: "Wait for listReviewers query" + } + ); + + /** + * Should created a reviewer entry after login. + */ + const [listReviewersResponse] = await reviewer.listReviewersQuery({}); + expect(listReviewersResponse).toEqual({ + data: { + apw: { + listReviewers: { + data: [ + { + id: expect.any(String), + createdOn: expect.stringMatching(/^20/), + savedOn: expect.stringMatching(/^20/), + createdBy: { + id: "12345678", + displayName: "John Doe", + type: "admin" + }, + identityId: "12345678", + displayName: "John Doe", + type: "admin", + email: "testing@webiny.com" + } + ], + error: null, + meta: { + hasMoreItems: false, + cursor: null, + totalCount: 1 + } + } + } + } + }); + /* + * Login again with same identity. + */ + await securityIdentity.login(); + + await until( + () => reviewer.listReviewersQuery({}).then(([data]) => data), + (response: any) => { + const list = response.data.apw.listReviewers.data; + return list.length === 1; + }, + { + name: "Wait for listReviewers query" + } + ); + + /* + * Should not have 2 reviewers. + */ + const [listReviewersAgainResponse] = await reviewer.listReviewersQuery({}); + expect(listReviewersAgainResponse).toEqual({ + data: { + apw: { + listReviewers: { + data: [ + { + id: expect.any(String), + createdOn: expect.stringMatching(/^20/), + savedOn: expect.stringMatching(/^20/), + createdBy: { + id: "12345678", + displayName: "John Doe", + type: "admin" + }, + identityId: "12345678", + displayName: "John Doe", + type: "admin", + email: "testing@webiny.com" + } + ], + error: null, + meta: { + hasMoreItems: false, + cursor: null, + totalCount: 1 + } + } + } + } + }); + }); + + it(`should update "displayName" after login if identity has been updated`, async () => { + const [response] = await securityIdentityRoot.login(); + expect(response).toMatchObject({ + data: { + security: { + login: { + data: { + id: "root" + }, + error: null + } + } + } + }); + + await until( + () => reviewer.listReviewersQuery({}).then(([data]) => data), + (response: any) => { + const list = response.data.apw.listReviewers.data; + return list.length === 1; + }, + { + name: "Wait for listReviewers query" + } + ); + + /** + * Should created a reviewer entry after login. + */ + const [listReviewersResponse] = await reviewer.listReviewersQuery({}); + expect(listReviewersResponse).toEqual({ + data: { + apw: { + listReviewers: { + data: [ + { + id: expect.any(String), + createdOn: expect.stringMatching(/^20/), + savedOn: expect.stringMatching(/^20/), + createdBy: { + id: "root", + displayName: "root", + type: "admin" + }, + identityId: "root", + displayName: "root", + type: "admin", + email: identityRoot.email + } + ], + error: null, + meta: { + hasMoreItems: false, + cursor: null, + totalCount: 1 + } + } + } + } + }); + /* + * Login again with same identity. + */ + await securityIdentityRootUpdated.login(); + + await until( + () => reviewer.listReviewersQuery({}).then(([data]) => data), + (response: any) => { + const list = response.data.apw.listReviewers.data; + return list.length === 1; + }, + { + name: "Wait for listReviewers query" + } + ); + + /* + * Should not have 2 reviewers. + */ + const [listReviewersAgainResponse] = await reviewer.listReviewersQuery({}); + expect(listReviewersAgainResponse).toEqual({ + data: { + apw: { + listReviewers: { + data: [ + { + id: expect.any(String), + createdOn: expect.stringMatching(/^20/), + savedOn: expect.stringMatching(/^20/), + createdBy: { + id: "root", + displayName: "root", + type: "admin" + }, + identityId: "root", + displayName: updatedDisplayName, + type: "admin", + email: identityRoot.email + } + ], + error: null, + meta: { + hasMoreItems: false, + cursor: null, + totalCount: 1 + } + } + } + } + }); + }); + + it("should update reviewer when login info changes", async () => { + const { securityIdentity: baseSecurityIdentity, reviewer } = useGraphQlHandler({ + path: "/graphql", + identity: { + id: "mockUpdateIdentityId", + type: "admin", + displayName: "Base Identity" + } + }); + await securityIdentity.login(); + await baseSecurityIdentity.login(); + + await until( + () => reviewer.listReviewersQuery({}).then(([data]) => data), + (response: any) => { + const list = response.data.apw.listReviewers.data; + return list.length === 2; + }, + { + name: "Wait for listReviewers query" + } + ); + + const [response] = await reviewer.listReviewersQuery({}); + + expect(response).toMatchObject({ + data: { + apw: { + listReviewers: { + data: [ + { + identityId: "mockUpdateIdentityId" + }, + { + identityId: "12345678" + } + ], + error: null, + meta: { + totalCount: 2, + hasMoreItems: false, + cursor: null + } + } + } + } + }); + + const email = "mock@webiny.local"; + + const { securityIdentity: updatedSecurityIdentity, reviewer: updatedReviewer } = + useGraphQlHandler({ + path: "/graphql", + identity: { + id: "mockUpdateIdentityId", + type: "admin", + displayName: "Base Identity", + email + }, + permissions: [] + }); + + await updatedSecurityIdentity.login(); + + await until( + () => updatedReviewer.listReviewersQuery({}).then(([data]) => data), + (response: any) => { + const list = response.data.apw.listReviewers.data as any[]; + if (list.length !== 2) { + return false; + } + return list.some(reviewer => reviewer.email === email); + }, + { + name: "Wait for listReviewers query after updated login" + } + ); + + const [responseAfterUpdatedLogin] = await reviewer.listReviewersQuery({}); + + expect(responseAfterUpdatedLogin).toMatchObject({ + data: { + apw: { + listReviewers: { + data: [ + { + identityId: "mockUpdateIdentityId", + email + }, + { + identityId: "12345678" + } + ], + error: null, + meta: { + totalCount: 2, + hasMoreItems: false, + cursor: null + } + } + } + } + }); + }); +}); From ddbb8c84882aa0ff8e5b1d647042c6b75d392da0 Mon Sep 17 00:00:00 2001 From: Sasho Mihajlov Date: Mon, 2 Oct 2023 11:40:44 +0200 Subject: [PATCH 3/3] wip: updated with reviewers group model --- .../src/storageOperations/models/index.ts | 3 + .../models/reviewersGroup.model.ts | 80 +++++++++++++++++++ packages/api-apw/src/types.ts | 20 +++-- yarn.lock | 10 +++ 4 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 packages/api-apw/src/storageOperations/models/reviewersGroup.model.ts diff --git a/packages/api-apw/src/storageOperations/models/index.ts b/packages/api-apw/src/storageOperations/models/index.ts index 2ac358cdba5..cbcce5f9b86 100644 --- a/packages/api-apw/src/storageOperations/models/index.ts +++ b/packages/api-apw/src/storageOperations/models/index.ts @@ -8,6 +8,7 @@ import { createCommentModelDefinition } from "./comment.model"; import { createChangeRequestModelDefinition } from "./changeRequest.model"; import { CmsContext } from "@webiny/api-headless-cms/types"; import { isInstallationPending } from "~/plugins/utils"; +import { createReviewersGroupModelDefinition } from "~/storageOperations/models/reviewersGroup.model"; export const createApwModels = (context: CmsContext) => { /** @@ -52,6 +53,7 @@ export const createApwModels = (context: CmsContext) => { */ const changeRequestModelDefinition = createChangeRequestModelDefinition(); const reviewerModelDefinition = createReviewerModelDefinition(); + const reviewersGroupDefinition = createReviewersGroupModelDefinition(); const workflowModelDefinition = createWorkflowModelDefinition({ reviewerModelId: reviewerModelDefinition.modelId }); @@ -66,6 +68,7 @@ export const createApwModels = (context: CmsContext) => { workflowModelDefinition, contentReviewModelDefinition, reviewerModelDefinition, + reviewersGroupDefinition, changeRequestModelDefinition, commentModelDefinition ]; diff --git a/packages/api-apw/src/storageOperations/models/reviewersGroup.model.ts b/packages/api-apw/src/storageOperations/models/reviewersGroup.model.ts new file mode 100644 index 00000000000..b2ad2cdf7e5 --- /dev/null +++ b/packages/api-apw/src/storageOperations/models/reviewersGroup.model.ts @@ -0,0 +1,80 @@ +import { createModelField } from "./utils"; +import { WorkflowModelDefinition } from "~/types"; + +const idField = () => + createModelField({ + label: "Identity Id", + type: "text", + parent: "reviewersGroup", + validation: [ + { + message: "`identityId` field value is required in reviewersGroup.", + name: "required" + } + ] + }); + +const nameField = () => + createModelField({ + label: "Name", + type: "text", + parent: "reviewersGroup", + validation: [ + { + message: "`name` field value is required in reviewersGroup.", + name: "required" + } + ] + }); + +const slugField = () => + createModelField({ + label: "Slug", + type: "text", + parent: "reviewersGroup", + validation: [ + { + message: "`slug` field value is required in reviewersGroup.", + name: "required" + } + ] + }); + +const descriptionField = () => + createModelField({ + label: "Description", + type: "text", + parent: "reviewersGroup" + }); + +const reviewersField = () => + createModelField({ + label: "Reviewers", + type: "object", + parent: "reviewersGroup", + validation: [ + { + message: "`reviewers` field value is required in reviewersGroup.", + name: "required" + } + ] + }); + +export const REVIEWERS_GROUP_MODEL_ID = "apwReviewersGroupModelDefinition"; + +export const createReviewersGroupModelDefinition = (): WorkflowModelDefinition => { + return { + name: "APW - Reviewer", + modelId: REVIEWERS_GROUP_MODEL_ID, + titleFieldId: "name", + layout: [ + ["reviewer_identityId"], + ["reviewersGroup_name"], + ["reviewersGroup_description"], + ["reviewersGroup_reviewers"] + ], + fields: [idField(), nameField(), slugField(), descriptionField(), reviewersField()], + description: "", + isPrivate: true + }; +}; diff --git a/packages/api-apw/src/types.ts b/packages/api-apw/src/types.ts index 74d5ff8c35a..a82da86df5f 100644 --- a/packages/api-apw/src/types.ts +++ b/packages/api-apw/src/types.ts @@ -164,7 +164,9 @@ export interface ApwReviewerWithEmail extends Omit { } export interface ApwReviewersGroup extends ApwBaseFields { - displayName: string | null; + name: string; + slug: string; + description?: string; reviewers: ApwReviewer[]; } @@ -340,13 +342,17 @@ export interface UpdateApwContentReviewParams { } export interface CreateApwReviewsGroupParams { - displayName: string; + name: string; + slug: string; + description?: string; reviewers: ApwReviewer[]; } export interface UpdateApwReviewsGroupParams { groupId: string; - displayName: string; + name: string; + slug: string; + description?: string; reviewers: ApwReviewer[]; } @@ -582,7 +588,9 @@ interface StorageOperationsDeleteReviewerParams { } interface CreateApwReviewersGroupData { - displayName: string; + name: string; + slug: string; + description?: string; reviewers: ApwReviewer[]; } @@ -1008,7 +1016,9 @@ export type WorkflowModelDefinition = Omit