├── .gitignore
├── README.md
├── demo
└── cat.jpg
├── env
└── prod.yml
├── handler.js
├── package.json
├── serverless.yml
├── src
├── db
│ └── model
│ │ └── imageMeta.ts
├── handlers
│ ├── getImage
│ │ ├── index.ts
│ │ └── setSize.ts
│ ├── index.ts
│ └── uploadImage
│ │ └── index.ts
├── helpers
│ ├── filenameMaker.ts
│ └── s3Manager.ts
└── index.ts
├── tsconfig.json
├── tslint.json
└── typings
└── dynamoose.d.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | # package directories
2 | node_modules
3 | jspm_packages
4 | dst
5 | dst.*
6 | # Serverless directories
7 | .serverless
8 | .idea/
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Lambda Image Manager
2 |
3 | Lambda Image Manager is AWS Lambda function that store, get, manipulate images in AWS S3.
4 |
5 | If you are using Cloudinary or Imgix like service just for resizing or basic image manipulation, maybe you don't need them anymore.
6 |
7 | ## Before Start
8 |
9 | If you aren't used to [Serverless](https://serverless.com/) or AWS Lambda, I highly recommend that read Serverless docs first.
10 |
11 | Remember! AWS provides free tier amount, but this whole service is not **FREE**.
12 |
13 | ### Todo List
14 | * Add DynamoDB logic.
15 |
16 | ### Prerequisites
17 |
18 | - Make empty AWS S3 bucket for image file store.
19 | ~~- Make and set AWS DynamoDB for cache.~~(Not supported yet)
20 |
21 | ### Installing
22 |
23 | ```
24 | git clone https://github.com/TylorShin/lambda-image-manager.git
25 | cd lambda-image-manager
26 | npm install
27 | ```
28 |
29 | And set production environment.
30 | (If you want to stage or other)
31 |
32 | ```
33 | # /env/prod.yml
34 | S3_BUCKET_NAME: lambdaImage
35 | S3_DEST_PREFIX: images/original
36 | ```
37 |
38 | The S3 folder structure should be like below.
39 | ```
40 | # lambdaImage Bucket
41 |
42 | /root
43 | /images
44 | /original
45 | ```
46 |
47 | Then if you ready to deploy Serverless project,
48 | just run
49 |
50 | ```
51 | npm run deploy:prod
52 | ```
53 |
54 | **IMPORTANT**
55 |
56 | 
57 | (this image is actually from my own service by using this repository)
58 |
59 |
60 | You should set lambda-image-manager's API Gateway's binary support option.
61 |
62 | At API Gateway setting page, set binary option allow header to "image/*".
63 | Then, **Deploy** API Gateway once again in console dashboard.
64 |
65 | ## Running the tests
66 |
67 | WIP
68 |
69 |
70 | ## How It Works
71 | #### getImage
72 | * A browser try to presentation
tag with src property heading our API gateway that trigger lambda function.
73 | * Lambda get request with image id, image file name, image manipulation options.
74 | * Trying to find pre-manipulated result from AWS DynamoDB as cache. If the target image is already manipulated with same option, then read that image's address from DynamoDB and return redirect response to that address.
75 | * If there is no cache, manipulate target image with given options.
76 | * Change the image to Buffer with BASE64 encode.
77 | * After that, store the result to S3 and record result address to DynamoDB with manipulation options.
78 | * return manipulated target image to user.
79 |
80 | ## Built With
81 |
82 | * [Serverless](https://serverless.com/) - The web framework used
83 |
84 | ## Contributing
85 |
86 | WIP
87 |
88 | ## Authors
89 |
90 | * **Tylor Shin** - *Project Manager* - [TylorShin](https://github.com/TylorShin)
91 | * **breath103** - *Tech lead & Initial idea maker* - [breath103](https://github.com/breath103)
92 |
93 | ## License
94 |
95 | WIP
96 |
--------------------------------------------------------------------------------
/demo/cat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TylerShin/lambda-image-manager/52753671e6ef375a25c3acdb5a54521ea33ea711/demo/cat.jpg
--------------------------------------------------------------------------------
/env/prod.yml:
--------------------------------------------------------------------------------
1 | S3_BUCKET_NAME: tylor-lambda-dashboard
2 | S3_DEST_PREFIX: lambda
3 | DYNAMO_DB_TABLE_NAME: lambda-image-manager-stats
4 | MAX_SIZE_LIMIT: 2000000
5 |
--------------------------------------------------------------------------------
/handler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports.hello = (event, context, callback) => {
4 | const response = {
5 | statusCode: 200,
6 | body: JSON.stringify({
7 | message: 'Go Serverless v1.0! Your function executed successfully!',
8 | input: event,
9 | }),
10 | };
11 |
12 | callback(null, response);
13 |
14 | // Use this code if you don't use the http event with the LAMBDA-PROXY integration
15 | // callback(null, { message: 'Go Serverless v1.0! Your function executed successfully!', event });
16 | };
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lambda-image",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "rm -rf dst && tsc && cd src && find . -name '*.json' -type f -exec cp {} ../dst/{} \\; && cd ..",
8 | "pack": "rm -f dst.zip && cp package.json dst/package.json && cp -r demo dst/demo && cd dst && npm install --cache=./.npm --production && zip -r ../dst.zip . ** && cd ..",
9 | "deploy:prod": "npm run build && npm run pack && serverless deploy --stage prod",
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "author": "",
13 | "license": "ISC",
14 | "devDependencies": {
15 | "@types/aws-lambda": "0.0.10",
16 | "@types/busboy": "^0.2.3",
17 | "@types/gm": "^1.17.30",
18 | "@types/uuid": "^3.0.0",
19 | "serverless": "^1.12.1",
20 | "tslint": "^5.2.0",
21 | "tslint-microsoft-contrib": "^4.0.1",
22 | "typescript": "^2.3.2"
23 | },
24 | "dependencies": {
25 | "aws-sdk": "^2.50.0",
26 | "busboy": "^0.2.14",
27 | "dynamoose": "^0.7.0",
28 | "gm": "^1.23.0",
29 | "uuid": "^3.0.1"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/serverless.yml:
--------------------------------------------------------------------------------
1 |
2 | service: lambda-image-resizer
3 |
4 | provider:
5 | name: aws
6 | runtime: nodejs6.10
7 | stage: prod
8 | region: us-east-1
9 | environment: ${file(./env/${opt:stage}.yml)}
10 |
11 | package:
12 | artifact: dst.zip
13 |
14 | functions:
15 | getImage:
16 | handler: handlers.getImage
17 | events:
18 | - http:
19 | path: getImage
20 | method: get
21 |
22 | uploadImage:
23 | handler: handlers.uploadImage
24 | events:
25 | - http:
26 | path: uploadImage
27 | method: post
28 | cors: true
29 |
30 | resources:
31 | Resources:
32 | S3BucketToDashboard:
33 | Type: AWS::S3::Bucket
34 | Properties:
35 | BucketName: "${file(./env/${opt:stage}.yml):S3_BUCKET_NAME}"
36 |
37 | S3IamPolicy:
38 | Type: AWS::IAM::Policy
39 | Properties:
40 | PolicyName: S3BucketToDashboard-s3
41 | PolicyDocument:
42 | Version: '2012-10-17'
43 | Statement:
44 | - Effect: Allow
45 | Action: s3:*
46 | Resource: "arn:aws:s3:::${file(./env/${opt:stage}.yml):S3_BUCKET_NAME}/*"
47 | Roles:
48 | - Ref: IamRoleLambdaExecution
49 |
50 | DynamoDbTable:
51 | Type: AWS::DynamoDB::Table
52 | Properties:
53 | TableName: "${file(./env/${opt:stage}.yml):DYNAMO_DB_TABLE_NAME}"
54 | AttributeDefinitions:
55 | - AttributeName: fileId
56 | AttributeType: S
57 | - AttributeName: id
58 | AttributeType: S
59 | KeySchema:
60 | - AttributeName: fileId
61 | KeyType: HASH
62 | - AttributeName: id
63 | KeyType: RANGE
64 | ProvisionedThroughput:
65 | ReadCapacityUnits: 5
66 | WriteCapacityUnits: 5
67 |
68 | DynamoDBIamPolicy:
69 | Type: AWS::IAM::Policy
70 | DependsOn: DynamoDbTable
71 | Properties:
72 | PolicyName: AmazonDynamoDBFullAccess
73 | PolicyDocument:
74 | Version: '2012-10-17'
75 | Statement:
76 | - Effect: Allow
77 | Action: [
78 | "dynamodb:*"
79 | ]
80 | Resource: "arn:aws:dynamodb:*:*:table/${file(./env/${opt:stage}.yml):DYNAMO_DB_TABLE_NAME}"
81 | Roles:
82 | - Ref: IamRoleLambdaExecution
83 |
--------------------------------------------------------------------------------
/src/db/model/imageMeta.ts:
--------------------------------------------------------------------------------
1 | const dynamoose = require("dynamoose");
2 |
3 | const Schema = dynamoose.Schema;
4 |
5 | const imageMetaSchema = new Schema({
6 | fileId: {
7 | type: String,
8 | hashKey: true,
9 | },
10 | id: {
11 | type: String,
12 | rangeKey: true,
13 | },
14 | version: {
15 | type: String,
16 | },
17 | fileName: {
18 | type: String,
19 | },
20 | }, {
21 | throughput: {
22 | read: 1,
23 | write: 1,
24 | },
25 | timestamps: true,
26 | });
27 |
28 | const ImageMeta = dynamoose.model("lambda-image-manager-stats", imageMetaSchema);
29 |
30 | export default ImageMeta;
31 |
--------------------------------------------------------------------------------
/src/handlers/getImage/index.ts:
--------------------------------------------------------------------------------
1 | import * as GraphicMagick from "gm";
2 | import * as uuid from "uuid";
3 | import setSize from "./setSize";
4 | import S3Manager from "../../helpers/s3Manager";
5 | import ImageMeta from "../../db/model/imageMeta";
6 | // interfaces
7 | import FileNameMaker, { IImageProcessOptions } from "../../helpers/filenameMaker";
8 |
9 | const handler: AWSLambda.ProxyHandler = async (event, context, _callback) => {
10 | const gm = GraphicMagick.subClass({ imageMagick: true });
11 | const imageProcessOptions: IImageProcessOptions = {
12 | width: 0,
13 | height: 0,
14 | };
15 |
16 | let buffer: Buffer = new Buffer("");
17 |
18 | let originalImage = gm("./demo/cat.jpg");
19 |
20 | if (event.queryStringParameters) {
21 | // Get target image size
22 | const size = await setSize(event, originalImage);
23 | imageProcessOptions.width = size.width;
24 | imageProcessOptions.height = size.height;
25 |
26 | if (event.queryStringParameters["id"] && event.queryStringParameters["filename"]) {
27 | const fileId = event.queryStringParameters["id"];
28 | const fileName = event.queryStringParameters["filename"];
29 |
30 | let version = FileNameMaker.getVersion(imageProcessOptions);
31 | if (imageProcessOptions.width === 0 || imageProcessOptions.height === 0) {
32 | version = "original";
33 | }
34 |
35 | try {
36 | const originFileBuffer = await S3Manager.getFile(fileId, fileName, version);
37 | originalImage = (gm as any)(originFileBuffer);
38 |
39 | await new Promise((resolve, reject) => {
40 | originalImage
41 | .setFormat("jpeg")
42 | .toBuffer((err, resultBuffer) => {
43 | if (err) {
44 | console.error(`Had error to make buffer from image`, err);
45 | reject(err);
46 | } else {
47 | buffer = resultBuffer;
48 | resolve();
49 | }
50 | });
51 | });
52 | } catch (_err) {
53 | const originFileBuffer = await S3Manager.getFile(fileId, fileName);
54 | originalImage = (gm as any)(originFileBuffer);
55 |
56 | // image processing
57 | await new Promise((resolve, reject) => {
58 | originalImage
59 | .resize(imageProcessOptions.width, imageProcessOptions.height)
60 | .setFormat("jpeg")
61 | .toBuffer((err, resultBuffer) => {
62 | if (err) {
63 | reject(err);
64 | } else {
65 | buffer = resultBuffer;
66 | resolve();
67 | }
68 | });
69 | });
70 |
71 | await S3Manager.uploadFile(buffer, fileId, fileName, version);
72 | }
73 |
74 | // Record download count to DynamoDB
75 | const newImageMeta = new ImageMeta({
76 | fileId,
77 | version,
78 | id: uuid.v4(),
79 | fileName,
80 | });
81 |
82 | newImageMeta.save();
83 | }
84 | }
85 |
86 | // return HTTP result
87 | context.done(undefined, {
88 | statusCode: 200,
89 | headers: {
90 | "Content-Type": "image/*",
91 | },
92 | body: buffer.toString("base64"),
93 | isBase64Encoded: true,
94 | });
95 | };
96 |
97 | export default handler;
98 |
--------------------------------------------------------------------------------
/src/handlers/getImage/setSize.ts:
--------------------------------------------------------------------------------
1 | import * as gm from "gm";
2 |
3 | export default async function setSize(event: AWSLambda.APIGatewayEvent, gmImage: gm.State) {
4 | const result = {
5 | width: 0,
6 | height: 0,
7 | };
8 | // Set resizing options
9 | if (event.queryStringParameters) {
10 | if (Number(event.queryStringParameters["width"])) {
11 | result.width = Number(event.queryStringParameters["width"]);
12 | }
13 | if (Number(event.queryStringParameters["height"])) {
14 | result.height = Number(event.queryStringParameters["height"]);
15 | }
16 | } else {
17 | await new Promise((resolve, reject) => {
18 | gmImage
19 | .size((err, size) => {
20 | if (err) {
21 | console.error(`Had error to get original image size`, err);
22 | reject(err);
23 | } else {
24 | result.width = size.width;
25 | result.height = size.height;
26 | resolve(result);
27 | }
28 | });
29 | });
30 | }
31 |
32 | return result;
33 | }
34 |
--------------------------------------------------------------------------------
/src/handlers/index.ts:
--------------------------------------------------------------------------------
1 | import getImage from "./getImage";
2 | import uploadImage from "./uploadImage";
3 |
4 | const handlers = {
5 | getImage,
6 | uploadImage,
7 | };
8 |
9 | export = handlers;
10 |
--------------------------------------------------------------------------------
/src/handlers/uploadImage/index.ts:
--------------------------------------------------------------------------------
1 | import S3Manager from "../../helpers/s3Manager";
2 | import FilenameMaker from "../../helpers/filenameMaker";
3 |
4 | const handler: AWSLambda.ProxyHandler = async (event, context, _callback) => {
5 | const buffer = new Buffer(event.body as string, "base64");
6 | const fileSize = buffer.byteLength;
7 | console.log(fileSize, "=== file buffer size");
8 |
9 | // Size validation
10 | if (fileSize > process.env.MAX_SIZE_LIMIT) {
11 | return context.done(undefined, {
12 | statusCode: 403,
13 | body: JSON.stringify("File size is too big"),
14 | });
15 | }
16 |
17 | try {
18 | const result: any = await S3Manager.uploadFile(
19 | buffer,
20 | FilenameMaker.getNewFileId(),
21 | FilenameMaker.getNewFileName(),
22 | );
23 |
24 | const keyArray = result.Key.split("/");
25 | const uploadResult = {
26 | version: keyArray[keyArray.length - 1],
27 | fileName: keyArray[keyArray.length - 2],
28 | id: keyArray[keyArray.length - 3],
29 | };
30 |
31 | // return HTTP result
32 | context.done(undefined, {
33 | statusCode: 200,
34 | headers: {
35 | "Content-Type": "application/json",
36 | "Access-Control-Allow-Origin": "*",
37 | "Access-Control-Allow-Methods": "OPTIONS, POST",
38 | "Access-Control-Allow-Headers": "Content-Type",
39 | },
40 | body: JSON.stringify(uploadResult),
41 | });
42 | } catch (err) {
43 | context.done(err);
44 | }
45 | };
46 |
47 | export default handler;
48 |
--------------------------------------------------------------------------------
/src/helpers/filenameMaker.ts:
--------------------------------------------------------------------------------
1 | import * as uuid from "uuid";
2 |
3 | export interface IImageProcessOptions {
4 | width: number;
5 | height: number;
6 | }
7 |
8 | export default class FileNameMaker {
9 | public static getNewFileId() {
10 | const date = new Date();
11 | return date.toISOString();
12 | }
13 |
14 | public static getNewFileName() {
15 | return uuid.v4();
16 | }
17 |
18 | public static getVersion(processOptions: IImageProcessOptions) {
19 | return JSON.stringify(processOptions);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/helpers/s3Manager.ts:
--------------------------------------------------------------------------------
1 | import * as AWS from "aws-sdk";
2 |
3 | class S3Manager {
4 | private s3 = new AWS.S3();
5 |
6 | public uploadFile(buffer: Buffer, fileId: string, fileName: string, version = "original") {
7 | return new Promise((resolve, reject) => {
8 | this.s3.upload({
9 | Body: buffer,
10 | Bucket: process.env.S3_BUCKET_NAME,
11 | Key: `${process.env.S3_DEST_PREFIX}/${fileId}/${fileName}/${version}`,
12 | }, (err: Error, data: any) => {
13 | if (err) {
14 | console.error(err);
15 | reject(err);
16 | } else {
17 | resolve(data);
18 | }
19 | });
20 | });
21 | }
22 |
23 | public checkFileExist(fileId: string, fileName: string, version = "original") {
24 | return new Promise((resolve, _reject) => {
25 | this.s3.headObject({
26 | Bucket: process.env.S3_BUCKET_NAME,
27 | Key: `${process.env.S3_DEST_PREFIX}/${fileId}/${fileName}/${version}`,
28 | }, (err, _metadata) => {
29 | if (err) {
30 | resolve(false);
31 | } else {
32 | resolve(true);
33 | }
34 | });
35 | });
36 | }
37 |
38 | public getFile(fileId: string, fileName: string, version = "original") {
39 | return new Promise((resolve, reject) => {
40 | this.s3.getObject({
41 | Bucket: process.env.S3_BUCKET_NAME,
42 | Key: `${process.env.S3_DEST_PREFIX}/${fileId}/${fileName}/${version}`,
43 | }, (err, data) => {
44 | if (err) {
45 | console.error(err);
46 | reject(err);
47 | } else {
48 | resolve(data.Body);
49 | }
50 | });
51 | });
52 | }
53 | }
54 |
55 | const s3Manager = new S3Manager();
56 | export default s3Manager;
57 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TylerShin/lambda-image-manager/52753671e6ef375a25c3acdb5a54521ea33ea711/src/index.ts
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./dst/",
4 | "sourceMap": true,
5 | "noImplicitAny": true,
6 | "allowUnusedLabels": false,
7 | "noUnusedLocals": true,
8 | "noUnusedParameters": true,
9 | "strictNullChecks": true,
10 | "module": "commonjs",
11 | "target": "es5",
12 | "lib": [
13 | "es6",
14 | "es2016",
15 | "dom"
16 | ],
17 | "removeComments": true,
18 | "experimentalDecorators": true
19 | },
20 | "include": [
21 | "src/**/*"
22 | ],
23 | "exclude": [
24 | "node_modules"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "extends": "tslint:recommended",
4 | "rulesDirectory": ["node_modules/tslint-microsoft-contrib"],
5 | "rules": {
6 | "jsdoc-format": false,
7 | "no-string-literal": false,
8 | "object-literal-sort-keys": false,
9 | "variable-name": false,
10 | "no-console": false,
11 | "indent": ["spaces"],
12 | "no-var-requires": false,
13 | "ordered-imports": [false],
14 | "max-line-length": [
15 | false
16 | ]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/typings/dynamoose.d.ts:
--------------------------------------------------------------------------------
1 | // Type definitions for dynamoose 0.7
2 | // Project: https://github.com/automategreen/dynamoose
3 | // Definitions by: Rahul Vaidya
4 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
5 |
6 | declare module "dynamoose" {
7 | import AWS from 'aws-sdk';
8 |
9 | export class Dynamoose {
10 | constructor();
11 | Dynamoose(): void;
12 | Schema(obj: any, options: any): Schema;
13 | Table(name: string, schema: any, options: any, base: any): Table;
14 | VirtualType(options: any, name: string): VirtualType;
15 | ddb(): AWS.DynamoDB;
16 | local(url: string): void;
17 | model(name: string, schema: Schema, options?: ModelOptions): Model;
18 | setDefaults(options: Options): void;
19 | }
20 | export class Schema {
21 | constructor(obj: SchemaObject, options?: SchemaOptions);
22 | method(name: string, fn: any): any;
23 | parseDynamo(model: any, dynamoObj: any): any;
24 | static(name: string, fn: any): any;
25 | toDynamo(model: any): any;
26 | virtual(name: string, options: any): any;
27 | virtualpath(name: string): any;
28 | }
29 | export class Table {
30 | constructor(name: string, schema: any, options: any, base: any);
31 | create(next: any): any;
32 | createIndex(attributes: any, indexSpec: any): any;
33 | delete(next: any): any;
34 | deleteIndex(indexname: string): any;
35 | describe(next: any): any;
36 | init(next: any): any;
37 | update(next: any): any;
38 | waitForActive(timeout: any, next: any): any;
39 | }
40 | export class Model {
41 | constructor(obj: ModelObject);
42 | put(options, callback: (err, obj) => void);
43 | save(options, callback: (err, obj) => void);
44 | delete(callback: (err) => void);
45 | delete(key, options, callback: (err) => void);
46 | get(key, options?, callback?: (err, obj) => void): Promise;
47 | batchPut(items: ModelObject[], options, callback: (err, obj) => void);
48 | create(obj: T, callback: (err, obj: T) => void);
49 | create(obj: T, options, callback: (err, obj: T) => void);
50 | batchGet(keys, options?, callback?: (err, obj) => void): Promise;
51 | batchDelete(keys, options, callback: (err) => void);
52 | query(query, options, callback: (err, results: T[]) => void): void;
53 | query(query, callback: (err, results: T[]) => void): void;
54 | query(key: string): Query;
55 | queryOne(query, options, callback: (err, results: T[]) => void): void;
56 | queryOne(query, callback: (err, results: T[]) => void): void;
57 | queryOne(key: string): Query;
58 | scan(filter, options, callback: (err, results: T[]) => void): void;
59 | scan(filter, callback: (err, results: T[]) => void): void;
60 | scan(filter: string): Scan;
61 | update(key, update, options, callback: (err) => void);
62 | }
63 | export class VirtualType {
64 | constructor(options: any, name: string);
65 | applyVirtuals(model: any): void;
66 | get(fn: any): any;
67 | set(fn: any): any;
68 | }
69 |
70 | export function ddb(): AWS.DynamoDB;
71 | export function local(url: string): void;
72 | export function model(name: string, schema: Schema, options?: ModelOptions): Model;
73 | export function setDefaults(options: Options): void;
74 | export import AWS = AWS;
75 |
76 | export default {
77 | ddb: ddb,
78 | local: local,
79 | model: model,
80 | setDefaults: setDefaults,
81 | AWS: AWS,
82 | Schema: Schema
83 | };
84 |
85 | // Global options
86 | export interface Options {
87 | create?: boolean;
88 | prefix?: string;
89 | waitForActive?: boolean;
90 | waitForActiveTimeout?: number;
91 | }
92 |
93 | // tslint:disable-next-line:forbidden-types THIS IS INTENTIONAL, THESE ARE ARGUMENTS FOR THE LIBRARY
94 | type ST = String | Number | Boolean | Date | Object;
95 | type TT = ST | ST[] | Buffer;
96 | export interface ModelObject {
97 | [key: string]: TT;
98 | }
99 | export interface SchemaObject {
100 | [key: string]: {
101 | type: TT;
102 | hashKey?: boolean;
103 | rangeKey?: boolean;
104 | required?: boolean;
105 | index?: boolean | {
106 | name: string,
107 | global?: boolean
108 | rangeKey?: string
109 | project?: boolean | string[],
110 | throughput?: number | { read?: number, write?: number };
111 | };
112 | default?: () => TT | TT;
113 | validate?: (value: TT) => boolean | RegExp | TT;
114 | set?: (value: TT) => void;
115 | get?: () => TT;
116 | trim?: boolean;
117 | lowercase?: boolean;
118 | uppercase?: boolean;
119 | };
120 | }
121 |
122 | // Schema options
123 | export interface SchemaOptions {
124 | throughput?: number | { read?: number, write?: number };
125 | timestamps?: boolean | { createdAt?: string, updatedAt?: string };
126 | }
127 |
128 | // Model options
129 | export interface ModelOptions {
130 | overwrite?: boolean;
131 | condition?: string;
132 | conditionNames: any;
133 | conditionValues: any;
134 | }
135 |
136 | export class Query {
137 | exec(callback: (err, objs: T[]) => void): void;
138 | exec(): Promise;
139 | where(rangeKey: string): Query;
140 | filter(filter: string): Query;
141 | and(): Query;
142 | or(): Query;
143 | not(): Query;
144 | null(): Query;
145 | eq(value: string): Query;
146 | lt(value): Query;
147 | le(value): Query;
148 | ge(value): Query;
149 | gt(value): Query;
150 | beginsWith(value): Query;
151 | between(valueA, valueB): Query;
152 | contains(value): Query;
153 | beginsWith(value): Query;
154 | in(values): Query;
155 | limit(limit: number): Query;
156 | consistent(): Query;
157 | descending(): Query;
158 | ascending(): Query;
159 | startAt(key): Query;
160 | attributes(attributes): Query;
161 | }
162 |
163 | export class Scan {
164 | exec(callback: (err, objs: T[]) => void): void;
165 | exec(): Promise;
166 | where(rangeKey: string): Scan;
167 | filter(filter: string): Scan;
168 | and(): Scan;
169 | not(): Scan;
170 | null(): Scan;
171 | eq(value: string): Scan;
172 | lt(value): Scan;
173 | le(value): Scan;
174 | ge(value): Scan;
175 | gt(value): Scan;
176 | beginsWith(value): Scan;
177 | between(valueA, valueB): Scan;
178 | contains(value): Scan;
179 | beginsWith(value): Scan;
180 | in(values): Scan;
181 | limit(limit: number): Scan;
182 | startAt(key): Scan;
183 | attributes(attributes): Scan;
184 | }
185 | }
186 |
--------------------------------------------------------------------------------