Skip to content

Commit 024057a

Browse files
committed
retain locks
1 parent 764b0da commit 024057a

File tree

6 files changed

+54
-33
lines changed

6 files changed

+54
-33
lines changed

lambdas/sftp-letters/src/__tests__/app/send.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ describe('App', () => {
239239
`${baseUploadDir}/${sftpEnvironment}/batches/${templateId}/${batchId}_MANIFEST.csv`
240240
);
241241

242-
expect(mocks.templateRepository.clearLock).toHaveBeenCalledTimes(1);
243-
expect(mocks.templateRepository.clearLock).toHaveBeenCalledWith(
242+
expect(mocks.templateRepository.finaliseLock).toHaveBeenCalledTimes(1);
243+
expect(mocks.templateRepository.finaliseLock).toHaveBeenCalledWith(
244244
owner,
245245
templateId
246246
);
@@ -355,10 +355,10 @@ describe('App', () => {
355355
expect(mocks.sftpClient.exists).not.toHaveBeenCalled();
356356
expect(mocks.sftpClient.mkdir).not.toHaveBeenCalled();
357357
expect(mocks.sftpClient.put).not.toHaveBeenCalled();
358-
expect(mocks.templateRepository.clearLock).not.toHaveBeenCalled();
358+
expect(mocks.templateRepository.finaliseLock).not.toHaveBeenCalled();
359359
});
360360

361-
test('exits early and does not send if the manifest is already in the SFTP server, clears existing lock', async () => {
361+
test('exits early and does not send if the manifest is already in the SFTP server, finalises existing lock', async () => {
362362
const { app, mocks, logMessages } = setup();
363363

364364
const personalisationParameters = ['pdsField'];
@@ -442,8 +442,8 @@ describe('App', () => {
442442
`${baseUploadDir}/${sftpEnvironment}/batches/${templateId}/${batchId}_MANIFEST.csv`
443443
);
444444

445-
expect(mocks.templateRepository.clearLock).toHaveBeenCalledTimes(1);
446-
expect(mocks.templateRepository.clearLock).toHaveBeenCalledWith(
445+
expect(mocks.templateRepository.finaliseLock).toHaveBeenCalledTimes(1);
446+
expect(mocks.templateRepository.finaliseLock).toHaveBeenCalledWith(
447447
owner,
448448
templateId
449449
);

lambdas/sftp-letters/src/__tests__/infra/template-lock-repository.test.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,13 @@ describe('TemplateLockRepository', () => {
4141
'#sftpSendLockTime': 'sftpSendLockTime',
4242
},
4343
ExpressionAttributeValues: {
44-
':updatedAt': expect.stringMatching(isoDateRegExp),
44+
':condition_1_sftpSendLockTime': 0,
4545
':condition_2_sftpSendLockTime': mockDate.getTime() + sendLockTtlMs,
46+
':updatedAt': expect.stringMatching(isoDateRegExp),
4647
':sftpSendLockTime': mockDate.getTime(),
4748
},
4849
ConditionExpression:
49-
'attribute_not_exists (#sftpSendLockTime) OR #sftpSendLockTime > :condition_2_sftpSendLockTime',
50+
'#sftpSendLockTime <> :condition_1_sftpSendLockTime AND #sftpSendLockTime > :condition_2_sftpSendLockTime OR attribute_not_exists (#sftpSendLockTime)',
5051
Key: {
5152
id: templateId,
5253
owner,
@@ -83,28 +84,30 @@ describe('TemplateLockRepository', () => {
8384
});
8485
});
8586

86-
describe('clearLock', () => {
87-
test('removes lock attribute', async () => {
87+
describe('finaliseLock', () => {
88+
test('unconditionally sets lockTime to zero', async () => {
8889
const { mocks, templateRepository } = setup();
8990

9091
mocks.client.on(UpdateCommand).resolvesOnce({});
9192

92-
await templateRepository.clearLock(owner, templateId);
93+
await templateRepository.finaliseLock(owner, templateId);
94+
9395
expect(mocks.client).toHaveReceivedCommandWith(UpdateCommand, {
9496
ExpressionAttributeNames: {
95-
'#sftpSendLockTime': 'sftpSendLockTime',
9697
'#updatedAt': 'updatedAt',
98+
'#sftpSendLockTime': 'sftpSendLockTime',
9799
},
98100
ExpressionAttributeValues: {
99101
':updatedAt': expect.stringMatching(isoDateRegExp),
102+
':sftpSendLockTime': 0,
100103
},
101104
Key: {
102105
id: templateId,
103106
owner,
104107
},
105108
TableName: templatesTableName,
106109
UpdateExpression:
107-
'SET #updatedAt = :updatedAt REMOVE #sftpSendLockTime',
110+
'SET #sftpSendLockTime = :sftpSendLockTime, #updatedAt = :updatedAt',
108111
});
109112
});
110113
});

lambdas/sftp-letters/src/app/send.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export class App {
7575
templateLogger.warn(
7676
'Manifest already exists, assuming duplicate event'
7777
);
78-
await this.templateRepository.clearLock(owner, templateId);
78+
await this.templateRepository.finaliseLock(owner, templateId);
7979
return 'already-sent';
8080
}
8181

@@ -97,7 +97,7 @@ export class App {
9797

9898
templateLogger.info('Removing lock');
9999

100-
await this.templateRepository.clearLock(owner, templateId);
100+
await this.templateRepository.finaliseLock(owner, templateId);
101101

102102
templateLogger.info('Sent proofing request');
103103

lambdas/sftp-letters/src/infra/template-lock-repository.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export class TemplateLockRepository {
1414
const time = this.getDate().getTime();
1515

1616
const update = new TemplateUpdateBuilder(this.templatesTableName, owner, id)
17-
.setLockTime('sftpSendLockTime', time, time + this.lockTtl)
17+
.setLockTimeConditionally('sftpSendLockTime', time, time + this.lockTtl)
1818
.build();
1919

2020
try {
@@ -29,9 +29,9 @@ export class TemplateLockRepository {
2929
return true;
3030
}
3131

32-
async clearLock(owner: string, id: string) {
32+
async finaliseLock(owner: string, id: string) {
3333
const update = new TemplateUpdateBuilder(this.templatesTableName, owner, id)
34-
.removeLockTime('sftpSendLockTime')
34+
.setLockTimeUnconditionally('sftpSendLockTime', 0)
3535
.build();
3636

3737
return await this.client.send(new UpdateCommand(update));

utils/entity-update-command-builder/src/__tests__/template-update-builder.test.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -167,25 +167,29 @@ describe('TemplateBuilder', () => {
167167
});
168168
});
169169

170-
describe('setLockTime', () => {
171-
test('sets lock time if no lock exists', () => {
170+
describe('setLockTimeConditionally', () => {
171+
test('sets lock time if no lock exists, or lock is not finalised (set to zero)', () => {
172172
const builder = new TemplateUpdateBuilder(
173173
mockTableName,
174174
mockOwner,
175175
mockId
176176
);
177177

178-
const res = builder.setLockTime('sftpSendLockTime', 500).build();
178+
const res = builder
179+
.setLockTimeConditionally('sftpSendLockTime', 500)
180+
.build();
179181

180182
expect(res).toEqual({
181-
ConditionExpression: 'attribute_not_exists (#sftpSendLockTime)',
183+
ConditionExpression:
184+
'#sftpSendLockTime <> :condition_1_sftpSendLockTime OR attribute_not_exists (#sftpSendLockTime)',
182185
ExpressionAttributeNames: {
183186
'#sftpSendLockTime': 'sftpSendLockTime',
184187
'#updatedAt': 'updatedAt',
185188
},
186189
ExpressionAttributeValues: {
187190
':sftpSendLockTime': 500,
188191
':updatedAt': '2025-01-01T09:00:00.000Z',
192+
':condition_1_sftpSendLockTime': 0,
189193
},
190194
Key: {
191195
id: 'Hello2',
@@ -204,16 +208,19 @@ describe('TemplateBuilder', () => {
204208
mockId
205209
);
206210

207-
const res = builder.setLockTime('sftpSendLockTime', 1000, 1500).build();
211+
const res = builder
212+
.setLockTimeConditionally('sftpSendLockTime', 1000, 1500)
213+
.build();
208214

209215
expect(res).toEqual({
210216
ConditionExpression:
211-
'attribute_not_exists (#sftpSendLockTime) OR #sftpSendLockTime > :condition_2_sftpSendLockTime',
217+
'#sftpSendLockTime <> :condition_1_sftpSendLockTime AND #sftpSendLockTime > :condition_2_sftpSendLockTime OR attribute_not_exists (#sftpSendLockTime)',
212218
ExpressionAttributeNames: {
213219
'#sftpSendLockTime': 'sftpSendLockTime',
214220
'#updatedAt': 'updatedAt',
215221
},
216222
ExpressionAttributeValues: {
223+
':condition_1_sftpSendLockTime': 0,
217224
':condition_2_sftpSendLockTime': 1500,
218225
':sftpSendLockTime': 1000,
219226
':updatedAt': '2025-01-01T09:00:00.000Z',
@@ -229,22 +236,25 @@ describe('TemplateBuilder', () => {
229236
});
230237
});
231238

232-
describe('removeLockTime', () => {
233-
test('clears lockTime attribute', () => {
239+
describe('setLockTimeUnconditionally', () => {
240+
test('sets lock time without conditions', () => {
234241
const builder = new TemplateUpdateBuilder(
235242
mockTableName,
236243
mockOwner,
237244
mockId
238245
);
239246

240-
const res = builder.removeLockTime('sftpSendLockTime').build();
247+
const res = builder
248+
.setLockTimeUnconditionally('sftpSendLockTime', 500)
249+
.build();
241250

242251
expect(res).toEqual({
243252
ExpressionAttributeNames: {
244253
'#sftpSendLockTime': 'sftpSendLockTime',
245254
'#updatedAt': 'updatedAt',
246255
},
247256
ExpressionAttributeValues: {
257+
':sftpSendLockTime': 500,
248258
':updatedAt': '2025-01-01T09:00:00.000Z',
249259
},
250260
Key: {
@@ -253,7 +263,7 @@ describe('TemplateBuilder', () => {
253263
},
254264
TableName: 'TABLE_NAME',
255265
UpdateExpression:
256-
'SET #updatedAt = :updatedAt REMOVE #sftpSendLockTime',
266+
'SET #sftpSendLockTime = :sftpSendLockTime, #updatedAt = :updatedAt',
257267
});
258268
});
259269
});

utils/entity-update-command-builder/src/template-update-builder.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,32 @@ export class TemplateUpdateBuilder extends EntityUpdateBuilder<MergedTemplate> {
3434
return this;
3535
}
3636

37-
setLockTime(
37+
setLockTimeConditionally(
3838
lockField: 'sftpSendLockTime',
3939
timeMs: number,
4040
lockExpiryTimeMs?: number
4141
) {
4242
this.updateBuilder
4343
.setValue(lockField, timeMs)
44-
.fnCondition(lockField, null, 'attribute_not_exists');
44+
.andCondition(lockField, 0, '<>');
4545

4646
if (lockExpiryTimeMs) {
47-
this.updateBuilder.orCondition(lockField, lockExpiryTimeMs, '>');
47+
this.updateBuilder.andCondition(lockField, lockExpiryTimeMs, '>');
4848
}
4949

50+
this.updateBuilder.fnCondition(
51+
lockField,
52+
null,
53+
'attribute_not_exists',
54+
false,
55+
'OR'
56+
);
57+
5058
return this;
5159
}
5260

53-
removeLockTime(lockField: 'sftpSendLockTime') {
54-
this.updateBuilder.removeAttribute(lockField);
61+
setLockTimeUnconditionally(lockField: 'sftpSendLockTime', timeMs: number) {
62+
this.updateBuilder.setValue(lockField, timeMs);
5563

5664
return this;
5765
}

0 commit comments

Comments
 (0)