caprover-api 0.0.20 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/ApiManager.d.ts +5 -3
- package/dist/api/ApiManager.js +16 -3
- package/dist/api/HttpClient.d.ts +6 -2
- package/dist/api/HttpClient.js +35 -0
- package/dist/models/AppDefinition.d.ts +7 -0
- package/package.json +1 -1
- package/src/api/ApiManager.ts +35 -9
- package/src/api/HttpClient.ts +52 -2
- package/src/models/AppDefinition.ts +31 -0
- package/test/ApiManager.test.js +103 -0
- package/test/HttpClient.test.js +15 -0
package/dist/api/ApiManager.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IAppDef } from '../models/AppDefinition';
|
|
1
|
+
import { IAppDef, IAppDefinitionPatch } from '../models/AppDefinition';
|
|
2
2
|
import AppDefinitionsResponse from '../models/AppDefinitionsResponse';
|
|
3
3
|
import AppDeleteResponse from '../models/AppDeleteResponse';
|
|
4
4
|
import { IAutomatedCleanupConfigs } from '../models/AutomatedCleanupConfigs';
|
|
@@ -75,11 +75,13 @@ export default class ApiManager {
|
|
|
75
75
|
getAllProjects(): Promise<ProjectsResponse>;
|
|
76
76
|
fetchBuildLogs(appName: string): Promise<BuildLogsResponse>;
|
|
77
77
|
fetchAppLogsInHex(appName: string): Promise<LogsResponse>;
|
|
78
|
-
|
|
78
|
+
fetchAppLogs(appName: string, encoding?: string): Promise<LogsResponse>;
|
|
79
|
+
uploadAppData(appName: string, file: File, detached?: boolean): Promise<void>;
|
|
79
80
|
registerProject(selectedProject: ProjectDefinition): Promise<ProjectDefinition>;
|
|
80
81
|
updateProject(project: ProjectDefinition): Promise<void>;
|
|
81
82
|
uploadCaptainDefinitionContent(appName: string, captainDefinition: ICaptainDefinition, gitHash: string, detached: boolean): Promise<void>;
|
|
82
83
|
updateConfigAndSave(appName: string, appDefinition: IAppDef): Promise<void>;
|
|
84
|
+
patchAppDefinition(appName: string, patch: IAppDefinitionPatch): Promise<void>;
|
|
83
85
|
renameApp(oldAppName: string, newAppName: string): Promise<void>;
|
|
84
86
|
registerNewApp(appName: string, projectId: string, hasPersistentData: boolean, detached: boolean): Promise<void>;
|
|
85
87
|
deleteApp(appName: string | undefined, volumes: string[], appNames: string[] | undefined): Promise<AppDeleteResponse>;
|
|
@@ -128,5 +130,5 @@ export default class ApiManager {
|
|
|
128
130
|
jobId: string;
|
|
129
131
|
}>;
|
|
130
132
|
getOneClickAppDeployProgress(jobId: string): Promise<OneClickAppDeploymentState>;
|
|
131
|
-
executeGenericApiCommand(verb: 'GET' | 'POST', endpoint: string, data: any): Promise<any>;
|
|
133
|
+
executeGenericApiCommand(verb: 'GET' | 'POST' | 'PATCH', endpoint: string, data: any): Promise<any>;
|
|
132
134
|
}
|
package/dist/api/ApiManager.js
CHANGED
|
@@ -163,16 +163,21 @@ class ApiManager {
|
|
|
163
163
|
.then(http.fetch(http.GET, `/user/apps/appData/${appName}`, {}));
|
|
164
164
|
}
|
|
165
165
|
fetchAppLogsInHex(appName) {
|
|
166
|
+
return this.fetchAppLogs(appName, 'hex');
|
|
167
|
+
}
|
|
168
|
+
fetchAppLogs(appName, encoding = 'ascii') {
|
|
166
169
|
const http = this.http;
|
|
167
170
|
return Promise.resolve() //
|
|
168
|
-
.then(http.fetch(http.GET, `/user/apps/appData/${appName}/logs
|
|
171
|
+
.then(http.fetch(http.GET, `/user/apps/appData/${appName}/logs`, {
|
|
172
|
+
encoding,
|
|
173
|
+
}));
|
|
169
174
|
}
|
|
170
|
-
uploadAppData(appName, file) {
|
|
175
|
+
uploadAppData(appName, file, detached = true) {
|
|
171
176
|
const http = this.http;
|
|
172
177
|
let formData = new FormData();
|
|
173
178
|
formData.append('sourceFile', file);
|
|
174
179
|
return Promise.resolve() //
|
|
175
|
-
.then(http.fetch(http.POST, `/user/apps/appData/${appName}?detached=1`, formData, http.FORM_DATA));
|
|
180
|
+
.then(http.fetch(http.POST, `/user/apps/appData/${appName}${detached ? '?detached=1' : ''}`, formData, http.FORM_DATA));
|
|
176
181
|
}
|
|
177
182
|
registerProject(selectedProject) {
|
|
178
183
|
const http = this.http;
|
|
@@ -243,6 +248,14 @@ class ApiManager {
|
|
|
243
248
|
projectId: projectId,
|
|
244
249
|
}));
|
|
245
250
|
}
|
|
251
|
+
patchAppDefinition(appName, patch) {
|
|
252
|
+
const http = this.http;
|
|
253
|
+
return Promise.resolve() //
|
|
254
|
+
.then(http.fetch(http.PATCH, '/user/apps/appDefinitions/update', {
|
|
255
|
+
...patch,
|
|
256
|
+
appName,
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
246
259
|
renameApp(oldAppName, newAppName) {
|
|
247
260
|
const http = this.http;
|
|
248
261
|
return Promise.resolve() //
|
package/dist/api/HttpClient.d.ts
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
export type RequestBodyType = 'json' | 'form-data';
|
|
2
|
+
export type HttpMethod = 'GET' | 'POST' | 'PATCH';
|
|
2
3
|
export declare function createPostRequestInit(variables: any, headers: Record<string, string>, bodyType: RequestBodyType): RequestInit;
|
|
4
|
+
export declare function createPatchRequestInit(variables: any, headers: Record<string, string>): RequestInit;
|
|
3
5
|
export default class HttpClient {
|
|
4
6
|
private baseUrl;
|
|
5
7
|
private authTokenProvider;
|
|
6
8
|
private onLoginRequested;
|
|
7
9
|
readonly GET = "GET";
|
|
8
10
|
readonly POST = "POST";
|
|
11
|
+
readonly PATCH = "PATCH";
|
|
9
12
|
readonly FORM_DATA = "form-data";
|
|
10
13
|
isDestroyed: boolean;
|
|
11
14
|
constructor(baseUrl: string, authTokenProvider: () => Promise<string>, onLoginRequested: () => Promise<void>);
|
|
12
15
|
createHeaders(): Promise<any>;
|
|
13
16
|
destroy(): void;
|
|
14
|
-
fetch(method:
|
|
15
|
-
fetchInternal(method:
|
|
17
|
+
fetch(method: HttpMethod, endpoint: string, variables: any, bodyType?: RequestBodyType): () => Promise<any>;
|
|
18
|
+
fetchInternal(method: HttpMethod, endpoint: string, variables: any, bodyType?: RequestBodyType): Promise<any>;
|
|
16
19
|
getReq(endpoint: string, variables: any): Promise<any>;
|
|
17
20
|
postReq(endpoint: string, variables: any, bodyType?: RequestBodyType): Promise<any>;
|
|
21
|
+
patchReq(endpoint: string, variables: any): Promise<any>;
|
|
18
22
|
}
|
package/dist/api/HttpClient.js
CHANGED
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createPostRequestInit = createPostRequestInit;
|
|
7
|
+
exports.createPatchRequestInit = createPatchRequestInit;
|
|
7
8
|
const ErrorFactory_1 = __importDefault(require("./ErrorFactory"));
|
|
8
9
|
const cross_fetch_1 = __importDefault(require("cross-fetch"));
|
|
9
10
|
function createPostRequestInit(variables, headers, bodyType) {
|
|
@@ -23,6 +24,16 @@ function createPostRequestInit(variables, headers, bodyType) {
|
|
|
23
24
|
body: JSON.stringify(variables),
|
|
24
25
|
};
|
|
25
26
|
}
|
|
27
|
+
function createPatchRequestInit(variables, headers) {
|
|
28
|
+
return {
|
|
29
|
+
method: 'PATCH',
|
|
30
|
+
headers: {
|
|
31
|
+
'Content-Type': 'application/json',
|
|
32
|
+
...headers,
|
|
33
|
+
},
|
|
34
|
+
body: JSON.stringify(variables),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
26
37
|
function buildQueryParams(params) {
|
|
27
38
|
if (!params || Object.keys(params).length === 0)
|
|
28
39
|
return '';
|
|
@@ -61,6 +72,14 @@ class CrossFetchEngine {
|
|
|
61
72
|
}
|
|
62
73
|
return res.json();
|
|
63
74
|
}
|
|
75
|
+
static async patch(url, variables, headers) {
|
|
76
|
+
const res = await (0, cross_fetch_1.default)(url, createPatchRequestInit(variables, headers));
|
|
77
|
+
if (!res.ok) {
|
|
78
|
+
const errBody = await res.text();
|
|
79
|
+
throw new Error(`HTTP ${res.status}: ${errBody}`);
|
|
80
|
+
}
|
|
81
|
+
return res.json();
|
|
82
|
+
}
|
|
64
83
|
}
|
|
65
84
|
class HttpClient {
|
|
66
85
|
constructor(baseUrl, authTokenProvider, onLoginRequested) {
|
|
@@ -69,6 +88,7 @@ class HttpClient {
|
|
|
69
88
|
this.onLoginRequested = onLoginRequested;
|
|
70
89
|
this.GET = 'GET';
|
|
71
90
|
this.POST = 'POST';
|
|
91
|
+
this.PATCH = 'PATCH';
|
|
72
92
|
this.FORM_DATA = 'form-data';
|
|
73
93
|
this.isDestroyed = false;
|
|
74
94
|
//
|
|
@@ -151,6 +171,8 @@ class HttpClient {
|
|
|
151
171
|
return this.getReq(endpoint, variables);
|
|
152
172
|
if (method === this.POST)
|
|
153
173
|
return this.postReq(endpoint, variables, bodyType);
|
|
174
|
+
if (method === this.PATCH)
|
|
175
|
+
return this.patchReq(endpoint, variables);
|
|
154
176
|
throw new Error(`Unknown method: ${method}`);
|
|
155
177
|
}
|
|
156
178
|
getReq(endpoint, variables) {
|
|
@@ -181,5 +203,18 @@ class HttpClient {
|
|
|
181
203
|
return data;
|
|
182
204
|
});
|
|
183
205
|
}
|
|
206
|
+
patchReq(endpoint, variables) {
|
|
207
|
+
const self = this;
|
|
208
|
+
return Promise.resolve() //
|
|
209
|
+
.then(function () {
|
|
210
|
+
return self.createHeaders();
|
|
211
|
+
})
|
|
212
|
+
.then(function (headers) {
|
|
213
|
+
return CrossFetchEngine.patch(self.baseUrl + endpoint, variables, headers);
|
|
214
|
+
})
|
|
215
|
+
.then(function (data) {
|
|
216
|
+
return data;
|
|
217
|
+
});
|
|
218
|
+
}
|
|
184
219
|
}
|
|
185
220
|
exports.default = HttpClient;
|
|
@@ -72,6 +72,7 @@ export interface IAppDefinitionBase {
|
|
|
72
72
|
envVars: IAppEnvVar[];
|
|
73
73
|
versions: IAppVersion[];
|
|
74
74
|
appDeployTokenConfig?: AppDeployTokenConfig;
|
|
75
|
+
isLegacyAppName?: boolean;
|
|
75
76
|
}
|
|
76
77
|
export interface IHttpAuth {
|
|
77
78
|
user: string;
|
|
@@ -82,6 +83,12 @@ export interface AppDeployTokenConfig {
|
|
|
82
83
|
enabled: boolean;
|
|
83
84
|
appDeployToken?: string;
|
|
84
85
|
}
|
|
86
|
+
export type IAppDefinitionPatch = Partial<Pick<IAppDefinitionBase, 'projectId' | 'description' | 'instanceCount' | 'captainDefinitionRelativeFilePath' | 'envVars' | 'volumes' | 'tags' | 'nodeId' | 'notExposeAsWebApp' | 'containerHttpPort' | 'forceSsl' | 'ports' | 'customNginxConfig' | 'redirectDomain' | 'preDeployFunction' | 'serviceUpdateOverride' | 'websocketSupport' | 'appDeployTokenConfig'>> & {
|
|
87
|
+
httpAuth?: IHttpAuth;
|
|
88
|
+
appPushWebhook?: {
|
|
89
|
+
repoInfo?: RepoInfo;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
85
92
|
export interface IAppDef extends IAppDefinitionBase {
|
|
86
93
|
appPushWebhook?: {
|
|
87
94
|
tokenVersion: string;
|
package/package.json
CHANGED
package/src/api/ApiManager.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IAppDef } from '../models/AppDefinition'
|
|
1
|
+
import { IAppDef, IAppDefinitionPatch } from '../models/AppDefinition'
|
|
2
2
|
import AppDefinitionsResponse from '../models/AppDefinitionsResponse'
|
|
3
3
|
import AppDeleteResponse from '../models/AppDeleteResponse'
|
|
4
4
|
import { IAutomatedCleanupConfigs } from '../models/AutomatedCleanupConfigs'
|
|
@@ -270,19 +270,28 @@ export default class ApiManager {
|
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
fetchAppLogsInHex(appName: string): Promise<LogsResponse> {
|
|
273
|
+
return this.fetchAppLogs(appName, 'hex')
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
fetchAppLogs(
|
|
277
|
+
appName: string,
|
|
278
|
+
encoding: string = 'ascii'
|
|
279
|
+
): Promise<LogsResponse> {
|
|
273
280
|
const http = this.http
|
|
274
281
|
|
|
275
282
|
return Promise.resolve() //
|
|
276
283
|
.then(
|
|
277
|
-
http.fetch(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
{}
|
|
281
|
-
)
|
|
284
|
+
http.fetch(http.GET, `/user/apps/appData/${appName}/logs`, {
|
|
285
|
+
encoding,
|
|
286
|
+
})
|
|
282
287
|
)
|
|
283
288
|
}
|
|
284
289
|
|
|
285
|
-
uploadAppData(
|
|
290
|
+
uploadAppData(
|
|
291
|
+
appName: string,
|
|
292
|
+
file: File,
|
|
293
|
+
detached: boolean = true
|
|
294
|
+
): Promise<void> {
|
|
286
295
|
const http = this.http
|
|
287
296
|
let formData = new FormData()
|
|
288
297
|
formData.append('sourceFile', file)
|
|
@@ -290,7 +299,9 @@ export default class ApiManager {
|
|
|
290
299
|
.then(
|
|
291
300
|
http.fetch(
|
|
292
301
|
http.POST,
|
|
293
|
-
`/user/apps/appData/${appName}
|
|
302
|
+
`/user/apps/appData/${appName}${
|
|
303
|
+
detached ? '?detached=1' : ''
|
|
304
|
+
}`,
|
|
294
305
|
formData,
|
|
295
306
|
http.FORM_DATA
|
|
296
307
|
)
|
|
@@ -399,6 +410,21 @@ export default class ApiManager {
|
|
|
399
410
|
)
|
|
400
411
|
}
|
|
401
412
|
|
|
413
|
+
patchAppDefinition(
|
|
414
|
+
appName: string,
|
|
415
|
+
patch: IAppDefinitionPatch
|
|
416
|
+
): Promise<void> {
|
|
417
|
+
const http = this.http
|
|
418
|
+
|
|
419
|
+
return Promise.resolve() //
|
|
420
|
+
.then(
|
|
421
|
+
http.fetch(http.PATCH, '/user/apps/appDefinitions/update', {
|
|
422
|
+
...patch,
|
|
423
|
+
appName,
|
|
424
|
+
})
|
|
425
|
+
)
|
|
426
|
+
}
|
|
427
|
+
|
|
402
428
|
renameApp(oldAppName: string, newAppName: string): Promise<void> {
|
|
403
429
|
const http = this.http
|
|
404
430
|
|
|
@@ -895,7 +921,7 @@ export default class ApiManager {
|
|
|
895
921
|
}
|
|
896
922
|
|
|
897
923
|
executeGenericApiCommand(
|
|
898
|
-
verb: 'GET' | 'POST',
|
|
924
|
+
verb: 'GET' | 'POST' | 'PATCH',
|
|
899
925
|
endpoint: string,
|
|
900
926
|
data: any
|
|
901
927
|
): Promise<any> {
|
package/src/api/HttpClient.ts
CHANGED
|
@@ -2,6 +2,7 @@ import ErrorFactory from './ErrorFactory'
|
|
|
2
2
|
import fetch from 'cross-fetch'
|
|
3
3
|
|
|
4
4
|
export type RequestBodyType = 'json' | 'form-data'
|
|
5
|
+
export type HttpMethod = 'GET' | 'POST' | 'PATCH'
|
|
5
6
|
|
|
6
7
|
export function createPostRequestInit(
|
|
7
8
|
variables: any,
|
|
@@ -26,6 +27,20 @@ export function createPostRequestInit(
|
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
export function createPatchRequestInit(
|
|
31
|
+
variables: any,
|
|
32
|
+
headers: Record<string, string>
|
|
33
|
+
): RequestInit {
|
|
34
|
+
return {
|
|
35
|
+
method: 'PATCH',
|
|
36
|
+
headers: {
|
|
37
|
+
'Content-Type': 'application/json',
|
|
38
|
+
...headers,
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify(variables),
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
29
44
|
function buildQueryParams(params: Record<string, any>): string {
|
|
30
45
|
if (!params || Object.keys(params).length === 0) return ''
|
|
31
46
|
return (
|
|
@@ -86,11 +101,26 @@ class CrossFetchEngine {
|
|
|
86
101
|
}
|
|
87
102
|
return res.json()
|
|
88
103
|
}
|
|
104
|
+
|
|
105
|
+
public static async patch(
|
|
106
|
+
url: string,
|
|
107
|
+
variables: any,
|
|
108
|
+
headers: Record<string, string>
|
|
109
|
+
): Promise<any> {
|
|
110
|
+
const res = await fetch(url, createPatchRequestInit(variables, headers))
|
|
111
|
+
|
|
112
|
+
if (!res.ok) {
|
|
113
|
+
const errBody = await res.text()
|
|
114
|
+
throw new Error(`HTTP ${res.status}: ${errBody}`)
|
|
115
|
+
}
|
|
116
|
+
return res.json()
|
|
117
|
+
}
|
|
89
118
|
}
|
|
90
119
|
|
|
91
120
|
export default class HttpClient {
|
|
92
121
|
public readonly GET = 'GET'
|
|
93
122
|
public readonly POST = 'POST'
|
|
123
|
+
public readonly PATCH = 'PATCH'
|
|
94
124
|
public readonly FORM_DATA = 'form-data'
|
|
95
125
|
public isDestroyed = false
|
|
96
126
|
|
|
@@ -123,7 +153,7 @@ export default class HttpClient {
|
|
|
123
153
|
}
|
|
124
154
|
|
|
125
155
|
fetch(
|
|
126
|
-
method:
|
|
156
|
+
method: HttpMethod,
|
|
127
157
|
endpoint: string,
|
|
128
158
|
variables: any,
|
|
129
159
|
bodyType: RequestBodyType = 'json'
|
|
@@ -198,7 +228,7 @@ export default class HttpClient {
|
|
|
198
228
|
}
|
|
199
229
|
|
|
200
230
|
fetchInternal(
|
|
201
|
-
method:
|
|
231
|
+
method: HttpMethod,
|
|
202
232
|
endpoint: string,
|
|
203
233
|
variables: any,
|
|
204
234
|
bodyType: RequestBodyType = 'json'
|
|
@@ -208,6 +238,8 @@ export default class HttpClient {
|
|
|
208
238
|
if (method === this.POST)
|
|
209
239
|
return this.postReq(endpoint, variables, bodyType)
|
|
210
240
|
|
|
241
|
+
if (method === this.PATCH) return this.patchReq(endpoint, variables)
|
|
242
|
+
|
|
211
243
|
throw new Error(`Unknown method: ${method}`)
|
|
212
244
|
}
|
|
213
245
|
|
|
@@ -253,4 +285,22 @@ export default class HttpClient {
|
|
|
253
285
|
return data
|
|
254
286
|
})
|
|
255
287
|
}
|
|
288
|
+
|
|
289
|
+
patchReq(endpoint: string, variables: any) {
|
|
290
|
+
const self = this
|
|
291
|
+
return Promise.resolve() //
|
|
292
|
+
.then(function () {
|
|
293
|
+
return self.createHeaders()
|
|
294
|
+
})
|
|
295
|
+
.then(function (headers) {
|
|
296
|
+
return CrossFetchEngine.patch(
|
|
297
|
+
self.baseUrl + endpoint,
|
|
298
|
+
variables,
|
|
299
|
+
headers
|
|
300
|
+
)
|
|
301
|
+
})
|
|
302
|
+
.then(function (data) {
|
|
303
|
+
return data
|
|
304
|
+
})
|
|
305
|
+
}
|
|
256
306
|
}
|
|
@@ -84,6 +84,10 @@ export interface IAppDefinitionBase {
|
|
|
84
84
|
envVars: IAppEnvVar[]
|
|
85
85
|
versions: IAppVersion[]
|
|
86
86
|
appDeployTokenConfig?: AppDeployTokenConfig
|
|
87
|
+
|
|
88
|
+
// True for apps created before v1.15.0
|
|
89
|
+
// non-existent for apps created on or after v1.15.0
|
|
90
|
+
isLegacyAppName?: boolean
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
export interface IHttpAuth {
|
|
@@ -97,6 +101,33 @@ export interface AppDeployTokenConfig {
|
|
|
97
101
|
appDeployToken?: string
|
|
98
102
|
}
|
|
99
103
|
|
|
104
|
+
export type IAppDefinitionPatch = Partial<
|
|
105
|
+
Pick<
|
|
106
|
+
IAppDefinitionBase,
|
|
107
|
+
| 'projectId'
|
|
108
|
+
| 'description'
|
|
109
|
+
| 'instanceCount'
|
|
110
|
+
| 'captainDefinitionRelativeFilePath'
|
|
111
|
+
| 'envVars'
|
|
112
|
+
| 'volumes'
|
|
113
|
+
| 'tags'
|
|
114
|
+
| 'nodeId'
|
|
115
|
+
| 'notExposeAsWebApp'
|
|
116
|
+
| 'containerHttpPort'
|
|
117
|
+
| 'forceSsl'
|
|
118
|
+
| 'ports'
|
|
119
|
+
| 'customNginxConfig'
|
|
120
|
+
| 'redirectDomain'
|
|
121
|
+
| 'preDeployFunction'
|
|
122
|
+
| 'serviceUpdateOverride'
|
|
123
|
+
| 'websocketSupport'
|
|
124
|
+
| 'appDeployTokenConfig'
|
|
125
|
+
>
|
|
126
|
+
> & {
|
|
127
|
+
httpAuth?: IHttpAuth
|
|
128
|
+
appPushWebhook?: { repoInfo?: RepoInfo }
|
|
129
|
+
}
|
|
130
|
+
|
|
100
131
|
export interface IAppDef extends IAppDefinitionBase {
|
|
101
132
|
appPushWebhook?: {
|
|
102
133
|
tokenVersion: string
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const assert = require('node:assert/strict')
|
|
2
|
+
const test = require('node:test')
|
|
3
|
+
|
|
4
|
+
const ApiManagerModule = require('../dist/api/ApiManager')
|
|
5
|
+
const ApiManager = ApiManagerModule.default
|
|
6
|
+
const { SimpleAuthenticationProvider } = ApiManagerModule
|
|
7
|
+
|
|
8
|
+
function createApiWithRequestRecorder() {
|
|
9
|
+
const api = new ApiManager(
|
|
10
|
+
'https://captain.example.com',
|
|
11
|
+
new SimpleAuthenticationProvider(() =>
|
|
12
|
+
Promise.resolve({ password: 'password' })
|
|
13
|
+
)
|
|
14
|
+
)
|
|
15
|
+
const requests = []
|
|
16
|
+
|
|
17
|
+
api.http = {
|
|
18
|
+
GET: 'GET',
|
|
19
|
+
POST: 'POST',
|
|
20
|
+
PATCH: 'PATCH',
|
|
21
|
+
FORM_DATA: 'form-data',
|
|
22
|
+
fetch(method, endpoint, data, bodyType = 'json') {
|
|
23
|
+
requests.push({ method, endpoint, data, bodyType })
|
|
24
|
+
return () => Promise.resolve({})
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return { api, requests }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
test('patchAppDefinition uses the partial update endpoint', async () => {
|
|
32
|
+
const { api, requests } = createApiWithRequestRecorder()
|
|
33
|
+
|
|
34
|
+
await api.patchAppDefinition('test-app', {
|
|
35
|
+
instanceCount: 2,
|
|
36
|
+
description: 'scaled app',
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
assert.deepEqual(requests, [
|
|
40
|
+
{
|
|
41
|
+
method: 'PATCH',
|
|
42
|
+
endpoint: '/user/apps/appDefinitions/update',
|
|
43
|
+
data: {
|
|
44
|
+
instanceCount: 2,
|
|
45
|
+
description: 'scaled app',
|
|
46
|
+
appName: 'test-app',
|
|
47
|
+
},
|
|
48
|
+
bodyType: 'json',
|
|
49
|
+
},
|
|
50
|
+
])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('fetchAppLogs exposes the backend encoding argument', async () => {
|
|
54
|
+
const { api, requests } = createApiWithRequestRecorder()
|
|
55
|
+
|
|
56
|
+
await api.fetchAppLogs('test-app', 'utf8')
|
|
57
|
+
|
|
58
|
+
assert.deepEqual(requests[0], {
|
|
59
|
+
method: 'GET',
|
|
60
|
+
endpoint: '/user/apps/appData/test-app/logs',
|
|
61
|
+
data: { encoding: 'utf8' },
|
|
62
|
+
bodyType: 'json',
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('fetchAppLogsInHex remains backward compatible', async () => {
|
|
67
|
+
const { api, requests } = createApiWithRequestRecorder()
|
|
68
|
+
|
|
69
|
+
await api.fetchAppLogsInHex('test-app')
|
|
70
|
+
|
|
71
|
+
assert.deepEqual(requests[0].data, { encoding: 'hex' })
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test('uploadAppData keeps detached deployment as the default', async () => {
|
|
75
|
+
const { api, requests } = createApiWithRequestRecorder()
|
|
76
|
+
|
|
77
|
+
await api.uploadAppData('test-app', 'tar contents')
|
|
78
|
+
|
|
79
|
+
assert.equal(
|
|
80
|
+
requests[0].endpoint,
|
|
81
|
+
'/user/apps/appData/test-app?detached=1'
|
|
82
|
+
)
|
|
83
|
+
assert.equal(requests[0].bodyType, 'form-data')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('uploadAppData can wait for an attached deployment', async () => {
|
|
87
|
+
const { api, requests } = createApiWithRequestRecorder()
|
|
88
|
+
|
|
89
|
+
await api.uploadAppData('test-app', 'tar contents', false)
|
|
90
|
+
|
|
91
|
+
assert.equal(requests[0].endpoint, '/user/apps/appData/test-app')
|
|
92
|
+
assert.equal(requests[0].bodyType, 'form-data')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test('executeGenericApiCommand accepts PATCH', async () => {
|
|
96
|
+
const { api, requests } = createApiWithRequestRecorder()
|
|
97
|
+
|
|
98
|
+
await api.executeGenericApiCommand('PATCH', '/user/example', {
|
|
99
|
+
value: true,
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
assert.equal(requests[0].method, 'PATCH')
|
|
103
|
+
})
|
package/test/HttpClient.test.js
CHANGED
|
@@ -5,6 +5,7 @@ const ErrorFactory = require('../dist/api/ErrorFactory').default
|
|
|
5
5
|
const HttpClientModule = require('../dist/api/HttpClient')
|
|
6
6
|
const HttpClient = HttpClientModule.default
|
|
7
7
|
const { createPostRequestInit } = HttpClientModule
|
|
8
|
+
const { createPatchRequestInit } = HttpClientModule
|
|
8
9
|
|
|
9
10
|
test('JSON requests preserve the existing serialization and content type', () => {
|
|
10
11
|
const payload = { appName: 'test-app' }
|
|
@@ -31,6 +32,20 @@ test('form data is passed through without setting a content type', () => {
|
|
|
31
32
|
assert.equal(Object.hasOwn(request.headers, 'Content-Type'), false)
|
|
32
33
|
})
|
|
33
34
|
|
|
35
|
+
test('PATCH requests use JSON serialization and content type', () => {
|
|
36
|
+
const payload = { appName: 'test-app', instanceCount: 2 }
|
|
37
|
+
const request = createPatchRequestInit(payload, {
|
|
38
|
+
'x-captain-auth': 'token',
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
assert.equal(request.method, 'PATCH')
|
|
42
|
+
assert.deepEqual(request.headers, {
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
'x-captain-auth': 'token',
|
|
45
|
+
})
|
|
46
|
+
assert.equal(request.body, JSON.stringify(payload))
|
|
47
|
+
})
|
|
48
|
+
|
|
34
49
|
test('authentication retry preserves the form-data body type', async () => {
|
|
35
50
|
let loginRequests = 0
|
|
36
51
|
const bodyTypes = []
|