forked from redhat-developer/vscode-openshift-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdevfileRegistryWrapper.ts
More file actions
191 lines (174 loc) · 7.75 KB
/
devfileRegistryWrapper.ts
File metadata and controls
191 lines (174 loc) · 7.75 KB
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/
import { get as httpGet } from 'http';
import { get as httpsGet } from 'https';
import * as YAML from 'js-yaml';
import { ExecutionContext } from '../cli';
import { Registry } from '../odo/componentType';
import { OdoPreference } from '../odo/odoPreference';
import { DevfileData, DevfileInfo } from './devfileInfo';
export const DEVFILE_VERSION_LATEST: string = 'latest';
/**
* Wraps some the Devfile Registry REST API calls.
*/
export class DevfileRegistry {
private static instance: DevfileRegistry;
private executionContext: ExecutionContext = new ExecutionContext();
public static get Instance(): DevfileRegistry {
if (!DevfileRegistry.instance) {
DevfileRegistry.instance = new DevfileRegistry();
}
return DevfileRegistry.instance;
}
private constructor() {
// no state
}
/**
* Get list of Devfile Infos from the specified Registry.
*
* GET http://{registry host}/v2index/all
*
* @param url Devfile Registry URL
* @param abortTimeout (Optional) If provided, allow cancelling the operation by timeout
* @param abortController (Optional) If provided, allows cancelling the operation by signal
*/
public async getDevfileInfoList(url: string, abortTimeout?: number, abortController?: AbortController): Promise<DevfileInfo[]> {
const requestUrl = `${url}/v2index/all`;
const key = ExecutionContext.key(requestUrl);
if (this.executionContext && this.executionContext.has(key)) {
return this.executionContext.get(key);
}
const rawList = await DevfileRegistry._get(`${url}/v2index/all`, abortTimeout, abortController);
const jsonList = JSON.parse(rawList);
this.executionContext.set(key, jsonList);
return jsonList;
}
/**
* Get Devfile of specified version from Registry.
*
* GET http://{registry host}/devfiles/{stack}/{version}
*
* @param url Devfile Registry URL
* @param stack Devfile stack
* @param version (Optional) If specified, the version of Devfile to be received, otherwize 'latest' version is requested
* @param abortTimeout (Optional) If provided, allow cancelling the operation by timeout
* @param abortController (Optional) If provided, allows cancelling the operation by signal
*/
private async _getDevfile(url: string, stack: string, version?: string, abortTimeout?: number, abortController?: AbortController): Promise<string> {
const requestUrl = `${url}/devfiles/${stack}/${version ? version : DEVFILE_VERSION_LATEST}`;
const key = ExecutionContext.key(requestUrl);
if (this.executionContext && this.executionContext.has(key)) {
return this.executionContext.get(key);
}
const devfile = DevfileRegistry._get(`${url}/devfiles/${stack}/${version ? version : DEVFILE_VERSION_LATEST}`,
abortTimeout, abortController);
this.executionContext.set(key, devfile);
return devfile;
}
/**
* Returns a list of the devfile registries from ODO preferences.
*
* @returns a list of the devfile registries
*/
public async getRegistries(registryUrl?: string): Promise<Registry[]> {
// Return only registries registered for user (from ODO preferences)
// and filter by registryUrl (if provided)
let registries: Registry[] = [];
const key = ExecutionContext.key('getRegistries');
if (this.executionContext && !this.executionContext.has(key)) {
registries = await OdoPreference.Instance.getRegistries();
this.executionContext.set(key, registries);
} else {
registries = this.executionContext.get(key);
}
return !registries ? [] :
registries.filter((reg) => {
if (registryUrl) {
return (reg.url === registryUrl)
}
return true;
});
}
/**
* Returns a list of the devfile infos for the specified registry or all the
* registries, if not specified.
*
* @returns a list of the devfile infos
*/
public async getRegistryDevfileInfos(registryUrl?: string): Promise<DevfileInfo[]> {
const registries: Registry[] = await this.getRegistries(registryUrl);
if (!registries || registries.length === 0) {
// TODO: should throw 'new Error('No Devfile registries available. Default registry is missing');'
// here so we can report this to users when a webview is open
return [];
}
const devfiles: DevfileInfo[] = [];
await Promise.all(registries
.map(async (registry): Promise<void> => {
const devfileInfoList = (await this.getDevfileInfoList(registry.url))
.filter((devfileInfo) => 'stack' === devfileInfo.type.toLowerCase());
devfileInfoList.forEach((devfileInfo) => {
devfileInfo.registry = registry;
});
devfiles.push(...devfileInfoList);
}));
return devfiles.sort((a, b) => (a.name < b.name ? -1 : 1));
}
/**
* Returns a devfile data with the raw devfile text attached
*
* @returns a devfile data with raw devfile text attached
*/
public async getRegistryDevfile(registryUrl: string, name: string, version?: string): Promise<DevfileData> {
const rawDevfile = await this._getDevfile(registryUrl, name, version ? version : 'latest');
const devfile = YAML.load(rawDevfile) as DevfileData;
devfile.yaml = rawDevfile;
return devfile;
}
private static async _get(url: string, abortTimeout?: number, abortController?: AbortController): Promise<string> {
return new Promise<string>((resolve, reject) => {
let request = httpGet;
try {
request = new URL(url).protocol.startsWith('https') ? httpsGet : httpGet;
} catch (err) {
// continue
}
const signal = abortController?.signal;
const timeout = abortTimeout ? abortTimeout : 5000;
const options = { rejectUnauthorized: false, signal, timeout };
let result: string = '';
request(url, options, (response) => {
if (response.statusCode < 500) {
response.on('data', (d) => {
result = result.concat(d);
});
response.resume();
response.on('end', () => {
if (!response.complete) {
reject(new Error(`The connection was terminated while the message was still being sent: ${response.statusMessage}`));
} else {
resolve(result);
}
});
} else {
reject(new Error(`Connect error: ${response.statusMessage}`));
}
}).on('error', (e) => {
reject(new Error(`Connect error: ${e}`));
}).on('success', (s) => {
resolve(result);
});
});
}
/**
* Clears the Execution context as well as all cached data
*/
public clearCache() {
if (this.executionContext) {
this.executionContext.clear();
}
this.executionContext = new ExecutionContext();
}
}