@mbc-cqrs-serverless/master 0.1.66-beta.0 → 0.1.68-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/constants/index.d.ts +1 -0
  2. package/dist/constants/index.js +2 -1
  3. package/dist/constants/index.js.map +1 -1
  4. package/dist/interfaces/master-data.interface.d.ts +3 -0
  5. package/dist/interfaces/master-setting.interface.d.ts +4 -1
  6. package/dist/services/master-data.service.d.ts +14 -0
  7. package/dist/services/master-data.service.js +10 -0
  8. package/dist/services/master-data.service.js.map +1 -1
  9. package/dist/services/master-setting.service.d.ts +15 -1
  10. package/dist/services/master-setting.service.js +10 -0
  11. package/dist/services/master-setting.service.js.map +1 -1
  12. package/dist/services/master-setting.service.spec.d.ts +1 -0
  13. package/dist/services/master-setting.service.spec.js +662 -0
  14. package/dist/services/master-setting.service.spec.js.map +1 -0
  15. package/dist/update-scheme.js +262 -14
  16. package/dist/update-scheme.js.map +1 -1
  17. package/package.json +6 -4
  18. package/src/templates/custom-task/custom-task.module.ts +18 -0
  19. package/src/templates/custom-task/event/task-queue-event-factory.ts +26 -0
  20. package/src/templates/custom-task/my-task.controller.ts +28 -0
  21. package/src/templates/custom-task/my-task.service.ts +99 -0
  22. package/src/templates/master/dto/master-copy.dto.ts +73 -0
  23. package/src/templates/master/dto/master-data-create.dto.ts +20 -0
  24. package/src/templates/master/dto/master-data-search.dto.ts +20 -0
  25. package/src/templates/master/dto/master-data-update.dto.ts +25 -0
  26. package/src/templates/master/dto/master-rds-list.entity.ts +15 -0
  27. package/src/templates/master/dto/master-rds.entity.ts +77 -0
  28. package/src/templates/master/dto/master-setting-search.dto.ts +20 -0
  29. package/src/templates/master/dto/master-setting-update.dto.ts +11 -0
  30. package/src/templates/master/entity/master-command.entity.ts +11 -0
  31. package/src/templates/master/entity/master-data-list.entity.ts +13 -0
  32. package/src/templates/master/entity/master-data.entity.ts +11 -0
  33. package/src/templates/master/handler/master-rds.handler.ts +75 -0
  34. package/src/templates/master/handler/master-sfn-task.event.ts +3 -0
  35. package/src/templates/master/handler/master-sfn-task.handler.ts +334 -0
  36. package/src/templates/master/helpers/get-order.ts +19 -0
  37. package/src/templates/master/helpers/id.ts +42 -0
  38. package/src/templates/master/helpers/index.ts +3 -0
  39. package/src/templates/master/helpers/key.ts +11 -0
  40. package/src/templates/master/master-data.controller.ts +98 -0
  41. package/src/templates/master/master-data.service.ts +181 -0
  42. package/src/templates/master/master-setting.controller.ts +103 -0
  43. package/src/templates/master/master-setting.service.ts +201 -0
  44. package/src/templates/master/master.module.ts +36 -0
@@ -0,0 +1,25 @@
1
+ import {
2
+ IsBoolean,
3
+ IsNumber,
4
+ IsObject,
5
+ IsOptional,
6
+ IsString,
7
+ } from 'class-validator'
8
+
9
+ export class MasterDataUpdateDto {
10
+ @IsString()
11
+ @IsOptional()
12
+ name?: string
13
+
14
+ @IsBoolean()
15
+ @IsOptional()
16
+ isDeleted?: boolean
17
+
18
+ @IsNumber()
19
+ @IsOptional()
20
+ seq?: number
21
+
22
+ @IsObject()
23
+ @IsOptional()
24
+ attributes?: object
25
+ }
@@ -0,0 +1,15 @@
1
+ import { ApiProperty } from '@nestjs/swagger'
2
+
3
+ import { MasterRdsEntity } from './master-rds.entity'
4
+
5
+ export class MasterRdsListEntity {
6
+ @ApiProperty({ type: Number })
7
+ total?: number
8
+
9
+ @ApiProperty({ type: MasterRdsEntity, isArray: true })
10
+ items: MasterRdsEntity[]
11
+
12
+ constructor(data: Partial<MasterRdsListEntity>) {
13
+ Object.assign(this, data)
14
+ }
15
+ }
@@ -0,0 +1,77 @@
1
+ import { ApiProperty } from '@nestjs/swagger'
2
+ import { Master } from '@prisma/client'
3
+
4
+ export class MasterRdsEntity {
5
+ @ApiProperty()
6
+ id: string
7
+
8
+ @ApiProperty()
9
+ cpk: string
10
+
11
+ @ApiProperty()
12
+ csk: string
13
+
14
+ @ApiProperty()
15
+ pk: string
16
+
17
+ @ApiProperty()
18
+ sk: string
19
+
20
+ @ApiProperty()
21
+ masterType: string
22
+
23
+ @ApiProperty()
24
+ masterTypeCode: string
25
+
26
+ @ApiProperty()
27
+ masterCode: string
28
+
29
+ @ApiProperty()
30
+ tenantCode: string
31
+
32
+ @ApiProperty()
33
+ seq: number
34
+
35
+ @ApiProperty()
36
+ code: string
37
+
38
+ @ApiProperty()
39
+ name: string
40
+
41
+ @ApiProperty()
42
+ version: number
43
+
44
+ @ApiProperty()
45
+ isDeleted: boolean
46
+
47
+ @ApiProperty()
48
+ createdBy: string
49
+
50
+ @ApiProperty()
51
+ createdIp: string
52
+
53
+ @ApiProperty()
54
+ createdAt: Date
55
+
56
+ @ApiProperty()
57
+ updatedBy: string
58
+
59
+ @ApiProperty()
60
+ updatedIp: string
61
+
62
+ @ApiProperty()
63
+ updatedAt: Date
64
+
65
+ @ApiProperty({ required: false })
66
+ syncFrom?: string
67
+
68
+ @ApiProperty({ required: false })
69
+ syncDate?: Date
70
+
71
+ @ApiProperty({ type: 'object', required: false })
72
+ attributes?: Record<string, any>
73
+
74
+ constructor(data: Partial<Master>) {
75
+ Object.assign(this, data)
76
+ }
77
+ }
@@ -0,0 +1,20 @@
1
+ import { SearchDto } from '@mbc-cqrs-serverless/core'
2
+ import { Transform } from 'class-transformer'
3
+ import { IsBoolean, IsOptional, IsString } from 'class-validator'
4
+
5
+ export class MasterSettingSearchDto extends SearchDto {
6
+ @IsOptional()
7
+ @IsString()
8
+ code?: string
9
+
10
+ @IsOptional()
11
+ @IsString()
12
+ name?: string
13
+
14
+ @IsBoolean()
15
+ @Transform(({ value }) =>
16
+ value === 'true' ? true : value === 'false' ? false : value,
17
+ )
18
+ @IsOptional()
19
+ isDeleted?: boolean
20
+ }
@@ -0,0 +1,11 @@
1
+ import { IsObject, IsOptional, IsString } from 'class-validator'
2
+
3
+ export class MasterSettingUpdateDto {
4
+ @IsString()
5
+ @IsOptional()
6
+ name?: string
7
+
8
+ @IsObject()
9
+ @IsOptional()
10
+ attributes?: object
11
+ }
@@ -0,0 +1,11 @@
1
+ import { CommandEntity } from '@mbc-cqrs-serverless/core'
2
+
3
+ export class MasterCommandEntity extends CommandEntity {
4
+ attributes: Record<string, any>
5
+
6
+ constructor(partial: Partial<MasterCommandEntity>) {
7
+ super()
8
+
9
+ Object.assign(this, partial)
10
+ }
11
+ }
@@ -0,0 +1,13 @@
1
+ import { DataListEntity } from '@mbc-cqrs-serverless/core'
2
+
3
+ import { MasterDataEntity } from './master-data.entity'
4
+
5
+ export class MasterDataListEntity extends DataListEntity {
6
+ items: MasterDataEntity[]
7
+
8
+ constructor(partial: Partial<MasterDataListEntity>) {
9
+ super(partial)
10
+
11
+ Object.assign(this, partial)
12
+ }
13
+ }
@@ -0,0 +1,11 @@
1
+ import { DataEntity } from '@mbc-cqrs-serverless/core'
2
+
3
+ export class MasterDataEntity extends DataEntity {
4
+ attributes: Record<string, any>
5
+
6
+ constructor(partial: Partial<MasterDataEntity>) {
7
+ super(partial)
8
+
9
+ Object.assign(this, partial)
10
+ }
11
+ }
@@ -0,0 +1,75 @@
1
+ import {
2
+ CommandModel,
3
+ IDataSyncHandler,
4
+ KEY_SEPARATOR,
5
+ removeSortKeyVersion,
6
+ } from '@mbc-cqrs-serverless/core'
7
+ import { Injectable, Logger } from '@nestjs/common'
8
+ import { DATA_SK_PREFIX, SETTING_SK_PREFIX } from 'src/helpers/id'
9
+ import { PrismaService } from 'src/prisma'
10
+
11
+ import { MasterCommandEntity } from '../entity/master-command.entity'
12
+
13
+ @Injectable()
14
+ export class MasterDataSyncRdsHandler implements IDataSyncHandler {
15
+ private readonly logger = new Logger(MasterDataSyncRdsHandler.name)
16
+
17
+ constructor(private readonly prismaService: PrismaService) {}
18
+ // MASTER#9999
19
+ // MASTER_SETTING#ABC
20
+
21
+ // MASTER#9999
22
+ // ABC#1
23
+ async up(cmd: MasterCommandEntity): Promise<any> {
24
+ const sk = removeSortKeyVersion(cmd.sk)
25
+ const skSplit = sk.split(KEY_SEPARATOR)
26
+
27
+ const masterType =
28
+ skSplit[0] === SETTING_SK_PREFIX ? SETTING_SK_PREFIX : DATA_SK_PREFIX
29
+ const masterTypeCode = skSplit[0]
30
+ await this.prismaService.master.upsert({
31
+ where: {
32
+ id: cmd.id,
33
+ },
34
+ update: {
35
+ csk: cmd.sk,
36
+ name: cmd.name,
37
+ version: cmd.version,
38
+ seq: cmd.seq,
39
+ updatedAt: cmd.updatedAt,
40
+ updatedBy: cmd.updatedBy,
41
+ updatedIp: cmd.updatedIp,
42
+ isDeleted: cmd.isDeleted || false,
43
+
44
+ attributes: cmd.attributes as object,
45
+ },
46
+ create: {
47
+ id: cmd.id,
48
+ cpk: cmd.pk,
49
+ csk: cmd.sk,
50
+ pk: cmd.pk,
51
+ sk,
52
+ masterType,
53
+ masterTypeCode,
54
+ masterCode: skSplit[1],
55
+ tenantCode: cmd.tenantCode,
56
+ code: sk,
57
+ name: cmd.name,
58
+ version: cmd.version,
59
+ seq: cmd.seq,
60
+ createdAt: cmd.createdAt,
61
+ createdBy: cmd.createdBy,
62
+ createdIp: cmd.createdIp,
63
+ updatedAt: cmd.updatedAt,
64
+ updatedBy: cmd.updatedBy,
65
+ updatedIp: cmd.updatedIp,
66
+ isDeleted: cmd.isDeleted || false,
67
+
68
+ attributes: cmd.attributes as object,
69
+ },
70
+ })
71
+ }
72
+ async down(cmd: CommandModel): Promise<any> {
73
+ this.logger.debug(cmd)
74
+ }
75
+ }
@@ -0,0 +1,3 @@
1
+ import { StepFunctionTaskEvent } from '@mbc-cqrs-serverless/task'
2
+
3
+ export class MasterSfnTaskEvent extends StepFunctionTaskEvent {}
@@ -0,0 +1,334 @@
1
+ import {
2
+ DataModel,
3
+ DataService,
4
+ DynamoDbService,
5
+ EventHandler,
6
+ extractInvokeContext,
7
+ IEventHandler,
8
+ IInvoke,
9
+ KEY_SEPARATOR,
10
+ } from '@mbc-cqrs-serverless/core'
11
+ import {
12
+ MasterDataService,
13
+ MasterSettingService,
14
+ } from '@mbc-cqrs-serverless/master'
15
+ import { RotateByEnum, SequencesService } from '@mbc-cqrs-serverless/sequence'
16
+ import { Logger } from '@nestjs/common'
17
+ import { Prisma } from '@prisma/client'
18
+ import { chunk } from 'lodash'
19
+ import {
20
+ DATA_SK_PREFIX,
21
+ DEFAULT_TENANT_CODE,
22
+ generateMasterPk,
23
+ genSequenceSk,
24
+ parseId,
25
+ sequencePk,
26
+ } from 'src/helpers/id'
27
+ import { PrismaService } from 'src/prisma'
28
+
29
+ import {
30
+ CopyType,
31
+ DataCopyMode,
32
+ DataCopyOptionDto,
33
+ MasterCopyDto,
34
+ } from '../dto/master-copy.dto'
35
+ import { MasterSfnTaskEvent } from './master-sfn-task.event'
36
+
37
+ const BATCH_SIZE = 100
38
+
39
+ @EventHandler(MasterSfnTaskEvent)
40
+ export class MasterSfnTaskEventHandler
41
+ implements IEventHandler<MasterSfnTaskEvent>
42
+ {
43
+ private readonly logger = new Logger(MasterSfnTaskEventHandler.name)
44
+ private sequenceTableName
45
+
46
+ constructor(
47
+ private readonly dynamoDbService: DynamoDbService,
48
+
49
+ private readonly prismaService: PrismaService,
50
+ private readonly masterSettingService: MasterSettingService,
51
+ private readonly masterDataService: MasterDataService,
52
+ private readonly dataService: DataService,
53
+ private readonly sequencesService: SequencesService,
54
+ ) {
55
+ this.sequenceTableName = this.dynamoDbService.getTableName('sequences')
56
+ }
57
+
58
+ async execute(event: MasterSfnTaskEvent): Promise<any> {
59
+ const invokeContext = extractInvokeContext()
60
+ const masterCopyDto = event.input?.input as unknown as MasterCopyDto
61
+
62
+ this.logger.debug('sfn-event:masterCopyDto:', masterCopyDto)
63
+
64
+ const { masterSettingId, targetTenants, copyType, dataCopyOption } =
65
+ masterCopyDto
66
+ const targetTenant = `${targetTenants[0]}`
67
+
68
+ const setting = await this.fetchSetting(masterSettingId)
69
+ const masterCode = this.getMasterCodeFromSetting(setting)
70
+ if (copyType === CopyType.SETTING_ONLY || copyType === CopyType.BOTH) {
71
+ await this.copySettingToTenant(setting, targetTenant, invokeContext)
72
+ }
73
+
74
+ if (copyType === CopyType.DATA_ONLY || copyType === CopyType.BOTH) {
75
+ const isCopySequence = await this.shouldCopySequence(
76
+ setting,
77
+ targetTenant,
78
+ )
79
+ if (isCopySequence) {
80
+ await this.copySeqToTenant(setting, targetTenant)
81
+ }
82
+
83
+ const dataToCopy = await this.fetchMasterData(masterCode, dataCopyOption)
84
+
85
+ await this.copyDataToTenant(dataToCopy, targetTenant, invokeContext)
86
+ }
87
+
88
+ this.logger.debug('Completed copy process for tenant:', targetTenant)
89
+ return { message: 'Copy successfully', event }
90
+ }
91
+
92
+ private async fetchSetting(id: string) {
93
+ const setting = await this.dataService.getItem(parseId(id))
94
+ this.logger.debug('sfn-event-setting', setting)
95
+ return setting
96
+ }
97
+
98
+ private async fetchMasterData(
99
+ masterCode: string,
100
+ dataCopyOption?: DataCopyOptionDto,
101
+ tenant: string = DEFAULT_TENANT_CODE,
102
+ ): Promise<Prisma.MasterUncheckedCreateInput[]> {
103
+ const where: Prisma.MasterWhereInput = {
104
+ masterType: DATA_SK_PREFIX,
105
+ masterTypeCode: masterCode,
106
+ pk: `MASTER${KEY_SEPARATOR}${tenant}`,
107
+ }
108
+
109
+ if (dataCopyOption?.mode === DataCopyMode.PARTIAL) {
110
+ where.id = { in: dataCopyOption.id }
111
+ }
112
+
113
+ this.logger.debug('sfn-event-whereCondition', where)
114
+ const data = await this.prismaService.master.findMany({ where })
115
+ this.logger.debug('sfn-event-dataToCopy', data.length)
116
+ return data
117
+ }
118
+
119
+ private async copySettingToTenant(
120
+ setting: DataModel,
121
+ tenantCode: string,
122
+ invokeContext: IInvoke,
123
+ ) {
124
+ const sk = setting.sk
125
+ const pk = generateMasterPk(tenantCode)
126
+ const masterCode = this.getMasterCodeFromSetting(setting)
127
+
128
+ const tenantSetting = await this.dataService.getItem({ pk, sk })
129
+
130
+ this.logger.debug(
131
+ 'sfn-event-copySettingToTenant-tenantSetting',
132
+ tenantSetting,
133
+ )
134
+
135
+ if (tenantSetting && tenantSetting.isDeleted === false) {
136
+ this.logger.debug('sfn-event-copySettingToTenant-updateSetting', {
137
+ pk,
138
+ sk,
139
+ })
140
+
141
+ await this.masterSettingService.updateSetting(
142
+ { pk, sk },
143
+ {
144
+ name: setting.name,
145
+ code: masterCode,
146
+ settingValue: setting.attributes as object,
147
+ tenantCode,
148
+ },
149
+ {
150
+ invokeContext,
151
+ },
152
+ )
153
+ } else {
154
+ this.logger.debug('sfn-event-copySettingToTenant-createTenantSetting', {
155
+ pk,
156
+ sk,
157
+ })
158
+
159
+ await this.masterSettingService.createTenantSetting(
160
+ {
161
+ name: setting.name,
162
+ code: masterCode,
163
+ settingValue: setting.attributes,
164
+ tenantCode,
165
+ },
166
+ { invokeContext },
167
+ )
168
+ }
169
+ }
170
+
171
+ private async copyDataToTenant(
172
+ dataToCopy: Prisma.MasterUncheckedCreateInput[],
173
+ tenantCode: string,
174
+ invokeContext: IInvoke,
175
+ ) {
176
+ const chunks = chunk(dataToCopy, BATCH_SIZE)
177
+ for (const batch of chunks) {
178
+ await Promise.all(
179
+ batch.map(async (data) => {
180
+ const parts = data.sk.split(KEY_SEPARATOR)
181
+ const sk =
182
+ parts.length > 1 && parts[1].trim() === ''
183
+ ? `${data.sk}${data.masterCode}`
184
+ : data.sk
185
+ const pk = generateMasterPk(tenantCode)
186
+
187
+ const tenantData = await this.dataService.getItem({ pk, sk })
188
+
189
+ this.logger.debug('sfn-event-copyDataToTenant-tenantData', tenantData)
190
+
191
+ // des tenant data is exist and not deleted => update des data same as src data
192
+ if (tenantData && tenantData.isDeleted === false) {
193
+ this.logger.debug('sfn-event-copyDataToTenant-update', { pk, sk })
194
+
195
+ return this.masterDataService.update(
196
+ { pk, sk },
197
+ {
198
+ name: data.name,
199
+ attributes: data.attributes as object,
200
+ isDeleted: data.isDeleted,
201
+ seq: data.seq,
202
+ },
203
+ {
204
+ invokeContext,
205
+ },
206
+ )
207
+ }
208
+
209
+ // src data is deleted => do nothing
210
+ if (data.isDeleted === true) return
211
+
212
+ // src data is exist => create des data
213
+ return this.masterDataService.create(
214
+ {
215
+ code: data.masterCode,
216
+ tenantCode,
217
+ name: data.name,
218
+ settingCode: data.masterTypeCode,
219
+ attributes: data.attributes as object,
220
+ seq: data.seq,
221
+ },
222
+ { invokeContext },
223
+ )
224
+ }),
225
+ )
226
+ }
227
+ }
228
+
229
+ private async shouldCopySequence(
230
+ setting: DataModel,
231
+ tenantCode: string,
232
+ ): Promise<boolean> {
233
+ const fields = setting?.attributes?.['fields'] || []
234
+ const codeField = fields.find((f) => f.physicalName === 'code')
235
+ if (codeField?.dataType !== 'auto_number') {
236
+ this.logger.debug('Sequence not required: code field not auto_number')
237
+ return false
238
+ }
239
+
240
+ const jcciSeqKey = this.generateSequenceKey(tenantCode, setting)
241
+ const { seq: jcciSeq = 0 } =
242
+ (await this.sequencesService.getCurrentSequence(jcciSeqKey)) ?? {}
243
+ this.logger.debug('sfn-event-shouldCopySequence-jcciSeq', {
244
+ jcciSeqKey,
245
+ jcciSeq,
246
+ })
247
+
248
+ if (jcciSeq === 0) {
249
+ this.logger.debug('Skipping sequence copy: JCCI sequence is 0')
250
+ return false
251
+ }
252
+
253
+ const tenantSeqKey = this.generateSequenceKey(tenantCode, setting)
254
+ const { seq: tenantSeq } =
255
+ (await this.sequencesService.getCurrentSequence(tenantSeqKey)) ?? {}
256
+ this.logger.debug('sfn-event-shouldCopySequence-tenantSeq', {
257
+ tenantSeqKey,
258
+ tenantSeq,
259
+ })
260
+
261
+ if (tenantSeq === undefined || tenantSeq === null) {
262
+ this.logger.debug('Tenant sequence missing: copying sequence required')
263
+ return true
264
+ }
265
+
266
+ if (jcciSeq > tenantSeq) {
267
+ this.logger.debug('Tenant sequence is behind: copying required')
268
+ return true
269
+ }
270
+
271
+ this.logger.debug('Tenant sequence is up to date or ahead: no copy needed')
272
+ return false
273
+ }
274
+
275
+ private async copySeqToTenant(setting: DataModel, tenantCode: string) {
276
+ const fields = setting?.attributes?.['fields'] || []
277
+ const codeField = fields.find((f) => f.physicalName === 'code')
278
+ const typeCode = codeField.formatCode ?? codeField.dataFormat
279
+
280
+ const jcciSeqKey = this.generateSequenceKey(tenantCode, setting)
281
+ const { seq: jcciSeq = 0 } =
282
+ (await this.sequencesService.getCurrentSequence(jcciSeqKey)) ?? {}
283
+
284
+ const tenantSeqKey = this.generateSequenceKey(tenantCode, setting)
285
+ const { seq: tenantSeq = 0 } =
286
+ (await this.sequencesService.getCurrentSequence(tenantSeqKey)) ?? {}
287
+
288
+ const distance = jcciSeq - tenantSeq
289
+ this.logger.debug('Copying sequence gap:', {
290
+ jcciSeq,
291
+ tenantSeq,
292
+ distance,
293
+ })
294
+
295
+ this.logger.debug('putItem', {
296
+ pk: tenantSeqKey.pk,
297
+ sk: tenantSeqKey.sk,
298
+ code: tenantSeqKey.sk,
299
+ name: tenantSeqKey.sk.split(KEY_SEPARATOR).at(-1),
300
+ seq: jcciSeq,
301
+ tenantCode,
302
+ type: typeCode,
303
+ })
304
+
305
+ this.logger.debug('this.sequenceTableName', this.sequenceTableName)
306
+
307
+ await this.dynamoDbService.putItem(this.sequenceTableName, {
308
+ pk: tenantSeqKey.pk,
309
+ sk: tenantSeqKey.sk,
310
+ code: tenantSeqKey.sk,
311
+ name: tenantSeqKey.sk.split(KEY_SEPARATOR).at(-1),
312
+ seq: jcciSeq,
313
+ tenantCode,
314
+ type: typeCode,
315
+ })
316
+ }
317
+
318
+ private generateSequenceKey(tenantCode: string, setting: DataModel) {
319
+ const fields = setting?.attributes?.['fields'] || []
320
+ const codeField = fields.find((f) => f.physicalName === 'code')
321
+ const seqSk = codeField.formatCode ?? codeField.dataFormat
322
+
323
+ const pk = sequencePk(tenantCode)
324
+ const masterCode = this.getMasterCodeFromSetting(setting)
325
+ const sk = genSequenceSk(seqSk, masterCode, RotateByEnum.NONE)
326
+
327
+ return { pk, sk }
328
+ }
329
+
330
+ private getMasterCodeFromSetting(setting: DataModel): string {
331
+ const parts = setting.sk.split(KEY_SEPARATOR)
332
+ return parts[1]
333
+ }
334
+ }
@@ -0,0 +1,19 @@
1
+ import { Prisma } from '@prisma/client'
2
+
3
+ export function getOrderBy(order: string) {
4
+ let orderValue: Prisma.SortOrder = Prisma.SortOrder.asc,
5
+ orderKey = order
6
+ if (order.startsWith('-')) {
7
+ orderValue = Prisma.SortOrder.desc
8
+ orderKey = order.slice(1)
9
+ }
10
+
11
+ return { [orderKey]: orderValue }
12
+ }
13
+
14
+ export function getOrderBys<T>(orders: string[]): T[] {
15
+ if (!orders) {
16
+ return undefined
17
+ }
18
+ return orders.map((order) => getOrderBy(order)) as T[]
19
+ }
@@ -0,0 +1,42 @@
1
+ import { KEY_SEPARATOR } from '@mbc-cqrs-serverless/core'
2
+ import { RotateByEnum } from '@mbc-cqrs-serverless/sequence'
3
+
4
+ import { MASTER_PK_PREFIX } from './key'
5
+
6
+ const genSequenceSk = (
7
+ seqSettingCode: string,
8
+ settingCode: string,
9
+ rotateBy: RotateByEnum,
10
+ ) =>
11
+ `${seqSettingCode}${KEY_SEPARATOR}${settingCode}${KEY_SEPARATOR}${rotateBy}`
12
+
13
+ const sequencePk = (tenantCode: string) => `SEQ${KEY_SEPARATOR}${tenantCode}`
14
+
15
+ function generateMasterPk(tenantCode: string) {
16
+ return `${MASTER_PK_PREFIX}${KEY_SEPARATOR}${tenantCode}`
17
+ }
18
+
19
+ function parsePk(pk: string): { type: string; tenantCode: string } {
20
+ if (pk.split(KEY_SEPARATOR).length !== 2) {
21
+ throw new Error('Invalid PK')
22
+ }
23
+ const [type, tenantCode] = pk.split(KEY_SEPARATOR)
24
+ return {
25
+ type,
26
+ tenantCode,
27
+ }
28
+ }
29
+ function parseId(masterId: string): { pk: string; sk: string } {
30
+ const parts = masterId.split('#')
31
+
32
+ if (parts.length < 4 || parts[0] !== 'MASTER') {
33
+ throw new Error('Invalid masterId format')
34
+ }
35
+
36
+ const pk = `${parts[0]}#${parts[1]}`
37
+ const sk = `${parts[2]}#${parts[3]}`
38
+
39
+ return { pk, sk }
40
+ }
41
+
42
+ export { generateMasterPk, genSequenceSk, parseId, parsePk, sequencePk }
@@ -0,0 +1,3 @@
1
+ export * from './get-order'
2
+ export * from './id'
3
+ export * from './key'
@@ -0,0 +1,11 @@
1
+ const MASTER_PK_PREFIX = 'MASTER'
2
+ const SETTING_SK_PREFIX = 'MASTER_SETTING'
3
+ const DATA_SK_PREFIX = 'MASTER_DATA'
4
+ const MASTER_COPY_SK_PREFIX = 'MASTER_COPY'
5
+
6
+ export {
7
+ DATA_SK_PREFIX,
8
+ MASTER_COPY_SK_PREFIX,
9
+ MASTER_PK_PREFIX,
10
+ SETTING_SK_PREFIX,
11
+ }