-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
/
Copy pathbase.js
157 lines (137 loc) · 4.39 KB
/
base.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
'use strict';
const {
makeLogo,
toArray,
makeColor,
setBadgeColor,
} = require('../lib/badge-data');
module.exports = class BaseService {
constructor({sendAndCacheRequest}) {
this._sendAndCacheRequest = sendAndCacheRequest;
}
/**
* Asynchronous function to handle requests for this service. Takes the URI
* parameters (as defined in the `uri` property), performs a request using
* `this._sendAndCacheRequest`, and returns the badge data.
*/
async handle(namedParams) {
throw new Error(
`Handler not implemented for ${this.constructor.name}`
);
}
// Metadata
/**
* Name of the category to sort this badge into (eg. "build"). Used to sort
* the badges on the main shields.io website.
*/
static get category() {
return 'unknown';
}
/**
* Returns an object with two fields:
* - format: Regular expression to use for URIs for this service's badges
* - capture: Array of names for the capture groups in the regular
* expression. The handler will be passed an object containing
* the matches.
*/
static get uri() {
throw new Error(`URI not defined for ${this.name}`);
}
/**
* Default data for the badge. Can include things such as default logo, color,
* etc. These defaults will be used if the value is not explicitly overridden
* by either the handler or by the user via URL parameters.
*/
static get defaultBadgeData() {
return {};
}
/**
* Example URIs for this service. These should use the format
* specified in `uri`, and can be used to demonstrate how to use badges for
* this service.
*/
static getExamples() {
return [];
}
static get _regex() {
// Regular expressions treat "/" specially, so we need to escape them
const escapedPath = this.uri.format.replace(/\//g, '\\/');
const fullRegex = '^' + escapedPath + '.(svg|png|gif|jpg|json)$';
return new RegExp(fullRegex);
}
static _namedParamsForMatch(match) {
// Assume the last match is the format, and drop match[0], which is the
// entire match.
const captures = match.slice(1, -1);
if (this.uri.capture.length !== captures.length) {
throw new Error(
`Service ${this.constructor.name} declares incorrect number of capture groups `+
`(expected ${this.uri.capture.length}, got ${captures.length})`
);
}
const result = {};
this.uri.capture.forEach((name, index) => {
result[name] = captures[index];
});
return result;
}
async invokeHandler(namedParams) {
try {
return await this.handle(namedParams);
} catch (error) {
console.log(error);
return { message: 'error' };
}
}
static _makeBadgeData(overrides, serviceData) {
const {
style,
label: overrideLabel,
logo: overrideLogo,
logoWidth: overrideLogoWidth,
link: overrideLink,
colorA: overrideColorA,
colorB: overrideColorB,
} = overrides;
const {
label: serviceLabel,
message: serviceMessage,
color: serviceColor,
link: serviceLink,
} = serviceData;
const defaultLabel = this.category;
const {
color: defaultColor,
logo: defaultLogo,
} = this.defaultBadgeData;
const badgeData = {
text: [
overrideLabel || serviceLabel || defaultLabel,
serviceMessage || 'n/a',
],
template: style,
logo: makeLogo(style === 'social' ? defaultLogo : undefined, { logo: overrideLogo }),
logoWidth: +overrideLogoWidth,
links: toArray(overrideLink || serviceLink),
colorA: makeColor(overrideColorA),
};
const color = overrideColorB || serviceColor || defaultColor || 'lightgrey';
setBadgeColor(badgeData, color);
return badgeData;
}
static register(camp, handleRequest) {
const ServiceClass = this; // In a static context, "this" is the class.
camp.route(this._regex,
handleRequest(async (queryParams, match, sendBadge, request) => {
const namedParams = this._namedParamsForMatch(match);
const serviceInstance = new ServiceClass({
sendAndCacheRequest: request.asPromise,
});
const serviceData = await serviceInstance.invokeHandler(namedParams);
const badgeData = this._makeBadgeData(queryParams, serviceData);
// Assumes the final capture group is the extension
const format = match.slice(-1)[0];
sendBadge(format, badgeData);
}));
}
};