Skip to content

Commit 4935ca1

Browse files
committed
api-util
1 parent 578453c commit 4935ca1

File tree

6 files changed

+227
-11
lines changed

6 files changed

+227
-11
lines changed

.prettierrc.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"tabWidth": 2,
3+
"printWidth": 90,
4+
"singleQuote": true,
5+
"trailingComma": "none"
6+
}

index.d.ts

+31-9
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
21
/* @file ./src/log.js */
3-
42
interface LogggerOptions {
5-
level: number; /* 0 | 1 | 2 | 3 | 4 */
3+
level: number /* 0 | 1 | 2 | 3 | 4 */;
64
time: 'iso' | 'ru' | 'en';
75
}
86
interface Loggger {
@@ -17,30 +15,54 @@ interface Loggger {
1715

1816
export declare var log: Loggger;
1917

20-
2118
/* @file ./src/safe.js */
2219
interface SafeOps {
2320
bit(value: number, bit: number): boolean;
2421

25-
get<TObject extends object, TKey extends keyof TObject>(object: TObject, path: TKey): TObject[TKey];
22+
get<TObject extends object, TKey extends keyof TObject>(
23+
object: TObject,
24+
path: TKey
25+
): TObject[TKey];
2626
set<TObject extends object, T>(object: TObject, path: string, value: T): T;
2727
unset<TObject extends object>(object: TObject, path: string): void;
2828
}
2929
export declare var safe: SafeOps;
3030

31-
3231
/* @file ./src/traverse.js */
3332
interface Traverse<T> {
3433
each(pred: (node: T, parent: T) => any): void;
3534
find(pred: (node: T) => boolean): T;
3635
filter(pred: (node: T) => boolean): T[];
3736
}
38-
export declare function traverse<T>(tree: T & { children: T[] }): Traverse<T & { children: T[] }>;
37+
export declare function traverse<T>(
38+
tree: T & { children: T[] }
39+
): Traverse<T & { children: T[] }>;
3940

4041
/* @file ./src/template.js */
4142
export declare function template<T>(template: string, context: T): any;
4243

43-
4444
/* @file ./src/filter.js */
4545
export declare function unique<T>(array: T[] = []): T[];
46-
export declare function only<T, K extends keyof T>(object: T, keys: K | K[] = []): Pick<T, K>;
46+
export declare function only<T, K extends keyof T>(
47+
object: T,
48+
keys: K | K[] = []
49+
): Pick<T, K>;
50+
51+
/* @file ./src/api.js */
52+
53+
interface ApiClientOpts {
54+
url?: string;
55+
token?: string;
56+
}
57+
class ApiClient {
58+
constructor(opts: ApiClientOpts): ApiClient;
59+
60+
with(opts: ApiClientOpts): ApiClient;
61+
62+
get<T, Q = { [k: string]: any }>(path: string, query: Q): Promise<T>;
63+
post<T>(path: string, data: Partial<T>): Promise<T>;
64+
patch<T>(path: string, data: Partial<T>): Promise<T>;
65+
delete<T>(path: string): Promise<T>;
66+
}
67+
68+
export declare var api: ApiClient;

index.js

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ module.exports = Object.assign({}, {
2525
traverse: require('./src/traverse'),
2626
template: require('./src/template'),
2727
log: require('./src/log'),
28+
api: require('./src/api'),
2829
unique,
2930
only
3031
});

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rightech/utils",
3-
"version": "0.1.15",
3+
"version": "1.1.15",
44
"description": "",
55
"main": "index.js",
66
"private": false,

src/api.js

+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
const { resolve, parse } = require('url');
2+
const { join } = require('path');
3+
4+
const http = require('http');
5+
const https = require('https');
6+
7+
const { unique } = require('./filter');
8+
9+
class ApiError extends Error {
10+
constructor(message) {
11+
super(message);
12+
13+
this.jti = '';
14+
this.url = '';
15+
this.tags = [];
16+
this.code = 500;
17+
18+
this.name = this.constructor.name;
19+
if (typeof Error.captureStackTrace === 'function') {
20+
Error.captureStackTrace(this, this.constructor);
21+
} else {
22+
this.stack = new Error(message).stack;
23+
}
24+
this.trySetTokenId();
25+
}
26+
27+
trySetTokenId() {
28+
const [, b64] = (getToken() || '').split('.');
29+
if (b64) {
30+
try {
31+
const payload = Buffer.from(b64, 'base64').toString();
32+
this.jti = JSON.parse(payload).jti || '';
33+
} catch (err) {}
34+
}
35+
}
36+
37+
withTags(tags = []) {
38+
this.tags = unique([...this.tags, ...(tags || [])]);
39+
return this;
40+
}
41+
42+
withCode(code) {
43+
this.code = +code;
44+
return this;
45+
}
46+
47+
withUrl(url) {
48+
this.url = url;
49+
return this;
50+
}
51+
}
52+
53+
class NginxError extends ApiError {
54+
constructor() {
55+
super(...arguments);
56+
this.title = '';
57+
}
58+
59+
withTitle(title = '') {
60+
this.title = title;
61+
return this;
62+
}
63+
64+
static fromHtml(resp = '', statusCode = 500) {
65+
let [, title = 'Unknown nginx errror'] = /<title>(.*?)<\/title>/gi.exec(resp) || [];
66+
title = title.replace(statusCode.toString(), '').trim();
67+
return new NginxError(http.STATUS_CODES[statusCode])
68+
.withCode(statusCode)
69+
.withTitle(title);
70+
}
71+
}
72+
73+
function req(url, opts = {}, data = null) {
74+
const { protocol, hostname, port, path } = parse(url);
75+
console.log({ protocol, hostname, port, path })
76+
77+
const proto = protocol === 'https:' ? https : http;
78+
79+
opts = {
80+
method: 'GET',
81+
host: hostname,
82+
port: +port,
83+
path,
84+
...opts
85+
};
86+
87+
if (!port) {
88+
opts.port = protocol === 'https:' ? 443 : 80;
89+
}
90+
91+
return new Promise((resolve, reject) => {
92+
const req = proto.request(opts, (res) => {
93+
let resp = '';
94+
res.on('data', (chunk) => (resp += chunk.toString()));
95+
res.on('end', () => {
96+
try {
97+
/*
98+
* most nginx upstream errors should be handled by ingress default-backend
99+
* but who knows ...
100+
*/
101+
if (resp.startsWith('<html>') && resp.includes('nginx')) {
102+
return reject(NginxError.fromHtml(resp, res.statusCode).withUrl(url));
103+
}
104+
const json = JSON.parse(resp);
105+
if (res.statusCode >= 400) {
106+
return reject(
107+
new ApiError(json.message)
108+
.withCode(res.statusCode)
109+
.withTags(json.tags)
110+
.withUrl(url)
111+
);
112+
}
113+
resolve(json);
114+
} catch (err) {
115+
console.log(resp);
116+
reject(err);
117+
}
118+
});
119+
});
120+
121+
req.on('error', (err) => reject(err));
122+
123+
if (data) {
124+
let send = data;
125+
if (typeof data === 'object') {
126+
send = JSON.stringify(data);
127+
}
128+
req.write(send);
129+
}
130+
req.end();
131+
});
132+
}
133+
134+
class ApiClient {
135+
constructor(opts) {
136+
const { url, token } = opts || {};
137+
138+
this._opts = opts;
139+
this.url = url;
140+
this.token = token;
141+
}
142+
143+
getDefaultHeaders() {
144+
const defaults = {
145+
accept: 'application/json',
146+
'content-type': 'application/json',
147+
'user-agent': `npm:@rightech/utils 1.1`
148+
};
149+
if (this.token) {
150+
defaults.authorization = `Bearer ${this.token}`;
151+
}
152+
return defaults;
153+
}
154+
155+
get(path, query = {}) {
156+
const url = resolve(this.url, path);
157+
const headers = this.getDefaultHeaders();
158+
return req(url, { method: 'GET', headers });
159+
}
160+
161+
post(path, data = {}) {
162+
const url = resolve(this.url, path);
163+
const headers = this.getDefaultHeaders();
164+
return req(url, { method: 'POST', headers }, data);
165+
}
166+
167+
patch(path, data = {}) {
168+
const url = resolve(this.url, path);
169+
const headers = this.getDefaultHeaders();
170+
return req(url, { method: 'PATCH', headers }, data);
171+
}
172+
173+
delete(path) {
174+
const url = resolve(this.url, path);
175+
const headers = this.getDefaultHeaders();
176+
return req(url, { method: 'DELETE', headers });
177+
}
178+
179+
with(opts = {}) {
180+
return new ApiClient({ ...(this._opts || {}), ...opts });
181+
}
182+
}
183+
184+
module.exports = new ApiClient({
185+
url: process.env['RIC_URL'] || 'https://dev.rightech.io/',
186+
token: process.env['RIC_TOKEN']
187+
});

0 commit comments

Comments
 (0)