Skip to content

Commit

Permalink
feat: add support for browser streaming (#721)
Browse files Browse the repository at this point in the history
  • Loading branch information
AllanZhengYP authored and trivikr committed Jan 20, 2020
1 parent 97bbb9a commit 133430c
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 91 deletions.
8 changes: 1 addition & 7 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,9 @@ const base = require("./jest.config.base.js");
module.exports = {
...base,
projects: ["<rootDir>/packages/*/jest.config.js"],
testPathIgnorePatterns: [
"<rootDir>/packages/add-glacier-checksum-headers-browser",
"<rootDir>/clients/client-.*"
],
testPathIgnorePatterns: ["/node_modules/", "<rootDir>/clients/client-.*"],
coveragePathIgnorePatterns: [
"/node_modules/",
"<rootDir>/packages/add-glacier-checksum-headers-browser",
"<rootDir>/packages/crypto-sjcl-*",
"<rootDir>/packages/xml-body-parser/vendor/",
"<rootDir>/clients/client-.*",
"/__fixtures__/"
]
Expand Down
16 changes: 11 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"build:smithy-client": "yarn build:crypto-dependencies && lerna run --scope '@aws-sdk/client-rds-data' --include-dependencies pretest",
"build:all": "yarn build:crypto-dependencies && lerna run pretest --include-dependencies --include-dependents",
"pretest:all": "yarn build:all",
"test:all": "jest --coverage --passWithNoTests",
"test:all": "jest --coverage --passWithNoTests && lerna run test --scope @aws-sdk/stream-collector-browser --scope @aws-sdk/hash-blob-browser",
"test:functional": "jest --config tests/functional/jest.config.js --passWithNoTests",
"test:integration": "cucumber.js"
},
Expand Down Expand Up @@ -50,10 +50,16 @@
"typescript": "^3.7.0",
"yarn": "1.21.1"
},
"workspaces": [
"packages/*",
"clients/*"
],
"workspaces": {
"packages": [
"packages/*",
"clients/*"
],
"nohoist": [
"**/karma*",
"**/karma*/**"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged",
Expand Down
6 changes: 3 additions & 3 deletions packages/fetch-http-handler/src/fetch-http-handler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe("httpHandler", () => {
["bizz", "bazz"]
])
},
blob: jest.fn().mockResolvedValue("")
body: "FOO" //should be a ReadableStream in real life.
};
const mockFetch = jest.fn().mockResolvedValue(mockResponse);

Expand All @@ -50,7 +50,7 @@ describe("httpHandler", () => {
let response = await fetchHttpHandler.handle({} as any, {});

expect(mockFetch.mock.calls.length).toBe(1);
expect(mockResponse.blob.mock.calls.length).toBe(1);
expect(response.response.body).toBe("FOO");
});

it("properly constructs url", async () => {
Expand All @@ -61,7 +61,7 @@ describe("httpHandler", () => {
["bizz", "bazz"]
])
},
blob: jest.fn().mockResolvedValue("")
body: ""
};
const mockFetch = jest.fn().mockResolvedValue(mockResponse);

Expand Down
6 changes: 3 additions & 3 deletions packages/fetch-http-handler/src/fetch-http-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ export class FetchHttpHandler implements HttpHandler {
transformedHeaders[pair[0]] = pair[1];
}

return response.blob().then<{ response: HttpResponse }>(body => ({
return {
response: new HttpResponse({
headers: transformedHeaders,
statusCode: response.status,
body
body: response.body
})
}));
};
}),
requestTimeout(requestTimeoutInMs)
];
Expand Down
10 changes: 7 additions & 3 deletions packages/hash-blob-browser/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,13 @@ module.exports = function(config) {
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,

// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ["ChromeHeadless"],
browsers: ["ChromeHeadlessNoSandbox"],
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: "ChromeHeadless",
flags: ["--no-sandbox"]
}
},

// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
Expand Down
5 changes: 0 additions & 5 deletions packages/stream-collector-browser/jest.config.js

This file was deleted.

26 changes: 26 additions & 0 deletions packages/stream-collector-browser/karma.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
process.env.CHROME_BIN = require("puppeteer").executablePath();
module.exports = function(config) {
config.set({
frameworks: ["jasmine", "karma-typescript"],
files: ["src/**/*.ts"],
exclude: ["**/*.d.ts"],
preprocessors: {
"**/*.ts": "karma-typescript"
},
reporters: ["progress", "karma-typescript"],
browsers: ["ChromeHeadlessNoSandbox"],
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: "ChromeHeadless",
flags: ["--no-sandbox"]
}
},
karmaTypescriptConfig: {
tsconfig: "./tsconfig.json",
bundlerOptions: {
addNodeGlobals: false
}
},
singleRun: true
});
};
12 changes: 9 additions & 3 deletions packages/stream-collector-browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"description": "Provides a way to store the contents of a stream into a Uint8Array",
"scripts": {
"prepublishOnly": "tsc",
"pretest": "tsc -p tsconfig.test.json",
"test": "jest --coverage"
"pretest": "tsc -p tsconfig.json",
"test": "karma start karma.conf.js"
},
"author": {
"name": "AWS SDK for JavaScript Team",
Expand All @@ -20,7 +20,13 @@
},
"devDependencies": {
"@types/jest": "^24.0.12",
"jest": "^24.7.1",
"jasmine-core": "^3.5.0",
"karma": "^4.4.1",
"karma-chrome-launcher": "^3.1.0",
"karma-jasmine": "^2.0.1",
"karma-typescript": "^4.1.1",
"puppeteer": "^2.0.0",
"source-map": "^0.7.3",
"typescript": "~3.4.0"
}
}
56 changes: 15 additions & 41 deletions packages/stream-collector-browser/src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,22 @@ import { streamCollector } from "./index";
declare const global: any;

describe("streamCollector", () => {
const ambientFileReader =
typeof FileReader !== "undefined" ? FileReader : undefined;

beforeEach(() => {
const mockFileReader: FileReader = {
readAsArrayBuffer: jest.fn()
} as any;
global.FileReader = function() {
return mockFileReader;
} as any;
});

afterEach(() => {
global.FileReader = ambientFileReader as any;
});

it("returns a Uint8Array from a blob", async () => {
const dataPromise = streamCollector(new Blob());

const reader = new FileReader();
(reader as any).result = Uint8Array.from([0xde, 0xad]).buffer;
reader.onload!({} as any);

expect(await dataPromise).toEqual(Uint8Array.from([0xde, 0xad]));
it("returns a Uint8Array from a blob", done => {
const dataPromise = streamCollector(
new Response(new Uint8Array([102, 111, 111]).buffer).body
);
dataPromise.then((data: any) => {
expect(data).toEqual(Uint8Array.from([102, 111, 111]));
done();
});
});

it("propagates errors encountered by the file reader", async () => {
const dataPromise = streamCollector(new Blob());

const reader = new FileReader();
(reader as any).error = new Error("PANIC");
reader.onerror!({} as any);

await expect(dataPromise).rejects.toMatchObject(new Error("PANIC"));
});

it("rejects the promise when the read is aborted", async () => {
const dataPromise = streamCollector(new Blob());

const reader = new FileReader();
reader.onabort!({} as any);

await expect(dataPromise).rejects.toMatchObject(new Error("Read aborted"));
it("returns a Uint8Array when stream is empty", done => {
const expected = new Uint8Array(0);
const dataPromise = streamCollector(new Response(expected.buffer).body);
dataPromise.then((data: any) => {
expect(data).toEqual(expected);
done();
});
});
});
15 changes: 6 additions & 9 deletions packages/stream-collector-browser/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { StreamCollector } from "@aws-sdk/types";

export const streamCollector: StreamCollector = (
stream: Blob
): Promise<Uint8Array> =>
new Promise<Uint8Array>((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(new Uint8Array(reader.result as ArrayBuffer));
reader.onabort = () => reject(new Error("Read aborted"));
reader.onerror = () => reject(reader.error);
reader.readAsArrayBuffer(stream);
});
stream: ReadableStream
): Promise<Uint8Array> => {
return new Response(stream)
.arrayBuffer()
.then(arrayBuffer => new Uint8Array(arrayBuffer));
};
11 changes: 0 additions & 11 deletions packages/stream-collector-browser/tsconfig.test.json

This file was deleted.

2 changes: 1 addition & 1 deletion packages/types/src/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export interface HashConstructor {
* will consume the stream, so only replayable streams should be provided to an
* implementation of this interface.
*/
export interface StreamHasher<StreamType> {
export interface StreamHasher<StreamType = any> {
(hashCtor: { new (): Hash }, stream: StreamType): Promise<Uint8Array>;
}

Expand Down

0 comments on commit 133430c

Please sign in to comment.