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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ lightweight. You can use the projects you need in your web application.
The main object is the App. You have to instantiate a new App object and then
you can do what you need with it.
```typescript
import App, { IApp } from "lambda-framework";
import { App, IApp } from "lambda-framework";

const app: IApp = new App();
```
Expand All @@ -55,8 +55,8 @@ app.handle(event, callback);
You don't need to care about passing the event to the App handler, you can use the [AWS Lambda implementation](https://github.com/rogelio-o/lambda-framework-aws) or another provider
implementation. These will manage the creation of the raw event and passing it to the handler.
```typescript
import App, { IApp } from "lambda-framework";
import AWSHandler from "lambda-framework-aws";
import { App, IApp } from "lambda-framework";
import { AWSHandler } from "lambda-framework-aws";

const app: IApp = new App();
...
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"test": "nyc --reporter=html --reporter=text mocha --recursive dist/test/",
"posttest": "npm run lint && nyc check-coverage --statements 90 --branches 90 --functions 90 --lines 90",
"compile": "tsc",
"lint": "tslint --config tslint.json --type-check --project tsconfig.json src/{,**/,**/**/,**/**/**/}*.ts",
"lint": "tslint --config tslint.json --project tsconfig.json",
"coveralls": "nyc report --reporter=text-lcov | coveralls",
"prepublish": "npm-auto-version"
},
Expand All @@ -38,7 +38,8 @@
"nyc": "^11.3.0",
"tslint": "^5.7.0",
"tslint-microsoft-contrib": "^5.0.1",
"typescript": "^2.5.2"
"typescript": "^2.5.2",
"sinon": "^4.0.2"
},
"dependencies": {
"accepts": "^1.3.4",
Expand All @@ -57,7 +58,6 @@
"qs": "^6.5.1",
"querystring": "^0.2.0",
"randomstring": "^1.1.5",
"sinon": "^4.0.2",
"statuses": "^1.3.1",
"typeis": "^1.1.1",
"utils-merge": "^1.0.1"
Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* MIT Licensed
*/

export { default } from "./lib/App";
export { default as App } from "./lib/App";
export { default as configuration } from "./lib/configuration/configuration";

export { default as IApp } from "./lib/types/IApp";
Expand All @@ -26,6 +26,8 @@ export { default as IHttpRequest } from "./lib/types/http/IHttpRequest";
export { default as IHttpResponse } from "./lib/types/http/IHttpResponse";
export { default as IHttpRoute } from "./lib/types/http/IHttpRoute";
export { default as IHttpRouterExecutor } from "./lib/types/http/IHttpRouterExecutor";
export { default as ICookie } from "./lib/types/http/ICookie";
export { default as Cookie } from "./lib/http/Cookie";

export { default as IBodyParser } from "./lib/types/http/IBodyParser";
export { default as JsonParser } from "./lib/http/bodyParsers/JsonParser";
Expand Down
2 changes: 1 addition & 1 deletion src/lib/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default class App implements IApp {

public handle(event: IRawEvent, callback: IRawCallback): void {
if (event.isHttp) {
const req: IHttpRequest = new HttpRequest(event);
const req: IHttpRequest = new HttpRequest(this, event);
const res: IHttpResponse = new HttpResponse(this, req, callback);
const done = httpFinalHandler(req, res, {
env: this.get(configuration.ENVIRONMENT),
Expand Down
46 changes: 46 additions & 0 deletions src/lib/http/Cookie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import ICookie from "./../types/http/ICookie";

/**
* This object represents a browser cookie.
*/
export default class Cookie implements ICookie {

private _name: string;

private _value: string|{[name: string]: any};

private _expires: Date;

private _path: string;

private _signed: boolean;

constructor(name: string, value: string|{[name: string]: any}, expires?: Date, path?: string, signed?: boolean) {
this._name = name;
this._value = value;
this._expires = expires;
this._path = path;
this._signed = signed;
}

get name(): string {
return this._name;
}

get value(): string|{[name: string]: any} {
return this._value;
}

get expires(): Date {
return this._expires;
}

get path(): string {
return this._path;
}

get signed(): boolean {
return this._signed;
}

}
18 changes: 16 additions & 2 deletions src/lib/http/HttpRequest.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import * as accepts from "accepts";
import * as fresh from "fresh";
import configuration from "./../configuration/configuration";
import ICookie from "./../types/http/ICookie";
import IHttpRequest from "./../types/http/IHttpRequest";
import IHttpResponse from "./../types/http/IHttpResponse";
import IHttpRoute from "./../types/http/IHttpRoute";
import IHttpUploadedFile from "./../types/http/IHttpUploadedFile";
import IApp from "./../types/IApp";
import INext from "./../types/INext";
import IRawEvent from "./../types/IRawEvent";
import { mergeParams, normalizeType } from "./../utils/utils";
import { getCookiesFromHeader, mergeParams, normalizeType } from "./../utils/utils";

/**
* A incoming request created when the event is APIGatewayEvent.
Expand All @@ -24,8 +27,9 @@ export default class HttpRequest implements IHttpRequest {
private _event: IRawEvent;
private _headers: { [name: string]: string };
private _context: { [name: string]: any };
private _cookies: { [name: string]: ICookie };

constructor(event: IRawEvent) {
constructor(app: IApp, event: IRawEvent) {
this.body = event.body; // Default body
this._event = event;
this._context = {};
Expand All @@ -35,6 +39,8 @@ export default class HttpRequest implements IHttpRequest {
for (const key of Object.keys(this._event.headers)) {
this._headers[key.toLowerCase()] = this._event.headers[key];
}

this._cookies = getCookiesFromHeader(this._headers.cookie, app.get(configuration.COOKIE_SECRET));
}

get headers(): { [name: string]: string } {
Expand Down Expand Up @@ -85,6 +91,10 @@ export default class HttpRequest implements IHttpRequest {
return this._context;
}

get cookies(): { [name: string]: ICookie } {
return this._cookies;
}

public header(key: string): string {
return this.headers[key.toLowerCase()];
}
Expand Down Expand Up @@ -162,4 +172,8 @@ export default class HttpRequest implements IHttpRequest {
return !this.fresh(response);
}

public cookie(name: string): ICookie {
return this._cookies[name];
}

}
50 changes: 26 additions & 24 deletions src/lib/http/HttpResponse.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { parse, serialize } from "cookie";
import { serialize } from "cookie";
import { sign } from "cookie-signature";
import * as encodeUrl from "encodeurl";
import * as escapeHtml from "escape-html";
import * as statuses from "statuses";
import configuration from "./../configuration/configuration";
import HttpError from "./../exceptions/HttpError";
import IHttpError from "./../types/exceptions/IHttpError";
import ICookie from "./../types/http/ICookie";
import IHttpHandler from "./../types/http/IHttpHandler";
import IHttpRequest from "./../types/http/IHttpRequest";
import IHttpResponse from "./../types/http/IHttpResponse";
Expand All @@ -15,6 +16,7 @@ import INext from "./../types/INext";
import IRawCallback from "./../types/IRawCallback";
import IRouter from "./../types/IRouter";
import { merge, normalizeType, setCharset, stringify } from "./../utils/utils";
import Cookie from "./Cookie";

/**
* This class represents an HTTP response, with the helpers to be sent.
Expand All @@ -30,13 +32,15 @@ export default class HttpResponse implements IHttpResponse {
private _headers: { [name: string]: string|string[] };
private _error: IHttpError;
private _isSent: boolean;
private _cookies: { [name: string]: ICookie };

constructor(app: IApp, request: IHttpRequest, callback: IRawCallback) {
this._app = app;
this._request = request;
this._callback = callback;
this._headers = {};
this._isSent = false;
this._cookies = {};
}

get statusCode(): number {
Expand Down Expand Up @@ -245,40 +249,43 @@ export default class HttpResponse implements IHttpResponse {
return this;
}

public addCookie(name: string, value: string|object, options?: object): IHttpResponse {
const opts = merge({}, options);
public addCookie(cookie: ICookie): IHttpResponse {
const opts: {[name: string]: any} = {};
const secret = this._app.get(configuration.COOKIE_SECRET);
const signed = opts.signed;
const signed = cookie.signed;

if (signed && !secret) {
throw new Error("app.set(\"cookie_secret\", \"SECRET\") required for signed cookies.");
}

let val = typeof value === "object"
? "j:" + JSON.stringify(value)
: String(value);
let val = typeof cookie.value === "object"
? "j:" + JSON.stringify(cookie.value)
: String(cookie.value);

if (signed) {
val = "s:" + sign(val, secret);
opts.signed = true;
}

if ("maxAge" in opts) {
opts.expires = new Date(Date.now() + opts.maxAge);
opts.maxAge /= 1000;
if (cookie.expires) {
opts.expires = cookie.expires;
}

if (opts.path == null) {
if (cookie.path == null) {
opts.path = "/";
} else {
opts.path = cookie.path;
}

this.appendHeader("Set-Cookie", serialize(name, String(val), opts));
this._cookies[cookie.name] = cookie;
this.appendHeader("Set-Cookie", serialize(cookie.name, String(val), opts));

return this;
}

public addCookies(obj: object, options?: object): IHttpResponse {
for (const key of Object.keys(obj)) {
this.addCookie(key, obj[key], options);
public addCookies(cookies: ICookie[]): IHttpResponse {
for (const cookie of cookies) {
this.addCookie(cookie);
}

return this;
Expand All @@ -287,19 +294,14 @@ export default class HttpResponse implements IHttpResponse {
public clearCookie(name: string, options?: object): IHttpResponse {
const opts = merge({ expires: new Date(1), path: "/" }, options);

this.addCookie(name, "", opts);
const cookie: ICookie = new Cookie(name, "", opts.expires, opts.path);
this.addCookie(cookie);

return this;
}

public cookie(name: string): string {
const cookiesHeader = this.header("Set-Cookie");

const cookies = parse(Array.isArray(cookiesHeader) ?
cookiesHeader.join("; ") : cookiesHeader
);

return cookies[name];
public cookie(name: string): ICookie {
return this._cookies[name];
}

public location(url: string): IHttpResponse {
Expand Down
16 changes: 16 additions & 0 deletions src/lib/types/http/ICookie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* This object represents a browser cookie.
*/
export default interface ICookie {

readonly name: string;

readonly value: string|{[name: string]: any};

readonly expires: Date;

readonly path: string;

readonly signed: boolean;

}
17 changes: 17 additions & 0 deletions src/lib/types/http/IHttpRequest.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import INext from "./../INext";
import ICookie from "./ICookie";
import IHttpResponse from "./IHttpResponse";
import IHttpRoute from "./IHttpRoute";
import IHttpUploadedFile from "./IHttpUploadedFile";
Expand Down Expand Up @@ -94,6 +95,13 @@ export default interface IHttpRequest {
*/
readonly context: { [name: string]: any };

/**
* Returns the all the cookies retrieving their values and
* options from the HTTP request header. The key will be the name of
* the cookie and the value the object representing the cookie.
*/
readonly cookies: {[name: string]: ICookie};

/**
* Return request header.
*
Expand Down Expand Up @@ -180,4 +188,13 @@ export default interface IHttpRequest {
*/
stale(response: IHttpResponse): boolean;

/**
* Returns the cookie with the given `name` retrieving the value and
* options from the HTTP request header.
*
* @param {string} name
* @return {ICookie}
*/
cookie(name: string): ICookie;

}
23 changes: 10 additions & 13 deletions src/lib/types/http/IHttpResponse.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import IHttpError from "./../exceptions/IHttpError";
import INext from "./../INext";
import IRouter from "./../IRouter";
import ICookie from "./ICookie";
import IHttpHandler from "./IHttpHandler";

/**
Expand Down Expand Up @@ -146,32 +147,28 @@ export default interface IHttpResponse {
clearCookie(name: string, options?: object): IHttpResponse;

/**
* Set cookie `name` to `value`, with the given `options`.
* Add the given cookie to response header.
*
* @param {string} name
* @param {string|object} value
* @param {object} options
* @param {ICookie} cookie
* @return {IHttpResponse}
*/
addCookie(name: string, value: string|object, options?: object): IHttpResponse;
addCookie(cookie: ICookie): IHttpResponse;

/**
* Set each cookie indicated by the key of `object` to the value indicated
* by the value of the key.
* Add the given cookies to response header.
*
* @param {object} name
* @param {object} options
* @param {ICookie[]} cookies
* @return {IHttpResponse}
*/
addCookies(name: object, options?: object): IHttpResponse;
addCookies(cookies: ICookie[]): IHttpResponse;

/**
* Get the value of the cookie `name`.
* Get the cookie with the given `name`.
*
* @param {string} name
* @return {string}
* @return {ICookie}
*/
cookie(name: string): string;
cookie(name: string): ICookie;

/**
* Set the location header to `url`.
Expand Down
Loading