Skip to content

Instantly share code, notes, and snippets.

@crrobinson14
Last active January 7, 2020 17:21
Show Gist options
  • Save crrobinson14/7cdfcda6d7c15551923d4d235197f5d3 to your computer and use it in GitHub Desktop.
Save crrobinson14/7cdfcda6d7c15551923d4d235197f5d3 to your computer and use it in GitHub Desktop.
FROM node:8.6.0
#USER node
# Deal with node-gyp permissions issues in Docker
RUN npm -g config set user root
RUN mkdir /root/.npm-global
ENV PATH=/root/.npm-global/bin:$PATH
ENV NPM_CONFIG_PREFIX=/root/.npm-global
# Base tools
RUN apt-get update && \
apt-get -y install less man ssh python python-pip libpython-dev python-dev jq apt-transport-https \
curl apt-transport-https ca-certificates mysql-client && \
rm -rf /var/lib/apt/lists/*
# Install Docker
RUN echo deb https://apt.dockerproject.org/repo debian-jessie main > /etc/apt/sources.list.d/docker.list && \
apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D && \
apt-get update && \
apt-cache policy docker-engine && \
apt-get install docker-engine -y && \
rm -rf /var/lib/apt/lists/*
# jFrog CLI
RUN curl -fL https://getcli.jfrog.io | sh
# AWS CLI and MkDocs tools
ENV PYTHONIOENCODING=UTF-8
RUN pip install awscli mkdocs mkdocs-material pymdown-extensions pygments && \
npm install -g jsdoc-to-markdown swagger-markdown && \
npm cache clean --force
# Install vault for firebase vault sync service
RUN apt-get update && apt-get -y install unzip
RUN curl -O https://releases.hashicorp.com/vault/0.8.3/vault_0.8.3_linux_amd64.zip && \
unzip vault_0.8.3_linux_amd64.zip && mv vault /usr/local/bin/
# Module publishing tools
COPY scripts/bump-npm.sh /usr/local/bin/
COPY scripts/bump-docker.sh /usr/local/bin/
COPY scripts/notify-slack-npm.sh /usr/local/bin/
COPY scripts/push-docker-jfrog.sh /usr/local/bin/
COPY scripts/copy-maxmind-db.sh /usr/local/bin/
COPY scripts/ecs-deploy.sh /usr/local/bin/
COPY scripts/copy-docker-jfrog-ecr.sh /usr/local/bin/
COPY scripts/s3-deploy.sh /usr/local/bin/
COPY scripts/s3-copy-api-docs.sh /usr/local/bin/
COPY scripts/s3-copy-project-docs.sh /usr/local/bin/
COPY scripts/notify-slack-microsvc.sh /usr/local/bin/
COPY scripts/notify-slack-site.sh /usr/local/bin/
COPY scripts/notify-slack-on-failure.sh /usr/local/bin/
COPY scripts/launch-review-app.sh /usr/local/bin/
COPY scripts/destroy-review-app.sh /usr/local/bin/
COPY scripts/notify-slack-reviewapp.sh /usr/local/bin/
site_name: Auth Service
site_url: http://web-ng-docs.s3-website-us-east-1.amazonaws.com/web-services/auth
theme: material
extra:
palette:
primary: 'blue grey'
accent: 'green'
site_dir: public
pages:
- Home: index.md
- API: api.md
- TODO: todo.md
- Last Test Results: test.md
- Schema: schema.md
- Security: security.md
- Link Dump: links.md
markdown_extensions:
- admonition
- codehilite(linenums=true)
- footnotes
- meta
- toc(permalink=true)
{
"scripts": {
"docs_schema": "sequelize-erd --source ./lib/models --destination ./docs/img/schema.svg",
"docs_readme": "cp README.md docs/index.md",
"docs_mkdocs": "mkdocs build",
"docs_todo": "leasot -r markdown */*.js functions/**/*.js test/**/*s.js > docs/todo.md || true",
"docs_swagger": "node_modules/.bin/serverless-swagger && swagger-markdown -i docs/swagger.yml -o docs/api.md",
"docs": "npm run build_yml && npm run docs_swagger && npm run docs_readme && npm run docs_mkdocs && npm run docs_todo"
}
}
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const json2yaml = require('json2yaml');
const yaml = require('js-yaml');
// eslint-disable-next-line import/no-dynamic-require
const packageJson = require(path.join(process.cwd(), 'package.json'));
const SWAGGER_SPEC_DIR = "docs";
const SWAGGER_SPEC_NAME = "swagger.yml";
const MODELS_FILE = "lib/orm.js";
class Swagger {
constructor(functions) {
this.functions = functions;
this.doc = {};
this.definitions = {};
}
static standardResponses() {
return {
401: {
description: 'Session Error. Required authentication was missing or invalid for the requested resource.'
},
403: {
description: 'Forbidden. Authentication was provided, but not sufficient for the request.'
},
404: {
description: 'Not Found. The primary content related to the request does not exist.'
},
405: {
description: 'Request Error. An required input parameter was missing or invalid.'
},
429: {
description: 'Quota Exceeded. Reduce request rate.'
},
500: {
description: 'Internal server error. Try again later.'
}
};
}
static typeLookup(attribute) {
const types = {
STRING: 'string',
CHAR: 'string',
TEXT: 'string',
NUMBER: 'integer',
INTEGER: 'integer',
BIGINT: 'integer',
FLOAT: 'number',
TIME: 'string',
DATE: 'string',
DATEONLY: 'string',
BOOLEAN: 'boolean',
NOW: 'string',
BLOB: 'string',
DECIMAL: 'number',
NUMERIC: 'number',
UUID: 'string',
UUIDV1: 'string',
UUIDV4: 'string',
ENUM: 'string',
INT32: 'integer',
INT64: 'integer',
DOUBLE: 'number',
BYTE: 'string',
'DATE-TIME': 'string',
VARCHAR: 'string',
TIMESTAMP: 'string',
REAL: 'number',
};
const formats = {
INTEGER: 'int32',
INT32: 'integer',
INT64: 'int64',
BIGINT: 'int64',
FLOAT: 'float',
DOUBLE: 'double',
DATE: 'date-time',
'DATE-TIME': 'date-time',
DATEONLY: 'date',
BLOB: 'binary',
};
const typeKey = attribute.type.key;
const type = {
type: types[typeKey] || 'string',
description: attribute.comment,
};
if (formats[typeKey]) {
type.format = formats[typeKey];
}
if (typeKey === 'ENUM') {
type.enum = attribute.type.values;
}
if (typeKey === 'UUID') {
type.maxLength = 36;
}
if (attribute.allowNull) {
type.nullable = true;
}
return type;
}
generate() {
return this
.header()
.paths()
.objectDefinitions();
}
toYAML() {
return json2yaml.stringify(this.doc);
}
// add spec summary
header() {
const swaggerMeta = packageJson.swagger || {};
Object.assign(this.doc, {
swagger: '2.0',
info: {
description: swaggerMeta.description || packageJson.description,
version: swaggerMeta.version || packageJson.version,
title: swaggerMeta.title || packageJson.name,
termsOfService: swaggerMeta.termsOfService || 'http://www.snap-interactive.com/terms-of-service/',
contact: {
email: swaggerMeta.contact || packageJson.author
}
},
host: swaggerMeta.uri || `${packageJson.name}.apis.theirweb.net`,
basePath: swaggerMeta.basePath || '/v1',
schemes: swaggerMeta.schemes || ['https'],
});
return this;
}
// return the metadata of the given function handler
getHandlerData(handler) {
return require(path.join(process.cwd(), handler));
}
// add path entries
paths() {
this.doc.paths = {};
// functions
Object.keys(this.functions).forEach(f => {
const func = this.functions[f];
const handlerPath = this.getHandlerPath(func.handler);
const metadata = this.getHandlerData(handlerPath);
// include only the funtions with a description for its handler
if (metadata.description) {
func.events.forEach(event => { // events
// summary
const path = event.http.path;
const method = event.http.method;
this.doc.paths[path] = this.doc.paths[path] || {};
this.doc.paths[path][method] = this.doc.paths[path][method] || {};
if (metadata.tags) {
this.doc.paths[path][method].tags = metadata.tags;
}
this.doc.paths[path][method].summary = metadata.name;
this.doc.paths[path][method].description = metadata.description;
this.doc.paths[path][method].operationId = metadata.name;
this.doc.paths[path][method].consumes = ['application/json'];
this.doc.paths[path][method].produces = ['application/json'];
// output (response)
const responseSchema = {};
Object.keys(metadata.output).sort().forEach(key => {
const type = metadata.output[key].type;
if (['string', 'object', 'number', 'integer', 'boolean'].indexOf(type) !== -1) {
responseSchema.type = type;
} else if (type === 'array') {
responseSchema.type = type;
const items = metadata.output[key].items.substring(1);
this.definitions[items] = true;
responseSchema.items = {
$ref: `#/definitions/${items}`
};
} else if (type.charAt(0) === '#') {
const model = type.substring(1);
this.definitions[model] = true;
responseSchema.$ref = `#/definitions/${model}`;
}
});
const response = {
200: {
description: metadata.output.description || "",
headers: metadata.output.headers || {},
schema: responseSchema,
}
};
this.doc.paths[path][method].responses = Object.assign(
response,
Swagger.standardResponses()
);
// inputs (parameters)
if (metadata.inputs) {
this.doc.paths[path][method].parameters = [];
Object.keys(metadata.inputs).sort().forEach(inputKey => {
const input = metadata.inputs[inputKey];
const paramLocation = input.type && input.type.charAt(0) === '#' ? 'body' : 'query';
const param = { in: paramLocation,
name: inputKey,
description: input.description,
required: input.required,
};
if (paramLocation === 'body') {
const definition = input.type.substring(1);
this.definitions[definition] = true;
param.schema = {
$ref: `#/definitions/${definition}`
};
} else {
param.type = input.type || 'string';
}
this.doc.paths[path][method].parameters.push(param);
});
}
});
}
})
return this;
}
// models
objectDefinitions() {
const orm_path = path.join(process.cwd(), MODELS_FILE);
if (fs.existsSync(orm_path)) {
const ORM = require(orm_path);
Object.keys(ORM.models).sort().forEach(model => {
const rawAttributes = ORM.models[model].rawAttributes || {};
const properties = {};
Object.keys(rawAttributes).sort().forEach(key => {
properties[key] = Swagger.typeLookup(rawAttributes[key]);
});
this.doc.definitions = this.doc.definitions || {};
this.doc.definitions[model] = {
type: 'object',
description: ORM.models[model].options.comment,
properties
};
});
} else {
console.log(`ORM models file does not exist.`);
}
return this;
}
// Return the path of the given handler
getHandlerPath(handler) {
const parsedPath = path.parse(handler);
return path.join(parsedPath.dir, parsedPath.name);
}
}
if (require.main === module) {
const configFilePath = path.join(process.cwd(), 'serverless.yml');
try {
// Read functions
var _config = yaml.safeLoad(fs.readFileSync(configFilePath, 'utf8'));
var _functions = _config.functions;
// Generate Swagger yml from the functions' meta-data
const spec = new Swagger(_functions).generate().toYAML();
// Write swagger spec to the specified file
fs.writeFileSync(path.join(SWAGGER_SPEC_DIR, SWAGGER_SPEC_NAME), spec);
process.exit(0);
} catch (e) {
console.log(`Error in reading the serverless functions. ${e}`);
}
} else {
module.exports = Swagger;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment