Skip to content

Commit 422a468

Browse files
authored
JavaScript (v3): S3 - Audit 🧵 3/3 (#6958)
JavaScript (v3): S3 - Standardize object-locking example.
1 parent cbfaa13 commit 422a468

File tree

3 files changed

+149
-89
lines changed

3 files changed

+149
-89
lines changed

javascriptv3/example_code/libs/scenario/scenario.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ export class Step {
4646
console.log(
4747
`[DEBUG ${new Date().toISOString()}] Handling step: ${
4848
this.constructor.name
49-
}<${this.name}>`
49+
}<${this.name}>`,
5050
);
5151
console.log(
52-
`[DEBUG ${new Date().toISOString()}] State: ${JSON.stringify(state)}`
52+
`[DEBUG ${new Date().toISOString()}] State: ${JSON.stringify(state)}`,
5353
);
5454
}
5555
}
@@ -162,7 +162,7 @@ export class ScenarioInput extends Step {
162162
return true;
163163
}
164164
throw new Error(
165-
`Error handling ScenarioInput. confirmAll was selected for ${this.name} but no default was provided.`
165+
`Error handling ScenarioInput. confirmAll was selected for ${this.name} but no default was provided.`,
166166
);
167167
}
168168

@@ -181,7 +181,7 @@ export class ScenarioInput extends Step {
181181
break;
182182
default:
183183
throw new Error(
184-
`Error handling ScenarioInput, ${this.stepOptions?.type} is not supported.`
184+
`Error handling ScenarioInput, ${this.stepOptions?.type} is not supported.`,
185185
);
186186
}
187187

@@ -203,7 +203,7 @@ export class ScenarioInput extends Step {
203203

204204
if (!rawChoices) {
205205
throw new Error(
206-
`Error handling ScenarioInput. Could not get choices for ${this.name}.`
206+
`Error handling ScenarioInput. Could not get choices for ${this.name}.`,
207207
);
208208
}
209209

@@ -235,7 +235,7 @@ export class ScenarioInput extends Step {
235235
state[this.name] = this.default;
236236
} else if (!result.length) {
237237
throw new Error(
238-
`Error handing ScenarioInput. Result of ${this.name} was empty.`
238+
`Error handing ScenarioInput. Result of ${this.name} was empty.`,
239239
);
240240
} else {
241241
state[this.name] = result;
@@ -261,9 +261,9 @@ export class ScenarioInput extends Step {
261261

262262
if (!result && this.default) {
263263
state[this.name] = this.default;
264-
} else if (!result) {
264+
} else if (result == null) {
265265
throw new Error(
266-
`Error handing ScenarioInput. Result of ${this.name} was empty.`
266+
`Error handing ScenarioInput. Result of ${this.name} was empty.`,
267267
);
268268
} else {
269269
state[this.name] = result;
@@ -289,7 +289,7 @@ export class ScenarioInput extends Step {
289289
state[this.name] = this.default;
290290
} else if (result === undefined) {
291291
throw new Error(
292-
`Error handing ScenarioInput. Result of ${this.name} was empty.`
292+
`Error handing ScenarioInput. Result of ${this.name} was empty.`,
293293
);
294294
} else {
295295
state[this.name] = result;
@@ -362,7 +362,7 @@ export class ScenarioAction extends Step {
362362
output &&
363363
(await this.stepOptions.whileConfig.output.handle(
364364
state,
365-
stepHandlerOptions
365+
stepHandlerOptions,
366366
));
367367
await input.handle(state, stepHandlerOptions);
368368
runAction = whileFn(state);

javascriptv3/example_code/s3/scenarios/object-locking/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
confirmUpdateRetention,
2020
createBuckets,
2121
createBucketsAction,
22+
getBucketPrefix,
2223
populateBuckets,
2324
populateBucketsAction,
2425
setLegalHoldFileEnabledAction,
@@ -45,6 +46,7 @@ export const getWorkflowStages = (scenarios, initialState = {}) => {
4546
welcome(scenarios),
4647
welcomeContinue(scenarios),
4748
exitOnFalse(scenarios, "welcomeContinue"),
49+
getBucketPrefix(scenarios),
4850
createBuckets(scenarios),
4951
confirmCreateBuckets(scenarios),
5052
exitOnFalse(scenarios, "confirmCreateBuckets"),

javascriptv3/example_code/s3/scenarios/object-locking/setup.steps.js

Lines changed: 137 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,15 @@ import {
1212
PutObjectRetentionCommand,
1313
ObjectLockLegalHoldStatus,
1414
ObjectLockRetentionMode,
15+
GetBucketVersioningCommand,
16+
BucketAlreadyExists,
17+
BucketAlreadyOwnedByYou,
18+
S3ServiceException,
19+
waitUntilBucketExists,
1520
} from "@aws-sdk/client-s3";
1621

22+
import { retry } from "@aws-doc-sdk-examples/lib/utils/util-timers.js";
23+
1724
/**
1825
* @typedef {import("@aws-doc-sdk-examples/lib/scenario/index.js")} Scenarios
1926
*/
@@ -22,19 +29,26 @@ import {
2229
* @typedef {import("@aws-sdk/client-s3").S3Client} S3Client
2330
*/
2431

25-
const bucketPrefix = "js-object-locking";
32+
/**
33+
* @param {Scenarios} scenarios
34+
*/
35+
const getBucketPrefix = (scenarios) =>
36+
new scenarios.ScenarioInput(
37+
"bucketPrefix",
38+
"Provide a prefix that will be used for bucket creation.",
39+
{ type: "input", default: "amzn-s3-demo-bucket" },
40+
);
2641

2742
/**
2843
* @param {Scenarios} scenarios
29-
* @param {S3Client} client
3044
*/
3145
const createBuckets = (scenarios) =>
3246
new scenarios.ScenarioOutput(
3347
"createBuckets",
34-
`The following buckets will be created:
35-
${bucketPrefix}-no-lock with object lock False.
36-
${bucketPrefix}-lock-enabled with object lock True.
37-
${bucketPrefix}-retention-after-creation with object lock False.`,
48+
(state) => `The following buckets will be created:
49+
${state.bucketPrefix}-no-lock with object lock False.
50+
${state.bucketPrefix}-lock-enabled with object lock True.
51+
${state.bucketPrefix}-retention-after-creation with object lock False.`,
3852
{ preformatted: true },
3953
);
4054

@@ -52,22 +66,42 @@ const confirmCreateBuckets = (scenarios) =>
5266
*/
5367
const createBucketsAction = (scenarios, client) =>
5468
new scenarios.ScenarioAction("createBucketsAction", async (state) => {
55-
const noLockBucketName = `${bucketPrefix}-no-lock`;
56-
const lockEnabledBucketName = `${bucketPrefix}-lock-enabled`;
57-
const retentionBucketName = `${bucketPrefix}-retention-after-creation`;
69+
const noLockBucketName = `${state.bucketPrefix}-no-lock`;
70+
const lockEnabledBucketName = `${state.bucketPrefix}-lock-enabled`;
71+
const retentionBucketName = `${state.bucketPrefix}-retention-after-creation`;
5872

59-
await client.send(new CreateBucketCommand({ Bucket: noLockBucketName }));
60-
await client.send(
61-
new CreateBucketCommand({
62-
Bucket: lockEnabledBucketName,
63-
ObjectLockEnabledForBucket: true,
64-
}),
65-
);
66-
await client.send(new CreateBucketCommand({ Bucket: retentionBucketName }));
73+
try {
74+
await client.send(new CreateBucketCommand({ Bucket: noLockBucketName }));
75+
await waitUntilBucketExists({ client }, { Bucket: noLockBucketName });
76+
await client.send(
77+
new CreateBucketCommand({
78+
Bucket: lockEnabledBucketName,
79+
ObjectLockEnabledForBucket: true,
80+
}),
81+
);
82+
await waitUntilBucketExists(
83+
{ client },
84+
{ Bucket: lockEnabledBucketName },
85+
);
86+
await client.send(
87+
new CreateBucketCommand({ Bucket: retentionBucketName }),
88+
);
89+
await waitUntilBucketExists({ client }, { Bucket: retentionBucketName });
6790

68-
state.noLockBucketName = noLockBucketName;
69-
state.lockEnabledBucketName = lockEnabledBucketName;
70-
state.retentionBucketName = retentionBucketName;
91+
state.noLockBucketName = noLockBucketName;
92+
state.lockEnabledBucketName = lockEnabledBucketName;
93+
state.retentionBucketName = retentionBucketName;
94+
} catch (caught) {
95+
if (
96+
caught instanceof BucketAlreadyExists ||
97+
caught instanceof BucketAlreadyOwnedByYou
98+
) {
99+
console.error(`${caught.name}: ${caught.message}`);
100+
state.earlyExit = true;
101+
} else {
102+
throw caught;
103+
}
104+
}
71105
});
72106

73107
/**
@@ -76,13 +110,13 @@ const createBucketsAction = (scenarios, client) =>
76110
const populateBuckets = (scenarios) =>
77111
new scenarios.ScenarioOutput(
78112
"populateBuckets",
79-
`The following test files will be created:
80-
file0.txt in ${bucketPrefix}-no-lock.
81-
file1.txt in ${bucketPrefix}-no-lock.
82-
file0.txt in ${bucketPrefix}-lock-enabled.
83-
file1.txt in ${bucketPrefix}-lock-enabled.
84-
file0.txt in ${bucketPrefix}-retention-after-creation.
85-
file1.txt in ${bucketPrefix}-retention-after-creation.`,
113+
(state) => `The following test files will be created:
114+
file0.txt in ${state.bucketPrefix}-no-lock.
115+
file1.txt in ${state.bucketPrefix}-no-lock.
116+
file0.txt in ${state.bucketPrefix}-lock-enabled.
117+
file1.txt in ${state.bucketPrefix}-lock-enabled.
118+
file0.txt in ${state.bucketPrefix}-retention-after-creation.
119+
file1.txt in ${state.bucketPrefix}-retention-after-creation.`,
86120
{ preformatted: true },
87121
);
88122

@@ -102,54 +136,64 @@ const confirmPopulateBuckets = (scenarios) =>
102136
*/
103137
const populateBucketsAction = (scenarios, client) =>
104138
new scenarios.ScenarioAction("populateBucketsAction", async (state) => {
105-
await client.send(
106-
new PutObjectCommand({
107-
Bucket: state.noLockBucketName,
108-
Key: "file0.txt",
109-
Body: "Content",
110-
ChecksumAlgorithm: ChecksumAlgorithm.SHA256,
111-
}),
112-
);
113-
await client.send(
114-
new PutObjectCommand({
115-
Bucket: state.noLockBucketName,
116-
Key: "file1.txt",
117-
Body: "Content",
118-
ChecksumAlgorithm: ChecksumAlgorithm.SHA256,
119-
}),
120-
);
121-
await client.send(
122-
new PutObjectCommand({
123-
Bucket: state.lockEnabledBucketName,
124-
Key: "file0.txt",
125-
Body: "Content",
126-
ChecksumAlgorithm: ChecksumAlgorithm.SHA256,
127-
}),
128-
);
129-
await client.send(
130-
new PutObjectCommand({
131-
Bucket: state.lockEnabledBucketName,
132-
Key: "file1.txt",
133-
Body: "Content",
134-
ChecksumAlgorithm: ChecksumAlgorithm.SHA256,
135-
}),
136-
);
137-
await client.send(
138-
new PutObjectCommand({
139-
Bucket: state.retentionBucketName,
140-
Key: "file0.txt",
141-
Body: "Content",
142-
ChecksumAlgorithm: ChecksumAlgorithm.SHA256,
143-
}),
144-
);
145-
await client.send(
146-
new PutObjectCommand({
147-
Bucket: state.retentionBucketName,
148-
Key: "file1.txt",
149-
Body: "Content",
150-
ChecksumAlgorithm: ChecksumAlgorithm.SHA256,
151-
}),
152-
);
139+
try {
140+
await client.send(
141+
new PutObjectCommand({
142+
Bucket: state.noLockBucketName,
143+
Key: "file0.txt",
144+
Body: "Content",
145+
ChecksumAlgorithm: ChecksumAlgorithm.SHA256,
146+
}),
147+
);
148+
await client.send(
149+
new PutObjectCommand({
150+
Bucket: state.noLockBucketName,
151+
Key: "file1.txt",
152+
Body: "Content",
153+
ChecksumAlgorithm: ChecksumAlgorithm.SHA256,
154+
}),
155+
);
156+
await client.send(
157+
new PutObjectCommand({
158+
Bucket: state.lockEnabledBucketName,
159+
Key: "file0.txt",
160+
Body: "Content",
161+
ChecksumAlgorithm: ChecksumAlgorithm.SHA256,
162+
}),
163+
);
164+
await client.send(
165+
new PutObjectCommand({
166+
Bucket: state.lockEnabledBucketName,
167+
Key: "file1.txt",
168+
Body: "Content",
169+
ChecksumAlgorithm: ChecksumAlgorithm.SHA256,
170+
}),
171+
);
172+
await client.send(
173+
new PutObjectCommand({
174+
Bucket: state.retentionBucketName,
175+
Key: "file0.txt",
176+
Body: "Content",
177+
ChecksumAlgorithm: ChecksumAlgorithm.SHA256,
178+
}),
179+
);
180+
await client.send(
181+
new PutObjectCommand({
182+
Bucket: state.retentionBucketName,
183+
Key: "file1.txt",
184+
Body: "Content",
185+
ChecksumAlgorithm: ChecksumAlgorithm.SHA256,
186+
}),
187+
);
188+
} catch (caught) {
189+
if (caught instanceof S3ServiceException) {
190+
console.error(
191+
`Error from S3 while uploading object. ${caught.name}: ${caught.message}`,
192+
);
193+
} else {
194+
throw caught;
195+
}
196+
}
153197
});
154198

155199
/**
@@ -158,8 +202,10 @@ const populateBucketsAction = (scenarios, client) =>
158202
const updateRetention = (scenarios) =>
159203
new scenarios.ScenarioOutput(
160204
"updateRetention",
161-
`A bucket can be configured to use object locking with a default retention period.
162-
A default retention period will be configured for ${bucketPrefix}-retention-after-creation.`,
205+
(
206+
state,
207+
) => `A bucket can be configured to use object locking with a default retention period.
208+
A default retention period will be configured for ${state.bucketPrefix}-retention-after-creation.`,
163209
{ preformatted: true },
164210
);
165211

@@ -189,6 +235,17 @@ const updateRetentionAction = (scenarios, client) =>
189235
}),
190236
);
191237

238+
const getBucketVersioning = new GetBucketVersioningCommand({
239+
Bucket: state.retentionBucketName,
240+
});
241+
242+
await retry({ intervalInMs: 500, maxRetries: 10 }, async () => {
243+
const { Status } = await client.send(getBucketVersioning);
244+
if (Status !== "Enabled") {
245+
throw new Error(`Bucket versioning is not enabled.`);
246+
}
247+
});
248+
192249
await client.send(
193250
new PutObjectLockConfigurationCommand({
194251
Bucket: state.retentionBucketName,
@@ -211,8 +268,8 @@ const updateRetentionAction = (scenarios, client) =>
211268
const updateLockPolicy = (scenarios) =>
212269
new scenarios.ScenarioOutput(
213270
"updateLockPolicy",
214-
`Object lock policies can also be added to existing buckets.
215-
An object lock policy will be added to ${bucketPrefix}-lock-enabled.`,
271+
(state) => `Object lock policies can also be added to existing buckets.
272+
An object lock policy will be added to ${state.bucketPrefix}-lock-enabled.`,
216273
{ preformatted: true },
217274
);
218275

@@ -403,6 +460,7 @@ const setRetentionPeriodFileRetentionAction = (scenarios, client) =>
403460
);
404461

405462
export {
463+
getBucketPrefix,
406464
createBuckets,
407465
confirmCreateBuckets,
408466
createBucketsAction,

0 commit comments

Comments
 (0)