@baadal-sdk/dapi 0.31.3 → 0.31.5

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baadal-sdk/dapi",
3
- "version": "0.31.3",
3
+ "version": "0.31.5",
4
4
  "description": "Dead-simple API wrappers",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -39,55 +39,9 @@
39
39
  "ncu": "ncu",
40
40
  "ncuu": "ncu -u"
41
41
  },
42
- "devDependencies": {
43
- "@babel/core": "7.15.5",
44
- "@babel/preset-env": "7.15.6",
45
- "@babel/preset-react": "^7.14.5",
46
- "@babel/preset-typescript": "7.15.0",
47
- "@types/mime-types": "^2.1.1",
48
- "@types/node": "16.9.6",
49
- "@types/react": "^16.0.0",
50
- "@types/react-dom": "^16.0.0",
51
- "@types/rimraf": "3.0.2",
52
- "@typescript-eslint/eslint-plugin": "4.31.2",
53
- "@typescript-eslint/parser": "4.31.2",
54
- "babel-loader": "8.2.2",
55
- "eslint": "7.32.0",
56
- "eslint-config-airbnb": "18.2.1",
57
- "eslint-config-airbnb-typescript": "14.0.0",
58
- "eslint-config-prettier": "8.3.0",
59
- "eslint-plugin-import": "2.24.2",
60
- "eslint-plugin-jsx-a11y": "6.4.1",
61
- "eslint-plugin-prettier": "4.0.0",
62
- "eslint-plugin-react": "7.26.0",
63
- "eslint-plugin-react-hooks": "4.2.0",
64
- "husky": "4.3.8",
65
- "jsonlint": "1.6.3",
66
- "lint-staged": "11.1.2",
67
- "npm-run-all": "4.1.5",
68
- "prettier": "2.4.1",
69
- "shx": "0.3.3",
70
- "ts-node": "10.2.1",
71
- "typescript": "4.4.3",
72
- "webpack": "5.53.0",
73
- "webpack-cli": "4.8.0",
74
- "webpack-node-externals": "3.0.0"
75
- },
76
- "dependencies": {
77
- "@aws-sdk/client-dynamodb": "^3.34.0",
78
- "@aws-sdk/client-s3": "^3.36.0",
79
- "@aws-sdk/lib-dynamodb": "^3.34.0",
80
- "@baadal-sdk/utils": "0.12.0",
81
- "@octokit/core": "^3.5.1",
82
- "chalk": "^2.4.2",
83
- "core-js": "^3.18.0",
84
- "mime-types": "^2.1.33",
85
- "rimraf": "^3.0.2",
86
- "short-uuid": "^4.2.0"
87
- },
88
42
  "peerDependencies": {
89
- "react": ">=16.8.6",
90
- "react-dom": ">=16.8.6"
43
+ "react": ">=18.2.0",
44
+ "react-dom": ">=18.2.0"
91
45
  },
92
46
  "peerDependenciesMeta": {
93
47
  "react": {
@@ -97,6 +51,52 @@
97
51
  "optional": true
98
52
  }
99
53
  },
54
+ "dependencies": {
55
+ "@aws-sdk/client-dynamodb": "^3.264.0",
56
+ "@aws-sdk/client-s3": "^3.264.0",
57
+ "@aws-sdk/lib-dynamodb": "^3.264.0",
58
+ "@baadal-sdk/utils": "0.12.0",
59
+ "@octokit/core": "^4.2.0",
60
+ "chalk": "^5.2.0",
61
+ "core-js": "^3.27.2",
62
+ "mime-types": "^2.1.35",
63
+ "rimraf": "^4.1.2",
64
+ "short-uuid": "^4.2.2"
65
+ },
66
+ "devDependencies": {
67
+ "@babel/core": "7.20.12",
68
+ "@babel/preset-env": "7.20.2",
69
+ "@babel/preset-react": "^7.18.6",
70
+ "@babel/preset-typescript": "7.18.6",
71
+ "@types/mime-types": "2.1.1",
72
+ "@types/node": "18.11.19",
73
+ "@types/react": "18.0.27",
74
+ "@types/react-dom": "18.0.10",
75
+ "@types/rimraf": "3.0.2",
76
+ "@typescript-eslint/eslint-plugin": "5.50.0",
77
+ "@typescript-eslint/parser": "5.50.0",
78
+ "babel-loader": "9.1.2",
79
+ "eslint": "8.33.0",
80
+ "eslint-config-airbnb": "19.0.4",
81
+ "eslint-config-airbnb-typescript": "17.0.0",
82
+ "eslint-config-prettier": "8.6.0",
83
+ "eslint-plugin-import": "2.27.5",
84
+ "eslint-plugin-jsx-a11y": "6.7.1",
85
+ "eslint-plugin-prettier": "4.2.1",
86
+ "eslint-plugin-react": "7.32.2",
87
+ "eslint-plugin-react-hooks": "4.6.0",
88
+ "husky": "4.3.8",
89
+ "jsonlint": "1.6.3",
90
+ "lint-staged": "11.1.2",
91
+ "npm-run-all": "4.1.5",
92
+ "prettier": "2.8.3",
93
+ "shx": "0.3.4",
94
+ "ts-node": "10.9.1",
95
+ "typescript": "4.9.5",
96
+ "webpack": "5.75.0",
97
+ "webpack-cli": "5.0.1",
98
+ "webpack-node-externals": "3.0.0"
99
+ },
100
100
  "browserslist": [
101
101
  "chrome >= 23"
102
102
  ],
package/src/aws/db.ts CHANGED
@@ -33,7 +33,7 @@ import { dbClient } from './client';
33
33
  import { StringIndexable } from '../common/common.model';
34
34
  import { CustomError } from '../common/error';
35
35
  import { warn, error } from '../common/logger';
36
- import { BATCH_SIZE, CHUNK_SIZE } from '../common/const';
36
+ import { BATCH_SIZE, CHUNK_SIZE, MAX_RETRY_ATTEMPTS } from '../common/const';
37
37
 
38
38
  const DynamoDBError = (msg: string) => new CustomError(msg, { name: 'DynamoDBError' });
39
39
 
@@ -70,7 +70,13 @@ const tryInit = (silent = false) => {
70
70
  // auto-initialize on load
71
71
  tryInit(true);
72
72
 
73
- const writeItemForceHelper = async <T = any>(table: string, item: T, key: string, i: number): Promise<T | null> => {
73
+ const writeItemForceHelper = async <T = any>(
74
+ table: string,
75
+ item: T,
76
+ key: string,
77
+ i: number,
78
+ imax?: number
79
+ ): Promise<T | null> => {
74
80
  if (!dbClient.client) tryInit();
75
81
  if (!dbClient.client) return null;
76
82
  if (!table || !item) return null;
@@ -80,7 +86,7 @@ const writeItemForceHelper = async <T = any>(table: string, item: T, key: string
80
86
  }
81
87
  const cmdParams: PutCommandInput = { TableName: table, Item: item, ConditionExpression: `attribute_not_exists(${key})` };
82
88
  const command = new PutCommand(cmdParams);
83
- const numberOfAttempts = 3;
89
+ const numberOfAttempts = imax ?? MAX_RETRY_ATTEMPTS;
84
90
 
85
91
  try {
86
92
  await dbClient.client.send(command);
@@ -88,11 +94,15 @@ const writeItemForceHelper = async <T = any>(table: string, item: T, key: string
88
94
  if (err.name === 'ConditionalCheckFailedException') {
89
95
  if (i < numberOfAttempts - 1) {
90
96
  (item as any)[key] = short.uuid(); // new primary key
91
- const ret: T | null = await writeItemForceHelper(table, item, key, i + 1);
97
+ const ret: T | null = await writeItemForceHelper(table, item, key, i + 1, imax);
92
98
  return ret;
93
99
  }
94
100
  console.error('PutCommandInput:', cmdParams);
95
- error('[ERROR] Maximum attempts overflow!');
101
+ if (numberOfAttempts === 1) {
102
+ error(`[ERROR] An item with the same key(${(item as any)[key]}) already exists!`);
103
+ } else {
104
+ error('[ERROR] Maximum attempts overflow!');
105
+ }
96
106
  }
97
107
  return null;
98
108
  }
@@ -106,6 +116,8 @@ export interface WriteItemForceInput<T = any> {
106
116
  key?: string;
107
117
  }
108
118
 
119
+ export type WriteItemUniqueInput<T = any> = WriteItemForceInput<T>;
120
+
109
121
  /**
110
122
  * Write an item to a DynamoDB table, retry in case of key conflict
111
123
  * @param input input command object
@@ -130,6 +142,32 @@ export const writeItemForce = async <T = any>(input: WriteItemForceInput<T>): Pr
130
142
  return writeItemForceHelper<T>(input.table, input.item, key, 0);
131
143
  };
132
144
 
145
+ /**
146
+ * Write an item (uniquely) to a DynamoDB table.
147
+ * Unlike `writeItemForce`, it does not retry in case of key conflict
148
+ * Unlike `writeItem`, it does not overwrite an item with the same key (if it exists)
149
+ * @param input input command object
150
+ * @returns the created item, null in case of error or key conflict (i.e., if item with same key already exists)
151
+ *
152
+ * ```js
153
+ * writeItemUnique({
154
+ * table: 'lesson_list',
155
+ * item: { id: 'id_001', title: 'My Lesson' },
156
+ * key: 'id',
157
+ * });
158
+ *
159
+ * interface WriteItemUniqueInput<T = any> {
160
+ * table: string;
161
+ * item: T;
162
+ * key?: string; // default: `id`
163
+ * }
164
+ * ```
165
+ */
166
+ export const writeItemUnique = async <T = any>(input: WriteItemUniqueInput<T>): Promise<T | null> => {
167
+ const key = input.key || 'id';
168
+ return writeItemForceHelper<T>(input.table, input.item, key, 0, 1);
169
+ };
170
+
133
171
  export interface WriteItemInput {
134
172
  table: string;
135
173
  item: StringIndexable;
@@ -1,2 +1,3 @@
1
1
  export const BATCH_SIZE = 20; // (max) number of items in a batch request
2
2
  export const CHUNK_SIZE = 10; // (max) number of parallel requests at a time
3
+ export const MAX_RETRY_ATTEMPTS = 3; // (max) number of retry attempts in case of key conflict