Skip to content

Commit fa24af5

Browse files
authored
Merge pull request #4 from FoxxMD/apiSyncErrorLogging
feat: improve api sync error logging
2 parents 179fba3 + fd73382 commit fa24af5

File tree

4 files changed

+108
-36
lines changed

4 files changed

+108
-36
lines changed

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ RUN npm install --omit=dev \
2323

2424
FROM base as app
2525

26+
ARG APP_BUILD_VERSION
27+
ENV APP_VERSION=$APP_BUILD_VERSION
28+
2629
USER 1000
2730

2831
WORKDIR /usr/src/app

src/common/KomodoApi.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ export class KomodoApi {
4444
if (isUndefinedOrEmptyString(process.env.KOMODO_URL)) {
4545
throw new SimpleError(`Cannot use Komodo API because env KOMODO_URL is missing`);
4646
}
47-
this.logger.verbose(`KOMODO_URL: ${process.env.KOMODO_URL} | Normalized: ${this.urlData.url.toString()}`);
47+
this.logger.verbose(`KOMODO_URL: ${process.env.KOMODO_URL} | Normalized: ${this.urlData.url.toString().replace(/\/$/, '')}`);
4848

49-
this.api = KomodoClient(this.urlData.url.toString(), {
49+
// remove any trailing slash or calls may result in a 405
50+
this.api = KomodoClient(this.urlData.url.toString().replace(/\/$/, ''), {
5051
type: "api-key",
5152
params: {
5253
key: process.env.API_KEY,

src/common/errors.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,54 @@
1+
import { __Serror } from "komodo_client/dist/types.js";
2+
13
export class SimpleError extends Error {
24

5+
}
6+
7+
export class KomodoApiError extends Error {
8+
9+
constructor(e: KomodoApiErrorResponse) {
10+
super(`(HTTP ${e.status}) ${e.result.error}`);
11+
this.name = 'KomodoApiError';
12+
if(e.error !== undefined) {
13+
this.cause = e.error;
14+
}
15+
let kTrace: string;
16+
if(e.result.trace.length > 0) {
17+
kTrace = `${e.result.trace.join('\n')}`
18+
} else {
19+
kTrace = '(No trace returned from Komodo)'
20+
}
21+
this.stack = `${this.stack ?? ''}\nKomodo Trace: ${kTrace}`;
22+
}
23+
}
24+
25+
export type KomodoResultError = __Serror;
26+
27+
export const asKomodoResultError = (e: any): e is KomodoResultError => {
28+
return e !== null
29+
&& typeof e === 'object'
30+
&& 'error' in e
31+
&& typeof e.error === 'string'
32+
&& 'trace' in e
33+
&& Array.isArray(e.trace);
34+
}
35+
36+
export interface KomodoApiErrorResponse {
37+
result: KomodoResultError
38+
status: number
39+
error?: Error
40+
}
41+
42+
export const asKomodoApiErrorResponse = (e: any): e is KomodoApiErrorResponse => {
43+
return e !== null
44+
&& typeof e === 'object'
45+
&& 'result' in e
46+
&& asKomodoResultError(e.result)
47+
&& 'status' in e
48+
&& typeof e.status === 'number'
49+
&& (
50+
!('error' in e)
51+
||
52+
('error' in e && e.error instanceof Error)
53+
);
354
}

src/exporters/exportToApiSync.ts

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ import path from 'path';
55
import { stripIndents } from "common-tags";
66
import dayjs from "dayjs";
77
import { getDefaultKomodoApi } from "../common/utils/komodo.js";
8+
import { asKomodoApiErrorResponse, KomodoApiError } from "../common/errors.js";
89

910
export const exportToSync = async (toml: string, parentLogger: Logger): Promise<void> => {
1011
const logger = childLogger(parentLogger, 'Sync API');
1112

1213
if (parseBool(process.env.OUTPUT_API_SYNC)) {
1314
try {
1415
const komodo = getDefaultKomodoApi();
15-
if(komodo === undefined) {
16-
throw new Error('Komodo API is unavaiable, cannot export to Sync');
16+
if (komodo === undefined) {
17+
throw new Error('Komodo API is unavailable, cannot export to Sync');
1718
}
1819
const syncName = isUndefinedOrEmptyString(process.env.SYNC_NAME) ? 'komodo-import' : process.env.SYNC_NAME;
1920
logger.info(`Using '${syncName}' as Sync Name`);
@@ -41,46 +42,68 @@ export const exportToSync = async (toml: string, parentLogger: Logger): Promise<
4142
}
4243
logger.verbose(`Resource Sync ${syncName} alerady exists (ID ${syncId}), will ${existingBehavior} contents`);
4344
} catch (e) {
44-
// do nothing
45+
if (asKomodoApiErrorResponse(e)) {
46+
if (e.result.error.toLocaleLowerCase().includes('did not find any resourcesync matching')) {
47+
logger.verbose(`No Sync named ${syncName} exists, creating a new one.`);
48+
} else {
49+
const ke = new KomodoApiError(e);
50+
throw new Error('Unexpected error from Komodo API while checking for existing Sync', { cause: ke });
51+
}
52+
} else {
53+
throw new Error('Unexpected error while trying to check for existing Sync', { cause: e });
54+
}
4555
}
4656

4757

48-
if (syncId === undefined) {
49-
const sync = await komodo.api.write('CreateResourceSync', {
50-
name: syncName,
51-
config: {
52-
include_resources: true,
53-
file_contents: toml
54-
}
55-
});
56-
syncId = sync._id.$oid;
57-
logger.info('Resource Sync created.');
58-
} else {
59-
if (existingBehavior === 'overwrite') {
60-
const sync = await komodo.api.write('UpdateResourceSync', {
61-
id: syncId,
58+
try {
59+
60+
61+
if (syncId === undefined) {
62+
logger.debug('Trying to create Sync via API...');
63+
const sync = await komodo.api.write('CreateResourceSync', {
64+
name: syncName,
6265
config: {
6366
include_resources: true,
6467
file_contents: toml
6568
}
6669
});
67-
logger.info('Resource Sync overwrriten.');
70+
syncId = sync._id.$oid;
71+
logger.info('Resource Sync created.');
6872
} else {
69-
const time = dayjs().format('YYYY-MM-DD--HH-mm-ss');
70-
const sync = await komodo.api.write('UpdateResourceSync', {
71-
id: syncId,
72-
config: {
73-
include_resources: true,
74-
file_contents: stripIndents`${existingSync.config.file_contents}
73+
if (existingBehavior === 'overwrite') {
74+
logger.debug('Trying to overwrite existing Sync via API...');
75+
const sync = await komodo.api.write('UpdateResourceSync', {
76+
id: syncId,
77+
config: {
78+
include_resources: true,
79+
file_contents: toml
80+
}
81+
});
82+
logger.info('Resource Sync overwritten.');
83+
} else {
84+
logger.debug('Trying to append to existing Sync via API...');
85+
const time = dayjs().format('YYYY-MM-DD--HH-mm-ss');
86+
const sync = await komodo.api.write('UpdateResourceSync', {
87+
id: syncId,
88+
config: {
89+
include_resources: true,
90+
file_contents: stripIndents`${existingSync.config.file_contents}
7591
7692
## Added by Komodo Import ${time}
7793
7894
${toml}
7995
8096
##`
81-
}
82-
});
83-
logger.info('Resource Sync appended.');
97+
}
98+
});
99+
logger.info('Resource Sync appended.');
100+
}
101+
}
102+
} catch (e) {
103+
if (asKomodoApiErrorResponse(e)) {
104+
throw new Error('Unexpected error from Komodo API while writing Sync', { cause: new KomodoApiError(e) });
105+
} else {
106+
throw new Error('Unexpected error while trying to write to Sync', { cause: e });
84107
}
85108
}
86109

@@ -89,13 +112,7 @@ export const exportToSync = async (toml: string, parentLogger: Logger): Promise<
89112
logger.info(`Resource Sync URL: ${syncUrl}`);
90113
}
91114
catch (e) {
92-
let hint: string;
93-
switch (e.result?.error) {
94-
case 'Must provide unique name for resource.':
95-
hint = 'Sync already exists! Use a different name.'
96-
break;
97-
}
98-
throw new Error(`Komodo API error occurred while trying to export to Resource Sync${hint !== undefined ? `. Hint: ${hint}` : ''}`, { cause: e });
115+
throw new Error(`Error occurred while trying to export to Resource Sync`, { cause: e });
99116
}
100117
} else if (isDebugMode()) {
101118
logger.debug('Not exporting to Resource Sync because env KOMODO_URL was not defined.');

0 commit comments

Comments
 (0)