Skip to content

Commit e16e076

Browse files
authored
Submission URL parser (#34)
* URL parsers * Code review suggestions * Nullable token
1 parent 2dccdb2 commit e16e076

File tree

7 files changed

+228
-16
lines changed

7 files changed

+228
-16
lines changed

packages/sdk-core/src/model/http/BacktraceReportSubmission.ts

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,15 @@ import { BacktraceAttachment } from '../attachment';
22
import { BacktraceConfiguration } from '../configuration/BacktraceConfiguration';
33
import { BacktraceData } from '../data/BacktraceData';
44
import { BacktraceRequestHandler } from './BacktraceRequestHandler';
5+
import { SubmissionUrlInformation } from './SubmissionUrlInformation';
56

67
export class BacktraceReportSubmission {
78
private readonly _submissionUrl: string;
89
constructor(options: BacktraceConfiguration, private readonly _requestHandler: BacktraceRequestHandler) {
9-
this._submissionUrl = this.generateReportSubmissionUrl(options.url, options.token);
10+
this._submissionUrl = SubmissionUrlInformation.toJsonReportSubmissionUrl(options.url, options.token);
1011
}
1112

1213
public send(data: BacktraceData, attachments: BacktraceAttachment[]) {
1314
return this._requestHandler.postError(this._submissionUrl, data, attachments);
1415
}
15-
16-
private generateReportSubmissionUrl(url: string, token?: string) {
17-
// if the token doesn't exist - use URL
18-
if (!token) {
19-
return url;
20-
}
21-
22-
// if the URL has token in the URL, the user probably added a token once again
23-
// in this case, don't do anything
24-
if (url.indexOf(token) !== -1) {
25-
return url;
26-
}
27-
28-
return new URL(`/post?format=json&token=${token}`, url).href;
29-
}
3016
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
export class SubmissionUrlInformation {
2+
private static SUBMIT_PREFIX = 'submit.backtrace.io/';
3+
4+
/**
5+
* Convert url/token from credentials to JSON submission URL
6+
* @param url credentials URL
7+
* @param token credentials token
8+
* @returns JSON submissionURL
9+
*/
10+
public static toJsonReportSubmissionUrl(url: string, token?: string): string {
11+
// if the token doesn't exist - use URL
12+
if (!token) {
13+
return url;
14+
}
15+
16+
// if the url points to submit, we should always use it without any modifications
17+
if (url.includes(this.SUBMIT_PREFIX)) {
18+
return url;
19+
}
20+
21+
// if the URL has token in the URL, the user probably added a token once again
22+
// in this case, don't do anything
23+
if (url.indexOf(token) !== -1) {
24+
return url;
25+
}
26+
27+
const result = new URL(`/post`, url);
28+
result.searchParams.append('format', 'json');
29+
result.searchParams.append('token', token);
30+
return result.href;
31+
}
32+
33+
/**
34+
* Find the universe based on the submission URL
35+
* @param submissionUrl submission URL
36+
* @returns universe name
37+
*/
38+
public static findUniverse(submissionUrl: string): string | undefined {
39+
const submitIndex = submissionUrl.indexOf(this.SUBMIT_PREFIX);
40+
if (submitIndex !== -1) {
41+
// submit format URL
42+
// submit.backtrace.io/universe/token/format
43+
// we can expect the universe name just after the hostname
44+
const universeStartIndex = submitIndex + this.SUBMIT_PREFIX.length;
45+
const endOfUniverseName = submissionUrl.indexOf('/', universeStartIndex);
46+
return submissionUrl.substring(universeStartIndex, endOfUniverseName);
47+
}
48+
// the universe name should be available in the hostname
49+
// for example abc.sp.backtrace.io or zyx.in.backtrace.io or foo.backtrace.io
50+
const hostname = new URL(submissionUrl).hostname;
51+
if (!hostname.endsWith('backtrace.io')) {
52+
return undefined;
53+
}
54+
55+
const endOfUniverseName = hostname.indexOf('.');
56+
return hostname.substring(0, endOfUniverseName);
57+
}
58+
59+
public static findToken(submissionUrl: string): string | null {
60+
const submitIndex = submissionUrl.indexOf(this.SUBMIT_PREFIX);
61+
if (submitIndex !== -1) {
62+
const submissionUrlParts = submissionUrl.split('/');
63+
// submit format URL
64+
// submit.backtrace.io/universe/token/format
65+
// by spliting the submission URL by `/` and dropping the last
66+
// part of the URL, the last element on the list is the token.
67+
return submissionUrlParts[submissionUrlParts.length - 2];
68+
}
69+
70+
const url = new URL(submissionUrl);
71+
72+
return url.searchParams.get('token');
73+
}
74+
}

packages/sdk-core/src/model/http/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './common/ConnectionError';
33
export * from './model/BacktraceSubmissionResponse';
44
export * from './model/BacktraceSubmissionResult';
55
export * from './model/BacktraceSubmissionStatus';
6+
export * from './SubmissionUrlInformation';
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { SubmissionUrlInformation } from '../../model/http';
2+
3+
export class MetricsUrlInformation {
4+
public static generateSummedEventsUrl(
5+
hostname: string,
6+
submissionUrl: string,
7+
credentialsToken: string | null,
8+
): string | undefined {
9+
const submissionInformation = this.findSubmissionInformation(submissionUrl, credentialsToken);
10+
if (!submissionInformation) {
11+
return undefined;
12+
}
13+
return this.generateEventsServiceUrl(
14+
hostname,
15+
'summed-events',
16+
submissionInformation.universe,
17+
submissionInformation.token,
18+
);
19+
}
20+
21+
public static generateUniqueEventsUrl(
22+
hostname: string,
23+
submissionUrl: string,
24+
credentialsToken: string | null,
25+
): string | undefined {
26+
const submissionInformation = this.findSubmissionInformation(submissionUrl, credentialsToken);
27+
if (!submissionInformation) {
28+
return undefined;
29+
}
30+
31+
return this.generateEventsServiceUrl(
32+
hostname,
33+
'unique-events',
34+
submissionInformation.universe,
35+
submissionInformation.token,
36+
);
37+
}
38+
39+
private static generateEventsServiceUrl(
40+
hostname: string,
41+
eventServiceName: string,
42+
universe: string,
43+
token: string,
44+
): string {
45+
return new URL(`/api/${eventServiceName}/submit?universe=${universe}&token=${token}`, hostname).toString();
46+
}
47+
48+
private static findSubmissionInformation(
49+
submissionUrl: string,
50+
token: string | null,
51+
): { universe: string; token: string } | undefined {
52+
const universe = SubmissionUrlInformation.findUniverse(submissionUrl);
53+
if (!universe) {
54+
return undefined;
55+
}
56+
57+
token = token ?? SubmissionUrlInformation.findToken(submissionUrl);
58+
59+
if (!token) {
60+
return undefined;
61+
}
62+
return { universe, token };
63+
}
64+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { SubmissionUrlInformation } from '../../src/model/http';
2+
describe('Submission Url generation tests', () => {
3+
describe('Submit', () => {
4+
const sampleSubmitUrl = `https://submit.backtrace.io/name/000000000000a1eb7ae344f6e002de2e20c81fbdedf6991c2f3bb45b11111111/json`;
5+
it('Should use submit url from the configuration options', () => {
6+
expect(SubmissionUrlInformation.toJsonReportSubmissionUrl(sampleSubmitUrl)).toBe(sampleSubmitUrl);
7+
});
8+
9+
it(`Shouldnt mix token with the submission url`, () => {
10+
expect(SubmissionUrlInformation.toJsonReportSubmissionUrl(sampleSubmitUrl, '123')).toBe(sampleSubmitUrl);
11+
});
12+
});
13+
14+
describe('Direct URL', () => {
15+
const hostname = `https://instance.sp.backtrace.io`;
16+
const token = `000000000000a1eb7ae344f6e002de2e20c81fbdedf6991c2f3bb45b11111111`;
17+
const fullUrl = `${hostname}/post?format=json&token=${token}`;
18+
it('Should use the direct url if the token is not available', () => {
19+
expect(SubmissionUrlInformation.toJsonReportSubmissionUrl(fullUrl)).toBe(fullUrl);
20+
});
21+
22+
it(`Shouldn't mix token with the submission url if the token is already there`, () => {
23+
expect(SubmissionUrlInformation.toJsonReportSubmissionUrl(fullUrl, token)).toBe(fullUrl);
24+
});
25+
26+
it(`Should generate a full url if the token and instance are passed separated`, () => {
27+
expect(SubmissionUrlInformation.toJsonReportSubmissionUrl(hostname, token)).toBe(fullUrl);
28+
});
29+
30+
it(`Should override the token in the submission url`, () => {
31+
const testedToken = '111111110000000000001111111100000000000020c81fbdedf6991c2f3bb45b';
32+
const expectedUrl = `${hostname}/post?format=json&token=${testedToken}`;
33+
34+
expect(SubmissionUrlInformation.toJsonReportSubmissionUrl(fullUrl, testedToken)).toBe(expectedUrl);
35+
});
36+
});
37+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { SubmissionUrlInformation } from '../../src/model/http';
2+
3+
describe('Token tests', () => {
4+
const testedToken = '000000000000a1eb7ae344f6e002de2e20c81fbdedf6991c2f3bb45b11111111';
5+
describe('Submit', () => {
6+
const sampleSubmitUrl = `https://submit.backtrace.io/test/${testedToken}/json`;
7+
8+
it('Should correctly find the universe name', () => {
9+
expect(SubmissionUrlInformation.findToken(sampleSubmitUrl)).toBe(testedToken);
10+
});
11+
});
12+
13+
describe('Direct', () => {
14+
it(`Should return null if the url doesn't contain the submission token`, () => {
15+
expect(SubmissionUrlInformation.findToken(`https://foo.sp.backtrace.io`)).toBeNull();
16+
});
17+
18+
it(`Should return token from the direct url`, () => {
19+
expect(
20+
SubmissionUrlInformation.findToken(`https://foo.sp.backtrace.io/post?format=json&token=${testedToken}`),
21+
).toBe(testedToken);
22+
});
23+
});
24+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { SubmissionUrlInformation } from '../../src/model/http';
2+
describe('Universe tests', () => {
3+
const testedUniverseName = 'foo-bar-baz';
4+
describe('Submit', () => {
5+
const sampleSubmitUrl = `https://submit.backtrace.io/${testedUniverseName}/000000000000a1eb7ae344f6e002de2e20c81fbdedf6991c2f3bb45b11111111/json`;
6+
7+
it('Should correctly find the universe name', () => {
8+
expect(SubmissionUrlInformation.findUniverse(sampleSubmitUrl)).toBe(testedUniverseName);
9+
});
10+
});
11+
12+
describe('Direct', () => {
13+
const testedBacktraceDomainPrefixes = ['', '.sp', '.in'];
14+
for (const backtracePrefix of testedBacktraceDomainPrefixes) {
15+
it(`Should correctly find the universe name with prefix ${backtracePrefix}`, () => {
16+
const sampleDirectUrl = `https://${testedUniverseName}${backtracePrefix}.backtrace.io`;
17+
expect(SubmissionUrlInformation.findUniverse(sampleDirectUrl)).toBe(testedUniverseName);
18+
});
19+
}
20+
21+
it('Should correctly find the universe in the direct url with the token', () => {
22+
const sampleDirectUrl = `https://${testedUniverseName}.sp.backtrace.io/post?format=json&token=000000000000a1eb7ae344f6e002de2e20c81fbdedf6991c2f3bb45b11111111`;
23+
expect(SubmissionUrlInformation.findUniverse(sampleDirectUrl)).toBe(testedUniverseName);
24+
});
25+
});
26+
});

0 commit comments

Comments
 (0)