aws-service-stack 0.17.278 โ†’ 0.17.280

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 CHANGED
@@ -1,304 +1,425 @@
1
- # AWS Service Stack
2
-
3
- [![npm version](https://badge.fury.io/js/aws-service-stack.svg)](https://badge.fury.io/js/aws-service-stack)
4
- [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
5
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.8+-blue.svg)](https://www.typescriptlang.org/)
6
-
7
- ๐Ÿ“ฆ **NPM Package**: [aws-service-stack](https://www.npmjs.com/package/aws-service-stack)
8
-
9
- A comprehensive TypeScript backend framework for AWS serverless applications, providing enterprise-grade abstractions for DynamoDB, OpenSearch, API Gateway, Lambda, and Cognito integration.
10
-
11
- ## ๐Ÿš€ Features
12
-
13
- ### **Core Infrastructure**
14
- - **๐Ÿ—๏ธ Base Controllers** - Abstract AWS Lambda event handling for API Gateway, SQS, DynamoDB Streams, and WebSocket events
15
- - **๐Ÿ—„๏ธ Repository Pattern** - Unified interfaces for DynamoDB and OpenSearch with transaction support
16
- - **๐Ÿ” Authentication & Authorization** - Built-in Cognito integration with role-based permissions
17
- - **๐Ÿ“Š Search & Analytics** - Full-text search with OpenSearch integration
18
- - **โœ… Validation** - Support for both Zod and Yup schemas with type-safe validation
19
-
20
- ### **AWS Services Integration**
21
- - **DynamoDB** - Complete CRUD operations, batch processing, transactions, and advanced querying
22
- - **OpenSearch** - Full-text search, faceted search, and analytics
23
- - **API Gateway** - RESTful API handling with automatic request/response parsing
24
- - **Lambda** - Event-driven architecture with SQS, DynamoDB Streams, and scheduled events
25
- - **Cognito** - User authentication, authorization, and group management
26
- - **Secrets Manager** - Secure credential management
27
-
28
- ### **Developer Experience**
29
- - **๐Ÿ”ง TypeScript First** - Full type safety with comprehensive interfaces
30
- - **๐Ÿ›๏ธ Dependency Injection** - TypeDI integration for clean architecture
31
- - **๐Ÿ“ Comprehensive Logging** - Structured logging with different levels
32
- - **๐Ÿงช Testing Ready** - Jest configuration with TypeScript support
33
- - **๐Ÿ“ฆ Modular Design** - Tree-shakable modules for optimal bundle size
34
-
35
- ## ๐Ÿ“ฆ Installation
36
-
37
- ```bash
38
- npm install aws-service-stack
39
- ```
40
-
41
- ### Peer Dependencies
42
-
43
- This package requires the following AWS SDK v3 packages as peer dependencies:
44
-
45
- ```bash
46
- npm install @aws-sdk/client-dynamodb @aws-sdk/client-cognito-identity-provider @aws-sdk/client-secrets-manager @aws-sdk/util-dynamodb @opensearch-project/opensearch
47
- ```
48
-
49
- ## ๐Ÿ Quick Start
50
-
51
- ### 1. Basic Controller Setup
52
-
53
- ```typescript
54
- import { BaseController, BaseService, EntityConfig } from 'aws-service-stack';
55
-
56
- class UserController extends BaseController<UserService> {
57
- constructor() {
58
- super(new UserService());
59
- }
60
- }
61
-
62
- // Lambda handler
63
- export const handler = async (event: APIGatewayProxyEvent) => {
64
- const controller = new UserController();
65
- return await controller.handleRequest(event);
66
- };
67
- ```
68
-
69
- ### 2. Entity Configuration
70
-
71
- ```typescript
72
- import { EntityConfigImpl, DynamoIndexMap } from 'aws-service-stack';
73
-
74
- const userConfig = new EntityConfigImpl('User')
75
- .setDynamoDB('User-table', new DynamoIndexMap())
76
- .setOpenSearch('user-search', 'user-index')
77
- .setPermissions([
78
- { method: 'GET', path: '/users', allowed: ['PUBLIC'] },
79
- { method: 'POST', path: '/users', allowed: ['ADMIN'] }
80
- ])
81
- .setValidators(createUserSchema, updateUserSchema)
82
- .setAdminGroup('admin');
83
- ```
84
-
85
- ### 3. Repository Operations
86
-
87
- ```typescript
88
- import { BaseRepoDBImpl } from 'aws-service-stack';
89
-
90
- class UserRepository extends BaseRepoDBImpl<User> {
91
- // Inherits all CRUD operations
92
- // save(), update(), delete(), findById(), findAll()
93
- // Advanced: batch operations, transactions, filtering
94
- }
95
-
96
- // Usage
97
- const userRepo = new UserRepository();
98
- const user = await userRepo.save({ name: 'John Doe', email: 'john@example.com' });
99
- ```
100
-
101
- ### 4. Search Integration
102
-
103
- ```typescript
104
- import { BaseRepoESImpl } from 'aws-service-stack';
105
-
106
- class UserSearchRepository extends BaseRepoESImpl<User> {
107
- async searchUsers(query: string, filters: FilterAndSort) {
108
- return await this.search({
109
- query: {
110
- multi_match: {
111
- query,
112
- fields: ['name^2', 'email', 'description']
113
- }
114
- },
115
- ...filters
116
- });
117
- }
118
- }
119
- ```
120
-
121
- ## ๐Ÿ› ๏ธ Built-in API Endpoints
122
-
123
- Your AWS Service Stack automatically provides these RESTful endpoints for any entity:
124
-
125
- ### **Standard CRUD Operations**
126
- ```
127
- POST /products # Insert new product (create)
128
- GET /products/{id} # Get single product by ID
129
- GET /products # List products via DynamoDB Query (by categoryId, supplierId, status, โ€ฆ)
130
- GET /products/search # Search products via OpenSearch (full-text, sort, pagination)
131
- GET /products/scan # Scan all products (Admin only, expensive in DynamoDB)
132
- PUT /products/{id} # Update full product (replace all fields)
133
- PATCH /products/{id} # Update partial product (only specific fields)
134
- DELETE /products/{id} # Delete product by ID
135
- ```
136
-
137
- ### **Advanced Features**
138
- - **๐Ÿ” Smart Filtering** - DynamoDB queries with complex conditions
139
- - **๐Ÿ”Ž Full-Text Search** - OpenSearch integration with faceted search
140
- - **๐Ÿ“„ Pagination** - Built-in pagination with `lastKey` support
141
- - **๐Ÿ” Authorization** - Role-based access control per endpoint
142
- - **โœ… Validation** - Automatic request/response validation
143
- - **๐Ÿ“Š Analytics** - Built-in logging and monitoring
144
-
145
- ## ๐Ÿ—๏ธ Architecture
146
-
147
- ### **Layered Architecture**
148
- ```
149
- โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
150
- โ”‚ Controllers โ”‚ โ† API Gateway, Lambda Events
151
- โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
152
- โ”‚ Services โ”‚ โ† Business Logic
153
- โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
154
- โ”‚ Repositories โ”‚ โ† Data Access (DynamoDB, OpenSearch)
155
- โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
156
- โ”‚ Providers โ”‚ โ† AWS SDK Clients
157
- โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
158
- ```
159
-
160
- ### **Event Handling**
161
- - **API Gateway** - RESTful API endpoints
162
- - **SQS** - Message queue processing
163
- - **DynamoDB Streams** - Real-time data processing
164
- - **WebSocket** - Real-time communication
165
- - **Scheduled Events** - Cron-based tasks
166
-
167
- ## ๐Ÿ”ง Configuration
168
-
169
- ### Environment Variables
170
-
171
- ```bash
172
- # AWS Configuration
173
- AWS_REGION=us-east-1
174
- IAM_ACCESS_KEY=your-access-key
175
- IAM_SECRET_ACCESS_KEY=your-secret-key
176
-
177
- # OpenSearch
178
- OPENSEARCH_ENDPOINT=https://your-domain.us-east-1.es.amazonaws.com
179
-
180
- # API Gateway
181
- WS_ENDPOINT=wss://your-websocket-api.execute-api.us-east-1.amazonaws.com/prod
182
- ```
183
-
184
- ### TypeScript Configuration
185
-
186
- ```typescript
187
- // tsconfig.json
188
- {
189
- "compilerOptions": {
190
- "baseUrl": "./",
191
- "paths": {
192
- "@chinggis/core": ["node_modules/aws-service-stack/dist/index.d.ts"],
193
- "@chinggis/types": ["node_modules/aws-service-stack/dist/model/index.d.ts"]
194
- }
195
- }
196
- }
197
- ```
198
-
199
- ## ๐Ÿ“š API Reference
200
-
201
- ### **BaseController**
202
- - `handleRequest(event)` - Main Lambda handler
203
- - `handleSQS(event)` - SQS message processing
204
- - `handleWebSocket(event)` - WebSocket connection handling
205
-
206
- ### **BaseService**
207
- - `save(entity, ownerId)` - Create/update entity
208
- - `findById(id, userId)` - Find entity by ID
209
- - `findAll(filter, userId)` - List entities with filtering
210
- - `delete(id, userId)` - Delete entity
211
-
212
- ### **Repository Interfaces**
213
- - `CoreRepo<T>` - Generic CRUD operations
214
- - `BaseRepoDB<T>` - DynamoDB-specific operations
215
- - `BaseRepoES<T>` - OpenSearch-specific operations
216
-
217
- ## ๐Ÿงช Testing
218
-
219
- ```bash
220
- # Run tests
221
- npm test
222
-
223
- # Run tests with coverage
224
- npm run test:coverage
225
-
226
- # Run specific test file
227
- npm test -- user.test.ts
228
- ```
229
-
230
- ## ๐Ÿš€ Deployment
231
-
232
- ### Serverless Framework
233
-
234
- ```yaml
235
- # serverless.yml
236
- service: my-aws-service
237
- provider:
238
- name: aws
239
- runtime: nodejs18.x
240
- region: us-east-1
241
-
242
- functions:
243
- api:
244
- handler: dist/handler.handler
245
- events:
246
- - http:
247
- path: /{proxy+}
248
- method: ANY
249
- ```
250
-
251
- ### AWS CDK
252
-
253
- ```typescript
254
- import { Function, Runtime } from 'aws-cdk-lib/aws-lambda';
255
-
256
- new Function(this, 'MyFunction', {
257
- runtime: Runtime.NODEJS_18_X,
258
- handler: 'dist/handler.handler',
259
- code: Code.fromAsset('dist')
260
- });
261
- ```
262
-
263
- ## ๐Ÿค Contributing
264
-
265
- 1. Fork the repository
266
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
267
- 3. Commit your changes (`git commit -m 'feat(core): add amazing feature'`)
268
- 4. Push to the branch (`git push origin feature/amazing-feature`)
269
- 5. Open a Pull Request
270
-
271
- ### Development Setup
272
-
273
- ```bash
274
- # Clone the repository
275
- git clone https://github.com/chinggis-systems/aws-service-stack.git
276
-
277
- # Install dependencies
278
- npm install
279
-
280
- # Run tests
281
- npm test
282
-
283
- # Build the project
284
- npm run build
285
- ```
286
-
287
- ## ๐Ÿ“„ License
288
-
289
- This project is licensed under the ISC License - see the [LICENSE](LICENSE) file for details.
290
-
291
- ## ๐Ÿข About Chinggis Systems
292
-
293
- Built by [Chinggis Systems](https://chinggis.systems) - Enterprise software solutions for modern cloud architectures.
294
-
295
- ## ๐Ÿ“ž Support
296
-
297
- - ๐Ÿ’ฌ Discord: [Join our community](https://discord.gg/DAPNvk4F)
298
- - ๐Ÿ“ง Email: support@chinggis.systems
299
- - ๐Ÿ› Issues: [GitHub Issues](https://github.com/chinggis-systems/aws-service-stack/issues)
300
- - ๐Ÿ“– Documentation: [Wiki](https://github.com/chinggis-systems/aws-service-stack/wiki)
301
-
302
- ---
303
-
1
+ # AWS Service Stack
2
+
3
+ [![npm version](https://badge.fury.io/js/aws-service-stack.svg)](https://badge.fury.io/js/aws-service-stack)
4
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.8+-blue.svg)](https://www.typescriptlang.org/)
6
+
7
+ ๐Ÿ“ฆ **NPM Package**: [aws-service-stack](https://www.npmjs.com/package/aws-service-stack)
8
+
9
+ A comprehensive TypeScript backend framework for AWS serverless applications, providing enterprise-grade abstractions
10
+ for DynamoDB, OpenSearch, API Gateway, Lambda, and Cognito integration.
11
+
12
+ ## ๐Ÿš€ Features
13
+
14
+ ### **Core Infrastructure**
15
+
16
+ - **๐Ÿ—๏ธ Base Controllers** - Abstract AWS Lambda event handling for API Gateway, SQS, DynamoDB Streams, and WebSocket
17
+ events
18
+ - **๐Ÿ—„๏ธ Repository Pattern** - Unified interfaces for DynamoDB and OpenSearch with transaction support
19
+ - **๐Ÿ” Authentication & Authorization** - Built-in Cognito integration with role-based permissions
20
+ - **๐Ÿ“Š Search & Analytics** - Full-text search with OpenSearch integration
21
+ - **โœ… Validation** - Support for both Zod and Yup schemas with type-safe validation
22
+ - **โœ… Permission** - Item owner logic with built-in access control and parent child user management
23
+
24
+ ### **AWS Services Integration**
25
+
26
+ - **DynamoDB** - Complete CRUD operations, batch processing, transactions, and advanced querying
27
+ - **OpenSearch** - Full-text search, faceted search, and analytics
28
+ - **API Gateway** - RESTful API handling with automatic request/response parsing
29
+ - **Lambda** - Event-driven architecture with SQS, DynamoDB Streams, and scheduled events
30
+ - **Cognito** - User authentication, authorization, and group management
31
+ - **Secrets Manager** - Secure credential management
32
+
33
+ ### **Developer Experience**
34
+
35
+ - **๐Ÿ”ง TypeScript First** - Full type safety with comprehensive interfaces
36
+ - **๐Ÿ›๏ธ Dependency Injection** - TypeDI integration for clean architecture
37
+ - **๐Ÿ“ Comprehensive Logging** - Structured logging with different levels
38
+ - **๐Ÿงช Testing Ready** - Jest configuration with TypeScript support
39
+ - **๐Ÿ“ฆ Modular Design** - Tree-shakable modules for optimal bundle size
40
+
41
+ ## ๐Ÿ“ฆ Installation
42
+
43
+ ```bash
44
+ npm install aws-service-stack
45
+ ```
46
+
47
+ ### Peer Dependencies
48
+
49
+ This package requires the following AWS SDK v3 packages as peer dependencies:
50
+
51
+ ```bash
52
+ npm install
53
+ @aws-sdk/client-dynamodb
54
+ @aws-sdk/client-cognito-identity-provider
55
+ @aws-sdk/client-secrets-manager
56
+ @aws-sdk/util-dynamodb
57
+ @opensearch-project/opensearch
58
+ ```
59
+
60
+ ## ๐Ÿ Quick Start
61
+
62
+ ### 1. Basic Controller Setup
63
+
64
+ ```typescript
65
+ import Container, { Service } from "typedi";
66
+ import { CONFIG_Product, path } from "./Product-config";
67
+
68
+ import { ProductService } from "../../service/product/product-service-crud.interface";
69
+ import "../../service/product/product-service-crud";
70
+ import { ControllerApi, HttpRequest } from "@chinggis/core";
71
+ import { Product } from "src/model-shared/product.model";
72
+
73
+ @Service("ProductControllerApi")
74
+ export class ProductControllerApi extends ControllerApi<Product, ProductService> {
75
+ constructor() {
76
+ const service: ProductService = Container.get("ProductCrudService");
77
+ super(service, CONFIG_PRODUCT.toObject());
78
+ }
79
+
80
+ /** Additional custom endpoints */
81
+ protected async processCrudRequest(req: HttpRequest): Promise<any> {
82
+ if (req.resourcePath === `GET ${path}/list-non-level-one`) {
83
+ return await this.service.ListNonLevelOne(req.filter);
84
+ }
85
+ }
86
+ }
87
+
88
+ ```
89
+
90
+ ### 2. Entity Configuration
91
+
92
+ ```typescript
93
+ import { Access as a, DynamoIndexMap, EndpointPolicy, EntityConfigImpl, HttpMethod as m } from "@chinggis/core";
94
+ import {
95
+ CREATE,
96
+ REPLACE,
97
+ RESPONSE_FIELDS_DETAILS as FIELDS_D,
98
+ RESPONSE_FIELDS_LIST as FIELDS_L,
99
+ UPDATE,
100
+ } from "../../model-shared/product.model";
101
+
102
+ export const openSearch_Product = {
103
+ domain: "https://search-amplify-opense-abcd.ap-southeast-1.es.amazonaws.com",
104
+ index: "product",
105
+ };
106
+ export const path = "/products"; // url path base
107
+
108
+ // Product configuration
109
+ export class ProductConfig extends EntityConfigImpl {
110
+ constructor() {
111
+ // DYNAMODB
112
+ const tableName = "product-dev";
113
+ const ownerFieldName = "owner";
114
+ const indexMap = new DynamoIndexMap()
115
+ .setFields("id")
116
+ .set("byAccountType", { field: "accountType", rFields: ["accountType"] });
117
+
118
+ // PERMISSIONS
119
+ const adminGroupName = ["adminUsers"];
120
+ const policyList: EndpointPolicy[] = [
121
+ { method: m.GET, path: `${path}`, access: [a.ADMIN], response: FIELDS_L },
122
+ { method: m.GET, path: `${path}/search`, access: [a.ADMIN], response: FIELDS_L },
123
+ { method: m.GET, path: `${path}/list-non-level-one`, access: [a.ADMIN], response: FIELDS_L },
124
+ { method: m.GET, path: `${path}/{id}`, access: [a.OWNER, a.ADMIN], response: FIELDS_D },
125
+ { method: m.POST, path: `${path}`, access: [a.ADMIN], validator: CREATE, response: FIELDS_D },
126
+ { method: m.PUT, path: `${path}/{id}`, access: [a.ADMIN], validator: REPLACE, response: FIELDS_D },
127
+ { method: m.PATCH, path: `${path}/{id}`, access: [a.ADMIN], validator: UPDATE, response: FIELDS_D },
128
+ { method: m.DELETE, path: `${path}/{id}`, access: [a.ADMIN] },
129
+ ];
130
+
131
+ // INIT
132
+ super(path, adminGroupName);
133
+ this.setDynamoDB(tableName, ownerFieldName, indexMap)
134
+ .setOpenSearch(openSearch_Product.domain, openSearch_Product.index)
135
+ .setPolicies(policyList);
136
+ }
137
+ }
138
+
139
+ // Export default Product configuration
140
+ export const CONFIG_PRODUCT = new ProductConfig();
141
+
142
+ ```
143
+
144
+ ### 3. Repository Definition - DynamoDB
145
+
146
+ ```typescript
147
+ import { Service } from "typedi";
148
+ import { BaseRepoDBImpl } from "@chinggis/core";
149
+ import { Product } from "../../model-shared/product.model";
150
+ import { ProductRepoDB } from "./product-repo-db.interface";
151
+ import "./product-repo-db";
152
+
153
+ @Service("ProductRepoDB")
154
+ export class ProductRepoDBImpl extends BaseRepoDBImpl<Product> implements ProductRepoDB {
155
+ }
156
+
157
+ /** additional custom repo methode for dynamodb integration**/
158
+ //...
159
+ ```
160
+
161
+ ### 4. Repository Definition - OpenSearch
162
+
163
+ ```typescript
164
+ import { Service } from "typedi";
165
+ import { BaseRepoESImpl } from "@chinggis/core";
166
+ import { Product } from "../../model-shared/product.model";
167
+ import { ProductRepoES } from "./product-repo-es.interface";
168
+ import "./product-repo-es";
169
+
170
+ @Service("ProductRepoES")
171
+ export class ProductRepoESImpl extends BaseRepoESImpl<Product> implements ProductRepoES {
172
+ }
173
+
174
+ /** additional custom repo methode for openSearch integration**/
175
+ //...
176
+ ```
177
+
178
+ ### 5. Service Definition
179
+
180
+ ```typescript
181
+ import { Container, Service } from "typedi";
182
+ import { ProfileService } from "./Product-service.interface";
183
+ import "./Product-service";
184
+ import { ProfileRepoDB } from "../repositories/Product/Product-repo-db.interface";
185
+ import "../repositories/Product/Product-repo-db";
186
+
187
+ import { ErrorHttp } from "../../exception/errors";
188
+ import { CrudServiceImpl } from "@chinggis/core";
189
+ import { Product } from "../model-shared/example.model";
190
+
191
+ @Service("ProfileService")
192
+ export class ProductServiceImpl extends CrudServiceImpl<Product, ProductRepoDB, ProductRepoES> implements ProductService {
193
+ constructor() {
194
+ super(Container.get("ProductRepoDB"), Container.get("ProductRepoES"));
195
+ }
196
+
197
+ /** Custom service methode **/
198
+ //...
199
+
200
+ }
201
+ ```
202
+
203
+ ## ๐Ÿ› ๏ธ Built-in API Endpoints
204
+
205
+ Your AWS Service Stack automatically provides these RESTful endpoints for any entity:
206
+
207
+ ### **Standard CRUD Operations**
208
+
209
+ ```
210
+ POST /products # Insert new product (create)
211
+ GET /products/{id} # Get single product by ID
212
+ GET /products # List products via DynamoDB Query (by categoryId, ProductId, status, โ€ฆ)
213
+ GET /products/search # Search products via OpenSearch (full-text, sort, pagination)
214
+ GET /products/scan # Scan all products (Admin only, expensive in DynamoDB)
215
+ PUT /products/{id} # Update full product (replace all fields)
216
+ PATCH /products/{id} # Update partial product (only specific fields)
217
+ DELETE /products/{id} # Delete product by ID
218
+ ```
219
+
220
+ ### **List Operation - URL Filter**
221
+
222
+ index filter, combined index filter, range filter for date or number field, sort, search, paged
223
+
224
+ ```
225
+ openSearch: .../products/search?category=Toys&color=red&age_from=5&age_to=8&page=5&size=50
226
+ dynamoDB: .../products/?category=Toys&color=red&age_from=5&age_to=8&size=50&lastKey=dfashdfjkasd89843dfa
227
+ ```
228
+
229
+ - **order**=ASC
230
+ - **oderBy**=createdDate
231
+ - **searchKeyword**=marvel
232
+ - **searchBy**=name,manufacture,description
233
+ - **[customFieldName]**=[matchValue]
234
+ - **size**=20
235
+ - **page**=3 or **lastKey**=...
236
+ - **[dateOrNumberFieldName]_from**=[Value]
237
+ - **[dateOrNumberFieldName]_to**=[Value]
238
+ - **begin_[dateOrNumberFieldName]**=[Value]
239
+ - **end_[dateOrNumberFieldName]**=[Value]
240
+
241
+ ### **Advanced Features**
242
+
243
+ - **๐Ÿ” Smart Filtering** - DynamoDB queries with complex conditions
244
+ - **๐Ÿ”Ž Full-Text Search** - OpenSearch integration with faceted search
245
+ - **๐Ÿ“„ Pagination** - Built-in pagination with `lastKey` support
246
+ - **๐Ÿ” Authorization** - Role-based access control per endpoint
247
+ - **โœ… Validation** - Automatic request/response validation
248
+ - **๐Ÿ“Š Analytics** - Built-in logging and monitoring
249
+
250
+ ## ๐Ÿ—๏ธ Architecture
251
+
252
+ ### **Layered Architecture**
253
+
254
+ ```
255
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
256
+ โ”‚ Controllers โ”‚ โ† API Gateway, Lambda Events
257
+ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
258
+ โ”‚ Services โ”‚ โ† Business Logic
259
+ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
260
+ โ”‚ Repositories โ”‚ โ† Data Access (DynamoDB, OpenSearch)
261
+ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
262
+ โ”‚ Providers โ”‚ โ† AWS SDK Clients
263
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
264
+ ```
265
+
266
+ ### **Event Handling**
267
+
268
+ - **API Gateway** - RESTful API endpoints
269
+ - **SQS** - Message queue processing
270
+ - **DynamoDB Streams** - Real-time data processing
271
+ - **WebSocket** - Real-time communication
272
+ - **Scheduled Events** - Cron-based tasks
273
+
274
+ ## ๐Ÿ”ง Configuration
275
+
276
+ ### Environment Variables
277
+
278
+ ```bash
279
+ # AWS Configuration
280
+ AWS_REGION=us-east-1
281
+ IAM_ACCESS_KEY=your-access-key
282
+ IAM_SECRET_ACCESS_KEY=your-secret-key
283
+
284
+ # OpenSearch
285
+ OPENSEARCH_ENDPOINT=https://your-domain.us-east-1.es.amazonaws.com
286
+
287
+ # API Gateway
288
+ WS_ENDPOINT=wss://your-websocket-api.execute-api.us-east-1.amazonaws.com/prod
289
+ ```
290
+
291
+ ### TypeScript Configuration
292
+
293
+ ```typescript
294
+ // tsconfig.json
295
+ {
296
+ "compilerOptions"
297
+ :
298
+ {
299
+ "baseUrl"
300
+ :
301
+ "./",
302
+ "paths"
303
+ :
304
+ {
305
+ "@chinggis/core"
306
+ :
307
+ ["node_modules/aws-service-stack/dist/index.d.ts"],
308
+ "@chinggis/types"
309
+ :
310
+ ["node_modules/aws-service-stack/dist/model/index.d.ts"]
311
+ }
312
+ }
313
+ }
314
+ ```
315
+
316
+ ## ๐Ÿ“š API Reference
317
+
318
+ ### **BaseController**
319
+
320
+ - `handleRequest(event)` - Main Lambda handler
321
+ - `handleSQS(event)` - SQS message processing
322
+ - `handleWebSocket(event)` - WebSocket connection handling
323
+
324
+ ### **BaseService**
325
+
326
+ - `save(entity, ownerId)` - Create/update entity
327
+ - `findById(id, userId)` - Find entity by ID
328
+ - `find(filter: Filter, userId)` - Find entity by filter
329
+ - `delete(id, userId)` - Delete entity
330
+ - `...` - Usefully crud methods
331
+
332
+ ### **Repository Interfaces**
333
+
334
+ - `CoreRepo<T>` - Generic CRUD operations
335
+ - `BaseRepoDB<T>` - DynamoDB-specific operations
336
+ - `BaseRepoES<T>` - OpenSearch-specific operations
337
+
338
+ ## ๐Ÿงช Testing
339
+
340
+ ```bash
341
+ # Run tests
342
+ npm test
343
+
344
+ # Run tests with coverage
345
+ npm run test:coverage
346
+
347
+ # Run specific test file
348
+ npm test -- user.test.ts
349
+ ```
350
+
351
+ ## ๐Ÿš€ Example Deployment
352
+
353
+ ### Serverless Framework - For Example
354
+
355
+ ```yaml
356
+ # serverless.yml
357
+ service: my-aws-service
358
+ provider:
359
+ name: aws
360
+ runtime: nodejs18.x
361
+ region: us-east-1
362
+
363
+ functions:
364
+ api:
365
+ handler: dist/handler.handler
366
+ events:
367
+ - http:
368
+ path: /{proxy+}
369
+ method: ANY
370
+ ```
371
+
372
+ ### AWS CDK
373
+
374
+ ```typescript
375
+ import { Function, Runtime } from 'aws-cdk-lib/aws-lambda';
376
+
377
+ new Function(this, 'MyFunction', {
378
+ runtime: Runtime.NODEJS_18_X,
379
+ handler: 'dist/handler.handler',
380
+ code: Code.fromAsset('dist')
381
+ });
382
+ ```
383
+
384
+ ## ๐Ÿค Contributing
385
+
386
+ 1. Fork the repository
387
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
388
+ 3. Commit your changes (`git commit -m 'feat(core): add amazing feature'`)
389
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
390
+ 5. Open a Pull Request
391
+
392
+ ### Development Setup
393
+
394
+ ```bash
395
+ # Clone the repository
396
+ git clone https://github.com/chinggis-systems/aws-service-stack.git
397
+
398
+ # Install dependencies
399
+ npm install
400
+
401
+ # Run tests
402
+ npm test
403
+
404
+ # Build the project
405
+ npm run build
406
+ ```
407
+
408
+ ## ๐Ÿ“„ License
409
+
410
+ This project is licensed under the ISC License - see the [LICENSE](LICENSE) file for details.
411
+
412
+ ## ๐Ÿข About Chinggis Systems
413
+
414
+ Built by [Chinggis Systems](https://chinggis.systems) - Enterprise software solutions for modern cloud architectures.
415
+
416
+ ## ๐Ÿ“ž Support
417
+
418
+ - ๐Ÿ’ฌ Discord: [Join our community](https://discord.gg/DAPNvk4F)
419
+ - ๐Ÿ“ง Email: support@chinggis.systems
420
+ - ๐Ÿ› Issues: [GitHub Issues](https://github.com/chinggis-systems/aws-service-stack/issues)
421
+ - ๐Ÿ“– Documentation: [Wiki](https://github.com/chinggis-systems/aws-service-stack/wiki)
422
+
423
+ ---
424
+
304
425
  **Made with โค๏ธ for the AWS serverless community**
@@ -1 +1 @@
1
- {"version":3,"file":"supplier-config.js","sourceRoot":"","sources":["../../../../src/_examples/controller/supplier/supplier-config.ts"],"names":[],"mappings":";;;AAAA,yCAAgH;AAChH,wFAMoD;AAEvC,QAAA,mBAAmB,GAAG;IACjC,MAAM,EAAE,wGAAwG;IAChH,KAAK,EAAE,UAAU;CAClB,CAAC;AACW,QAAA,IAAI,GAAG,oBAAoB,CAAC,CAAC,gBAAgB;AAE1D,yBAAyB;AACzB,MAAa,cAAe,SAAQ,uBAAgB;IAClD;QACE,WAAW;QACX,MAAM,SAAS,GAAG,yCAAyC,CAAC;QAC5D,MAAM,cAAc,GAAG,UAAU,CAAC;QAClC,MAAM,QAAQ,GAAG,IAAI,qBAAc,EAAE;aAClC,SAAS,CAAC,UAAU,CAAC;aACrB,GAAG,CAAC,eAAe,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QAE5E,cAAc;QACd,MAAM,cAAc,GAAG,CAAC,YAAY,CAAC,CAAC;QACtC,MAAM,UAAU,GAAqB;YACnC,EAAE,MAAM,EAAE,iBAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,YAAI,EAAE,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,8CAAQ,EAAE;YACzE,EAAE,MAAM,EAAE,iBAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,YAAI,SAAS,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,8CAAQ,EAAE;YAChF,EAAE,MAAM,EAAE,iBAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,YAAI,qBAAqB,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,8CAAQ,EAAE;YAC5F,EAAE,MAAM,EAAE,iBAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,YAAI,OAAO,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,EAAE,aAAC,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,iDAAQ,EAAE;YAEvF,EAAE,MAAM,EAAE,iBAAC,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,YAAI,EAAE,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,gCAAM,EAAE,QAAQ,EAAE,iDAAQ,EAAE;YAE7F,EAAE,MAAM,EAAE,iBAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,YAAI,OAAO,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,iCAAO,EAAE,QAAQ,EAAE,iDAAQ,EAAE;YAElG,EAAE,MAAM,EAAE,iBAAC,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,YAAI,OAAO,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,gCAAM,EAAE,QAAQ,EAAE,iDAAQ,EAAE;YAEnG,EAAE,MAAM,EAAE,iBAAC,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,YAAI,OAAO,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,CAAC,EAAE;SAC9D,CAAC;QAEF,OAAO;QACP,KAAK,CAAC,YAAI,EAAE,cAAc,CAAC,CAAC;QAC5B,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,cAAc,EAAE,QAAQ,CAAC;aAClD,aAAa,CAAC,2BAAmB,CAAC,MAAM,EAAE,2BAAmB,CAAC,KAAK,CAAC;aACpE,WAAW,CAAC,UAAU,CAAC,CAAC;IAC7B,CAAC;CACF;AAhCD,wCAgCC;AAED,wCAAwC;AAC3B,QAAA,eAAe,GAAG,IAAI,cAAc,EAAE,CAAC","sourcesContent":["import { Access as a, DynamoIndexMap, EndpointPolicy, EntityConfigImpl, HttpMethod as m } from \"@chinggis/core\";\nimport {\n CREATE,\n REPLACE,\n RESPONSE_FIELDS_DETAILS as FIELDS_D,\n RESPONSE_FIELDS_LIST as FIELDS_L,\n UPDATE,\n} from \"../../model-shared/suppler.model-to-remove\";\n\nexport const openSearch_supplier = {\n domain: \"https://search-amplify-opense-1qu4zfdauaeh1-ncvbubjnpcsfempstbsykhoadi.ap-southeast-1.es.amazonaws.com\",\n index: \"saccount\",\n};\nexport const path = \"/supplier/accounts\"; // url path base\n\n// Supplier configuration\nexport class SupplierConfig extends EntityConfigImpl {\n constructor() {\n // DYNAMODB\n const tableName = \"SAccount-2i5o74z4mjf6lpabqp7myrkdre-dev\";\n const ownerFieldName = \"username\";\n const indexMap = new DynamoIndexMap()\n .setFields(\"username\")\n .set(\"byAccountType\", { field: \"accountType\", rFields: [\"accountType\"] });\n\n // PERMISSIONS\n const adminGroupName = [\"adminUsers\"];\n const policyList: EndpointPolicy[] = [\n { method: m.GET, path: `${path}`, access: [a.ADMIN], response: FIELDS_L },\n { method: m.GET, path: `${path}/search`, access: [a.ADMIN], response: FIELDS_L },\n { method: m.GET, path: `${path}/list-non-level-one`, access: [a.ADMIN], response: FIELDS_L },\n { method: m.GET, path: `${path}/{id}`, access: [a.OWNER, a.ADMIN], response: FIELDS_D },\n\n { method: m.POST, path: `${path}`, access: [a.ADMIN], validator: CREATE, response: FIELDS_D },\n\n { method: m.PUT, path: `${path}/{id}`, access: [a.ADMIN], validator: REPLACE, response: FIELDS_D },\n\n { method: m.PATCH, path: `${path}/{id}`, access: [a.ADMIN], validator: UPDATE, response: FIELDS_D },\n\n { method: m.DELETE, path: `${path}/{id}`, access: [a.ADMIN] },\n ];\n\n // INIT\n super(path, adminGroupName);\n this.setDynamoDB(tableName, ownerFieldName, indexMap)\n .setOpenSearch(openSearch_supplier.domain, openSearch_supplier.index)\n .setPolicies(policyList);\n }\n}\n\n// Export default Supplier configuration\nexport const CONFIG_SUPPLIER = new SupplierConfig();\n"]}
1
+ {"version":3,"file":"supplier-config.js","sourceRoot":"","sources":["../../../../src/_examples/controller/supplier/supplier-config.ts"],"names":[],"mappings":";;;AAAA,yCAAgH;AAChH,wFAMoD;AAEvC,QAAA,mBAAmB,GAAG;IACjC,MAAM,EAAE,wGAAwG;IAChH,KAAK,EAAE,UAAU;CAClB,CAAC;AACW,QAAA,IAAI,GAAG,oBAAoB,CAAC,CAAC,gBAAgB;AAE1D,yBAAyB;AACzB,MAAa,cAAe,SAAQ,uBAAgB;IAClD;QACE,WAAW;QACX,MAAM,SAAS,GAAG,yCAAyC,CAAC;QAC5D,MAAM,cAAc,GAAG,UAAU,CAAC;QAClC,MAAM,QAAQ,GAAG,IAAI,qBAAc,EAAE;aAClC,SAAS,CAAC,UAAU,CAAC;aACrB,GAAG,CAAC,eAAe,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QAE5E,cAAc;QACd,MAAM,cAAc,GAAG,CAAC,YAAY,CAAC,CAAC;QACtC,MAAM,UAAU,GAAqB;YACnC,EAAE,MAAM,EAAE,iBAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,YAAI,EAAE,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,8CAAQ,EAAE;YACzE,EAAE,MAAM,EAAE,iBAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,YAAI,SAAS,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,8CAAQ,EAAE;YAChF,EAAE,MAAM,EAAE,iBAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,YAAI,qBAAqB,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,8CAAQ,EAAE;YAC5F,EAAE,MAAM,EAAE,iBAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,YAAI,OAAO,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,EAAE,aAAC,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,iDAAQ,EAAE;YACvF,EAAE,MAAM,EAAE,iBAAC,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,YAAI,EAAE,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,gCAAM,EAAE,QAAQ,EAAE,iDAAQ,EAAE;YAC7F,EAAE,MAAM,EAAE,iBAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,YAAI,OAAO,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,iCAAO,EAAE,QAAQ,EAAE,iDAAQ,EAAE;YAClG,EAAE,MAAM,EAAE,iBAAC,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,YAAI,OAAO,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,gCAAM,EAAE,QAAQ,EAAE,iDAAQ,EAAE;YACnG,EAAE,MAAM,EAAE,iBAAC,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,YAAI,OAAO,EAAE,MAAM,EAAE,CAAC,aAAC,CAAC,KAAK,CAAC,EAAE;SAC9D,CAAC;QAEF,OAAO;QACP,KAAK,CAAC,YAAI,EAAE,cAAc,CAAC,CAAC;QAC5B,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,cAAc,EAAE,QAAQ,CAAC;aAClD,aAAa,CAAC,2BAAmB,CAAC,MAAM,EAAE,2BAAmB,CAAC,KAAK,CAAC;aACpE,WAAW,CAAC,UAAU,CAAC,CAAC;IAC7B,CAAC;CACF;AA5BD,wCA4BC;AAED,wCAAwC;AAC3B,QAAA,eAAe,GAAG,IAAI,cAAc,EAAE,CAAC","sourcesContent":["import { Access as a, DynamoIndexMap, EndpointPolicy, EntityConfigImpl, HttpMethod as m } from \"@chinggis/core\";\nimport {\n CREATE,\n REPLACE,\n RESPONSE_FIELDS_DETAILS as FIELDS_D,\n RESPONSE_FIELDS_LIST as FIELDS_L,\n UPDATE,\n} from \"../../model-shared/suppler.model-to-remove\";\n\nexport const openSearch_supplier = {\n domain: \"https://search-amplify-opense-1qu4zfdauaeh1-ncvbubjnpcsfempstbsykhoadi.ap-southeast-1.es.amazonaws.com\",\n index: \"saccount\",\n};\nexport const path = \"/supplier/accounts\"; // url path base\n\n// Supplier configuration\nexport class SupplierConfig extends EntityConfigImpl {\n constructor() {\n // DYNAMODB\n const tableName = \"SAccount-2i5o74z4mjf6lpabqp7myrkdre-dev\";\n const ownerFieldName = \"username\";\n const indexMap = new DynamoIndexMap()\n .setFields(\"username\")\n .set(\"byAccountType\", { field: \"accountType\", rFields: [\"accountType\"] });\n\n // PERMISSIONS\n const adminGroupName = [\"adminUsers\"];\n const policyList: EndpointPolicy[] = [\n { method: m.GET, path: `${path}`, access: [a.ADMIN], response: FIELDS_L },\n { method: m.GET, path: `${path}/search`, access: [a.ADMIN], response: FIELDS_L },\n { method: m.GET, path: `${path}/list-non-level-one`, access: [a.ADMIN], response: FIELDS_L },\n { method: m.GET, path: `${path}/{id}`, access: [a.OWNER, a.ADMIN], response: FIELDS_D },\n { method: m.POST, path: `${path}`, access: [a.ADMIN], validator: CREATE, response: FIELDS_D },\n { method: m.PUT, path: `${path}/{id}`, access: [a.ADMIN], validator: REPLACE, response: FIELDS_D },\n { method: m.PATCH, path: `${path}/{id}`, access: [a.ADMIN], validator: UPDATE, response: FIELDS_D },\n { method: m.DELETE, path: `${path}/{id}`, access: [a.ADMIN] },\n ];\n\n // INIT\n super(path, adminGroupName);\n this.setDynamoDB(tableName, ownerFieldName, indexMap)\n .setOpenSearch(openSearch_supplier.domain, openSearch_supplier.index)\n .setPolicies(policyList);\n }\n}\n\n// Export default Supplier configuration\nexport const CONFIG_SUPPLIER = new SupplierConfig();\n"]}
@@ -1,9 +1,9 @@
1
1
  import { SupplierService } from "../../service/supplier/supplier-service-crud.interface";
2
2
  import "../../service/supplier/supplier-service-crud";
3
- import { ControllerApi, HttpRequest, List } from "../../../index.js";
3
+ import { ControllerApi, HttpRequest } from "../../../index.js";
4
4
  import { SAccount } from "src/_examples/model-shared/suppler.model-to-remove";
5
5
  export declare class SupplierControllerApi extends ControllerApi<SAccount, SupplierService> {
6
6
  constructor();
7
- processCrudRequestPost(request: HttpRequest, response: SAccount | List<SAccount>): Promise<SAccount | List<SAccount>>;
7
+ /***/
8
8
  protected processCrudRequest(req: HttpRequest): Promise<any>;
9
9
  }
@@ -52,34 +52,14 @@ let SupplierControllerApi = class SupplierControllerApi extends core_1.Controlle
52
52
  const service = typedi_1.default.get("SupplierCrudService");
53
53
  super(service, supplier_config_1.CONFIG_SUPPLIER.toObject());
54
54
  }
55
- // processCrudRequestPre(req: HttpRequest): Promise<HttpRequest> {
56
- // return Promise.resolve(req);
57
- // }
58
- //
59
- // processCrudRequestPost(request: HttpRequest, response: SupplierService | List<SupplierService>): Promise<any> {
60
- // return Promise.resolve(response);
61
- // }
62
- processCrudRequestPost(request, response) {
63
- return Promise.resolve(response);
64
- }
55
+ /***/
65
56
  async processCrudRequest(req) {
66
57
  log.debug(`_example processCrudRequest: ${JSON.stringify(req.event, null, 2)}`);
67
58
  if (req.resourcePath === `GET ${supplier_config_1.path}/list-non-level-one`) {
68
59
  return await this.service.ListNonLevelOne(req.filter);
69
60
  }
70
61
  if (req.resourcePath === `GET ${supplier_config_1.path}/find-all`) {
71
- this.service.setConfig(supplier_config_1.CONFIG_SUPPLIER.toObject());
72
- try {
73
- // return await this.service.findAll();
74
- return await this.service.search(req.filter);
75
- }
76
- catch (err) {
77
- log.error(JSON.stringify(err, null, 2));
78
- }
79
- }
80
- if (req.resourcePath === `PUT ${supplier_config_1.path}/convert-to-cognito` && req.isAdmin) {
81
- const { username, email } = req.body;
82
- log.debug(`_example convert to cognito user: ${username}, email: ${email}`);
62
+ return await this.service.search(req.filter);
83
63
  }
84
64
  }
85
65
  };
@@ -1 +1 @@
1
- {"version":3,"file":"supplier-controller-api.js","sourceRoot":"","sources":["../../../../src/_examples/controller/supplier/supplier-controller-api.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iDAA4C;AAC5C,uDAA0D;AAG1D,wDAAsD;AACtD,yCAAkE;AAI3D,IAAM,qBAAqB,GAA3B,MAAM,qBAAsB,SAAQ,oBAAwC;IACjF;QACE,MAAM,OAAO,GAAoB,gBAAS,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACtE,KAAK,CAAC,OAAO,EAAE,iCAAe,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,mEAAmE;IACnE,kCAAkC;IAClC,KAAK;IACL,EAAE;IACF,mHAAmH;IACnH,uCAAuC;IACvC,KAAK;IAEL,sBAAsB,CACpB,OAAoB,EACpB,QAAmC;QAEnC,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAES,KAAK,CAAC,kBAAkB,CAAC,GAAgB;QACjD,GAAG,CAAC,KAAK,CAAC,gCAAgC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAEhF,IAAI,GAAG,CAAC,YAAY,KAAK,OAAO,sBAAI,qBAAqB,EAAE,CAAC;YAC1D,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,GAAG,CAAC,YAAY,KAAK,OAAO,sBAAI,WAAW,EAAE,CAAC;YAChD,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,iCAAe,CAAC,QAAQ,EAAE,CAAC,CAAC;YACnD,IAAI,CAAC;gBACH,8CAA8C;gBAC9C,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC/C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC1C,CAAC;QACH,CAAC;QAED,IAAI,GAAG,CAAC,YAAY,KAAK,OAAO,sBAAI,qBAAqB,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YACzE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;YACrC,GAAG,CAAC,KAAK,CAAC,qCAAqC,QAAQ,YAAY,KAAK,EAAE,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;CACF,CAAA;AA1CY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,gBAAO,EAAC,uBAAuB,CAAC;;GACpB,qBAAqB,CA0CjC","sourcesContent":["import Container, { Service } from \"typedi\";\nimport { CONFIG_SUPPLIER, path } from \"./supplier-config\";\n\nimport { SupplierService } from \"../../service/supplier/supplier-service-crud.interface\";\nimport \"../../service/supplier/supplier-service-crud\";\nimport { ControllerApi, HttpRequest, List } from \"@chinggis/core\";\nimport { SAccount } from \"src/_examples/model-shared/suppler.model-to-remove\";\n\n@Service(\"SupplierControllerApi\")\nexport class SupplierControllerApi extends ControllerApi<SAccount, SupplierService> {\n constructor() {\n const service: SupplierService = Container.get(\"SupplierCrudService\");\n super(service, CONFIG_SUPPLIER.toObject());\n }\n\n // processCrudRequestPre(req: HttpRequest): Promise<HttpRequest> {\n // return Promise.resolve(req);\n // }\n //\n // processCrudRequestPost(request: HttpRequest, response: SupplierService | List<SupplierService>): Promise<any> {\n // return Promise.resolve(response);\n // }\n\n processCrudRequestPost(\n request: HttpRequest,\n response: SAccount | List<SAccount>,\n ): Promise<SAccount | List<SAccount>> {\n return Promise.resolve(response);\n }\n\n protected async processCrudRequest(req: HttpRequest): Promise<any> {\n log.debug(`_example processCrudRequest: ${JSON.stringify(req.event, null, 2)}`);\n\n if (req.resourcePath === `GET ${path}/list-non-level-one`) {\n return await this.service.ListNonLevelOne(req.filter);\n }\n if (req.resourcePath === `GET ${path}/find-all`) {\n this.service.setConfig(CONFIG_SUPPLIER.toObject());\n try {\n // return await this.service.findAll();\n return await this.service.search(req.filter);\n } catch (err) {\n log.error(JSON.stringify(err, null, 2));\n }\n }\n\n if (req.resourcePath === `PUT ${path}/convert-to-cognito` && req.isAdmin) {\n const { username, email } = req.body;\n log.debug(`_example convert to cognito user: ${username}, email: ${email}`);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"supplier-controller-api.js","sourceRoot":"","sources":["../../../../src/_examples/controller/supplier/supplier-controller-api.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iDAA4C;AAC5C,uDAA0D;AAG1D,wDAAsD;AACtD,yCAA4D;AAIrD,IAAM,qBAAqB,GAA3B,MAAM,qBAAsB,SAAQ,oBAAwC;IACjF;QACE,MAAM,OAAO,GAAoB,gBAAS,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACtE,KAAK,CAAC,OAAO,EAAE,iCAAe,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK;IACK,KAAK,CAAC,kBAAkB,CAAC,GAAgB;QACjD,GAAG,CAAC,KAAK,CAAC,gCAAgC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAEhF,IAAI,GAAG,CAAC,YAAY,KAAK,OAAO,sBAAI,qBAAqB,EAAE,CAAC;YAC1D,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxD,CAAC;QAED,IAAI,GAAG,CAAC,YAAY,KAAK,OAAO,sBAAI,WAAW,EAAE,CAAC;YAChD,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;CACF,CAAA;AAlBY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,gBAAO,EAAC,uBAAuB,CAAC;;GACpB,qBAAqB,CAkBjC","sourcesContent":["import Container, { Service } from \"typedi\";\nimport { CONFIG_SUPPLIER, path } from \"./supplier-config\";\n\nimport { SupplierService } from \"../../service/supplier/supplier-service-crud.interface\";\nimport \"../../service/supplier/supplier-service-crud\";\nimport { ControllerApi, HttpRequest } from \"@chinggis/core\";\nimport { SAccount } from \"src/_examples/model-shared/suppler.model-to-remove\";\n\n@Service(\"SupplierControllerApi\")\nexport class SupplierControllerApi extends ControllerApi<SAccount, SupplierService> {\n constructor() {\n const service: SupplierService = Container.get(\"SupplierCrudService\");\n super(service, CONFIG_SUPPLIER.toObject());\n }\n\n /***/\n protected async processCrudRequest(req: HttpRequest): Promise<any> {\n log.debug(`_example processCrudRequest: ${JSON.stringify(req.event, null, 2)}`);\n\n if (req.resourcePath === `GET ${path}/list-non-level-one`) {\n return await this.service.ListNonLevelOne(req.filter);\n }\n\n if (req.resourcePath === `GET ${path}/find-all`) {\n return await this.service.search(req.filter);\n }\n }\n}\n"]}
@@ -165,6 +165,8 @@ class CrudServiceImpl {
165
165
  }
166
166
  async findById(entityId, profileId) {
167
167
  const entity = await this.repoDB.findById(entityId);
168
+ if (!entity)
169
+ return null;
168
170
  const isOwnerParent = entity[this.config.OWNER_PARENT_ID_FIELD_NAME] == profileId;
169
171
  const isParent = entity[this.config.OWNER_ID_FIELD_NAME] == profileId;
170
172
  if (!profileId || isOwnerParent || isParent) {
@@ -1 +1 @@
1
- {"version":3,"file":"crud.service.js","sourceRoot":"","sources":["../../src/service/crud.service.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n addDates,\n addDatesAndId,\n BaseEntity,\n BaseRepoDB,\n BaseRepoDBImpl,\n BaseRepoES,\n BaseRepoESImpl,\n DynamoIndexMap,\n Filter,\n List,\n parseIndexFilter,\n} from \"../index\";\nimport { ErrorBase, ErrorDynamoDB, ErrorHttp, ErrorValidation } from \"../exception\";\n\nimport { EntityConfig } from \"@chinggis/types\";\nimport { CrudService } from \"./crud.service.interface\";\n\nexport abstract class CrudServiceImpl<T extends BaseEntity, D extends BaseRepoDB<T>, S extends BaseRepoES<T>>\n implements CrudService<T>\n{\n protected repoDB: D;\n protected repoES: S;\n protected config: EntityConfig;\n\n protected constructor(repoDB: D, repoES: S) {\n if (repoDB != null && !(repoDB instanceof BaseRepoDBImpl))\n console.error(\"repoDB is not an instance of BaseRepoDBImpl\");\n\n if (repoES != null && !(repoES instanceof BaseRepoESImpl))\n console.error(\"repoES is not an instance of BaseRepoESImpl\");\n\n if (repoDB == null && repoES == null) console.error(\"repo initialization is required\");\n\n this.repoDB = repoDB;\n this.repoES = repoES;\n }\n\n getTableName(): string {\n return this.repoDB.getTableName();\n }\n\n async findQuery(queryParams: string): Promise<List<Partial<T>>> {\n const filter = parseIndexFilter(queryParams, this.getIndexMapDB());\n this.checkIsIndexedField(filter);\n return await this.find(filter);\n }\n\n async searchQuery(queryParams: string): Promise<Partial<T>[]> {\n let filter = parseIndexFilter(queryParams);\n filter = this.parseOwnerFields(filter, this.config);\n this.checkIsIndexedField(filter);\n return await this.search(filter);\n }\n\n async incrementValueByField(entityId: string, fieldName: string, value?: number): Promise<T> {\n return await this.repoDB.incrementValueByField(entityId, fieldName, value);\n }\n\n async decrementValueByField(entityId: string, fieldName: string, value?: number): Promise<T> {\n return await this.repoDB.decrementValueByField(entityId, fieldName, value);\n }\n\n async search(filter: Filter): Promise<T[]> {\n const parsedFilter = this.parseOwnerFields(filter, this.config);\n return (await this.repoES.find(parsedFilter)).items as T[];\n }\n\n async searchFieldNotExist(fieldName: string, from: number, size: number): Promise<Partial<T>[]> {\n return await this.repoES.fieldNotExists(fieldName, from, size);\n }\n\n async save(entity: Partial<T>, ownerId: string, parentId?: string, createdByUserId?: string): Promise<T> {\n addDatesAndId(entity);\n\n if (ownerId) {\n entity[this.config.OWNER_ID_FIELD_NAME] = ownerId;\n entity.createdBy = createdByUserId || ownerId;\n }\n if (parentId) entity[this.config.OWNER_PARENT_ID_FIELD_NAME] = parentId;\n\n return await this.repoDB.save(entity);\n }\n\n async saveAll(\n entities: Partial<T>[],\n ownerId: string,\n parentId?: string,\n createdByUserId?: string,\n isTransactional?: boolean,\n ): Promise<Partial<T>[]> {\n const entitiesUpdated = [];\n\n for (const item of entities) {\n const entity = addDatesAndId(item);\n\n if (parentId) entity[this.config.OWNER_PARENT_ID_FIELD_NAME] = parentId;\n\n entity[this.config.OWNER_ID_FIELD_NAME] = ownerId;\n entity.createdBy = createdByUserId || ownerId;\n\n entitiesUpdated.push(entity);\n }\n\n return await this.repoDB.saveMany(entitiesUpdated, isTransactional);\n }\n\n async update(entity: T, requestUserId?: string): Promise<T> {\n if (requestUserId) {\n const item = await this.repoDB.findById(entity.id);\n if (\n item[this.config.OWNER_ID_FIELD_NAME] !== requestUserId ||\n item[this.config.OWNER_PARENT_ID_FIELD_NAME] !== requestUserId\n )\n throw new ErrorHttp({ code: 403, error: \"PermissionDenied\" }, `No permission to resource: ${entity.id}`);\n entity.updatedBy = requestUserId;\n }\n entity = addDates(entity) as T;\n\n // if (!hasPermission(entity, userId, Permission.UPDATE)) throw new Error(\"Unauthorized access\");\n return await this.repoDB.update(entity);\n }\n\n async updateAll(\n entities: Partial<T>[],\n fields: string[],\n requestUserId?: string,\n isTransactional?: boolean,\n ): Promise<boolean> {\n if (requestUserId) log.warn(\"this methode is under development, cannot check ownership by updateAll\");\n\n return await this.repoDB.updateMany(entities, fields, isTransactional);\n }\n\n async remove(entityId: string, ownerId?: string): Promise<boolean> {\n const entity = await this.repoDB.findById(entityId);\n if (!entity) throw new Error(\"Entity not found\");\n\n if (ownerId && entity[this.config.OWNER_ID_FIELD_NAME] !== ownerId) return null;\n\n return await this.repoDB.delete(entityId);\n }\n\n async removeAll(entityIds: string[], ownerId?: string): Promise<boolean> {\n if (ownerId) log.warn(\"this methode is under development, cannot check ownership by removeAll\");\n return await this.repoDB.deleteMany(entityIds);\n }\n\n async changeOwner(entityId: string, ownerId: string): Promise<T> {\n const entity = await this.repoDB.findById(entityId);\n entity[this.config.OWNER_ID_FIELD_NAME] = ownerId;\n return this.repoDB.update(entity);\n }\n\n setTable(tableName: string, dynamoIndexMap: DynamoIndexMap): boolean {\n this.repoDB.setTable(tableName);\n this.repoDB.setIndexMap(dynamoIndexMap);\n return true;\n }\n\n setOpensearch(indexName: string, opensearchEndpoint: string): boolean {\n this.repoES.setIndexName(indexName);\n this.repoES.setEndpoint(opensearchEndpoint);\n return true;\n }\n\n getIndexES(): string {\n return this.repoES.getIndexName();\n }\n\n getIndexMapDB(): DynamoIndexMap {\n return this.repoDB.getIndexMap();\n }\n\n setIndexMapDB(indexMap: DynamoIndexMap): boolean {\n this.repoDB.setIndexMap(indexMap);\n return true;\n }\n\n async findAll(): Promise<T[]> {\n const result: T[] = [];\n\n const filter = parseIndexFilter(\"size=250\");\n\n do {\n const response = await this.repoDB.find(filter);\n result.push(...(response.items as T[]));\n filter.lastKey = response.lastKey;\n } while (filter.lastKey);\n\n return result;\n }\n\n async findAllMatch(filter: Filter): Promise<T[]> {\n this.checkIsIndexedField(filter);\n return await this.fetchAll(filter);\n }\n\n async findAllByIndex(indexName: string, indexValue: any): Promise<T[]> {\n // Create a filter object for validation\n const filter = { indexName: indexName, indexValue: indexValue };\n this.checkIsIndexedField(filter);\n\n const parsedFilter = parseIndexFilter(filter, this.getIndexMapDB());\n return this.fetchAll(parsedFilter);\n }\n\n async findByField(fieldName: string, fieldValue: any): Promise<List<T>> {\n // Create a filter object for validation\n const filter = { [fieldName]: fieldValue, size: 100 };\n this.checkIsIndexedField(filter);\n const parsedFilter = parseIndexFilter(fieldName + \"=\" + fieldValue + \"&size=100\", this.getIndexMapDB());\n return (await this.find(parsedFilter)) as List<T>;\n }\n\n async findFirst(fieldName: string, fieldValue: string | boolean | number | Date): Promise<T> {\n // Validate that the field name exists in the index map\n const filter = { [fieldName]: fieldValue };\n this.checkIsIndexedField(filter);\n\n const result = await this.findByField(fieldName, fieldValue);\n return result.items.length > 0 ? result.items[0] : null;\n }\n\n async find(filter: Filter): Promise<List<Partial<T>>> {\n // Validate that all field names in the filter exist in the index map\n let parsedFilter = this.parseOwnerFields(filter, this.config);\n\n this.checkIsIndexedField(parsedFilter);\n\n parsedFilter = parseIndexFilter(filter, this.getIndexMapDB());\n return await this.repoDB.find(parsedFilter);\n }\n\n async findById(entityId: string, profileId?: string): Promise<T> {\n const entity = await this.repoDB.findById(entityId);\n\n const isOwnerParent = entity[this.config.OWNER_PARENT_ID_FIELD_NAME] == profileId;\n const isParent = entity[this.config.OWNER_ID_FIELD_NAME] == profileId;\n\n if (!profileId || isOwnerParent || isParent) {\n return entity;\n }\n\n return null;\n }\n\n async findByIds(entityIds: string[], profileId?: string): Promise<T[]> {\n if (entityIds.length > 25) throw new ErrorDynamoDB(this.getTableName(), \"findByIds\", \"Too many entities\");\n\n const entities = await this.repoDB.findByIds(entityIds);\n if (!profileId) return entities;\n const ownEntities = [];\n for (const entity of entities) {\n if (\n entity[this.config.OWNER_PARENT_ID_FIELD_NAME] == profileId ||\n entity[this.config.OWNER_ID_FIELD_NAME] == profileId\n ) {\n ownEntities.push(entity);\n }\n }\n return ownEntities;\n }\n\n async scan(filter: Filter): Promise<List<Partial<T>>> {\n this.checkIsIndexedField(filter);\n const parsedFilter = parseIndexFilter(filter, this.getIndexMapDB());\n return await this.repoDB.scan(parsedFilter);\n }\n\n throwErrorHttp(error: { message: string; code: number }, message?: string, errorContent?: any): void {\n throw new ErrorHttp({ code: error.code, error: error.message }, message, errorContent);\n }\n\n throwErrorDatabase(tableName: string, command: any, errorContent?: any): void {\n throw new ErrorDynamoDB(tableName, command, errorContent);\n }\n\n throwErrorValidation(entityName: string, validation: Map<string, any>, errorContent?: any): void {\n throw new ErrorValidation(entityName, validation, errorContent);\n }\n\n throwError(code: number, message: string, errorContent?: any): void {\n throw new ErrorBase(code, message, errorContent);\n }\n\n setConfig(config: EntityConfig) {\n this.config = config;\n\n if (this.repoDB) {\n this.repoDB.setIndexMap(config.DYNAMO_DB?.MAP);\n this.repoDB.setTable(config.DYNAMO_DB?.NAME);\n }\n\n if (this.repoES) {\n this.repoES.setEndpoint(config.OPEN_SEARCH.DOMAIN);\n this.repoES.setIndexName(config.OPEN_SEARCH.INDEX);\n }\n }\n\n /**\n * Validates that all field names in the filter exist in the index map.\n * Throws a Bad Request error if any field is not found in the available indexes.\n *\n * @param filter - The filter object containing field names to validate\n * @throws ErrorHttp with status 400 if validation fails\n */\n private checkIsIndexedField(filter: Filter): void {\n if (!filter || typeof filter !== \"object\") {\n return; // Skip validation for invalid filters\n }\n\n const indexMap = this.getIndexMapDB();\n if (!indexMap || indexMap.size === 0) {\n throw new ErrorHttp({ code: 400, error: \"Bad Request\" }, \"No indexes configured for this table\");\n }\n\n // Get all valid field names from all indexes\n const validFields = new Set<string>();\n\n // Add a partition key\n if (indexMap.partitionKey) {\n validFields.add(indexMap.partitionKey);\n }\n\n // Add fields from all indexes\n for (const [indexName, indexConfig] of indexMap.entries()) {\n if (indexConfig.field) {\n validFields.add(indexConfig.field);\n }\n if (indexConfig.rFields) {\n indexConfig.rFields.forEach((field) => validFields.add(field));\n }\n if (indexConfig.sortKeyField) {\n validFields.add(indexConfig.sortKeyField);\n }\n }\n\n // Check each field in the filter\n const invalidFields: string[] = [];\n for (const fieldName of Object.keys(filter)) {\n // Skip special filter properties that are not field names\n if (\n [\"indexName\", \"indexValue\", \"size\", \"lastKey\", \"fieldsInclude\", \"fieldsExclude\", \"rangeFilters\"].includes(\n fieldName,\n )\n ) {\n continue;\n }\n\n // Skip range filter fields (ageMin, ageMax, etc.)\n const rangeSuffixes = [\"Min\", \"Max\", \"From\", \"To\", \"Start\", \"End\"];\n const isRangeField = rangeSuffixes.some((suffix) => fieldName.endsWith(suffix));\n if (isRangeField) {\n continue;\n }\n\n if (!validFields.has(fieldName)) {\n invalidFields.push(fieldName);\n }\n }\n\n if (invalidFields.length > 0) {\n const availableFields = Array.from(validFields).sort();\n throw new ErrorHttp(\n { code: 400, error: \"Bad Request\" },\n `Invalid field names in filter: ${invalidFields.join(\", \")}. Available fields: ${availableFields.join(\", \")}`,\n );\n }\n }\n\n private async fetchAll(filter: Filter): Promise<T[]> {\n const result: Partial<T>[] = [];\n do {\n const response = await this.repoDB.find(filter);\n result.push(...response.items);\n filter.lastKey = response.lastKey;\n } while (filter.lastKey);\n\n return result as T[];\n }\n\n private parseOwnerFields(filter: Filter, entityConfig: EntityConfig) {\n const parsedFilter = filter;\n\n log.debug(\"input filter\", parsedFilter);\n\n if (parsedFilter.ownerParentId && !entityConfig.OWNER_PARENT_ID_FIELD_NAME) {\n parsedFilter.profileId = parsedFilter.ownerParentId;\n delete parsedFilter.ownerParentId;\n }\n if (!parsedFilter.profileId) delete parsedFilter.profileId;\n\n if (parsedFilter.ownerParentId && entityConfig.OWNER_PARENT_ID_FIELD_NAME) {\n parsedFilter[entityConfig.OWNER_PARENT_ID_FIELD_NAME] = parsedFilter.ownerParentId;\n if (entityConfig.OWNER_PARENT_ID_FIELD_NAME !== parsedFilter.ownerParentId) delete parsedFilter.ownerParentId;\n }\n\n if (parsedFilter.profileId && entityConfig.OWNER_ID_FIELD_NAME) {\n parsedFilter[entityConfig.OWNER_ID_FIELD_NAME] = parsedFilter.profileId;\n if (entityConfig.OWNER_ID_FIELD_NAME !== parsedFilter.owderId) delete parsedFilter.profileId;\n }\n\n // if (parsedFilter.ownerId && entityConfig.OWNER_ID_FIELD_NAME) {\n // parsedFilter[entityConfig.OWNER_ID_FIELD_NAME] = parsedFilter.ownerId;\n // if (entityConfig.OWNER_ID_FIELD_NAME !== parsedFilter.owderId) delete parsedFilter.ownerId;\n // }\n log.debug(\"parsedFilter\", parsedFilter);\n\n return parsedFilter;\n }\n}\n"]}
1
+ {"version":3,"file":"crud.service.js","sourceRoot":"","sources":["../../src/service/crud.service.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n addDates,\n addDatesAndId,\n BaseEntity,\n BaseRepoDB,\n BaseRepoDBImpl,\n BaseRepoES,\n BaseRepoESImpl,\n DynamoIndexMap,\n Filter,\n List,\n parseIndexFilter,\n} from \"../index\";\nimport { ErrorBase, ErrorDynamoDB, ErrorHttp, ErrorValidation } from \"../exception\";\n\nimport { EntityConfig } from \"@chinggis/types\";\nimport { CrudService } from \"./crud.service.interface\";\n\nexport abstract class CrudServiceImpl<T extends BaseEntity, D extends BaseRepoDB<T>, S extends BaseRepoES<T>>\n implements CrudService<T>\n{\n protected repoDB: D;\n protected repoES: S;\n protected config: EntityConfig;\n\n protected constructor(repoDB: D, repoES: S) {\n if (repoDB != null && !(repoDB instanceof BaseRepoDBImpl))\n console.error(\"repoDB is not an instance of BaseRepoDBImpl\");\n\n if (repoES != null && !(repoES instanceof BaseRepoESImpl))\n console.error(\"repoES is not an instance of BaseRepoESImpl\");\n\n if (repoDB == null && repoES == null) console.error(\"repo initialization is required\");\n\n this.repoDB = repoDB;\n this.repoES = repoES;\n }\n\n getTableName(): string {\n return this.repoDB.getTableName();\n }\n\n async findQuery(queryParams: string): Promise<List<Partial<T>>> {\n const filter = parseIndexFilter(queryParams, this.getIndexMapDB());\n this.checkIsIndexedField(filter);\n return await this.find(filter);\n }\n\n async searchQuery(queryParams: string): Promise<Partial<T>[]> {\n let filter = parseIndexFilter(queryParams);\n filter = this.parseOwnerFields(filter, this.config);\n this.checkIsIndexedField(filter);\n return await this.search(filter);\n }\n\n async incrementValueByField(entityId: string, fieldName: string, value?: number): Promise<T> {\n return await this.repoDB.incrementValueByField(entityId, fieldName, value);\n }\n\n async decrementValueByField(entityId: string, fieldName: string, value?: number): Promise<T> {\n return await this.repoDB.decrementValueByField(entityId, fieldName, value);\n }\n\n async search(filter: Filter): Promise<T[]> {\n const parsedFilter = this.parseOwnerFields(filter, this.config);\n return (await this.repoES.find(parsedFilter)).items as T[];\n }\n\n async searchFieldNotExist(fieldName: string, from: number, size: number): Promise<Partial<T>[]> {\n return await this.repoES.fieldNotExists(fieldName, from, size);\n }\n\n async save(entity: Partial<T>, ownerId: string, parentId?: string, createdByUserId?: string): Promise<T> {\n addDatesAndId(entity);\n\n if (ownerId) {\n entity[this.config.OWNER_ID_FIELD_NAME] = ownerId;\n entity.createdBy = createdByUserId || ownerId;\n }\n if (parentId) entity[this.config.OWNER_PARENT_ID_FIELD_NAME] = parentId;\n\n return await this.repoDB.save(entity);\n }\n\n async saveAll(\n entities: Partial<T>[],\n ownerId: string,\n parentId?: string,\n createdByUserId?: string,\n isTransactional?: boolean,\n ): Promise<Partial<T>[]> {\n const entitiesUpdated = [];\n\n for (const item of entities) {\n const entity = addDatesAndId(item);\n\n if (parentId) entity[this.config.OWNER_PARENT_ID_FIELD_NAME] = parentId;\n\n entity[this.config.OWNER_ID_FIELD_NAME] = ownerId;\n entity.createdBy = createdByUserId || ownerId;\n\n entitiesUpdated.push(entity);\n }\n\n return await this.repoDB.saveMany(entitiesUpdated, isTransactional);\n }\n\n async update(entity: T, requestUserId?: string): Promise<T> {\n if (requestUserId) {\n const item = await this.repoDB.findById(entity.id);\n if (\n item[this.config.OWNER_ID_FIELD_NAME] !== requestUserId ||\n item[this.config.OWNER_PARENT_ID_FIELD_NAME] !== requestUserId\n )\n throw new ErrorHttp({ code: 403, error: \"PermissionDenied\" }, `No permission to resource: ${entity.id}`);\n entity.updatedBy = requestUserId;\n }\n entity = addDates(entity) as T;\n\n // if (!hasPermission(entity, userId, Permission.UPDATE)) throw new Error(\"Unauthorized access\");\n return await this.repoDB.update(entity);\n }\n\n async updateAll(\n entities: Partial<T>[],\n fields: string[],\n requestUserId?: string,\n isTransactional?: boolean,\n ): Promise<boolean> {\n if (requestUserId) log.warn(\"this methode is under development, cannot check ownership by updateAll\");\n\n return await this.repoDB.updateMany(entities, fields, isTransactional);\n }\n\n async remove(entityId: string, ownerId?: string): Promise<boolean> {\n const entity = await this.repoDB.findById(entityId);\n if (!entity) throw new Error(\"Entity not found\");\n\n if (ownerId && entity[this.config.OWNER_ID_FIELD_NAME] !== ownerId) return null;\n\n return await this.repoDB.delete(entityId);\n }\n\n async removeAll(entityIds: string[], ownerId?: string): Promise<boolean> {\n if (ownerId) log.warn(\"this methode is under development, cannot check ownership by removeAll\");\n return await this.repoDB.deleteMany(entityIds);\n }\n\n async changeOwner(entityId: string, ownerId: string): Promise<T> {\n const entity = await this.repoDB.findById(entityId);\n entity[this.config.OWNER_ID_FIELD_NAME] = ownerId;\n return this.repoDB.update(entity);\n }\n\n setTable(tableName: string, dynamoIndexMap: DynamoIndexMap): boolean {\n this.repoDB.setTable(tableName);\n this.repoDB.setIndexMap(dynamoIndexMap);\n return true;\n }\n\n setOpensearch(indexName: string, opensearchEndpoint: string): boolean {\n this.repoES.setIndexName(indexName);\n this.repoES.setEndpoint(opensearchEndpoint);\n return true;\n }\n\n getIndexES(): string {\n return this.repoES.getIndexName();\n }\n\n getIndexMapDB(): DynamoIndexMap {\n return this.repoDB.getIndexMap();\n }\n\n setIndexMapDB(indexMap: DynamoIndexMap): boolean {\n this.repoDB.setIndexMap(indexMap);\n return true;\n }\n\n async findAll(): Promise<T[]> {\n const result: T[] = [];\n\n const filter = parseIndexFilter(\"size=250\");\n\n do {\n const response = await this.repoDB.find(filter);\n result.push(...(response.items as T[]));\n filter.lastKey = response.lastKey;\n } while (filter.lastKey);\n\n return result;\n }\n\n async findAllMatch(filter: Filter): Promise<T[]> {\n this.checkIsIndexedField(filter);\n return await this.fetchAll(filter);\n }\n\n async findAllByIndex(indexName: string, indexValue: any): Promise<T[]> {\n // Create a filter object for validation\n const filter = { indexName: indexName, indexValue: indexValue };\n this.checkIsIndexedField(filter);\n\n const parsedFilter = parseIndexFilter(filter, this.getIndexMapDB());\n return this.fetchAll(parsedFilter);\n }\n\n async findByField(fieldName: string, fieldValue: any): Promise<List<T>> {\n // Create a filter object for validation\n const filter = { [fieldName]: fieldValue, size: 100 };\n this.checkIsIndexedField(filter);\n const parsedFilter = parseIndexFilter(fieldName + \"=\" + fieldValue + \"&size=100\", this.getIndexMapDB());\n return (await this.find(parsedFilter)) as List<T>;\n }\n\n async findFirst(fieldName: string, fieldValue: string | boolean | number | Date): Promise<T> {\n // Validate that the field name exists in the index map\n const filter = { [fieldName]: fieldValue };\n this.checkIsIndexedField(filter);\n\n const result = await this.findByField(fieldName, fieldValue);\n return result.items.length > 0 ? result.items[0] : null;\n }\n\n async find(filter: Filter): Promise<List<Partial<T>>> {\n // Validate that all field names in the filter exist in the index map\n let parsedFilter = this.parseOwnerFields(filter, this.config);\n\n this.checkIsIndexedField(parsedFilter);\n\n parsedFilter = parseIndexFilter(filter, this.getIndexMapDB());\n return await this.repoDB.find(parsedFilter);\n }\n\n async findById(entityId: string, profileId?: string): Promise<T> {\n const entity = await this.repoDB.findById(entityId);\n\n if (!entity) return null;\n\n const isOwnerParent = entity[this.config.OWNER_PARENT_ID_FIELD_NAME] == profileId;\n const isParent = entity[this.config.OWNER_ID_FIELD_NAME] == profileId;\n\n if (!profileId || isOwnerParent || isParent) {\n return entity;\n }\n\n return null;\n }\n\n async findByIds(entityIds: string[], profileId?: string): Promise<T[]> {\n if (entityIds.length > 25) throw new ErrorDynamoDB(this.getTableName(), \"findByIds\", \"Too many entities\");\n\n const entities = await this.repoDB.findByIds(entityIds);\n if (!profileId) return entities;\n const ownEntities = [];\n for (const entity of entities) {\n if (\n entity[this.config.OWNER_PARENT_ID_FIELD_NAME] == profileId ||\n entity[this.config.OWNER_ID_FIELD_NAME] == profileId\n ) {\n ownEntities.push(entity);\n }\n }\n return ownEntities;\n }\n\n async scan(filter: Filter): Promise<List<Partial<T>>> {\n this.checkIsIndexedField(filter);\n const parsedFilter = parseIndexFilter(filter, this.getIndexMapDB());\n return await this.repoDB.scan(parsedFilter);\n }\n\n throwErrorHttp(error: { message: string; code: number }, message?: string, errorContent?: any): void {\n throw new ErrorHttp({ code: error.code, error: error.message }, message, errorContent);\n }\n\n throwErrorDatabase(tableName: string, command: any, errorContent?: any): void {\n throw new ErrorDynamoDB(tableName, command, errorContent);\n }\n\n throwErrorValidation(entityName: string, validation: Map<string, any>, errorContent?: any): void {\n throw new ErrorValidation(entityName, validation, errorContent);\n }\n\n throwError(code: number, message: string, errorContent?: any): void {\n throw new ErrorBase(code, message, errorContent);\n }\n\n setConfig(config: EntityConfig) {\n this.config = config;\n\n if (this.repoDB) {\n this.repoDB.setIndexMap(config.DYNAMO_DB?.MAP);\n this.repoDB.setTable(config.DYNAMO_DB?.NAME);\n }\n\n if (this.repoES) {\n this.repoES.setEndpoint(config.OPEN_SEARCH.DOMAIN);\n this.repoES.setIndexName(config.OPEN_SEARCH.INDEX);\n }\n }\n\n /**\n * Validates that all field names in the filter exist in the index map.\n * Throws a Bad Request error if any field is not found in the available indexes.\n *\n * @param filter - The filter object containing field names to validate\n * @throws ErrorHttp with status 400 if validation fails\n */\n private checkIsIndexedField(filter: Filter): void {\n if (!filter || typeof filter !== \"object\") {\n return; // Skip validation for invalid filters\n }\n\n const indexMap = this.getIndexMapDB();\n if (!indexMap || indexMap.size === 0) {\n throw new ErrorHttp({ code: 400, error: \"Bad Request\" }, \"No indexes configured for this table\");\n }\n\n // Get all valid field names from all indexes\n const validFields = new Set<string>();\n\n // Add a partition key\n if (indexMap.partitionKey) {\n validFields.add(indexMap.partitionKey);\n }\n\n // Add fields from all indexes\n for (const [indexName, indexConfig] of indexMap.entries()) {\n if (indexConfig.field) {\n validFields.add(indexConfig.field);\n }\n if (indexConfig.rFields) {\n indexConfig.rFields.forEach((field) => validFields.add(field));\n }\n if (indexConfig.sortKeyField) {\n validFields.add(indexConfig.sortKeyField);\n }\n }\n\n // Check each field in the filter\n const invalidFields: string[] = [];\n for (const fieldName of Object.keys(filter)) {\n // Skip special filter properties that are not field names\n if (\n [\"indexName\", \"indexValue\", \"size\", \"lastKey\", \"fieldsInclude\", \"fieldsExclude\", \"rangeFilters\"].includes(\n fieldName,\n )\n ) {\n continue;\n }\n\n // Skip range filter fields (ageMin, ageMax, etc.)\n const rangeSuffixes = [\"Min\", \"Max\", \"From\", \"To\", \"Start\", \"End\"];\n const isRangeField = rangeSuffixes.some((suffix) => fieldName.endsWith(suffix));\n if (isRangeField) {\n continue;\n }\n\n if (!validFields.has(fieldName)) {\n invalidFields.push(fieldName);\n }\n }\n\n if (invalidFields.length > 0) {\n const availableFields = Array.from(validFields).sort();\n throw new ErrorHttp(\n { code: 400, error: \"Bad Request\" },\n `Invalid field names in filter: ${invalidFields.join(\", \")}. Available fields: ${availableFields.join(\", \")}`,\n );\n }\n }\n\n private async fetchAll(filter: Filter): Promise<T[]> {\n const result: Partial<T>[] = [];\n do {\n const response = await this.repoDB.find(filter);\n result.push(...response.items);\n filter.lastKey = response.lastKey;\n } while (filter.lastKey);\n\n return result as T[];\n }\n\n private parseOwnerFields(filter: Filter, entityConfig: EntityConfig) {\n const parsedFilter = filter;\n\n log.debug(\"input filter\", parsedFilter);\n\n if (parsedFilter.ownerParentId && !entityConfig.OWNER_PARENT_ID_FIELD_NAME) {\n parsedFilter.profileId = parsedFilter.ownerParentId;\n delete parsedFilter.ownerParentId;\n }\n if (!parsedFilter.profileId) delete parsedFilter.profileId;\n\n if (parsedFilter.ownerParentId && entityConfig.OWNER_PARENT_ID_FIELD_NAME) {\n parsedFilter[entityConfig.OWNER_PARENT_ID_FIELD_NAME] = parsedFilter.ownerParentId;\n if (entityConfig.OWNER_PARENT_ID_FIELD_NAME !== parsedFilter.ownerParentId) delete parsedFilter.ownerParentId;\n }\n\n if (parsedFilter.profileId && entityConfig.OWNER_ID_FIELD_NAME) {\n parsedFilter[entityConfig.OWNER_ID_FIELD_NAME] = parsedFilter.profileId;\n if (entityConfig.OWNER_ID_FIELD_NAME !== parsedFilter.owderId) delete parsedFilter.profileId;\n }\n\n // if (parsedFilter.ownerId && entityConfig.OWNER_ID_FIELD_NAME) {\n // parsedFilter[entityConfig.OWNER_ID_FIELD_NAME] = parsedFilter.ownerId;\n // if (entityConfig.OWNER_ID_FIELD_NAME !== parsedFilter.owderId) delete parsedFilter.ownerId;\n // }\n log.debug(\"parsedFilter\", parsedFilter);\n\n return parsedFilter;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-service-stack",
3
- "version": "0.17.278",
3
+ "version": "0.17.280",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "author": "chinggis.systems",