Skip to content

Commit 445d8ae

Browse files
Merge pull request #22 from apivideo/add-origin-headers
feat: add origin headers
2 parents 9049e97 + c2604cf commit 445d8ae

File tree

7 files changed

+114
-12
lines changed

7 files changed

+114
-12
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Changelog
22
All changes to this project will be documented in this file.
33

4+
## [1.0.11] - 2022-07-06
5+
- Add origin headers
6+
-
47
## [1.0.10] - 2022-06-29
58
- Retry even if the server is not responding.
69
- Add possibility to define a custom retry policy.

dist/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/src/abstract-uploader.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,18 @@ export declare type VideoUploadResponse = {
3030
};
3131
};
3232
declare type RetryStrategy = (retryCount: number, error: VideoUploadError) => number | null;
33+
interface Origin {
34+
name: string;
35+
version: string;
36+
}
3337
export interface CommonOptions {
3438
apiHost?: string;
3539
retries?: number;
3640
retryStrategy?: RetryStrategy;
41+
origin?: {
42+
application?: Origin;
43+
sdk?: Origin;
44+
};
3745
}
3846
export interface WithUploadToken {
3947
uploadToken: string;
@@ -85,5 +93,6 @@ export declare abstract class AbstractUploader<T> {
8593
doRefreshToken(): Promise<void>;
8694
private createXhrPromise;
8795
private withRetrier;
96+
private static validateOrigin;
8897
}
8998
export {};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@api.video/video-uploader",
3-
"version": "1.0.10",
3+
"version": "1.0.11",
44
"description": "api.video video uploader",
55
"repository": {
66
"type": "git",

src/abstract-uploader.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,19 @@ export declare type VideoUploadResponse = {
3333

3434
type RetryStrategy = (retryCount: number, error: VideoUploadError) => number | null;
3535

36+
interface Origin {
37+
name: string;
38+
version: string;
39+
}
40+
3641
export interface CommonOptions {
3742
apiHost?: string;
3843
retries?: number;
3944
retryStrategy?: RetryStrategy;
45+
origin?: {
46+
application?: Origin;
47+
sdk?: Origin;
48+
};
4049
}
4150

4251
export interface WithUploadToken {
@@ -130,6 +139,17 @@ export abstract class AbstractUploader<T> {
130139
this.headers["AV-Origin-Client"] = "typescript-uploader:" + PACKAGE_VERSION;
131140
this.retries = options.retries || DEFAULT_RETRIES;
132141
this.retryStrategy = options.retryStrategy || DEFAULT_RETRY_STRATEGY(this.retries);
142+
143+
if (options.origin) {
144+
if (options.origin.application) {
145+
AbstractUploader.validateOrigin("application", options.origin.application);
146+
this.headers["AV-Origin-App"] = `${options.origin.application.name}:${options.origin.application.version}`;
147+
}
148+
if (options.origin.sdk) {
149+
AbstractUploader.validateOrigin("sdk", options.origin.sdk);
150+
this.headers["AV-Origin-Sdk"] = `${options.origin.sdk.name}:${options.origin.sdk.version}`;
151+
}
152+
}
133153
}
134154

135155
public onProgress(cb: (e: T) => void) {
@@ -195,7 +215,9 @@ export abstract class AbstractUploader<T> {
195215
return new Promise((resolve, reject) => {
196216
const xhr = new window.XMLHttpRequest();
197217
xhr.open("POST", `https://${this.apiHost}/auth/refresh`);
198-
218+
for (const headerName of Object.keys(this.headers)) {
219+
if(headerName !== "Authorization") xhr.setRequestHeader(headerName, this.headers[headerName]);
220+
}
199221
xhr.onreadystatechange = (_) => {
200222
if (xhr.readyState === 4 && xhr.status >= 400) {
201223
reject(this.parseErrorResponse(xhr));
@@ -286,4 +308,23 @@ export abstract class AbstractUploader<T> {
286308
}
287309
});
288310
}
311+
312+
private static validateOrigin(type: string, origin: Origin) {
313+
if (!origin.name) {
314+
throw new Error(`${type} name is required`);
315+
}
316+
if (!origin.version) {
317+
throw new Error(`${type} version is required`);
318+
}
319+
if (!/^[\w-]{1,50}$/.test(origin.name)) {
320+
throw new Error(
321+
`Invalid ${type} name value. Allowed characters: A-Z, a-z, 0-9, '-', '_'. Max length: 50.`
322+
);
323+
}
324+
if (!/^\d{1,3}(\.\d{1,3}(\.\d{1,3})?)?$/.test(origin.version)) {
325+
throw new Error(
326+
`Invalid ${type} version value. The version should match the xxx[.yyy][.zzz] pattern.`
327+
);
328+
}
329+
}
289330
}

test/abstract-uploader.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { expect } from 'chai';
2-
import { ProgressiveUploader, ProgressiveUploadProgressEvent } from '../src/index';
3-
import mock from 'xhr-mock';
4-
import { DEFAULT_RETRY_STRATEGY } from '../src/abstract-uploader';
2+
import { AbstractUploader, DEFAULT_RETRY_STRATEGY } from '../src/abstract-uploader';
53

64
describe('Default retrier', () => {
75
const retrier = DEFAULT_RETRY_STRATEGY(10);
@@ -16,4 +14,16 @@ describe('Default retrier', () => {
1614
expect(retrier(8, { status: 502, raw: "" })).to.be.equal(144200);
1715
expect(retrier(8, { raw: "" })).to.be.equal(144200);
1816
});
17+
});
18+
19+
describe('Origin header validation', () => {
20+
const validateOrigin = (AbstractUploader as any).validateOrigin as (name: string, origin: { name?: string, version?: string }) => void;
21+
it('should properly validate name', () => {
22+
expect(() => validateOrigin("test", { version: "aa" })).to.throw("test name is required");
23+
expect(() => validateOrigin("test", { name:"frf ds", version: "aa" })).to.throw("Invalid test name value. Allowed characters: A-Z, a-z, 0-9, '-', '_'. Max length: 50.");
24+
});
25+
it('should properly validate version', () => {
26+
expect(() => validateOrigin("test", { name: "aa" })).to.throw("test version is required");
27+
expect(() => validateOrigin("test", { name:"name", version: "a b" })).to.throw("Invalid test version value. The version should match the xxx[.yyy][.zzz] pattern.");
28+
});
1929
});

test/video-uploader.test.ts

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe('Instanciation', () => {
2020
expect(() => new VideoUploader({
2121
uploadToken: "aa",
2222
file: new File([""], ""),
23-
chunkSize: 1024*1024*1
23+
chunkSize: 1024 * 1024 * 1
2424
})).to.throw("Invalid chunk size. Minimal allowed value: 5MB, maximum allowed value: 128MB.");
2525
});
2626
});
@@ -35,7 +35,7 @@ describe('Content-range', () => {
3535
const uploader = new VideoUploader({
3636
file: new File([new ArrayBuffer(17000000)], "filename"),
3737
uploadToken,
38-
chunkSize: 5*1024*1024,
38+
chunkSize: 5 * 1024 * 1024,
3939
});
4040

4141
const expectedRanges = [
@@ -86,6 +86,45 @@ describe('Access token auth', () => {
8686
});
8787

8888

89+
describe('Origin headers', () => {
90+
beforeEach(() => mock.setup());
91+
afterEach(() => mock.teardown());
92+
93+
it('token value is correct', (done) => {
94+
const accessToken = "1234";
95+
const videoId = "9876";
96+
97+
const uploader = new VideoUploader({
98+
file: new File([new ArrayBuffer(200)], "filename"),
99+
accessToken,
100+
videoId,
101+
origin: {
102+
application: {
103+
name: "application-name",
104+
version: "1.0.0"
105+
},
106+
sdk: {
107+
name: "sdk-name",
108+
version: "2.0.0"
109+
}
110+
}
111+
});
112+
113+
mock.post(`https://ws.api.video/videos/${videoId}/source`, (req, res) => {
114+
expect(req.header("av-origin-app")).to.be.eq("application-name:1.0.0");
115+
expect(req.header("av-origin-sdk")).to.be.eq(`sdk-name:2.0.0`);
116+
return res.status(201).body("{}");
117+
});
118+
119+
uploader.upload().then(() => {
120+
done();
121+
});
122+
123+
});
124+
});
125+
126+
127+
89128
describe('Refresh token', () => {
90129
beforeEach(() => mock.setup());
91130
afterEach(() => mock.teardown());
@@ -110,7 +149,7 @@ describe('Refresh token', () => {
110149
sourceCalls++;
111150
expect(req.header("content-range")).to.be.eq("part 1/1");
112151

113-
if(sourceCalls === 1) {
152+
if (sourceCalls === 1) {
114153
expect(req.header("authorization")).to.be.eq(`Bearer ${accessToken1}`);
115154
return res.status(401).body("{}");
116155
}
@@ -172,7 +211,7 @@ describe('Progress listener', () => {
172211
file: new File([new ArrayBuffer(6000000)], "filename"),
173212
accessToken: "1234",
174213
videoId,
175-
chunkSize: 5*1024*1024
214+
chunkSize: 5 * 1024 * 1024
176215
});
177216

178217
mock.post(`https://ws.api.video/videos/${videoId}/source`, (req, res) => res.status(201).body("{}"));
@@ -184,7 +223,7 @@ describe('Progress listener', () => {
184223
...lastUploadProgressEvent,
185224
totalBytes: 6000000,
186225
chunksCount: 2,
187-
chunksBytes: 5*1024*1024,
226+
chunksBytes: 5 * 1024 * 1024,
188227
currentChunk: 2,
189228
});
190229
done();
@@ -228,7 +267,7 @@ describe('Errors & retries', () => {
228267
const uploader = new VideoUploader({
229268
file: new File([new ArrayBuffer(6000000)], "filename"),
230269
uploadToken,
231-
chunkSize: 5*1024*1024,
270+
chunkSize: 5 * 1024 * 1024,
232271
retryStrategy: (retryCount, error) => retryCount > 3 ? null : 10,
233272
});
234273

0 commit comments

Comments
 (0)