Skip to content

Commit 78a7c35

Browse files
author
Benni Rogge
committed
Merge branch 'master' into kotlin_153
2 parents d915a99 + 0ebf75f commit 78a7c35

File tree

65 files changed

+2466
-432
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+2466
-432
lines changed

.github/workflows/main.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: PushLSQLWebApp
2+
3+
permissions:
4+
id-token: write
5+
on:
6+
push:
7+
branches:
8+
- master
9+
10+
jobs:
11+
buildAndDeploy:
12+
runs-on: ubuntu-latest
13+
env:
14+
STEP_S3_BUCKET: amazon-dynamodb-labs.com
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v3
18+
with:
19+
submodules: 'recursive'
20+
fetch-depth: '0'
21+
- name: Setup Python
22+
uses: actions/setup-python@v4
23+
with:
24+
python-version: '3.10'
25+
- name: Configure AWS Credentials
26+
uses: aws-actions/configure-aws-credentials@v1
27+
with:
28+
aws-region: us-east-1
29+
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
30+
- name: S3SyncStaticWeb
31+
run: aws s3 sync ./workshops/relational-migration/webapp/ s3://amazon-dynamodb-labs-static/static/relational-migration/web/
32+

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ workshops/relational-migration/setenvd.sh
2828
workshops/relational-migration/source-tables/app_db.*
2929

3030
**/.aws-sam/
31+
**.db

examples/NERDS/recipe-example/backend/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,47 @@ To start development:
9393
3. Rebuild the application (`npm run build-backend`)
9494
4. Start the local API (`npm run start-backend`)
9595
5. Test your changes using the provided npm scripts or tools like Postman
96+
97+
## Sample Data
98+
99+
Please notice that we will be updating the sample data while we introduce new functionality.
100+
101+
### Release one (user and recipes)
102+
103+
#### User information
104+
105+
```json
106+
{
107+
"email": "user@example.com",
108+
"name": "John Doe",
109+
"nickname": "thedoe",
110+
"dietaryPreferences": [],
111+
"description": "Welcome to my recipe page! I hope you enjoy my food!",
112+
"pictureUrl": "url path",
113+
"profileThumbnails": "url path"
114+
}
115+
```
116+
117+
#### New Recipe
118+
119+
```json
120+
{
121+
"recipeName": "Fish and Chips",
122+
"recipeDescription": "The best fish and chips you will ever taste",
123+
"recipePrepTime": 40,
124+
"recipeCookTime": 10,
125+
"ingridients": [
126+
{ "Cod": 1, "units": "piece", "comment": "Don't use frozen pieces" },
127+
{ "Potatoes": 1, "units": "piece", "comment": "1 large" },
128+
{ "Oil": 20, "units": "oz", "comment": "Frying oil, use your favorite" }
129+
],
130+
"recipeComments": 0,
131+
"recipeReactions": { "likes": 0 },
132+
"recipeStars": 5,
133+
"recipePresentation": "I really love fish and chips, this is my recipe",
134+
"recipeDescription": "Welcome to my recipe page! I hope you enjoy my food!",
135+
"recipePictureUrl": "url path",
136+
"recipeThumbnails": "url path",
137+
"recipePhotos": ["url path", "url path"]
138+
}
139+
```
Binary file not shown.

examples/NERDS/recipe-example/backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"show-tables": "aws dynamodb list-tables --endpoint-url http://$npm_package_config_ddbhost:$npm_package_config_ddbport --no-cli-page",
2929
"scan-recipes": "aws dynamodb scan --table-name $npm_package_config_ddbRecipeTable --endpoint-url http://$npm_package_config_ddbhost:$npm_package_config_ddbport --no-cli-page",
3030
"build-backend": "sam build",
31-
"start-backend": "parent_dir_name=$(basename $(pwd)) && sam local start-api --host $npm_package_config_ddbhost --env-vars env.json --docker-network ${parent_dir_name}_default --debug",
31+
"start-backend": "parent_dir_name=$(basename $(pwd)) && sam local start-api --host $npm_package_config_ddbhost --env-vars env.json --docker-network ${parent_dir_name}_default --debug ",
3232
"create-user": "curl -s -X POST http://$npm_package_config_ddbhost:$npm_package_config_apiport/users -H 'Content-Type: application/json' -d '{\"email\":\"user@example.com\", \"name\":\"John Doe\",\"phone_number\":\"+1234567890\",\"dietary_preferences\":[\"vegetarian\",\"gluten-free\"]}' -w '\\n%{http_code}'"
3333
},
3434
"jest": {

examples/NERDS/recipe-example/backend/src/recipes/create/index.js

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,61 @@
1-
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";
1+
import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb";
22
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
33
import { nanoid } from "nanoid";
44

55
const ENDPOINT_OVERRIDE = process.env.DYNAMODB_ENDPOINT || undefined;
66
const clientConfig = ENDPOINT_OVERRIDE ? { endpoint: ENDPOINT_OVERRIDE } : {};
77
const client = new DynamoDBClient(clientConfig);
8-
const ddbDocClient = DynamoDBDocumentClient.from(client);
8+
const ddbDocClient = DynamoDBDocument.from(client);
99

1010
export const handler = async (event) => {
1111
try {
12+
const { userId } = event.pathParameters || {};
13+
if (!userId) {
14+
return {
15+
statusCode: 400,
16+
body: JSON.stringify({ message: "userId is required" }),
17+
};
18+
}
19+
1220
const recipe = JSON.parse(event.body);
1321
const recipeId = nanoid();
22+
const timestamp = new Date().toISOString();
1423

15-
const item = {
24+
const recipeItem = {
1625
PK: `RECIPE#${recipeId}`,
1726
SK: `RECIPE#${recipeId}`,
1827
recipeId,
28+
userId,
29+
createdAt: timestamp,
30+
updatedAt: timestamp,
1931
...recipe,
20-
createdAt: new Date().toISOString(),
2132
};
2233

23-
await ddbDocClient.send(
24-
new PutCommand({
25-
TableName: process.env.TABLE_NAME,
26-
Item: item,
27-
})
28-
);
34+
const userRecipeItem = {
35+
PK: `USER#${userId}`,
36+
SK: `RECIPE#${recipeId}`,
37+
recipeId,
38+
userId,
39+
recipeName: recipe.name, // Assuming the recipe has a 'name' field
40+
createdAt: timestamp,
41+
};
42+
43+
await ddbDocClient.transactWrite({
44+
TransactItems: [
45+
{
46+
Put: {
47+
TableName: process.env.TABLE_NAME,
48+
Item: recipeItem,
49+
},
50+
},
51+
{
52+
Put: {
53+
TableName: process.env.TABLE_NAME,
54+
Item: userRecipeItem,
55+
},
56+
},
57+
],
58+
});
2959

3060
return {
3161
statusCode: 201,

examples/NERDS/recipe-example/backend/src/recipes/delete/index.js

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,65 @@
1-
import { DynamoDBDocumentClient, DeleteCommand } from "@aws-sdk/lib-dynamodb";
1+
import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb";
22
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
33

44
const ENDPOINT_OVERRIDE = process.env.DYNAMODB_ENDPOINT || undefined;
55
const clientConfig = ENDPOINT_OVERRIDE ? { endpoint: ENDPOINT_OVERRIDE } : {};
66
const client = new DynamoDBClient(clientConfig);
7-
const ddbDocClient = DynamoDBDocumentClient.from(client);
7+
const ddbDocClient = DynamoDBDocument.from(client);
88

99
export const handler = async (event) => {
1010
try {
11-
const recipeId = event.pathParameters.recipeId;
11+
const { userId, recipeId } = event.pathParameters;
1212

13-
await ddbDocClient.send(
14-
new DeleteCommand({
15-
TableName: process.env.TABLE_NAME,
16-
Key: {
17-
PK: `RECIPE#${recipeId}`,
18-
SK: `RECIPE#${recipeId}`,
13+
if (!userId || !recipeId) {
14+
return {
15+
statusCode: 400,
16+
body: JSON.stringify({
17+
message: "Both userId and recipeId are required",
18+
}),
19+
};
20+
}
21+
22+
// First, get the recipe to ensure it exists and belongs to the user
23+
const getResult = await ddbDocClient.get({
24+
TableName: process.env.TABLE_NAME,
25+
Key: {
26+
PK: `RECIPE#${recipeId}`,
27+
SK: `RECIPE#${recipeId}`,
28+
},
29+
});
30+
31+
if (!getResult.Item || getResult.Item.userId !== userId) {
32+
return {
33+
statusCode: 404,
34+
body: JSON.stringify({
35+
message: "Recipe not found or does not belong to the user",
36+
}),
37+
};
38+
}
39+
40+
// If the recipe exists and belongs to the user, delete both items
41+
await ddbDocClient.transactWrite({
42+
TransactItems: [
43+
{
44+
Delete: {
45+
TableName: process.env.TABLE_NAME,
46+
Key: {
47+
PK: `RECIPE#${recipeId}`,
48+
SK: `RECIPE#${recipeId}`,
49+
},
50+
},
51+
},
52+
{
53+
Delete: {
54+
TableName: process.env.TABLE_NAME,
55+
Key: {
56+
PK: `USER#${userId}`,
57+
SK: `RECIPE#${recipeId}`,
58+
},
59+
},
1960
},
20-
})
21-
);
61+
],
62+
});
2263

2364
return {
2465
statusCode: 200,

examples/NERDS/recipe-example/backend/src/recipes/list/index.js

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,35 @@ const ddbDocClient = DynamoDBDocumentClient.from(client);
88

99
export const handler = async (event) => {
1010
try {
11-
const { userId, sort, search, ingredient, tag, prepTime } =
12-
event.queryStringParameters || {};
11+
const { userId } = event.pathParameters || {};
12+
13+
if (!userId) {
14+
return {
15+
statusCode: 400,
16+
body: JSON.stringify({ message: "userId is required" }),
17+
};
18+
}
1319

1420
let queryParams = {
1521
TableName: process.env.TABLE_NAME,
16-
KeyConditionExpression: "begins_with(PK, :pk)",
22+
KeyConditionExpression: "PK = :pk AND begins_with(SK, :sk)",
1723
ExpressionAttributeValues: {
18-
":pk": "RECIPE#",
24+
":pk": `USER#${userId}`,
25+
":sk": "RECIPE#",
1926
},
2027
};
2128

22-
if (userId) {
23-
queryParams.FilterExpression = "userId = :userId";
24-
queryParams.ExpressionAttributeValues[":userId"] = userId;
25-
}
26-
27-
// Add more conditions based on other query parameters (sort, search, ingredient, tag, prepTime)
28-
// This would require additional GSIs or complex filtering logic
29-
3029
const { Items } = await ddbDocClient.send(new QueryCommand(queryParams));
3130

31+
// Transform the items to remove the PK and SK
32+
const recipes = Items.map((item) => {
33+
const { PK, SK, ...recipeData } = item;
34+
return { ...recipeData };
35+
});
36+
3237
return {
3338
statusCode: 200,
34-
body: JSON.stringify(Items),
39+
body: JSON.stringify(recipes),
3540
};
3641
} catch (error) {
3742
console.error("Error listing recipes:", error);

examples/NERDS/recipe-example/backend/template.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ Resources:
9090
Type: Api
9191
Properties:
9292
RestApiId: !Ref RecipeAPI
93-
Path: /recipes
93+
Path: /users/{userId}/recipes
9494
Method: POST
9595

9696
GetRecipeFunction:
@@ -129,7 +129,7 @@ Resources:
129129
Type: Api
130130
Properties:
131131
RestApiId: !Ref RecipeAPI
132-
Path: /recipes/{recipeId}
132+
Path: /users/{userId}/recipes/{recipeId}
133133
Method: DELETE
134134

135135
ListRecipesFunction:
@@ -142,7 +142,7 @@ Resources:
142142
Type: Api
143143
Properties:
144144
RestApiId: !Ref RecipeAPI
145-
Path: /recipes
145+
Path: /users/{userId}/recipes
146146
Method: GET
147147

148148
# DynamoDB table for storing recipes and users

examples/SDK/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Explore a wide range of operations for managing individual items in your DynamoD
1717
| BatchGet | [<img src="https://img.shields.io/badge/Java-007396?style=flat-square&logo=openjdk&logoColor=white" alt="Java" >](./java/sdk_v1/data_plane/WorkingWithItems/BatchGetItem.java) [<img src="https://img.shields.io/badge/.NET-512BD4?style=flat-square&logo=dotnet&logoColor=white" alt=".NET" >](./dotnet/sdk_v2/WorkingWithItems/BatchGetItem.cs) [<img src="https://img.shields.io/badge/Node.js-339933?style=flat-square&logo=nodedotjs&logoColor=white" alt="Node.js" >](./node.js/sdk_v3/data_plane/WorkingWithItems/batch-get.js) [<img src="https://img.shields.io/badge/Python-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python" >](./python/data_plane/WorkingWithItems/batch_get.py) [<img src="https://img.shields.io/badge/Rust-000000?style=flat-square&logo=rust&logoColor=white" alt="Rust" >](./rust/data_plane/working_with_items/src/batch-get/main.rs) |
1818
| BatchWrite | [<img src="https://img.shields.io/badge/Java-007396?style=flat-square&logo=openjdk&logoColor=white" alt="Java" >](./java/sdk_v1/data_plane/WorkingWithItems/BatchWriteItem.java) [<img src="https://img.shields.io/badge/.NET-512BD4?style=flat-square&logo=dotnet&logoColor=white" alt=".NET" >](./dotnet/sdk_v2/WorkingWithItems//BatchWriteItem.cs) [<img src="https://img.shields.io/badge/Node.js-339933?style=flat-square&logo=nodedotjs&logoColor=white" alt="Node.js" >](./node.js/sdk_v3/data_plane/WorkingWithItems/batch-write.js) [<img src="https://img.shields.io/badge/Python-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python" >](./python/data_plane/WorkingWithItems/batch_write.py) [<img src="https://img.shields.io/badge/Rust-000000?style=flat-square&logo=rust&logoColor=white" alt="Rust" >](./rust/data_plane/working_with_items/src/batch-write/main.rs) |
1919
| DeleteItem | [<img src="https://img.shields.io/badge/Java-007396?style=flat-square&logo=openjdk&logoColor=white" alt="Java" >](./java/sdk_v1/data_plane/WorkingWithItems/DeleteItem.java) [<img src="https://img.shields.io/badge/.NET-512BD4?style=flat-square&logo=dotnet&logoColor=white" alt=".NET" >](./dotnet/sdk_v2/WorkingWithItems/DeleteItem.cs) [<img src="https://img.shields.io/badge/Node.js-339933?style=flat-square&logo=nodedotjs&logoColor=white" alt="Node.js" >](./node.js/sdk_v3/data_plane/WorkingWithItems/delete-item.js) [<img src="https://img.shields.io/badge/Python-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python" >](./python/data_plane/WorkingWithItems/delete_item.py) [<img src="https://img.shields.io/badge/Rust-000000?style=flat-square&logo=rust&logoColor=white" alt="Rust" >](./rust/data_plane/working_with_items/src/delete-item/main.rs) |
20-
| DeleteItemConditional | [<img src="https://img.shields.io/badge/.NET-512BD4?style=flat-square&logo=dotnet&logoColor=white" alt=".NET" >](./dotnet/sdk_v2/WorkingWithItems/DeleteItemConditional.cs) [<img src="https://img.shields.io/badge/Python-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python" >](./python/data_plane/WorkingWithItems/delete_item2.py) |
20+
| DeleteItemConditional | [<img src="https://img.shields.io/badge/.NET-512BD4?style=flat-square&logo=dotnet&logoColor=white" alt=".NET" >](./dotnet/sdk_v2/WorkingWithItems/DeleteItemConditional.cs) [<img src="https://img.shields.io/badge/Python-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python" >](./python/data_plane/WorkingWithItems/delete_item_conditional.py) |
2121
| GetItem | [<img src="https://img.shields.io/badge/Java-007396?style=flat-square&logo=openjdk&logoColor=white" alt="Java" >](./java/sdk_v1/data_plane/WorkingWithItems/GetItem.java) [<img src="https://img.shields.io/badge/.NET-512BD4?style=flat-square&logo=dotnet&logoColor=white" alt=".NET" >](./dotnet/sdk_v2/WorkingWithItems/GetItem.cs) [<img src="https://img.shields.io/badge/Node.js-339933?style=flat-square&logo=nodedotjs&logoColor=white" alt="Node.js" >](./node.js/sdk_v3/data_plane/WorkingWithItems//get-item.js) [<img src="https://img.shields.io/badge/Python-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python" >](./python/data_plane/WorkingWithItems/get_item.py) [<img src="https://img.shields.io/badge/Rust-000000?style=flat-square&logo=rust&logoColor=white" alt="Rust" >](./rust/data_plane/working_with_items/src/get-item/main.rs) |
2222
| PutItem | [<img src="https://img.shields.io/badge/Java-007396?style=flat-square&logo=openjdk&logoColor=white" alt="Java" >](./java/sdk_v1/data_plane/WorkingWithItems/PutItem.java) [<img src="https://img.shields.io/badge/.NET-512BD4?style=flat-square&logo=dotnet&logoColor=white" alt=".NET" >](./dotnet/sdk_v2/WorkingWithItems/PutItem.cs) [<img src="https://img.shields.io/badge/Node.js-339933?style=flat-square&logo=nodedotjs&logoColor=white" alt="Node.js" >](./node.js/sdk_v3/data_plane/WorkingWithItems//put-item.js) [<img src="https://img.shields.io/badge/Python-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python" >](./python/data_plane/WorkingWithItems/put_item.py) [<img src="https://img.shields.io/badge/Rust-000000?style=flat-square&logo=rust&logoColor=white" alt="Rust" >](./rust/data_plane/working_with_items/src/put-item/main.rs) |
2323
| PutItemConditional | [<img src="https://img.shields.io/badge/Java-007396?style=flat-square&logo=openjdk&logoColor=white" alt="Java" >](./java/sdk_v1/data_plane/WorkingWithItems/PutItemConditional.java) [<img src="https://img.shields.io/badge/.NET-512BD4?style=flat-square&logo=dotnet&logoColor=white" alt=".NET" >](./dotnet/sdk_v2/WorkingWithItems/PutItemConditional.cs) [<img src="https://img.shields.io/badge/Node.js-339933?style=flat-square&logo=nodedotjs&logoColor=white" alt="Node.js" >](./node.js/sdk_v3/data_plane/WorkingWithItems//put-item-conditional.js) [<img src="https://img.shields.io/badge/Python-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python" >](./python/data_plane/WorkingWithItems/put_item_conditional.py) [<img src="https://img.shields.io/badge/Rust-000000?style=flat-square&logo=rust&logoColor=white" alt="Rust" >](./rust/data_plane/working_with_items/src/conditional-put-item/main.rs) |

0 commit comments

Comments
 (0)