Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ sudo: false
language: node_js
# Specify the node versions to run on
node_js:
- "6.11.2"
- "6.11.5"
# Report code coverage to coveralls after successful test runs
after_success:
- npm run coveralls
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ export.eventHandler = eventHandler.handle;

```typescript
import { App, IApp } from "lambda-framework";
import { GCloudHttpHandler, GCloudEventHandler, CloudStorageTemplateLoader } from "lambda-framework-gcloud";
import { GCloudHttpHandler, GCloudEventHandler, GCloudStorageTemplateLoader } from "lambda-framework-gcloud";
import DustTemplateRenderer from "lambda-framework-dustjs";

const app: IApp = new App();
...
const cachedTime: number = 3000;
const templateRenderer: ITemplateRenderer = new DustTemplateRenderer(new CloudStorageTemplateLoader("bucket-name", cachedTime));
const templateRenderer: ITemplateRenderer = new DustTemplateRenderer(new CloudStorageTemplateLoader("PROJECT-ID", "bucket-name", cachedTime));
app.addTemplateEngine(templateRenderer);
...
```
Expand Down
3,453 changes: 3,453 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lambda-framework-gcloud",
"version": "1.0.6",
"version": "1.0.0",
"description": "Google Cloud Functions implementation of Lambda Framework",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
Expand Down Expand Up @@ -32,6 +32,7 @@
"license": "MIT",
"devDependencies": {
"@types/chai": "^4.0.4",
"@types/express": "^4.11.0",
"@types/mocha": "^2.2.43",
"@types/node": "^8.0.28",
"@types/sinon": "^2.3.7",
Expand All @@ -41,12 +42,16 @@
"mock-fs": "^4.4.2",
"nyc": "^11.3.0",
"sinon": "^4.0.2",
"sinon-express-mock": "^1.3.1",
"tslint": "^5.7.0",
"tslint-microsoft-contrib": "^5.0.1",
"typescript": "^2.5.2"
"typescript": "^2.5.2",
"npm-auto-version": "^1.0.0"
},
"dependencies": {
"lambda-framework": "^1.0.20"
"@google-cloud/storage": "^1.5.1",
"lambda-framework": "^1.1.0",
"node-cache": "^4.1.1"
},
"nyc": {
"exclude": [
Expand Down
10 changes: 10 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* lambda-framework-aws
* Copyright(c) 2017 Rogelio Orts
* MIT Licensed
*/

export { default as GCloudEventHandler } from "./lib/GCloudEventHandler";
export { default as GCloudHttpHandler } from "./lib/GCloudHttpHandler";
export { default as GCloudTransformer } from "./lib/GCloudTransformer";
export { default as GCloudStorageTemplateLoader } from "./lib/GCloudStorageTemplateLoader";
26 changes: 26 additions & 0 deletions src/lib/GCloudEventHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { IApp, IRawCallback, IRawEvent } from "lambda-framework";
import GCloudTransformer from "./GCloudTransformer";
import IGCloudFunctionsCallback from "./types/IGCloudFunctionsCallback";
import IGCloudFunctionsEvent from "./types/IGCloudFunctionsEvent";

/**
* The class that implements the Cloud Functions handler for events.
*/
export default class GCloudEventHandler {

private _app: IApp;
private _transformer: GCloudTransformer;

constructor(app: IApp) {
this._app = app;
this._transformer = new GCloudTransformer();
}

public handle(event: IGCloudFunctionsEvent, callback: IGCloudFunctionsCallback): void {
const rawEvent: IRawEvent = this._transformer.transformRawEvent(event);
const rawCallback: IRawCallback = this._transformer.transformRawCallback(callback);

this._app.handle(rawEvent, rawCallback);
}

}
25 changes: 25 additions & 0 deletions src/lib/GCloudHttpHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Request, Response } from "express";
import { IApp, IRawCallback, IRawEvent } from "lambda-framework";
import GCloudTransformer from "./GCloudTransformer";

/**
* The class that implements the Cloud Functions handler for HTTP events.
*/
export default class GCloudHttpHandler {

private _app: IApp;
private _transformer: GCloudTransformer;

constructor(app: IApp) {
this._app = app;
this._transformer = new GCloudTransformer();
}

public handle(request: Request, response: Response): void {
const rawEvent: IRawEvent = this._transformer.transformHttpRawEvent(request);
const rawCallback: IRawCallback = this._transformer.transformHttpRawCallback(response);

this._app.handle(rawEvent, rawCallback);
}

}
32 changes: 32 additions & 0 deletions src/lib/GCloudHttpRawCallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Response } from "express";
import { IRawCallback } from "lambda-framework";

/**
* The RAW callback implementation for Cloud Functions HTTP responses.
*/
export default class GCloudHttpRawCallback implements IRawCallback {

private _response: Response;

constructor(response: Response) {
this._response = response;
}

public sendError(error: Error): void {
console.error(error);
this._response.send("Error: " + error.message);
}

public send(statusCode: number, headers: {[name: string]: string|string[]}, body: object|Buffer): void {
this._response.status(statusCode);
for (const headerName of Object.keys(headers)) {
this._response.setHeader(headerName, headers[headerName]);
}
this._response.send(body);
}

public finalize(err?: Error): void {
throw new Error("The HTTP events can not be finalized. Use send or sendError.");
}

}
27 changes: 27 additions & 0 deletions src/lib/GCloudRawCallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { IRawCallback } from "lambda-framework";
import IGCloudFunctionsCallback from "./types/IGCloudFunctionsCallback";

/**
* The RAW callback implementation for Cloud Functions HTTP responses.
*/
export default class GCloudRawCallback implements IRawCallback {

private _callback: IGCloudFunctionsCallback;

constructor(callback: IGCloudFunctionsCallback) {
this._callback = callback;
}

public sendError(error: Error): void {
throw new Error("This method is only for HTTP events. Use 'finalize' for other events.");
}

public send(statusCode: number, headers: {[name: string]: string|string[]}, body: object|Buffer): void {
throw new Error("This method is only for HTTP events. Use 'finalize' for other events.");
}

public finalize(err?: Error): void {
this._callback(err);
}

}
77 changes: 77 additions & 0 deletions src/lib/GCloudStorageTemplateLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import * as Storage from "@google-cloud/storage";
import { Bucket, File } from "@google-cloud/storage";
import { ITemplate, ITemplateLoader, Template } from "lambda-framework";
import * as NodeCache from "node-cache";

/**
* Loads the template from Cloud Storage.
*/
export default class GCloudStorageTemplateLoader implements ITemplateLoader {

private _storage: Storage;
private _bucket: string;
private _cache: NodeCache;

constructor(projectId: string, bucket: string, ttl: number) {
this._storage = new Storage({ projectId });
this._bucket = bucket;
if (ttl) {
this._cache = new NodeCache({ stdTTL: ttl });
}
}

public load(fileName: string, callback: (err: Error, template: ITemplate) => void): void {
this.getFromCache(fileName, (cacheErr: Error, cacheValue: ITemplate) => {
if (cacheErr) {
callback(cacheErr, null);
} else {
if (cacheValue === undefined) {
const bucket: Bucket = this._storage.bucket(this._bucket);
bucket
.file(fileName)
.get()
.then(
(data) => {
const file: File = data[0];
file.download().then(
(buffer) => {
const content = buffer.toString();
const template: ITemplate = new Template(fileName, content);
this.setToCache(template);
callback(null, template);
},
(err) => {
callback(err, null);
}
);
},
(err) => {
callback(err, null);
}
);
} else {
callback(null, cacheValue);
}
}
});
}

private getFromCache(fileName: string, callback: (cacheErr: Error, cacheValue: ITemplate) => void): void {
if (this._cache) {
this._cache.get(fileName, callback);
} else {
callback(null, undefined);
}
}

private setToCache(template: ITemplate): void {
if (this._cache) {
this._cache.set(template.fileName, template, (err: Error, success: boolean) => {
if (err || !success) {
console.error("Error saving template into cache: " + template.fileName, err);
}
});
}
}

}
64 changes: 64 additions & 0 deletions src/lib/GCloudTransformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Request, Response } from "express";
import { IRawCallback, IRawEvent, RawEvent } from "lambda-framework";
import GCloudHttpRawCallback from "./GCloudHttpRawCallback";
import GCloudRawCallback from "./GCloudRawCallback";
import IGCloudFunctionsCallback from "./types/IGCloudFunctionsCallback";
import IGCloudFunctionsEvent from "./types/IGCloudFunctionsEvent";

/**
* The class that transform the Cloud Functions event and callback to LF raws.
*/
export default class GCloudTransformer {

public transformHttpRawEvent(request: Request): IRawEvent {
const result: IRawEvent = new RawEvent();
result.type = "HTTP";
result.original = request;
result.isHttp = true;
result.body = request.body;
result.queryParams = request.query;
result.stageVariables = {};
result.ip = request.ip;
result.path = request.path;
result.httpMethod = request.method;

result.headers = {};
if (request.headers) {
for (const headerName of Object.keys(request.headers)) {
const headerValue: string = Array.isArray(request.headers[headerName]) ?
(request.headers[headerName] as string[]).join("; ")
:
request.headers[headerName] as string;
result.headers[headerName] = headerValue;
}
}

return result;
}

public transformRawEvent(event: IGCloudFunctionsEvent): IRawEvent {
const result: IRawEvent = new RawEvent();
result.type = event.eventType;
result.original = event;
result.isHttp = false;

return result;
}

public transformHttpRawCallback(response: Response): IRawCallback {
if (response === undefined) {
return null;
} else {
return new GCloudHttpRawCallback(response);
}
}

public transformRawCallback(callback: IGCloudFunctionsCallback): IRawCallback {
if (callback === undefined) {
return null;
} else {
return new GCloudRawCallback(callback);
}
}

}
5 changes: 5 additions & 0 deletions src/lib/types/IGCloudFunctionsCallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* The Google Cloud Functions callback.
*/
type IGCloudFunctionsCallback = (error?: Error, result?: any) => void;
export default IGCloudFunctionsCallback;
16 changes: 16 additions & 0 deletions src/lib/types/IGCloudFunctionsEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* The Google Cloud Functions event.
*/
export default interface IGCloudFunctionsEvent {

readonly eventId: string;

readonly timestamp: string;

readonly eventType: string;

readonly resource: string;

readonly data: any;

}
36 changes: 36 additions & 0 deletions test/GCloudEventHandler.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* tslint:disable:no-unused-expression */
import * as Chai from "chai";
import { App, IApp, RawEvent } from "lambda-framework";
import { SinonStub, stub } from "sinon";
import GCloudEventHandler from "./../src/lib/GCloudEventHandler";
import GCloudRawCallback from "./../src/lib/GCloudRawCallback";
import IGCloudFunctionsCallback from "./../src/lib/types/IGCloudFunctionsCallback";
import IGCloudFunctionsEvent from "./../src/lib/types/IGCloudFunctionsEvent";
import eventGenerator from "./utils/eventGenerator";

/**
* Test for GCloudEventHandler.
*/
describe("GCloudEventHandler", () => {
const event: IGCloudFunctionsEvent = eventGenerator("testType", {});
const callback: IGCloudFunctionsCallback = null;
const app: IApp = new App();
const appHandle: SinonStub = stub(app, "handle");
const handler: GCloudEventHandler = new GCloudEventHandler(app);

afterEach(() => {
appHandle.reset();
});

describe("#handle", () => {

it("calls the app `handle` method with a raw event and a raw callback.", () => {
handler.handle(event, callback);

Chai.expect(appHandle.args[0][0]).instanceOf(RawEvent);
Chai.expect(appHandle.args[0][1]).instanceOf(GCloudRawCallback);
});

});

});
Loading