-
Notifications
You must be signed in to change notification settings - Fork 620
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(api-headless-cms): start with record locking
- Loading branch information
1 parent
66f9984
commit 4571658
Showing
21 changed files
with
592 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
packages/api-headless-cms/src/lockingMechanism/abstractions/IGetLockRecordUseCase.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { IHeadlessCmsLockRecord } from "~/lockingMechanism/types"; | ||
|
||
export interface IGetLockRecordUseCaseExecute { | ||
(id: string): Promise<IHeadlessCmsLockRecord | null>; | ||
} | ||
|
||
export interface IGetLockRecordUseCase { | ||
execute: IGetLockRecordUseCaseExecute; | ||
} |
14 changes: 14 additions & 0 deletions
14
packages/api-headless-cms/src/lockingMechanism/abstractions/ILockEntryUseCase.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { IHeadlessCmsLockRecord, IHeadlessCmsLockRecordEntryType } from "~/lockingMechanism/types"; | ||
|
||
export interface ILockEntryUseCaseExecuteParams { | ||
id: string; | ||
type: IHeadlessCmsLockRecordEntryType; | ||
} | ||
|
||
export interface ILockEntryUseCaseExecute { | ||
(params: ILockEntryUseCaseExecuteParams): Promise<IHeadlessCmsLockRecord>; | ||
} | ||
|
||
export interface ILockEntryUseCase { | ||
execute: ILockEntryUseCaseExecute; | ||
} |
14 changes: 14 additions & 0 deletions
14
packages/api-headless-cms/src/lockingMechanism/abstractions/IUnlockEntryUseCase.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { IHeadlessCmsLockRecordEntryType } from "~/lockingMechanism/types"; | ||
|
||
export interface IUnlockEntryUseCaseExecuteParams { | ||
id: string; | ||
type: IHeadlessCmsLockRecordEntryType; | ||
} | ||
|
||
export interface IUnlockEntryUseCaseExecute { | ||
(params: IUnlockEntryUseCaseExecuteParams): Promise<void>; | ||
} | ||
|
||
export interface IUnlockEntryUseCase { | ||
execute: IUnlockEntryUseCaseExecute; | ||
} |
11 changes: 11 additions & 0 deletions
11
packages/api-headless-cms/src/lockingMechanism/abstractions/IsEntryLocked.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { IHeadlessCmsLockingMechanismIsLockedParams } from "~/lockingMechanism/types"; | ||
|
||
export type IIsEntryLockedUseCaseExecuteParams = IHeadlessCmsLockingMechanismIsLockedParams; | ||
|
||
export interface IIsEntryLockedUseCaseExecute { | ||
(params: IIsEntryLockedUseCaseExecuteParams): Promise<boolean>; | ||
} | ||
|
||
export interface IIsEntryLockedUseCase { | ||
execute: IIsEntryLockedUseCaseExecute; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { CmsContext } from "~/types"; | ||
import { | ||
ICmsModelLockRecordManager, | ||
IHeadlessCmsLockingMechanism, | ||
IHeadlessCmsLockRecordValues | ||
} from "./types"; | ||
import { createLockingModel, RECORD_LOCKING_MODEL_ID } from "./model"; | ||
import { IGetLockRecordUseCaseExecute } from "./abstractions/IGetLockRecordUseCase"; | ||
import { IIsEntryLockedUseCaseExecute } from "./abstractions/IsEntryLocked"; | ||
import { ILockEntryUseCaseExecute } from "~/lockingMechanism/abstractions/ILockEntryUseCase"; | ||
import { IUnlockEntryUseCaseExecute } from "~/lockingMechanism/abstractions/IUnlockEntryUseCase"; | ||
import { createUseCases } from "./useCases"; | ||
|
||
interface Params { | ||
context: CmsContext; | ||
} | ||
|
||
export const createLockingMechanismCrud = ({ context }: Params): IHeadlessCmsLockingMechanism => { | ||
context.plugins.register(createLockingModel()); | ||
|
||
const getManager = async (): Promise<ICmsModelLockRecordManager> => { | ||
return await context.cms.getEntryManager<IHeadlessCmsLockRecordValues>( | ||
RECORD_LOCKING_MODEL_ID | ||
); | ||
}; | ||
|
||
const { unlockEntryUseCase, lockEntryUseCase, getLockRecordUseCase, isEntryLockedUseCase } = | ||
createUseCases({ | ||
getManager | ||
}); | ||
|
||
const getLockRecord: IGetLockRecordUseCaseExecute = async (id: string) => { | ||
return context.benchmark.measure("headlessCms.crud.locking.getLockRecord", async () => { | ||
return getLockRecordUseCase.execute(id); | ||
}); | ||
}; | ||
|
||
const isEntryLocked: IIsEntryLockedUseCaseExecute = async params => { | ||
return context.benchmark.measure("headlessCms.crud.locking.isEntryLocked", async () => { | ||
return isEntryLockedUseCase.execute(params); | ||
}); | ||
}; | ||
|
||
const lockEntry: ILockEntryUseCaseExecute = async params => { | ||
return context.benchmark.measure("headlessCms.crud.locking.lockEntry", async () => { | ||
return lockEntryUseCase.execute(params); | ||
}); | ||
}; | ||
|
||
const unlockEntry: IUnlockEntryUseCaseExecute = async params => { | ||
return context.benchmark.measure("headlessCms.crud.locking.lockEntry", async () => { | ||
return unlockEntryUseCase.execute(params); | ||
}); | ||
}; | ||
|
||
return { | ||
isEntryLocked, | ||
getLockRecord, | ||
lockEntry, | ||
unlockEntry | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { createCmsModel, createPrivateModel } from "~/plugins"; | ||
|
||
export const RECORD_LOCKING_MODEL_ID = "wby_recordLocking"; | ||
|
||
export const createLockingModel = () => { | ||
return createCmsModel( | ||
createPrivateModel({ | ||
modelId: RECORD_LOCKING_MODEL_ID, | ||
name: "Record Lock Tracking", | ||
fields: [ | ||
{ | ||
id: "targetId", | ||
type: "text", | ||
fieldId: "targetId", | ||
storageId: "text@targetId", | ||
label: "Target ID", | ||
validation: [ | ||
{ | ||
name: "required", | ||
message: "Target ID is required." | ||
} | ||
] | ||
}, | ||
/** | ||
* Since we need a generic way to track records, we will use type to determine if it's a cms record or a page or a form, etc... | ||
* Update IHeadlessCmsLockRecordValues in types.ts file with additional fields as required. | ||
* | ||
* @see IHeadlessCmsLockRecordValues | ||
*/ | ||
{ | ||
id: "type", | ||
type: "text", | ||
fieldId: "type", | ||
storageId: "text@type", | ||
label: "Record Type", | ||
validation: [ | ||
{ | ||
name: "required", | ||
message: "Record type is required." | ||
}, | ||
/** | ||
* Update pattern with additional types as required. | ||
* Also update IHeadlessCmsLockRecordEntryType in types.ts file with additional types as required. | ||
*/ | ||
{ | ||
name: "pattern", | ||
message: "Record type is required.", | ||
settings: { | ||
pattern: { | ||
name: "custom", | ||
regex: "^pb:page|cms:([a-zA-Z0-9_-]+)$", | ||
flags: "" | ||
} | ||
} | ||
} | ||
] | ||
} | ||
] | ||
}) | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { CmsIdentity, CmsModelManager } from "~/types"; | ||
|
||
export type ICmsModelLockRecordManager = CmsModelManager<IHeadlessCmsLockRecordValues>; | ||
|
||
export interface IHeadlessCmsLockRecordValues { | ||
targetId: string; | ||
type: IHeadlessCmsLockRecordEntryType; | ||
} | ||
|
||
export interface IHeadlessCmsLockRecord { | ||
id: string; | ||
targetId: string; | ||
type: IHeadlessCmsLockRecordEntryType; | ||
lockedBy: CmsIdentity; | ||
lockedOn: Date; | ||
} | ||
|
||
/** | ||
* Do not use any special chars other than #, as we use this to create lock record IDs. | ||
*/ | ||
export type IHeadlessCmsLockRecordEntryType = "pb#page" | `cms#${string}`; | ||
|
||
export interface IHeadlessCmsLockingMechanismIsLockedParams { | ||
id: string; | ||
type: IHeadlessCmsLockRecordEntryType; | ||
} | ||
|
||
export interface IHeadlessCmsLockingMechanismLockEntryParams { | ||
id: string; | ||
type: IHeadlessCmsLockRecordEntryType; | ||
} | ||
|
||
export interface IHeadlessCmsLockingMechanismUnlockEntryParams { | ||
id: string; | ||
type: IHeadlessCmsLockRecordEntryType; | ||
} | ||
|
||
export interface IHeadlessCmsLockingMechanism { | ||
getLockRecord(id: string): Promise<IHeadlessCmsLockRecord | null>; | ||
isEntryLocked(params: IHeadlessCmsLockingMechanismIsLockedParams): Promise<boolean>; | ||
lockEntry(params: IHeadlessCmsLockingMechanismLockEntryParams): Promise<IHeadlessCmsLockRecord>; | ||
unlockEntry(params: IHeadlessCmsLockingMechanismUnlockEntryParams): Promise<void>; | ||
} |
29 changes: 29 additions & 0 deletions
29
...ages/api-headless-cms/src/lockingMechanism/useCases/GetLockRecord/GetLockRecordUseCase.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { IGetLockRecordUseCase } from "~/lockingMechanism/abstractions/IGetLockRecordUseCase"; | ||
import { ICmsModelLockRecordManager, IHeadlessCmsLockRecord } from "~/lockingMechanism/types"; | ||
import { NotFoundError } from "@webiny/handler-graphql"; | ||
import { convertEntryToLockRecord } from "~/lockingMechanism/utils/convertEntryToLockRecord"; | ||
|
||
export interface IGetLockRecordUseCaseParams { | ||
getManager(): Promise<ICmsModelLockRecordManager>; | ||
} | ||
|
||
export class GetLockRecordUseCase implements IGetLockRecordUseCase { | ||
private readonly getManager: IGetLockRecordUseCaseParams["getManager"]; | ||
|
||
public constructor(params: IGetLockRecordUseCaseParams) { | ||
this.getManager = params.getManager; | ||
} | ||
|
||
public async execute(id: string): Promise<IHeadlessCmsLockRecord | null> { | ||
try { | ||
const manager = await this.getManager(); | ||
const result = await manager.get(id); | ||
return convertEntryToLockRecord(result); | ||
} catch (ex) { | ||
if (ex instanceof NotFoundError) { | ||
return null; | ||
} | ||
throw ex; | ||
} | ||
} | ||
} |
Oops, something went wrong.