Skip to content

Commit

Permalink
fix: remove faker and add unit tests
Browse files Browse the repository at this point in the history
* fix: remove faker

* chore: add rollup config

* test: add unit tests
  • Loading branch information
theBenForce authored Nov 21, 2019
1 parent c95100f commit 3079720
Show file tree
Hide file tree
Showing 10 changed files with 1,930 additions and 61 deletions.
3 changes: 2 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ src/
.travis.yml
.releaserc.yml
yarn-error.log
tsconfig.json
tsconfig.json
rollup.config.js
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
language: node_js
node_js: "11"
node_js: "12"

cache: yarn

Expand Down
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,30 @@ Finally you're ready to actually make the request.
const result = await axios.get(request.url, { headers: request.headers });
```

### Credendtials
### Making Requests with Exponential Backoff

The `ApiGatewaySigner` has a convenience method called `makeRequestWithRetries`.
Simply provide it with the same parameters as `signRequest` and a callback and it
will take care of automatically retrying requests.

```typescript
const data = await apiSigner.makeRequestWithRetries(
{
method: HttpMethods.GET,
path: `/path/to/some/resource`
},
request => axios.get(request.url, { headers: request.headers }),
3 // Number of times to retry the request
);
```

The method calls your callback with the signed request. If the callback throws an
exception, then it is called again after a short delay.

The last parameter is optional, and it determines the number of times to retry
the request. By default it's set to `5`.

### Credentials

To sign your request the library requires a set of credentials. You can provide
these credentials as part of the initial config, or in environment variables.
Expand All @@ -61,7 +84,7 @@ const apiSigner = new ApiGatewaySigner({

If values aren't provided in the constructor, the values of the `AWS_ACCESS_KEY_ID`,
`AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN` environment variables will be
used. These values will be populated by default in a lambda runtime envrionment.
used. These values will be populated by default in a lambda runtime environment.

See [Lambda Environment Variables](https://docs.aws.amazon.com/lambda/latest/dg/lambda-environment-variables.html) for more information.

Expand Down
7 changes: 7 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
roots: ["<rootDir>/src"],
testMatch: ["**/?(*.)+(spec|test).+(ts|tsx|js)"],
transform: {
"^.+\\.(ts|tsx)?$": "ts-jest"
}
};
22 changes: 13 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,34 @@
"repository": "https://github.com/drg-adaptive/api-gateway-request-signer",
"author": "Ben Force <bforce@dresources.com>",
"license": "MIT",
"dependencies": {
"crypto-js": "^3.1.9-1",
"delay": "^4.3.0",
"faker": "^4.1.0"
},
"dependencies": {},
"devDependencies": {
"@semantic-release/changelog": "^3.0.5",
"@semantic-release/commit-analyzer": "^6.3.2",
"@semantic-release/github": "^5.5.5",
"@semantic-release/npm": "^5.3.4",
"@semantic-release/release-notes-generator": "^7.3.2",
"@types/crypto-js": "^3.1.43",
"@types/faker": "^4.1.7",
"@types/jest": "^24.0.23",
"@types/node": "^12.12.6",
"commitizen": "^4.0.3",
"crypto-js": "^3.1.9-1",
"cz-conventional-changelog": "^3.0.2",
"delay": "^4.3.0",
"jest": "^24.9.0",
"rollup": "^1.27.3",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-typescript2": "^0.25.2",
"semantic-release": "^15.13.30",
"ts-jest": "^24.1.0",
"typescript": "^3.7.2"
},
"scripts": {
"build": "tsc",
"prepare": "tsc",
"build": "yarn rollup -c",
"prepare": "yarn build",
"semantic-release": "semantic-release",
"test": "echo \"TODO: Write some 🐞 tests...\""
"test": "jest"
},
"config": {
"commitizen": {
Expand Down
30 changes: 30 additions & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const commonjs = require("rollup-plugin-commonjs");
const typescript = require("rollup-plugin-typescript2");
const resolve = require("rollup-plugin-node-resolve");

module.exports = {
input: "./src/index.ts",
output: {
file: "dist/index.js",
format: "cjs",
exports: "named"
},
plugins: [
// @ts-ignore
typescript({
lib: ["es5", "es6"],
target: "es6",
typescript: require("typescript"),
module: "CommonJS"
}),
// @ts-ignore
commonjs({
extensions: [".js", ".ts"]
}),
// @ts-ignore
resolve({
preferBuiltins: true,
extensions: [".js", ".ts"]
})
]
};
71 changes: 71 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import ApiGatewaySigner, { HttpMethods, SignedRequestData } from "./index";
import { Jest } from "@jest/environment";

const BASE_URL = "https://example.com";

describe("signRequest", () => {
let signer: ApiGatewaySigner;
beforeEach(() => {
signer = new ApiGatewaySigner({
endpoint: BASE_URL
});
});

test("headers are created", () => {
const request = signer.signRequest({
method: HttpMethods.GET,
path: "/"
});

expect(request.headers).toBeDefined();
expect(request.headers).toHaveProperty("Accept", "application/json");
expect(request.headers).toHaveProperty("Authorization");
expect(request.headers.Authorization).toMatch(
/^AWS4-HMAC-SHA256\sCredential=.*,\s+SignedHeaders=.*,\s+Signature=/
);
expect(request.headers).toHaveProperty("Content-Type", "application/json");
expect(request.headers).toHaveProperty("x-amz-date");
});

test("URL is created", () => {
const request = signer.signRequest({
method: HttpMethods.GET,
path: "/"
});

expect(request).toHaveProperty("url", BASE_URL + "/");
});

test("URL has params", () => {
const request = signer.signRequest({
method: HttpMethods.GET,
path: "/",
queryParams: {
query: "TEST_QUERY"
}
});

expect(request).toHaveProperty("url");
expect(request.url).toMatch(/query=TEST_QUERY$/);
});

test("Exponential backoff", async () => {
const RETRY_COUNT = 3;

const callback = jest.fn(async (req: SignedRequestData) => {
throw new Error("Testing");
});

const result = await signer.makeRequestWithRetries(
{
method: HttpMethods.GET,
path: "/"
},
callback,
RETRY_COUNT
);

expect(callback).toHaveBeenCalledTimes(RETRY_COUNT);
expect(result).toBeUndefined();
}, 10000);
});
13 changes: 6 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Taken from https://github.com/AnomalyInnovations/sigV4Client and converted to typescript
// For an example of the original library, see https://serverless-stack.com/chapters/connect-to-api-gateway-with-iam-auth.html

import { LibWordArray, SHA256, HmacSHA256, enc } from "crypto-js";
import delay from "delay";
import { random } from "faker";
import { LibWordArray } from "crypto-js";
import encHex from "crypto-js/enc-hex";
import SHA256 from "crypto-js/sha256";
import HmacSHA256 from "crypto-js/hmac-sha256";

const encHex = enc.Hex;
import delay from "delay";

export interface RequestSignerConfig {
accessKey?: string;
Expand Down Expand Up @@ -367,9 +368,7 @@ export default class RequestSigner {
return data;
} catch (ex) {
console.error(ex);
await delay(
(2 ** n + random.float({ min: 0, max: 1, precision: 5 })) * 1000
);
await delay((2 ** n + Math.random()) * 1000);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
"baseUrl": "./src",
"outDir": "./dist",
"noImplicitAny": true,
"module": "commonjs",
"module": "ESNext",
"target": "es5",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"moduleResolution": "node",
"declaration": true
}
Expand Down
Loading

0 comments on commit 3079720

Please sign in to comment.