@dismissible/nestjs-dynamodb-storage 1.0.3-alpha.0b30e6d.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.
- package/README.md +334 -0
- package/bin/dismissible-dynamodb-setup.js +85 -0
- package/package.json +57 -0
- package/src/dynamodb-client.service.d.ts +19 -0
- package/src/dynamodb-client.service.js +114 -0
- package/src/dynamodb-client.service.js.map +1 -0
- package/src/dynamodb-storage.adapter.d.ts +16 -0
- package/src/dynamodb-storage.adapter.js +63 -0
- package/src/dynamodb-storage.adapter.js.map +1 -0
- package/src/dynamodb-storage.config.d.ts +9 -0
- package/src/dynamodb-storage.config.js +39 -0
- package/src/dynamodb-storage.config.js.map +1 -0
- package/src/dynamodb-storage.module.d.ts +17 -0
- package/src/dynamodb-storage.module.js +77 -0
- package/src/dynamodb-storage.module.js.map +1 -0
- package/src/index.d.ts +4 -0
- package/src/index.js +8 -0
- package/src/index.js.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://dismissible.io" target="_blank"><img src="../../docs/images/dismissible_logo.png" width="120" alt="Dismissible" /></a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">Never Show The Same Thing Twice!</p>
|
|
6
|
+
<p align="center">
|
|
7
|
+
<a href="https://www.npmjs.com/package/@dismissible/nestjs-dynamodb-storage" target="_blank"><img src="https://img.shields.io/npm/v/@dismissible/nestjs-dynamodb-storage.svg" alt="NPM Version" /></a>
|
|
8
|
+
<a href="https://github.com/dismissibleio/dismissible-api/blob/main/LICENSE" target="_blank"><img src="https://img.shields.io/npm/l/@dismissible/nestjs-dynamodb-storage.svg" alt="Package License" /></a>
|
|
9
|
+
<a href="https://www.npmjs.com/package/@dismissible/nestjs-dynamodb-storage" target="_blank"><img src="https://img.shields.io/npm/dm/@dismissible/nestjs-dynamodb-storage.svg" alt="NPM Downloads" /></a>
|
|
10
|
+
<a href="https://github.com/dismissibleio/dismissible-api" target="_blank"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/dismissibleio/dismissible-api/release.yml"></a>
|
|
11
|
+
<a href="https://paypal.me/joshstuartx" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
Dismissible manages the state of your UI elements across sessions, so your users see what matters, once! No more onboarding messages reappearing on every tab, no more notifications haunting users across devices. Dismissible syncs dismissal state everywhere, so every message is intentional, never repetitive.
|
|
15
|
+
|
|
16
|
+
# @dismissible/nestjs-dynamodb-storage
|
|
17
|
+
|
|
18
|
+
DynamoDB storage adapter for the Dismissible system using AWS SDK v3.
|
|
19
|
+
|
|
20
|
+
> **Part of the Dismissible API** - This library is part of the [Dismissible API](https://dismissible.io) ecosystem. Visit [dismissible.io](https://dismissible.io) for more information and documentation.
|
|
21
|
+
|
|
22
|
+
## Overview
|
|
23
|
+
|
|
24
|
+
This library provides a production-ready DynamoDB storage adapter for the Dismissible system. It uses AWS SDK v3 for DynamoDB access and includes:
|
|
25
|
+
|
|
26
|
+
- Persistent storage of dismissible items using DynamoDB
|
|
27
|
+
- Support for LocalStack/DynamoDB Local for local development
|
|
28
|
+
- Automatic document serialization/deserialization
|
|
29
|
+
- Full TypeScript support
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install @dismissible/nestjs-dynamodb-storage
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Prerequisites
|
|
38
|
+
|
|
39
|
+
- AWS account with DynamoDB access (or LocalStack for local development)
|
|
40
|
+
- Node.js 24 or higher
|
|
41
|
+
- AWS SDK for JavaScript v3 compatible environment
|
|
42
|
+
|
|
43
|
+
## Getting Started
|
|
44
|
+
|
|
45
|
+
### 1. Create DynamoDB Table
|
|
46
|
+
|
|
47
|
+
Before using the storage module, you must create the DynamoDB table.
|
|
48
|
+
|
|
49
|
+
#### Option A: Using the CLI Tool (Recommended)
|
|
50
|
+
|
|
51
|
+
The package includes a CLI helper for table creation:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# For local development with LocalStack
|
|
55
|
+
DISMISSIBLE_STORAGE_DYNAMODB_ENDPOINT=http://localhost:4566 npx dismissible-dynamodb-setup
|
|
56
|
+
|
|
57
|
+
# For production (uses default AWS credentials)
|
|
58
|
+
DISMISSIBLE_STORAGE_DYNAMODB_AWS_REGION=us-east-1 npx dismissible-dynamodb-setup
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
#### Option B: Using AWS Console or CLI
|
|
62
|
+
|
|
63
|
+
Create the table manually using AWS Console or AWS CLI:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
aws dynamodb create-table \
|
|
67
|
+
--table-name items \
|
|
68
|
+
--attribute-definitions \
|
|
69
|
+
AttributeName=userId,AttributeType=S \
|
|
70
|
+
AttributeName=id,AttributeType=S \
|
|
71
|
+
--key-schema \
|
|
72
|
+
AttributeName=userId,KeyType=HASH \
|
|
73
|
+
AttributeName=id,KeyType=RANGE \
|
|
74
|
+
--billing-mode PAY_PER_REQUEST \
|
|
75
|
+
--region us-east-1
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Example item stored in the table:**
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"userId": "user-123",
|
|
83
|
+
"id": "welcome-banner-v2",
|
|
84
|
+
"createdAt": "2024-01-15T10:30:00.000Z",
|
|
85
|
+
"dismissedAt": "2024-01-15T12:00:00.000Z"
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### Option C: Using CloudFormation/SAM
|
|
90
|
+
|
|
91
|
+
```yaml
|
|
92
|
+
Resources:
|
|
93
|
+
DismissibleTable:
|
|
94
|
+
Type: AWS::DynamoDB::Table
|
|
95
|
+
Properties:
|
|
96
|
+
TableName: dismissible-items
|
|
97
|
+
BillingMode: PAY_PER_REQUEST
|
|
98
|
+
AttributeDefinitions:
|
|
99
|
+
- AttributeName: userId
|
|
100
|
+
AttributeType: S
|
|
101
|
+
- AttributeName: id
|
|
102
|
+
AttributeType: S
|
|
103
|
+
KeySchema:
|
|
104
|
+
- AttributeName: userId
|
|
105
|
+
KeyType: HASH
|
|
106
|
+
- AttributeName: id
|
|
107
|
+
KeyType: RANGE
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 2. Module Configuration
|
|
111
|
+
|
|
112
|
+
Import and configure the module in your NestJS application:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { Module } from '@nestjs/common';
|
|
116
|
+
import { DismissibleModule } from '@dismissible/nestjs-core';
|
|
117
|
+
import { DynamoDBStorageModule } from '@dismissible/nestjs-dynamodb-storage';
|
|
118
|
+
import { LoggerModule } from '@dismissible/nestjs-logger';
|
|
119
|
+
|
|
120
|
+
@Module({
|
|
121
|
+
imports: [
|
|
122
|
+
LoggerModule.forRoot({}),
|
|
123
|
+
DynamoDBStorageModule.forRoot({
|
|
124
|
+
tableName: 'items',
|
|
125
|
+
region: 'us-east-1',
|
|
126
|
+
// Optional: endpoint for LocalStack
|
|
127
|
+
endpoint: process.env.DISMISSIBLE_STORAGE_DYNAMODB_ENDPOINT,
|
|
128
|
+
}),
|
|
129
|
+
DismissibleModule.forRoot({
|
|
130
|
+
storage: DynamoDBStorageModule,
|
|
131
|
+
}),
|
|
132
|
+
],
|
|
133
|
+
})
|
|
134
|
+
export class AppModule {}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 3. Async Configuration
|
|
138
|
+
|
|
139
|
+
You can also configure the module asynchronously:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import { Module } from '@nestjs/common';
|
|
143
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
144
|
+
import { DynamoDBStorageModule } from '@dismissible/nestjs-dynamodb-storage';
|
|
145
|
+
|
|
146
|
+
@Module({
|
|
147
|
+
imports: [
|
|
148
|
+
ConfigModule.forRoot(),
|
|
149
|
+
DynamoDBStorageModule.forRootAsync({
|
|
150
|
+
imports: [ConfigModule],
|
|
151
|
+
useFactory: (config: ConfigService) => ({
|
|
152
|
+
tableName: config.get<string>('DISMISSIBLE_STORAGE_DYNAMODB_TABLE_NAME')!,
|
|
153
|
+
region: config.get<string>('DISMISSIBLE_STORAGE_DYNAMODB_AWS_REGION'),
|
|
154
|
+
endpoint: config.get<string>('DISMISSIBLE_STORAGE_DYNAMODB_ENDPOINT'),
|
|
155
|
+
accessKeyId: config.get<string>('DISMISSIBLE_STORAGE_DYNAMODB_AWS_ACCESS_KEY_ID'),
|
|
156
|
+
secretAccessKey: config.get<string>('DISMISSIBLE_STORAGE_DYNAMODB_AWS_SECRET_ACCESS_KEY'),
|
|
157
|
+
sessionToken: config.get<string>('DISMISSIBLE_STORAGE_DYNAMODB_AWS_SESSION_TOKEN'),
|
|
158
|
+
}),
|
|
159
|
+
inject: [ConfigService],
|
|
160
|
+
}),
|
|
161
|
+
],
|
|
162
|
+
})
|
|
163
|
+
export class AppModule {}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## DynamoDB Table Schema
|
|
167
|
+
|
|
168
|
+
The library expects a DynamoDB table with the following schema:
|
|
169
|
+
|
|
170
|
+
| Attribute | Type | Key Type | Description |
|
|
171
|
+
| ----------- | ------ | ------------- | ----------------------------- |
|
|
172
|
+
| userId | String | Partition Key | User identifier |
|
|
173
|
+
| id | String | Sort Key | Item identifier |
|
|
174
|
+
| createdAt | String | - | ISO 8601 timestamp |
|
|
175
|
+
| dismissedAt | String | - | ISO 8601 timestamp (nullable) |
|
|
176
|
+
|
|
177
|
+
**Billing Mode:** PAY_PER_REQUEST (on-demand capacity)
|
|
178
|
+
|
|
179
|
+
## API Reference
|
|
180
|
+
|
|
181
|
+
### DynamoDBStorageModule
|
|
182
|
+
|
|
183
|
+
#### `DynamoDBStorageModule.forRoot(options)`
|
|
184
|
+
|
|
185
|
+
Configures the DynamoDB storage module synchronously.
|
|
186
|
+
|
|
187
|
+
**Options:**
|
|
188
|
+
|
|
189
|
+
- `tableName: string` - DynamoDB table name (required)
|
|
190
|
+
- `region?: string` - AWS region (default: us-east-1)
|
|
191
|
+
- `endpoint?: string` - DynamoDB endpoint URL (for LocalStack/DynamoDB Local)
|
|
192
|
+
- `accessKeyId?: string` - AWS access key ID
|
|
193
|
+
- `secretAccessKey?: string` - AWS secret access key
|
|
194
|
+
- `sessionToken?: string` - AWS session token (optional)
|
|
195
|
+
|
|
196
|
+
**Returns:** `DynamicModule`
|
|
197
|
+
|
|
198
|
+
#### `DynamoDBStorageModule.forRootAsync(options)`
|
|
199
|
+
|
|
200
|
+
Configures the DynamoDB storage module asynchronously.
|
|
201
|
+
|
|
202
|
+
**Options:**
|
|
203
|
+
|
|
204
|
+
- `imports?: any[]` - Modules to import
|
|
205
|
+
- `useFactory: (deps) => DynamoDBStorageModuleOptions` - Factory function
|
|
206
|
+
- `inject?: any[]` - Dependencies to inject into the factory
|
|
207
|
+
|
|
208
|
+
**Returns:** `DynamicModule`
|
|
209
|
+
|
|
210
|
+
### DynamoDBStorageAdapter
|
|
211
|
+
|
|
212
|
+
The adapter implements `IDismissibleStorage` and provides:
|
|
213
|
+
|
|
214
|
+
- `get(userId, itemId)` - Retrieve an item
|
|
215
|
+
- `create(item)` - Create a new item
|
|
216
|
+
- `update(item)` - Update an existing item
|
|
217
|
+
|
|
218
|
+
### DynamoDBClientService
|
|
219
|
+
|
|
220
|
+
The service manages the DynamoDB client lifecycle:
|
|
221
|
+
|
|
222
|
+
- Initializes DynamoDBClient with configured credentials
|
|
223
|
+
- Creates DynamoDBDocumentClient for automatic serialization
|
|
224
|
+
- Handles connection lifecycle (no explicit cleanup required)
|
|
225
|
+
|
|
226
|
+
## CLI Tool
|
|
227
|
+
|
|
228
|
+
The package includes a CLI tool for DynamoDB table setup:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# Create table with default settings
|
|
232
|
+
npx dismissible-dynamodb-setup
|
|
233
|
+
|
|
234
|
+
# With custom table name
|
|
235
|
+
DISMISSIBLE_STORAGE_DYNAMODB_TABLE_NAME=my-table npx dismissible-dynamodb-setup
|
|
236
|
+
|
|
237
|
+
# For LocalStack
|
|
238
|
+
DISMISSIBLE_STORAGE_DYNAMODB_ENDPOINT=http://localhost:4566 DISMISSIBLE_STORAGE_DYNAMODB_AWS_REGION=us-east-1 npx dismissible-dynamodb-setup
|
|
239
|
+
|
|
240
|
+
# For AWS
|
|
241
|
+
DISMISSIBLE_STORAGE_DYNAMODB_AWS_REGION=us-west-2 DISMISSIBLE_STORAGE_DYNAMODB_AWS_ACCESS_KEY_ID=xxx DISMISSIBLE_STORAGE_DYNAMODB_AWS_SECRET_ACCESS_KEY=yyy npx dismissible-dynamodb-setup
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Environment Variables
|
|
245
|
+
|
|
246
|
+
When using this package as part of the [`@dismissible/nestjs-api`](), the following environment variables will be available:
|
|
247
|
+
|
|
248
|
+
| Variable | Description | Default |
|
|
249
|
+
| ---------------------------------------------------- | ----------------------- | ----------------- |
|
|
250
|
+
| `DISMISSIBLE_STORAGE_DYNAMODB_TABLE_NAME` | DynamoDB table name | dismissible-items |
|
|
251
|
+
| `DISMISSIBLE_STORAGE_DYNAMODB_AWS_REGION` | AWS region | us-east-1 |
|
|
252
|
+
| `DISMISSIBLE_STORAGE_DYNAMODB_AWS_ACCESS_KEY_ID` | AWS access key ID | - |
|
|
253
|
+
| `DISMISSIBLE_STORAGE_DYNAMODB_AWS_SECRET_ACCESS_KEY` | AWS secret access key | - |
|
|
254
|
+
| `DISMISSIBLE_STORAGE_DYNAMODB_AWS_SESSION_TOKEN` | AWS session token | - |
|
|
255
|
+
| `DISMISSIBLE_STORAGE_DYNAMODB_ENDPOINT` | LocalStack endpoint URL | - |
|
|
256
|
+
|
|
257
|
+
For more information, [see all configuration](/docs/CONFIGURATION.md).
|
|
258
|
+
|
|
259
|
+
## Local Development with LocalStack
|
|
260
|
+
|
|
261
|
+
LocalStack provides a local AWS cloud stack for development. To use it with this library:
|
|
262
|
+
|
|
263
|
+
1. Start LocalStack:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
docker run -d --name localstack -p 4566:4566 localstack/localstack
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
2. Create the table:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
DISMISSIBLE_STORAGE_DYNAMODB_ENDPOINT=http://localhost:4566 DISMISSIBLE_STORAGE_DYNAMODB_AWS_REGION=us-east-1 npx dismissible-dynamodb-setup
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
3. Configure your application:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
DynamoDBStorageModule.forRoot({
|
|
279
|
+
tableName: 'items',
|
|
280
|
+
region: 'us-east-1',
|
|
281
|
+
endpoint: 'http://localhost:4566',
|
|
282
|
+
accessKeyId: 'test',
|
|
283
|
+
secretAccessKey: 'test',
|
|
284
|
+
}),
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Production Considerations
|
|
288
|
+
|
|
289
|
+
1. **IAM Permissions**: Ensure your application has the following permissions:
|
|
290
|
+
|
|
291
|
+
```json
|
|
292
|
+
{
|
|
293
|
+
"Version": "2012-10-17",
|
|
294
|
+
"Statement": [
|
|
295
|
+
{
|
|
296
|
+
"Effect": "Allow",
|
|
297
|
+
"Action": [
|
|
298
|
+
"dynamodb:GetItem",
|
|
299
|
+
"dynamodb:PutItem",
|
|
300
|
+
"dynamodb:UpdateItem",
|
|
301
|
+
"dynamodb:DeleteItem",
|
|
302
|
+
"dynamodb:Scan",
|
|
303
|
+
"dynamodb:BatchWriteItem",
|
|
304
|
+
"dynamodb:CreateTable",
|
|
305
|
+
"dynamodb:DescribeTable"
|
|
306
|
+
],
|
|
307
|
+
"Resource": "arn:aws:dynamodb:*:*:table/dismissible-items"
|
|
308
|
+
}
|
|
309
|
+
]
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
> [!IMPORTANT]
|
|
314
|
+
> **Note:** If you change the `DISMISSIBLE_STORAGE_DYNAMODB_TABLE_NAME` environment variable, you must update the `Resource` ARN in the IAM policy to match your table name. The Resource ARN is restricted to the specific table name (`dismissible-items` by default) to prevent access to other tables.
|
|
315
|
+
|
|
316
|
+
> [!CAUTION]
|
|
317
|
+
> **Best Practice:** While `dynamodb:CreateTable` is included in the permissions above, it's recommended to create the table before deploying your application and avoid running `DISMISSIBLE_RUN_STORAGE_SETUP=true` during production deployments. This reduces the risk of accidental table creation and follows the principle of least privilege.
|
|
318
|
+
|
|
319
|
+
2. **Table Creation**: Create the table before deploying your application. Use the CLI tool or CloudFormation/Terraform.
|
|
320
|
+
|
|
321
|
+
3. **Monitoring**: Enable CloudWatch metrics for DynamoDB to monitor throughput and latency.
|
|
322
|
+
|
|
323
|
+
4. **Backups**: Consider enabling point-in-time recovery for disaster recovery.
|
|
324
|
+
|
|
325
|
+
## Related Packages
|
|
326
|
+
|
|
327
|
+
- `@dismissible/nestjs-core` - Main dismissible service
|
|
328
|
+
- `@dismissible/nestjs-storage` - Storage interface
|
|
329
|
+
- `@dismissible/nestjs-item` - Data models
|
|
330
|
+
- `@dismissible/nestjs-logger` - Logging
|
|
331
|
+
|
|
332
|
+
## License
|
|
333
|
+
|
|
334
|
+
MIT
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const {
|
|
3
|
+
DynamoDBClient,
|
|
4
|
+
CreateTableCommand,
|
|
5
|
+
DescribeTableCommand,
|
|
6
|
+
ResourceNotFoundException,
|
|
7
|
+
waitUntilTableExists,
|
|
8
|
+
} = require('@aws-sdk/client-dynamodb');
|
|
9
|
+
|
|
10
|
+
const TABLE_NAME = process.env.DISMISSIBLE_STORAGE_DYNAMODB_TABLE_NAME || 'dismissible-items';
|
|
11
|
+
const REGION = process.env.DISMISSIBLE_STORAGE_DYNAMODB_AWS_REGION || 'us-east-1';
|
|
12
|
+
const ENDPOINT =
|
|
13
|
+
typeof process.env.DISMISSIBLE_STORAGE_DYNAMODB_ENDPOINT !== 'undefined'
|
|
14
|
+
? process.env.DISMISSIBLE_STORAGE_DYNAMODB_ENDPOINT
|
|
15
|
+
: 'http://localhost:4566';
|
|
16
|
+
const ACCESS_KEY_ID = process.env.DISMISSIBLE_STORAGE_DYNAMODB_AWS_ACCESS_KEY_ID || 'test';
|
|
17
|
+
const SECRET_ACCESS_KEY = process.env.DISMISSIBLE_STORAGE_DYNAMODB_AWS_SECRET_ACCESS_KEY || 'test';
|
|
18
|
+
const SESSION_TOKEN = process.env.DISMISSIBLE_STORAGE_DYNAMODB_AWS_SESSION_TOKEN || '';
|
|
19
|
+
|
|
20
|
+
function maskSecret(value, visibleChars = 4) {
|
|
21
|
+
if (!value || value.length <= visibleChars * 2) {
|
|
22
|
+
return '*'.repeat(Math.min(value?.length || 8, 8));
|
|
23
|
+
}
|
|
24
|
+
return `${value.substring(0, visibleChars)}${'*'.repeat(value.length - visibleChars * 2)}${value.substring(value.length - visibleChars)}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function createTable() {
|
|
28
|
+
const config = {
|
|
29
|
+
region: REGION,
|
|
30
|
+
endpoint: ENDPOINT,
|
|
31
|
+
credentials: {
|
|
32
|
+
accessKeyId: ACCESS_KEY_ID,
|
|
33
|
+
secretAccessKey: SECRET_ACCESS_KEY,
|
|
34
|
+
sessionToken: SESSION_TOKEN,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const client = new DynamoDBClient(config);
|
|
39
|
+
|
|
40
|
+
console.log(`Running DynamoDB setup for table ${TABLE_NAME}...`);
|
|
41
|
+
console.log(`Region: ${REGION}`);
|
|
42
|
+
console.log(`Endpoint: ${ENDPOINT ? ENDPOINT : '(not set)'}`);
|
|
43
|
+
console.log(`Access Key ID: ${ACCESS_KEY_ID ? maskSecret(ACCESS_KEY_ID) : '(not set)'}`);
|
|
44
|
+
console.log(
|
|
45
|
+
`Secret Access Key: ${SECRET_ACCESS_KEY ? maskSecret(SECRET_ACCESS_KEY) : '(not set)'}`,
|
|
46
|
+
);
|
|
47
|
+
console.log(`Session Token: ${SESSION_TOKEN ? maskSecret(SESSION_TOKEN) : '(not set)'}`);
|
|
48
|
+
|
|
49
|
+
// Check if table exists
|
|
50
|
+
try {
|
|
51
|
+
await client.send(new DescribeTableCommand({ TableName: TABLE_NAME }));
|
|
52
|
+
console.log(`Table ${TABLE_NAME} already exists`);
|
|
53
|
+
return;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (!(error instanceof ResourceNotFoundException)) {
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Create table
|
|
61
|
+
console.log(`Creating table ${TABLE_NAME}...`);
|
|
62
|
+
await client.send(
|
|
63
|
+
new CreateTableCommand({
|
|
64
|
+
TableName: TABLE_NAME,
|
|
65
|
+
KeySchema: [
|
|
66
|
+
{ AttributeName: 'userId', KeyType: 'HASH' },
|
|
67
|
+
{ AttributeName: 'id', KeyType: 'RANGE' },
|
|
68
|
+
],
|
|
69
|
+
AttributeDefinitions: [
|
|
70
|
+
{ AttributeName: 'userId', AttributeType: 'S' },
|
|
71
|
+
{ AttributeName: 'id', AttributeType: 'S' },
|
|
72
|
+
],
|
|
73
|
+
BillingMode: 'PAY_PER_REQUEST',
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// Wait for table to be active
|
|
78
|
+
await waitUntilTableExists({ client, maxWaitTime: 120 }, { TableName: TABLE_NAME });
|
|
79
|
+
console.log(`Table ${TABLE_NAME} created successfully`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
createTable().catch((error) => {
|
|
83
|
+
console.error('Failed to create DynamoDB table:', error.message);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dismissible/nestjs-dynamodb-storage",
|
|
3
|
+
"version": "1.0.3-alpha.0b30e6d.0",
|
|
4
|
+
"description": "DynamoDB storage adapter for Dismissible using AWS SDK v3",
|
|
5
|
+
"main": "./src/index.js",
|
|
6
|
+
"types": "./src/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"dismissible-dynamodb-setup": "./bin/dismissible-dynamodb-setup.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./src/index.mjs",
|
|
13
|
+
"require": "./src/index.js",
|
|
14
|
+
"types": "./src/index.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"src",
|
|
19
|
+
"bin",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE.md"
|
|
22
|
+
],
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@nestjs/common": "10.0.0 || ^11.0.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependenciesMeta": {
|
|
27
|
+
"@nestjs/common": {
|
|
28
|
+
"optional": false
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@aws-sdk/client-dynamodb": "3.958.0",
|
|
33
|
+
"@aws-sdk/lib-dynamodb": "3.958.0",
|
|
34
|
+
"@aws-sdk/util-waiter": "3.370.0",
|
|
35
|
+
"@dismissible/nestjs-item": "1.0.3-alpha.0b30e6d.0",
|
|
36
|
+
"@dismissible/nestjs-logger": "1.0.3-alpha.0b30e6d.0",
|
|
37
|
+
"@dismissible/nestjs-storage": "1.0.3-alpha.0b30e6d.0"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"nestjs",
|
|
41
|
+
"dismissible",
|
|
42
|
+
"storage",
|
|
43
|
+
"dynamodb",
|
|
44
|
+
"aws",
|
|
45
|
+
"adapter"
|
|
46
|
+
],
|
|
47
|
+
"author": "",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "https://github.com/DismissibleIo/dismissible-api"
|
|
52
|
+
},
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
},
|
|
56
|
+
"type": "commonjs"
|
|
57
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
|
2
|
+
import { DynamoDBStorageConfig } from './dynamodb-storage.config';
|
|
3
|
+
import { IDismissibleLogger } from '@dismissible/nestjs-logger';
|
|
4
|
+
export declare class DynamoDBClientService implements OnModuleInit, OnModuleDestroy {
|
|
5
|
+
private readonly config;
|
|
6
|
+
private readonly logger;
|
|
7
|
+
private readonly client;
|
|
8
|
+
private readonly documentClient;
|
|
9
|
+
constructor(config: DynamoDBStorageConfig, logger: IDismissibleLogger);
|
|
10
|
+
onModuleInit(): Promise<void>;
|
|
11
|
+
onModuleDestroy(): Promise<void>;
|
|
12
|
+
get(userId: string, itemId: string): Promise<Record<string, any> | null>;
|
|
13
|
+
create(item: Record<string, any>): Promise<void>;
|
|
14
|
+
update(userId: string, itemId: string, dismissedAt: string | null): Promise<void>;
|
|
15
|
+
delete(userId: string, itemId: string): Promise<void>;
|
|
16
|
+
deleteAll(): Promise<void>;
|
|
17
|
+
private batchDeleteItems;
|
|
18
|
+
private chunkArray;
|
|
19
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DynamoDBClientService = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const common_1 = require("@nestjs/common");
|
|
6
|
+
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
7
|
+
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
8
|
+
const lib_dynamodb_2 = require("@aws-sdk/lib-dynamodb");
|
|
9
|
+
const dynamodb_storage_config_1 = require("./dynamodb-storage.config");
|
|
10
|
+
const nestjs_logger_1 = require("@dismissible/nestjs-logger");
|
|
11
|
+
let DynamoDBClientService = class DynamoDBClientService {
|
|
12
|
+
constructor(config, logger) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
this.logger = logger;
|
|
15
|
+
this.client = new client_dynamodb_1.DynamoDBClient({
|
|
16
|
+
region: this.config.region ?? 'us-east-1',
|
|
17
|
+
endpoint: this.config.endpoint,
|
|
18
|
+
credentials: this.config.accessKeyId && this.config.secretAccessKey
|
|
19
|
+
? {
|
|
20
|
+
accessKeyId: this.config.accessKeyId,
|
|
21
|
+
secretAccessKey: this.config.secretAccessKey,
|
|
22
|
+
sessionToken: this.config.sessionToken,
|
|
23
|
+
}
|
|
24
|
+
: undefined,
|
|
25
|
+
});
|
|
26
|
+
this.documentClient = lib_dynamodb_1.DynamoDBDocumentClient.from(this.client, {
|
|
27
|
+
marshallOptions: {
|
|
28
|
+
removeUndefinedValues: true,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async onModuleInit() {
|
|
33
|
+
this.logger.debug('Initializing DynamoDB client', {
|
|
34
|
+
region: this.config.region,
|
|
35
|
+
tableName: this.config.tableName,
|
|
36
|
+
endpoint: this.config.endpoint,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async onModuleDestroy() {
|
|
40
|
+
// DynamoDB client doesn't require explicit cleanup
|
|
41
|
+
}
|
|
42
|
+
async get(userId, itemId) {
|
|
43
|
+
const response = await this.documentClient.send(new lib_dynamodb_2.GetCommand({
|
|
44
|
+
TableName: this.config.tableName,
|
|
45
|
+
Key: { userId, id: itemId },
|
|
46
|
+
}));
|
|
47
|
+
return response.Item ?? null;
|
|
48
|
+
}
|
|
49
|
+
async create(item) {
|
|
50
|
+
await this.documentClient.send(new lib_dynamodb_2.PutCommand({
|
|
51
|
+
TableName: this.config.tableName,
|
|
52
|
+
Item: item,
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
async update(userId, itemId, dismissedAt) {
|
|
56
|
+
await this.documentClient.send(new lib_dynamodb_2.UpdateCommand({
|
|
57
|
+
TableName: this.config.tableName,
|
|
58
|
+
Key: { userId, id: itemId },
|
|
59
|
+
UpdateExpression: 'SET dismissedAt = :dismissedAt',
|
|
60
|
+
ExpressionAttributeValues: {
|
|
61
|
+
':dismissedAt': dismissedAt,
|
|
62
|
+
},
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
async delete(userId, itemId) {
|
|
66
|
+
await this.documentClient.send(new lib_dynamodb_2.DeleteCommand({
|
|
67
|
+
TableName: this.config.tableName,
|
|
68
|
+
Key: { userId, id: itemId },
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
async deleteAll() {
|
|
72
|
+
let lastEvaluatedKey;
|
|
73
|
+
do {
|
|
74
|
+
const response = await this.documentClient.send(new lib_dynamodb_2.ScanCommand({
|
|
75
|
+
TableName: this.config.tableName,
|
|
76
|
+
ExclusiveStartKey: lastEvaluatedKey,
|
|
77
|
+
}));
|
|
78
|
+
if (response.Items && response.Items.length > 0) {
|
|
79
|
+
const batches = this.chunkArray(response.Items, 25);
|
|
80
|
+
for (const batch of batches) {
|
|
81
|
+
await this.batchDeleteItems(batch);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
lastEvaluatedKey = response.LastEvaluatedKey;
|
|
85
|
+
} while (lastEvaluatedKey);
|
|
86
|
+
}
|
|
87
|
+
async batchDeleteItems(items) {
|
|
88
|
+
const deleteRequests = items.map((item) => ({
|
|
89
|
+
DeleteRequest: {
|
|
90
|
+
Key: { userId: item.userId, id: item.id },
|
|
91
|
+
},
|
|
92
|
+
}));
|
|
93
|
+
await this.documentClient.send(new lib_dynamodb_2.BatchWriteCommand({
|
|
94
|
+
RequestItems: {
|
|
95
|
+
[this.config.tableName]: deleteRequests,
|
|
96
|
+
},
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
chunkArray(array, chunkSize) {
|
|
100
|
+
const chunks = [];
|
|
101
|
+
for (let i = 0; i < array.length; i += chunkSize) {
|
|
102
|
+
chunks.push(array.slice(i, i + chunkSize));
|
|
103
|
+
}
|
|
104
|
+
return chunks;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
exports.DynamoDBClientService = DynamoDBClientService;
|
|
108
|
+
exports.DynamoDBClientService = DynamoDBClientService = tslib_1.__decorate([
|
|
109
|
+
(0, common_1.Injectable)(),
|
|
110
|
+
tslib_1.__param(0, (0, common_1.Inject)(dynamodb_storage_config_1.DISMISSIBLE_STORAGE_DYNAMODB_CONFIG)),
|
|
111
|
+
tslib_1.__param(1, (0, common_1.Inject)(nestjs_logger_1.DISMISSIBLE_LOGGER)),
|
|
112
|
+
tslib_1.__metadata("design:paramtypes", [dynamodb_storage_config_1.DynamoDBStorageConfig, Object])
|
|
113
|
+
], DynamoDBClientService);
|
|
114
|
+
//# sourceMappingURL=dynamodb-client.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamodb-client.service.js","sourceRoot":"","sources":["../../../../libs/dynamodb-storage/src/dynamodb-client.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { IDismissibleLogger } from '@dismissible/nestjs-logger';
|
|
2
|
+
import { IDismissibleStorage } from '@dismissible/nestjs-storage';
|
|
3
|
+
import { DismissibleItemDto, DismissibleItemFactory } from '@dismissible/nestjs-item';
|
|
4
|
+
import { DynamoDBClientService } from './dynamodb-client.service';
|
|
5
|
+
export declare class DynamoDBStorageAdapter implements IDismissibleStorage {
|
|
6
|
+
private readonly dynamoDB;
|
|
7
|
+
private readonly logger;
|
|
8
|
+
private readonly itemFactory;
|
|
9
|
+
constructor(dynamoDB: DynamoDBClientService, logger: IDismissibleLogger, itemFactory: DismissibleItemFactory);
|
|
10
|
+
get(userId: string, itemId: string): Promise<DismissibleItemDto | null>;
|
|
11
|
+
create(item: DismissibleItemDto): Promise<DismissibleItemDto>;
|
|
12
|
+
update(item: DismissibleItemDto): Promise<DismissibleItemDto>;
|
|
13
|
+
delete(userId: string, itemId: string): Promise<void>;
|
|
14
|
+
deleteAll(): Promise<void>;
|
|
15
|
+
private mapToDto;
|
|
16
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DynamoDBStorageAdapter = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const common_1 = require("@nestjs/common");
|
|
6
|
+
const nestjs_logger_1 = require("@dismissible/nestjs-logger");
|
|
7
|
+
const nestjs_item_1 = require("@dismissible/nestjs-item");
|
|
8
|
+
const dynamodb_client_service_1 = require("./dynamodb-client.service");
|
|
9
|
+
let DynamoDBStorageAdapter = class DynamoDBStorageAdapter {
|
|
10
|
+
constructor(dynamoDB, logger, itemFactory) {
|
|
11
|
+
this.dynamoDB = dynamoDB;
|
|
12
|
+
this.logger = logger;
|
|
13
|
+
this.itemFactory = itemFactory;
|
|
14
|
+
}
|
|
15
|
+
async get(userId, itemId) {
|
|
16
|
+
this.logger.debug('DynamoDB storage get', { userId, itemId });
|
|
17
|
+
const item = await this.dynamoDB.get(userId, itemId);
|
|
18
|
+
if (!item) {
|
|
19
|
+
this.logger.debug('DynamoDB storage miss', { userId, itemId });
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
this.logger.debug('DynamoDB storage hit', { userId, itemId });
|
|
23
|
+
return this.mapToDto(item);
|
|
24
|
+
}
|
|
25
|
+
async create(item) {
|
|
26
|
+
this.logger.debug('DynamoDB storage create', { userId: item.userId, itemId: item.id });
|
|
27
|
+
await this.dynamoDB.create({
|
|
28
|
+
userId: item.userId,
|
|
29
|
+
id: item.id,
|
|
30
|
+
createdAt: item.createdAt.toISOString(),
|
|
31
|
+
dismissedAt: item.dismissedAt?.toISOString() ?? null,
|
|
32
|
+
});
|
|
33
|
+
return item;
|
|
34
|
+
}
|
|
35
|
+
async update(item) {
|
|
36
|
+
this.logger.debug('DynamoDB storage update', { userId: item.userId, itemId: item.id });
|
|
37
|
+
await this.dynamoDB.update(item.userId, item.id, item.dismissedAt?.toISOString() ?? null);
|
|
38
|
+
return item;
|
|
39
|
+
}
|
|
40
|
+
async delete(userId, itemId) {
|
|
41
|
+
this.logger.debug('DynamoDB storage delete', { userId, itemId });
|
|
42
|
+
await this.dynamoDB.delete(userId, itemId);
|
|
43
|
+
}
|
|
44
|
+
async deleteAll() {
|
|
45
|
+
this.logger.debug('DynamoDB storage deleteAll');
|
|
46
|
+
await this.dynamoDB.deleteAll();
|
|
47
|
+
}
|
|
48
|
+
mapToDto(item) {
|
|
49
|
+
return this.itemFactory.create({
|
|
50
|
+
id: item.id,
|
|
51
|
+
userId: item.userId,
|
|
52
|
+
createdAt: new Date(item.createdAt),
|
|
53
|
+
dismissedAt: item.dismissedAt ? new Date(item.dismissedAt) : undefined,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
exports.DynamoDBStorageAdapter = DynamoDBStorageAdapter;
|
|
58
|
+
exports.DynamoDBStorageAdapter = DynamoDBStorageAdapter = tslib_1.__decorate([
|
|
59
|
+
(0, common_1.Injectable)(),
|
|
60
|
+
tslib_1.__param(1, (0, common_1.Inject)(nestjs_logger_1.DISMISSIBLE_LOGGER)),
|
|
61
|
+
tslib_1.__metadata("design:paramtypes", [dynamodb_client_service_1.DynamoDBClientService, Object, nestjs_item_1.DismissibleItemFactory])
|
|
62
|
+
], DynamoDBStorageAdapter);
|
|
63
|
+
//# sourceMappingURL=dynamodb-storage.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamodb-storage.adapter.js","sourceRoot":"","sources":["../../../../libs/dynamodb-storage/src/dynamodb-storage.adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const DISMISSIBLE_STORAGE_DYNAMODB_CONFIG: unique symbol;
|
|
2
|
+
export declare class DynamoDBStorageConfig {
|
|
3
|
+
readonly tableName: string;
|
|
4
|
+
readonly region?: string;
|
|
5
|
+
readonly endpoint?: string;
|
|
6
|
+
readonly accessKeyId?: string;
|
|
7
|
+
readonly secretAccessKey?: string;
|
|
8
|
+
readonly sessionToken?: string;
|
|
9
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DynamoDBStorageConfig = exports.DISMISSIBLE_STORAGE_DYNAMODB_CONFIG = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const class_validator_1 = require("class-validator");
|
|
6
|
+
exports.DISMISSIBLE_STORAGE_DYNAMODB_CONFIG = Symbol('DISMISSIBLE_STORAGE_DYNAMODB_CONFIG');
|
|
7
|
+
class DynamoDBStorageConfig {
|
|
8
|
+
}
|
|
9
|
+
exports.DynamoDBStorageConfig = DynamoDBStorageConfig;
|
|
10
|
+
tslib_1.__decorate([
|
|
11
|
+
(0, class_validator_1.IsString)(),
|
|
12
|
+
tslib_1.__metadata("design:type", String)
|
|
13
|
+
], DynamoDBStorageConfig.prototype, "tableName", void 0);
|
|
14
|
+
tslib_1.__decorate([
|
|
15
|
+
(0, class_validator_1.IsString)(),
|
|
16
|
+
(0, class_validator_1.IsOptional)(),
|
|
17
|
+
tslib_1.__metadata("design:type", String)
|
|
18
|
+
], DynamoDBStorageConfig.prototype, "region", void 0);
|
|
19
|
+
tslib_1.__decorate([
|
|
20
|
+
(0, class_validator_1.IsString)(),
|
|
21
|
+
(0, class_validator_1.IsOptional)(),
|
|
22
|
+
tslib_1.__metadata("design:type", String)
|
|
23
|
+
], DynamoDBStorageConfig.prototype, "endpoint", void 0);
|
|
24
|
+
tslib_1.__decorate([
|
|
25
|
+
(0, class_validator_1.IsString)(),
|
|
26
|
+
(0, class_validator_1.IsOptional)(),
|
|
27
|
+
tslib_1.__metadata("design:type", String)
|
|
28
|
+
], DynamoDBStorageConfig.prototype, "accessKeyId", void 0);
|
|
29
|
+
tslib_1.__decorate([
|
|
30
|
+
(0, class_validator_1.IsString)(),
|
|
31
|
+
(0, class_validator_1.IsOptional)(),
|
|
32
|
+
tslib_1.__metadata("design:type", String)
|
|
33
|
+
], DynamoDBStorageConfig.prototype, "secretAccessKey", void 0);
|
|
34
|
+
tslib_1.__decorate([
|
|
35
|
+
(0, class_validator_1.IsString)(),
|
|
36
|
+
(0, class_validator_1.IsOptional)(),
|
|
37
|
+
tslib_1.__metadata("design:type", String)
|
|
38
|
+
], DynamoDBStorageConfig.prototype, "sessionToken", void 0);
|
|
39
|
+
//# sourceMappingURL=dynamodb-storage.config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamodb-storage.config.js","sourceRoot":"","sources":["../../../../libs/dynamodb-storage/src/dynamodb-storage.config.ts"],"names":[],"mappings":";;;;AAAA,qDAAuD;AAE1C,QAAA,mCAAmC,GAAG,MAAM,CAAC,qCAAqC,CAAC,CAAC;AAEjG,MAAa,qBAAqB;CAuBjC;AAvBD,sDAuBC;AArBiB;IADf,IAAA,0BAAQ,GAAE;;wDACwB;AAInB;IAFf,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;qDACmB;AAIhB;IAFf,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;uDACqB;AAIlB;IAFf,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;0DACwB;AAIrB;IAFf,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;8DAC4B;AAIzB;IAFf,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;2DACyB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DynamicModule, ModuleMetadata } from '@nestjs/common';
|
|
2
|
+
export interface DynamoDBStorageModuleOptions {
|
|
3
|
+
tableName: string;
|
|
4
|
+
region?: string;
|
|
5
|
+
endpoint?: string;
|
|
6
|
+
accessKeyId?: string;
|
|
7
|
+
secretAccessKey?: string;
|
|
8
|
+
sessionToken?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface DynamoDBStorageModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
|
|
11
|
+
inject?: any[];
|
|
12
|
+
useFactory: (...args: any[]) => DynamoDBStorageModuleOptions | Promise<DynamoDBStorageModuleOptions>;
|
|
13
|
+
}
|
|
14
|
+
export declare class DynamoDBStorageModule {
|
|
15
|
+
static forRoot(options: DynamoDBStorageModuleOptions): DynamicModule;
|
|
16
|
+
static forRootAsync(options: DynamoDBStorageModuleAsyncOptions): DynamicModule;
|
|
17
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var DynamoDBStorageModule_1;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.DynamoDBStorageModule = void 0;
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
6
|
+
const common_1 = require("@nestjs/common");
|
|
7
|
+
const nestjs_storage_1 = require("@dismissible/nestjs-storage");
|
|
8
|
+
const dynamodb_storage_adapter_1 = require("./dynamodb-storage.adapter");
|
|
9
|
+
const dynamodb_client_service_1 = require("./dynamodb-client.service");
|
|
10
|
+
const nestjs_item_1 = require("@dismissible/nestjs-item");
|
|
11
|
+
const nestjs_logger_1 = require("@dismissible/nestjs-logger");
|
|
12
|
+
const dynamodb_storage_config_1 = require("./dynamodb-storage.config");
|
|
13
|
+
let DynamoDBStorageModule = DynamoDBStorageModule_1 = class DynamoDBStorageModule {
|
|
14
|
+
static forRoot(options) {
|
|
15
|
+
return {
|
|
16
|
+
module: DynamoDBStorageModule_1,
|
|
17
|
+
imports: [nestjs_item_1.DismissibleItemModule],
|
|
18
|
+
providers: [
|
|
19
|
+
{
|
|
20
|
+
provide: dynamodb_storage_config_1.DISMISSIBLE_STORAGE_DYNAMODB_CONFIG,
|
|
21
|
+
useValue: {
|
|
22
|
+
tableName: options.tableName,
|
|
23
|
+
region: options.region,
|
|
24
|
+
endpoint: options.endpoint,
|
|
25
|
+
accessKeyId: options.accessKeyId,
|
|
26
|
+
secretAccessKey: options.secretAccessKey,
|
|
27
|
+
sessionToken: options.sessionToken,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
provide: dynamodb_client_service_1.DynamoDBClientService,
|
|
32
|
+
useFactory(config, logger) {
|
|
33
|
+
return new dynamodb_client_service_1.DynamoDBClientService(config, logger);
|
|
34
|
+
},
|
|
35
|
+
inject: [dynamodb_storage_config_1.DISMISSIBLE_STORAGE_DYNAMODB_CONFIG, nestjs_logger_1.DISMISSIBLE_LOGGER],
|
|
36
|
+
},
|
|
37
|
+
dynamodb_storage_adapter_1.DynamoDBStorageAdapter,
|
|
38
|
+
{
|
|
39
|
+
provide: nestjs_storage_1.DISMISSIBLE_STORAGE_ADAPTER,
|
|
40
|
+
useExisting: dynamodb_storage_adapter_1.DynamoDBStorageAdapter,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
exports: [nestjs_storage_1.DISMISSIBLE_STORAGE_ADAPTER, dynamodb_client_service_1.DynamoDBClientService],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
static forRootAsync(options) {
|
|
47
|
+
return {
|
|
48
|
+
module: DynamoDBStorageModule_1,
|
|
49
|
+
imports: [...(options.imports || []), nestjs_item_1.DismissibleItemModule],
|
|
50
|
+
providers: [
|
|
51
|
+
{
|
|
52
|
+
provide: dynamodb_storage_config_1.DISMISSIBLE_STORAGE_DYNAMODB_CONFIG,
|
|
53
|
+
useFactory: options.useFactory,
|
|
54
|
+
inject: options.inject || [],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
provide: dynamodb_client_service_1.DynamoDBClientService,
|
|
58
|
+
useFactory(config, logger) {
|
|
59
|
+
return new dynamodb_client_service_1.DynamoDBClientService(config, logger);
|
|
60
|
+
},
|
|
61
|
+
inject: [dynamodb_storage_config_1.DISMISSIBLE_STORAGE_DYNAMODB_CONFIG, nestjs_logger_1.DISMISSIBLE_LOGGER],
|
|
62
|
+
},
|
|
63
|
+
dynamodb_storage_adapter_1.DynamoDBStorageAdapter,
|
|
64
|
+
{
|
|
65
|
+
provide: nestjs_storage_1.DISMISSIBLE_STORAGE_ADAPTER,
|
|
66
|
+
useExisting: dynamodb_storage_adapter_1.DynamoDBStorageAdapter,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
exports: [nestjs_storage_1.DISMISSIBLE_STORAGE_ADAPTER, dynamodb_client_service_1.DynamoDBClientService],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
exports.DynamoDBStorageModule = DynamoDBStorageModule;
|
|
74
|
+
exports.DynamoDBStorageModule = DynamoDBStorageModule = DynamoDBStorageModule_1 = tslib_1.__decorate([
|
|
75
|
+
(0, common_1.Module)({})
|
|
76
|
+
], DynamoDBStorageModule);
|
|
77
|
+
//# sourceMappingURL=dynamodb-storage.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamodb-storage.module.js","sourceRoot":"","sources":["../../../../libs/dynamodb-storage/src/dynamodb-storage.module.ts"],"names":[],"mappings":";;;;;AAAA,2CAAuE;AACvE,gEAA0E;AAC1E,yEAAoE;AACpE,uEAAkE;AAClE,0DAAiE;AACjE,8DAAoF;AACpF,uEAGmC;AAmB5B,IAAM,qBAAqB,6BAA3B,MAAM,qBAAqB;IAChC,MAAM,CAAC,OAAO,CAAC,OAAqC;QAClD,OAAO;YACL,MAAM,EAAE,uBAAqB;YAC7B,OAAO,EAAE,CAAC,mCAAqB,CAAC;YAChC,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,6DAAmC;oBAC5C,QAAQ,EAAE;wBACR,SAAS,EAAE,OAAO,CAAC,SAAS;wBAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;wBACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;wBAC1B,WAAW,EAAE,OAAO,CAAC,WAAW;wBAChC,eAAe,EAAE,OAAO,CAAC,eAAe;wBACxC,YAAY,EAAE,OAAO,CAAC,YAAY;qBACnC;iBACF;gBACD;oBACE,OAAO,EAAE,+CAAqB;oBAC9B,UAAU,CAAC,MAA6B,EAAE,MAA0B;wBAClE,OAAO,IAAI,+CAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;oBACnD,CAAC;oBACD,MAAM,EAAE,CAAC,6DAAmC,EAAE,kCAAkB,CAAC;iBAClE;gBACD,iDAAsB;gBACtB;oBACE,OAAO,EAAE,4CAA2B;oBACpC,WAAW,EAAE,iDAAsB;iBACpC;aACF;YACD,OAAO,EAAE,CAAC,4CAA2B,EAAE,+CAAqB,CAAC;SAC9D,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,OAA0C;QAC5D,OAAO;YACL,MAAM,EAAE,uBAAqB;YAC7B,OAAO,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,mCAAqB,CAAC;YAC5D,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,6DAAmC;oBAC5C,UAAU,EAAE,OAAO,CAAC,UAAU;oBAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;iBAC7B;gBACD;oBACE,OAAO,EAAE,+CAAqB;oBAC9B,UAAU,CAAC,MAA6B,EAAE,MAA0B;wBAClE,OAAO,IAAI,+CAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;oBACnD,CAAC;oBACD,MAAM,EAAE,CAAC,6DAAmC,EAAE,kCAAkB,CAAC;iBAClE;gBACD,iDAAsB;gBACtB;oBACE,OAAO,EAAE,4CAA2B;oBACpC,WAAW,EAAE,iDAAsB;iBACpC;aACF;YACD,OAAO,EAAE,CAAC,4CAA2B,EAAE,+CAAqB,CAAC;SAC9D,CAAC;IACJ,CAAC;CACF,CAAA;AA5DY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,qBAAqB,CA4DjC"}
|
package/src/index.d.ts
ADDED
package/src/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./dynamodb-storage.config"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./dynamodb-storage.module"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./dynamodb-storage.adapter"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./dynamodb-client.service"), exports);
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/dynamodb-storage/src/index.ts"],"names":[],"mappings":";;;AAAA,oEAA0C;AAC1C,oEAA0C;AAC1C,qEAA2C;AAC3C,oEAA0C"}
|