@yopdev/dev-server 2.0.2 → 2.0.4

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.
@@ -0,0 +1,13 @@
1
+ import { Callback, Service } from './services';
2
+ export declare const newDynamoDbTableFromCloudFormationTemplate: (name: string, config: DynamoDbTableCloudFormationConfig, callback?: Callback<string>) => Service<string>;
3
+ type DynamoDbTableCloudFormationConfig = {
4
+ template: (name: string) => string;
5
+ resource: string;
6
+ tableName: (name: string) => string;
7
+ throughput: Throughput;
8
+ };
9
+ type Throughput = {
10
+ ReadCapacityUnits: number;
11
+ WriteCapacityUnits: number;
12
+ };
13
+ export {};
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.newDynamoDbTableFromCloudFormationTemplate = void 0;
4
+ const services_1 = require("./services");
5
+ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
6
+ const js_yaml_cloudformation_schema_1 = require("js-yaml-cloudformation-schema");
7
+ const js_yaml_1 = require("js-yaml");
8
+ const fs_1 = require("fs");
9
+ const logging_1 = require("@yopdev/logging");
10
+ const dynamodb_1 = require("./dynamodb");
11
+ const newDynamoDbTableFromCloudFormationTemplate = (name, config, callback) => new services_1.Service(new DynamoDbTableCloudFormation(name, config.template, config.resource, config.tableName, config.throughput), callback);
12
+ exports.newDynamoDbTableFromCloudFormationTemplate = newDynamoDbTableFromCloudFormationTemplate;
13
+ class DynamoDbTableCloudFormation {
14
+ constructor(name, template, resource, tableName, throughput) {
15
+ this.name = name;
16
+ this.template = template;
17
+ this.resource = resource;
18
+ this.throughput = throughput;
19
+ this.start = (config) => Promise.resolve(this.parseTableDefinition(this.template(this.name))
20
+ .then((definition) => definition[this.resource].Properties)
21
+ .then((resource) => this.createTableCommand(resource, this.throughput)
22
+ .then((command) => (0, dynamodb_1.newDynamoDbTable)(this.name, {
23
+ command: command,
24
+ ttlAttribute: resource.TimeToLiveSpecification?.AttributeName,
25
+ }))))
26
+ .tap(() => this.LOGGER.info('configured'))
27
+ .then((service) => service.start(config));
28
+ this.stop = () => Promise.resolve();
29
+ this.createTableCommand = async (definition, throughput) => new client_dynamodb_1.CreateTableCommand({
30
+ TableName: definition.TableName ?? this.tableName(this.name),
31
+ KeySchema: definition.KeySchema,
32
+ AttributeDefinitions: definition.AttributeDefinitions,
33
+ GlobalSecondaryIndexes: definition.GlobalSecondaryIndexes?.map((gsi) => ({
34
+ ...gsi,
35
+ ProvisionedThroughput: throughput,
36
+ })),
37
+ ProvisionedThroughput: throughput,
38
+ });
39
+ this.parseTableDefinition = async (path) => (0, js_yaml_1.load)((0, fs_1.readFileSync)(path).toString(), {
40
+ schema: js_yaml_cloudformation_schema_1.CLOUDFORMATION_SCHEMA,
41
+ }).Resources;
42
+ this.LOGGER = logging_1.LoggerFactory.create(`CFN:DYNAMO[${name}]`);
43
+ this.tableName = tableName ? tableName : (name) => { throw new Error(`table name not specified on ${name}`); };
44
+ }
45
+ }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CloudFormationSetup = void 0;
4
4
  const js_yaml_cloudformation_schema_1 = require("js-yaml-cloudformation-schema");
5
5
  const fs_1 = require("fs");
6
+ const path_1 = require("path");
6
7
  const js_yaml_1 = require("js-yaml");
7
8
  const logging_1 = require("@yopdev/logging");
8
9
  class CloudFormationSetup {
@@ -18,6 +19,7 @@ class CloudFormationSetup {
18
19
  const template = this.template(this.name);
19
20
  this.LOGGER.info('parsing %s', template);
20
21
  const definition = this.parseApiEvents(template);
22
+ const functions = this.handlers(this.name);
21
23
  const routes = Object.values(definition)
22
24
  .filter((resource) => resource.Type === 'AWS::Serverless::Function')
23
25
  .filter((resource) => resource.Properties.Events)
@@ -26,8 +28,10 @@ class CloudFormationSetup {
26
28
  Handler: resource.Properties.Handler,
27
29
  })))
28
30
  .filter(this.eventFilter)
29
- .map(async (event) => import(this.handlers(this.name)).then(async (handlers) => Promise.resolve(handlers[this.handlerNameResolver(event.Handler)])
30
- .then(async (handler) => this.handlerResolver(event, handler))
31
+ .map(async (event) => import(functions).then(async (handlers) => Promise.resolve(handlers[this.handlerNameResolver(event.Handler)])
32
+ .then(async (handler) => (handler !== undefined) ?
33
+ this.handlerResolver(event, handler) :
34
+ Promise.reject(new Error(`function ${event.Handler} defined in ${(0, path_1.resolve)(template)} not found in ${(0, path_1.resolve)(functions)}`)))
31
35
  .then((handler) => {
32
36
  this.LOGGER.info(this.logHandler(handler));
33
37
  return handler;
@@ -3,9 +3,14 @@ import { AwsConfig } from "./config";
3
3
  import { Service, Callback } from "./services";
4
4
  export declare const newDynamoDbTable: (name: string, config: {
5
5
  command: CreateTableCommand;
6
+ ttlAttribute?: string;
6
7
  }, callback?: Callback<string>) => Service<string>;
7
8
  export declare class DynamoDb {
8
9
  readonly client: DynamoDBClient;
9
10
  constructor(config: AwsConfig);
10
- createTable: (factory: CreateTableCommand) => Promise<import("@aws-sdk/client-dynamodb").CreateTableCommandOutput>;
11
+ createTable: (factory: CreateTableCommand) => Promise<import("@aws-sdk/client-dynamodb").CreateTableCommandOutput | {
12
+ TableDescription: {
13
+ TableName: string;
14
+ };
15
+ }>;
11
16
  }
@@ -5,15 +5,27 @@ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
5
5
  const logging_1 = require("@yopdev/logging");
6
6
  const services_1 = require("./services");
7
7
  const assert_1 = require("./assert");
8
- const newDynamoDbTable = (name, config, callback) => new services_1.Service(new DynamoDbTableCreator(name, config.command), callback);
8
+ const newDynamoDbTable = (name, config, callback) => new services_1.Service(new DynamoDbTableCreator(name, config.command, config.ttlAttribute), callback);
9
9
  exports.newDynamoDbTable = newDynamoDbTable;
10
10
  class DynamoDbTableCreator {
11
- constructor(name, command) {
11
+ constructor(name, command, ttlAttribute) {
12
12
  this.name = name;
13
13
  this.command = command;
14
+ this.ttlAttribute = ttlAttribute;
14
15
  this.start = async (config) => config.dynamo.createTable(this.command)
15
16
  .tap((output) => this.LOGGER.debug(output))
17
+ .then(() => this.ttlAttribute)
18
+ .then((ttl) => ttl !== undefined ? this.setupTimeToLive(config.dynamo, ttl) : Promise.resolve())
16
19
  .then(() => this.name);
20
+ this.setupTimeToLive = async (dynamodb, attributeName) => dynamodb.client.send(new client_dynamodb_1.UpdateTimeToLiveCommand({
21
+ TableName: this.command.input.TableName,
22
+ TimeToLiveSpecification: {
23
+ Enabled: true,
24
+ AttributeName: attributeName,
25
+ }
26
+ }))
27
+ .tap((out) => this.LOGGER.info('set ttl attribute %s? %s', attributeName, out.$metadata.httpStatusCode === 200))
28
+ .then(() => undefined);
17
29
  this.stop = () => Promise.resolve();
18
30
  this.LOGGER = logging_1.LoggerFactory.create(`TABLE[${name}]`);
19
31
  }
@@ -28,6 +40,7 @@ class DynamoDb {
28
40
  LOGGER.error('failed to create table');
29
41
  if (reason.name !== 'ResourceInUseException')
30
42
  return Promise.reject(reason);
43
+ return { TableDescription: { TableName: factory.input.TableName } };
31
44
  });
32
45
  this.client = new client_dynamodb_1.DynamoDBClient(config);
33
46
  }
@@ -37,7 +37,7 @@ class EventsProxy {
37
37
  return url;
38
38
  };
39
39
  this.extractErrorMessage = (e) => typeof e === 'string' ? e : e.message !== undefined && typeof e.message === 'string' ? e.message : '';
40
- this.onEachMessage = (message) => {
40
+ this.onEachMessage = async (message) => {
41
41
  if (this.stopped) {
42
42
  this.LOGGER.warn('stopped events proxy received message %o', message);
43
43
  return;
@@ -3,6 +3,7 @@ export { newHttpServer as httpServer, HttpSettings } from './http-server';
3
3
  export { newLambdaHttpProxy as lambdaHttpProxy, UNAUTHORIZED } from './lambda-http-proxy';
4
4
  export { v1 as v1LambdaProxyPayload, v2 as v2LambdaProxyPayload } from './mappers';
5
5
  export { newLambdaProxyFromCloudFormationTemplate as cloudFormationLambdaProxy } from './cloudformation-lambda-http-proxy';
6
+ export { newDynamoDbTableFromCloudFormationTemplate as cloudFormationDynamoDbTable } from './cloudformation-dynamodb-table';
6
7
  export { newSnsHttpProxy as snsHttpProxy } from './sns-http-proxy';
7
8
  export { newInternalQueue as internalQueue } from './internal-queue';
8
9
  export { newEventsProxy as eventsProxy } from './event-proxy';
package/dist/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Service = exports.promised = exports.lazy = exports.oneThenOther = exports.allOf = exports.tunnel = exports.container = exports.dynamoDbTable = exports.preTrafficHooks = exports.scheduledTasks = exports.cloudFormationSnsEventHandlers = exports.eventsProxy = exports.internalQueue = exports.snsHttpProxy = exports.cloudFormationLambdaProxy = exports.v2LambdaProxyPayload = exports.v1LambdaProxyPayload = exports.UNAUTHORIZED = exports.lambdaHttpProxy = exports.httpServer = exports.DevServer = void 0;
3
+ exports.Service = exports.promised = exports.lazy = exports.oneThenOther = exports.allOf = exports.tunnel = exports.container = exports.dynamoDbTable = exports.preTrafficHooks = exports.scheduledTasks = exports.cloudFormationSnsEventHandlers = exports.eventsProxy = exports.internalQueue = exports.snsHttpProxy = exports.cloudFormationDynamoDbTable = exports.cloudFormationLambdaProxy = exports.v2LambdaProxyPayload = exports.v1LambdaProxyPayload = exports.UNAUTHORIZED = exports.lambdaHttpProxy = exports.httpServer = exports.DevServer = void 0;
4
4
  var dev_server_1 = require("./dev-server");
5
5
  Object.defineProperty(exports, "DevServer", { enumerable: true, get: function () { return dev_server_1.DevServer; } });
6
6
  var http_server_1 = require("./http-server");
@@ -13,6 +13,8 @@ Object.defineProperty(exports, "v1LambdaProxyPayload", { enumerable: true, get:
13
13
  Object.defineProperty(exports, "v2LambdaProxyPayload", { enumerable: true, get: function () { return mappers_1.v2; } });
14
14
  var cloudformation_lambda_http_proxy_1 = require("./cloudformation-lambda-http-proxy");
15
15
  Object.defineProperty(exports, "cloudFormationLambdaProxy", { enumerable: true, get: function () { return cloudformation_lambda_http_proxy_1.newLambdaProxyFromCloudFormationTemplate; } });
16
+ var cloudformation_dynamodb_table_1 = require("./cloudformation-dynamodb-table");
17
+ Object.defineProperty(exports, "cloudFormationDynamoDbTable", { enumerable: true, get: function () { return cloudformation_dynamodb_table_1.newDynamoDbTableFromCloudFormationTemplate; } });
16
18
  var sns_http_proxy_1 = require("./sns-http-proxy");
17
19
  Object.defineProperty(exports, "snsHttpProxy", { enumerable: true, get: function () { return sns_http_proxy_1.newSnsHttpProxy; } });
18
20
  var internal_queue_1 = require("./internal-queue");
@@ -19,17 +19,15 @@ class LambdaHttpProxy {
19
19
  this.stop = () => this.server.stop();
20
20
  this.handler = (authorizer, lambdaHandler) => (request, body, response) => this.mapper.newInstance(request, body)
21
21
  .then(async (mapper) => authorizer(mapper.authorization())
22
- .then(async (context) => {
23
- return lambdaHandler(mapper.event(context), this.context())
24
- .then((lambda) => {
25
- const result = mapper.toResponse(lambda);
26
- return (0, responses_1.writeResponse)(response, result.statusCode, result.body(), result.contentType, result.location);
27
- })
28
- .catch((e) => {
29
- this.LOGGER.error(e, 'request failed to execute');
30
- (0, responses_1.internalServerError)(response, e.body);
31
- });
32
- }, (e) => e === exports.UNAUTHORIZED ? (0, responses_1.writeResponse)(response, 401, '') : (0, responses_1.internalServerError)(response, e)));
22
+ .then(async (context) => lambdaHandler(mapper.event(context), this.context())
23
+ .then((lambda) => {
24
+ const result = mapper.toResponse(lambda);
25
+ return (0, responses_1.writeResponse)(response, result.statusCode, result.body(), result.contentType, result.location);
26
+ })
27
+ .catch((e) => {
28
+ this.LOGGER.error(e, 'request failed to execute');
29
+ (0, responses_1.internalServerError)(response, e.body);
30
+ }), (e) => e === exports.UNAUTHORIZED ? (0, responses_1.writeResponse)(response, 401, '') : (0, responses_1.internalServerError)(response, e)));
33
31
  this.fallback = async (request, _, response) => Promise.resolve(this.LOGGER.warn(`FALLBACK: ${request.method} to ${request.url}`)).then(() => (0, responses_1.writeResponse)(response, 404, `no route found to handle ${request.method} to ${request.url}`));
34
32
  this.resolveRoute = (request, body, response) => (this.routes.filter((r) => r.method.test(request.method))
35
33
  .filter((r) => r.path.test(request.url))
@@ -28,7 +28,10 @@ class LocalStack {
28
28
  const localStatePath = concreteConfig.localStateBindMount;
29
29
  const withLocalState = localStatePath !== undefined ?
30
30
  container
31
- .withEnvironment({ PERSISTENCE: '1' })
31
+ .withEnvironment({
32
+ PERSISTENCE: '1',
33
+ DYNAMODB_REMOVE_EXPIRED_ITEMS: '1',
34
+ })
32
35
  .withBindMounts([{
33
36
  source: localStatePath,
34
37
  target: '/var/lib/localstack',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yopdev/dev-server",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "scripts": {
5
5
  "compile": "tsc",
6
6
  "pretest": "npm run compile",