@unito/integration-cli 0.58.1 → 0.58.3
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/.eslintrc.d.ts +4 -4
- package/dist/.eslintrc.js +0 -8
- package/dist/boilerplate/.dockerignore +1 -1
- package/dist/boilerplate/Dockerfile +1 -1
- package/dist/boilerplate/eslint.config.js +7 -0
- package/dist/boilerplate/package.json +10 -16
- package/dist/boilerplate/src/cache.ts +8 -0
- package/dist/boilerplate/src/handlers/me.ts +10 -0
- package/dist/boilerplate/src/handlers/root.ts +8 -0
- package/dist/boilerplate/src/helpers.ts +43 -0
- package/dist/boilerplate/src/index.ts +7 -88
- package/dist/boilerplate/src/provider.ts +19 -0
- package/dist/boilerplate/test/handlers/me.test.ts +19 -0
- package/dist/boilerplate/test/handlers/root.test.ts +17 -0
- package/dist/boilerplate/tsconfig.json +5 -13
- package/dist/src/commands/oauth2.js +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +3 -3
- package/dist/boilerplate/.eslintrc.js +0 -74
- package/dist/boilerplate/src/logger.ts +0 -55
- package/dist/boilerplate/src/middlewares/additionalLoggingContext.ts +0 -22
- package/dist/boilerplate/src/middlewares/correlationId.ts +0 -13
- package/dist/boilerplate/src/middlewares/credentials.ts +0 -38
- package/dist/boilerplate/src/request.ts +0 -59
- package/dist/boilerplate/src/routes/index.ts +0 -11
- package/dist/boilerplate/src/routes/me.ts +0 -15
- package/dist/boilerplate/src/routes/root.ts +0 -12
package/dist/.eslintrc.d.ts
CHANGED
|
@@ -4,13 +4,13 @@ export declare namespace env {
|
|
|
4
4
|
export declare let plugins: string[];
|
|
5
5
|
declare let _extends: string[];
|
|
6
6
|
export { _extends as extends };
|
|
7
|
+
export declare let rules: {
|
|
8
|
+
'no-console': string;
|
|
9
|
+
};
|
|
10
|
+
export declare let ignorePatterns: string[];
|
|
7
11
|
export declare let overrides: {
|
|
8
12
|
files: string[];
|
|
9
13
|
rules: {
|
|
10
14
|
'@typescript-eslint/no-floating-promises': string;
|
|
11
15
|
};
|
|
12
16
|
}[];
|
|
13
|
-
export declare let rules: {
|
|
14
|
-
'no-console': string;
|
|
15
|
-
};
|
|
16
|
-
export declare let ignorePatterns: string[];
|
package/dist/.eslintrc.js
CHANGED
|
@@ -10,14 +10,6 @@ module.exports = {
|
|
|
10
10
|
'eslint:recommended',
|
|
11
11
|
'plugin:@typescript-eslint/recommended',
|
|
12
12
|
],
|
|
13
|
-
overrides: [
|
|
14
|
-
{
|
|
15
|
-
'files': ['test/**/*.test.ts'],
|
|
16
|
-
'rules': {
|
|
17
|
-
'@typescript-eslint/no-floating-promises': 'off'
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
],
|
|
21
13
|
rules: {
|
|
22
14
|
'no-console': 'off',
|
|
23
15
|
},
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "integration-boilerplate",
|
|
3
|
+
"type": "module",
|
|
3
4
|
"version": "0.0.1",
|
|
4
5
|
"description": "Integration Boilerplate",
|
|
6
|
+
"license": "LicenseRef-LICENSE",
|
|
5
7
|
"scripts": {
|
|
6
8
|
"compile": "tsc",
|
|
7
|
-
"dev": "
|
|
8
|
-
"
|
|
9
|
+
"dev": "tsx watch src/index.ts",
|
|
10
|
+
"test": "NODE_ENV=test tsx --test --test-name-pattern=${ONLY:-.*} $(find test -type f -name '*.test.ts')",
|
|
11
|
+
"lint": "eslint --fix src && prettier --write src",
|
|
9
12
|
"ci:audit": "check-audit",
|
|
10
|
-
"ci:eslint": "eslint
|
|
13
|
+
"ci:eslint": "eslint src",
|
|
11
14
|
"ci:lint": "npm run ci:prettier && npm run ci:eslint || (echo \"Please run eslint and/or prettier and commit the changes\" && exit 1)",
|
|
12
15
|
"ci:prettier": "prettier --cache --ignore-unknown --check src",
|
|
13
|
-
"ci:test": "
|
|
16
|
+
"ci:test": "npm run test"
|
|
14
17
|
},
|
|
15
18
|
"author": {
|
|
16
19
|
"name": "Unito",
|
|
@@ -19,25 +22,16 @@
|
|
|
19
22
|
"engines": {
|
|
20
23
|
"node": ">=20.0.0"
|
|
21
24
|
},
|
|
22
|
-
"license": "LicenseRef-LICENSE",
|
|
23
25
|
"dependencies": {
|
|
24
|
-
"@unito/integration-
|
|
25
|
-
"express": "^5.0.0-beta.1",
|
|
26
|
-
"uuid": "9.x"
|
|
26
|
+
"@unito/integration-sdk": "^0.x"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@types/express": "4.x",
|
|
30
|
-
"@types/mocha": "10.x",
|
|
31
29
|
"@types/node": "20.x",
|
|
32
|
-
"
|
|
33
|
-
"@typescript-eslint/eslint-plugin": "5.x",
|
|
34
|
-
"@typescript-eslint/parser": "5.x",
|
|
30
|
+
"typescript-eslint": "7.x",
|
|
35
31
|
"eslint": "8.x",
|
|
36
|
-
"mocha": "10.x",
|
|
37
|
-
"nodemon": "2.x",
|
|
38
32
|
"npm-audit-resolver": "^3.0.0-RC.0",
|
|
39
33
|
"prettier": "2.x",
|
|
40
|
-
"
|
|
34
|
+
"tsx": "4.x",
|
|
41
35
|
"typescript": "5.x"
|
|
42
36
|
}
|
|
43
37
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Credentials, Secrets } from '@unito/integration-sdk';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Use this helper function to centralize the logic for getting the credentials from the context object.
|
|
5
|
+
* For example, you might want to enrich the original object with some additional properties, or ensure
|
|
6
|
+
* that some critical properties are present before proceeding.
|
|
7
|
+
*
|
|
8
|
+
* This function also ensures that your credentials are consistenly typed across your integration.
|
|
9
|
+
*
|
|
10
|
+
* As an example, to ensure access token is always set;
|
|
11
|
+
*
|
|
12
|
+
* ```
|
|
13
|
+
* if (!context.credentials.accessToken) {
|
|
14
|
+
* throw new HttpErrors.UnauthorizedError('Missing access token');
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* return { accessToken: context.credentials.accessToken };
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @param context The object containing the raw credentials
|
|
21
|
+
* @returns The enriched credentials object
|
|
22
|
+
*/
|
|
23
|
+
export const getCredentials = (context: { credentials: Credentials }) => {
|
|
24
|
+
return {
|
|
25
|
+
// Tweak these properties to suit your needs
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Use this helper function to centralize the logic for getting the secrets from the context object.
|
|
31
|
+
* For example, you might want to enrich the original object with some additional properties, or ensure
|
|
32
|
+
* that some critical properties are present before proceeding.
|
|
33
|
+
*
|
|
34
|
+
* This function also ensures that your secrets are consistenly typed across your integration.
|
|
35
|
+
*
|
|
36
|
+
* @param context The object containing the raw secrets
|
|
37
|
+
* @returns The enriched secrets object
|
|
38
|
+
*/
|
|
39
|
+
export const getSecrets = (context: { secrets: Secrets }) => {
|
|
40
|
+
return {
|
|
41
|
+
// Tweak these properties to suit your needs
|
|
42
|
+
};
|
|
43
|
+
};
|
|
@@ -1,92 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Error as APIError } from '@unito/integration-api';
|
|
1
|
+
import { Integration } from '@unito/integration-sdk';
|
|
3
2
|
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import { extractCorrelationId } from './middlewares/correlationId';
|
|
7
|
-
import { extractAdditionalLoggingContext } from './middlewares/additionalLoggingContext';
|
|
8
|
-
import { logger } from './logger';
|
|
3
|
+
import * as meHandler from './handlers/me.js';
|
|
4
|
+
import * as rootHandler from './handlers/root.js';
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
const app: express.Application = express();
|
|
6
|
+
const integration = new Integration();
|
|
12
7
|
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
integration.addHandler('/', rootHandler);
|
|
9
|
+
integration.addHandler('/me', meHandler);
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// Must be one of the first handlers (to catch all the errors).
|
|
19
|
-
app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
20
|
-
if (req.originalUrl !== '/health') {
|
|
21
|
-
res.on('finish', function () {
|
|
22
|
-
const loggerLevel = res.statusCode >= 500 ? 'error' : 'info';
|
|
23
|
-
|
|
24
|
-
// eslint-disable-next-line
|
|
25
|
-
logger[loggerLevel](`${req.method} ${req.originalUrl} ${res.statusCode}`);
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
next();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// Extract and validate the credentials.
|
|
33
|
-
app.use(extractCredentials);
|
|
34
|
-
|
|
35
|
-
// Extract the correlation id.
|
|
36
|
-
app.use(extractCorrelationId);
|
|
37
|
-
|
|
38
|
-
// Load the routes.
|
|
39
|
-
app.use('/', indexRouter);
|
|
40
|
-
|
|
41
|
-
// Extract the additional logging context.
|
|
42
|
-
app.use(extractAdditionalLoggingContext);
|
|
43
|
-
|
|
44
|
-
// Must be the (last - 1) handler.
|
|
45
|
-
app.use((err: Error, _req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
46
|
-
if (res.headersSent) {
|
|
47
|
-
return next(err);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const originalError: APIError = {
|
|
51
|
-
code: err.name,
|
|
52
|
-
message: err.message,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
res.status(500).json({
|
|
56
|
-
code: '500',
|
|
57
|
-
message: 'Oops! Something went wrong',
|
|
58
|
-
originalError: originalError,
|
|
59
|
-
} as APIError);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Must be the last handler.
|
|
63
|
-
app.use((req: express.Request, res: express.Response, _next: express.NextFunction) => {
|
|
64
|
-
const error: APIError = {
|
|
65
|
-
code: '404',
|
|
66
|
-
message: `Path ${req.path} not found.`,
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
res.status(404).json(error);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// eslint-disable-next-line
|
|
73
|
-
const instance = app.listen(process.env.PORT || 9200, () => console.log(`Server started on port ${process.env.PORT || 9200}.`));
|
|
74
|
-
|
|
75
|
-
// Trap exit signals.
|
|
76
|
-
['SIGTERM', 'SIGINT', 'SIGUSR2'].forEach(signalType => {
|
|
77
|
-
process.once(signalType, async () => {
|
|
78
|
-
// eslint-disable-next-line
|
|
79
|
-
console.log(`Received termination signal ${signalType}. Exiting.`);
|
|
80
|
-
|
|
81
|
-
try {
|
|
82
|
-
if (instance) {
|
|
83
|
-
instance.close();
|
|
84
|
-
}
|
|
85
|
-
} catch (e) {
|
|
86
|
-
// eslint-disable-next-line
|
|
87
|
-
console.error('Failed to gracefully exit', e);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
process.exit();
|
|
91
|
-
});
|
|
92
|
-
});
|
|
11
|
+
integration.start();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// This provider module is meant to be used to make requests to your provider's API.
|
|
2
|
+
// https://dev.unito.io/docs/connectors/SDK/modules/provider
|
|
3
|
+
|
|
4
|
+
import { Provider } from '@unito/integration-sdk';
|
|
5
|
+
|
|
6
|
+
const provider = new Provider({
|
|
7
|
+
prepareRequest: options => {
|
|
8
|
+
return {
|
|
9
|
+
url: 'api.provider.com',
|
|
10
|
+
headers: {
|
|
11
|
+
// Include custom headers here, for example;
|
|
12
|
+
// 'Authorization': `Bearer ${options.credentials.apiKey}`,
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
},
|
|
16
|
+
rateLimiter: undefined,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export default provider;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { GetCredentialAccountContext } from '@unito/integration-sdk';
|
|
5
|
+
|
|
6
|
+
import { getCredentialAccount } from '../../src/handlers/me.js';
|
|
7
|
+
|
|
8
|
+
describe('meHandler', () => {
|
|
9
|
+
describe('getCredentialAccount', () => {
|
|
10
|
+
it('returns the credential account', async () => {
|
|
11
|
+
const credentialAccount = await getCredentialAccount({} as GetCredentialAccountContext);
|
|
12
|
+
|
|
13
|
+
assert.deepEqual(credentialAccount.displayName, 'Me');
|
|
14
|
+
assert.deepEqual(credentialAccount.emails, []);
|
|
15
|
+
assert.deepEqual(credentialAccount.id, 'me');
|
|
16
|
+
assert.deepEqual(credentialAccount.partition, undefined);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { GetItemContext } from '@unito/integration-sdk';
|
|
5
|
+
|
|
6
|
+
import { getItem } from '../../src/handlers/root.js';
|
|
7
|
+
|
|
8
|
+
describe('rootHandler', () => {
|
|
9
|
+
describe('getItem', () => {
|
|
10
|
+
it('returns the root item', async () => {
|
|
11
|
+
const root = await getItem({} as GetItemContext);
|
|
12
|
+
|
|
13
|
+
assert.deepEqual(root.fields, {});
|
|
14
|
+
assert.deepEqual(root.relations, []);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"ts-node": {
|
|
3
|
-
"logError": true
|
|
4
|
-
},
|
|
5
2
|
"compilerOptions": {
|
|
6
|
-
"allowJs": true,
|
|
7
|
-
"allowSyntheticDefaultImports": true,
|
|
8
3
|
"baseUrl": ".",
|
|
9
4
|
"declaration": true,
|
|
10
5
|
"declarationMap": true,
|
|
@@ -15,23 +10,20 @@
|
|
|
15
10
|
"incremental": true,
|
|
16
11
|
"isolatedModules": false,
|
|
17
12
|
"lib": ["dom", "ES2022"],
|
|
18
|
-
"module": "
|
|
19
|
-
"moduleResolution": "node",
|
|
13
|
+
"module": "NodeNext",
|
|
20
14
|
"noImplicitAny": true,
|
|
21
15
|
"noFallthroughCasesInSwitch": true,
|
|
22
16
|
"noUnusedLocals": true,
|
|
23
17
|
"outDir": "dist",
|
|
24
18
|
"pretty": true,
|
|
25
|
-
"
|
|
26
|
-
"rootDir": "
|
|
19
|
+
"moduleResolution": "NodeNext",
|
|
20
|
+
"rootDir": "./src",
|
|
27
21
|
"skipLibCheck": true,
|
|
28
22
|
"sourceMap": true,
|
|
29
23
|
"strict": true,
|
|
30
24
|
"strictFunctionTypes": true,
|
|
31
25
|
"strictNullChecks": true,
|
|
32
26
|
"strictPropertyInitialization": false,
|
|
33
|
-
"target": "
|
|
34
|
-
}
|
|
35
|
-
"include": ["src/**/*"],
|
|
36
|
-
"exclude": ["node_modules"]
|
|
27
|
+
"target": "esnext"
|
|
28
|
+
}
|
|
37
29
|
}
|
|
@@ -85,7 +85,7 @@ class Oauth2 extends core_1.Command {
|
|
|
85
85
|
}
|
|
86
86
|
const decryptionResult = await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, testAccountCredentials);
|
|
87
87
|
const refreshToken = decryptionResult.entries?.refreshToken;
|
|
88
|
-
core_1.ux.action.start(`Refreshing test account ${
|
|
88
|
+
core_1.ux.action.start(`Refreshing test account ${testAccount}`, undefined, { stdout: true });
|
|
89
89
|
credentials = await Oauth2Resource.updateToken(oauth2, refreshToken);
|
|
90
90
|
// If provider response doesn't contain a new refresh token, use the one we already have
|
|
91
91
|
if (!credentials.refreshToken) {
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unito/integration-cli",
|
|
3
|
-
"version": "0.58.
|
|
3
|
+
"version": "0.58.3",
|
|
4
4
|
"description": "Integration CLI",
|
|
5
5
|
"bin": {
|
|
6
6
|
"integration-cli": "./bin/run"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"email": "hello@unito.io"
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
|
-
"node": ">=
|
|
16
|
+
"node": ">=18.0.0"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
19
|
"prepublishOnly": "npm run lint && npm run test",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"@types/gradient-string": "1.x",
|
|
63
63
|
"@types/inquirer": "9.x",
|
|
64
64
|
"@types/mocha": "10.x",
|
|
65
|
-
"@types/node": "
|
|
65
|
+
"@types/node": "18.x",
|
|
66
66
|
"@types/openurl": "1.x",
|
|
67
67
|
"@types/tmp": "0.x",
|
|
68
68
|
"c8": "9.x",
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
'env': {
|
|
3
|
-
'browser': true,
|
|
4
|
-
'es6': true,
|
|
5
|
-
'node': true
|
|
6
|
-
},
|
|
7
|
-
'parser': '@typescript-eslint/parser',
|
|
8
|
-
'parserOptions': {
|
|
9
|
-
'project': './tsconfig.json',
|
|
10
|
-
},
|
|
11
|
-
'plugins': [
|
|
12
|
-
'@typescript-eslint'
|
|
13
|
-
],
|
|
14
|
-
extends: [
|
|
15
|
-
'eslint:recommended',
|
|
16
|
-
'plugin:@typescript-eslint/recommended',
|
|
17
|
-
],
|
|
18
|
-
ignorePatterns: [
|
|
19
|
-
'node_modules',
|
|
20
|
-
'dist',
|
|
21
|
-
'.eslintrc.js'
|
|
22
|
-
],
|
|
23
|
-
'rules': {
|
|
24
|
-
'@typescript-eslint/no-loss-of-precision': 0,
|
|
25
|
-
'@typescript-eslint/no-explicit-any': 0,
|
|
26
|
-
'@typescript-eslint/ban-ts-comment': 0,
|
|
27
|
-
'@typescript-eslint/ban-ts-ignore': 0,
|
|
28
|
-
'@typescript-eslint/explicit-module-boundary-types': 0,
|
|
29
|
-
'@typescript-eslint/no-var-requires': 0,
|
|
30
|
-
'@typescript-eslint/no-floating-promises': 2,
|
|
31
|
-
'@typescript-eslint/no-unused-vars': 0,
|
|
32
|
-
// We need null in connectors because in some API, adding null to a field makes the API remove the value to the field
|
|
33
|
-
'@typescript-eslint/no-non-null-assertion': 0,
|
|
34
|
-
'@typescript-eslint/prefer-namespace-keyword': 0,
|
|
35
|
-
'@typescript-eslint/no-namespace': 0,
|
|
36
|
-
'@typescript-eslint/no-inferrable-types': 0,
|
|
37
|
-
'@typescript-eslint/naming-convention': [
|
|
38
|
-
1,
|
|
39
|
-
{
|
|
40
|
-
selector: [
|
|
41
|
-
'classProperty',
|
|
42
|
-
'objectLiteralProperty',
|
|
43
|
-
'typeProperty',
|
|
44
|
-
'classMethod',
|
|
45
|
-
'objectLiteralMethod',
|
|
46
|
-
'typeMethod',
|
|
47
|
-
'accessor',
|
|
48
|
-
'enumMember'
|
|
49
|
-
],
|
|
50
|
-
format: null,
|
|
51
|
-
modifiers: ['requiresQuotes']
|
|
52
|
-
}
|
|
53
|
-
],
|
|
54
|
-
'no-whitespace-before-property': 2,
|
|
55
|
-
'no-trailing-spaces': 2,
|
|
56
|
-
'no-extra-boolean-cast': 0,
|
|
57
|
-
'no-inner-declarations': 0,
|
|
58
|
-
'no-useless-escape': 0,
|
|
59
|
-
'no-case-declarations': 0,
|
|
60
|
-
'space-unary-ops': [2,
|
|
61
|
-
{
|
|
62
|
-
'words': true,
|
|
63
|
-
'nonwords': false,
|
|
64
|
-
},
|
|
65
|
-
],
|
|
66
|
-
'space-before-function-paren': [2, {
|
|
67
|
-
'anonymous': 'always',
|
|
68
|
-
'named': 'never',
|
|
69
|
-
'asyncArrow': 'always'
|
|
70
|
-
}],
|
|
71
|
-
'object-curly-spacing': [2, 'always'],
|
|
72
|
-
'no-console': 2,
|
|
73
|
-
}
|
|
74
|
-
};
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
enum LogLevel {
|
|
2
|
-
ERROR = 'error',
|
|
3
|
-
WARN = 'warn',
|
|
4
|
-
INFO = 'info',
|
|
5
|
-
LOG = 'log',
|
|
6
|
-
DEBUG = 'debug',
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export class Logger {
|
|
10
|
-
private metadata: { [key: string]: string } = {};
|
|
11
|
-
|
|
12
|
-
private send(logLevel: LogLevel, message: string) {
|
|
13
|
-
console[logLevel](
|
|
14
|
-
// Datadog automatically parses JSON-formatted logs
|
|
15
|
-
JSON.stringify({
|
|
16
|
-
message: message,
|
|
17
|
-
...this.metadata,
|
|
18
|
-
}),
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
public log(message: string) {
|
|
23
|
-
this.send(LogLevel.LOG, message);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
public error(message: string) {
|
|
27
|
-
this.send(LogLevel.ERROR, message);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
public warn(message: string) {
|
|
31
|
-
this.send(LogLevel.WARN, message);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
public info(message: string) {
|
|
35
|
-
this.send(LogLevel.INFO, message);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public debug(message: string) {
|
|
39
|
-
this.send(LogLevel.DEBUG, message);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
public decorate(metadata: { [key: string]: string }) {
|
|
43
|
-
this.metadata = metadata;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
public setMeta(key: string, value: string) {
|
|
47
|
-
this.metadata[key] = value;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
public clearMeta() {
|
|
51
|
-
this.metadata = {};
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export const logger = new Logger();
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import { logger } from '../logger';
|
|
3
|
-
|
|
4
|
-
export const extractAdditionalLoggingContext = (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
5
|
-
const additionalLoggingContextHeader = req.header('X-Unito-Additional-Logging-Context');
|
|
6
|
-
|
|
7
|
-
let additionalLoggingContext;
|
|
8
|
-
|
|
9
|
-
if (typeof additionalLoggingContextHeader === 'string') {
|
|
10
|
-
try {
|
|
11
|
-
additionalLoggingContext = JSON.parse(additionalLoggingContextHeader);
|
|
12
|
-
} catch (error) {
|
|
13
|
-
logger.warn(`Failed parsing header X-Unito-Additional-Logging-Context: ${additionalLoggingContextHeader}`);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
for (const [key, value] of Object.entries(additionalLoggingContext || {})) {
|
|
18
|
-
logger.setMeta(key, String(value));
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
next();
|
|
22
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import * as uuid from 'uuid';
|
|
3
|
-
import { logger } from '../logger';
|
|
4
|
-
|
|
5
|
-
export const extractCorrelationId = (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
6
|
-
const correlationIdHeader = req.header('X-Unito-Correlation-Id');
|
|
7
|
-
|
|
8
|
-
const correlationId = correlationIdHeader ?? uuid.v4();
|
|
9
|
-
|
|
10
|
-
logger.setMeta('correlation_id', correlationId);
|
|
11
|
-
|
|
12
|
-
next();
|
|
13
|
-
};
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { Request, Response, NextFunction } from 'express';
|
|
2
|
-
|
|
3
|
-
// This interface contains the different variables defined in the authorization methods
|
|
4
|
-
// of your integration (in the Integrations Platform registry).
|
|
5
|
-
//
|
|
6
|
-
// For example, if you add a "custom" authorization method in the registry with the variable "apiKey",
|
|
7
|
-
// you must add "apiKey: string" to this interface.
|
|
8
|
-
//
|
|
9
|
-
// For your convenience, this interface is initialized for an integration that can either have:
|
|
10
|
-
//
|
|
11
|
-
// * No authentication.
|
|
12
|
-
// * API key authentication (Bearer <accessToken>).
|
|
13
|
-
// * OAuth 2 authentication (Bearer <accessToken>).
|
|
14
|
-
//
|
|
15
|
-
export interface Credentials {
|
|
16
|
-
accessToken?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const extractCredentials = (req: Request, res: Response, next: NextFunction) => {
|
|
20
|
-
const credentialsHeader = req.header('X-Unito-Credentials');
|
|
21
|
-
|
|
22
|
-
let credentials: Credentials | null = null;
|
|
23
|
-
|
|
24
|
-
if (credentialsHeader) {
|
|
25
|
-
try {
|
|
26
|
-
credentials = JSON.parse(Buffer.from(credentialsHeader, 'base64').toString('utf8'));
|
|
27
|
-
} catch {
|
|
28
|
-
return res.status(400).json({ code: 400, message: 'Error parsing credentials' });
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// You can add additional verifications here to make sure you received
|
|
33
|
-
// all the necessary information to authenticate a user.
|
|
34
|
-
|
|
35
|
-
res.locals.credentials = credentials;
|
|
36
|
-
|
|
37
|
-
next();
|
|
38
|
-
};
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { Credentials } from './middlewares/credentials';
|
|
2
|
-
|
|
3
|
-
export interface RequestOptions {
|
|
4
|
-
queryParams?: { [key: string]: string };
|
|
5
|
-
method?: 'POST' | 'GET' | 'PATCH';
|
|
6
|
-
body?: Record<string, unknown>;
|
|
7
|
-
credentials?: Credentials;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const apiUrl = 'https://path_to_your_api';
|
|
11
|
-
|
|
12
|
-
export async function get<T>(endpoint: string, options: RequestOptions): Promise<T> {
|
|
13
|
-
return fetchWrapper(endpoint, options);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export async function post<T>(endpoint: string, options: RequestOptions): Promise<T> {
|
|
17
|
-
return fetchWrapper<T>(endpoint, {
|
|
18
|
-
...options,
|
|
19
|
-
method: 'POST',
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export async function patch<T>(endpoint: string, options: RequestOptions): Promise<T> {
|
|
24
|
-
return fetchWrapper<T>(endpoint, {
|
|
25
|
-
...options,
|
|
26
|
-
method: 'PATCH',
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async function fetchWrapper<T>(endpoint: string, options: RequestOptions): Promise<T> {
|
|
31
|
-
let absoluteUrl = [apiUrl, endpoint.charAt(0) === '/' ? endpoint.substring(1) : endpoint].join('/');
|
|
32
|
-
|
|
33
|
-
if (options.queryParams) {
|
|
34
|
-
absoluteUrl = `${absoluteUrl}?${new URLSearchParams(options.queryParams)}`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const headers: { [key: string]: string } = {
|
|
38
|
-
'Content-Type': 'application/json',
|
|
39
|
-
Accept: 'application/json',
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
if (options.credentials?.accessToken) {
|
|
43
|
-
headers['Authorization'] = `Bearer ${options.credentials?.accessToken}`;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const response = await fetch(absoluteUrl, {
|
|
47
|
-
method: options.method,
|
|
48
|
-
headers,
|
|
49
|
-
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
if (response.status === 200) {
|
|
53
|
-
return (await response.json()) as T;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const textResult = await response.text();
|
|
57
|
-
|
|
58
|
-
throw new Error(textResult);
|
|
59
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Request, Response, Router } from 'express';
|
|
2
|
-
|
|
3
|
-
import { CredentialAccount } from '@unito/integration-api';
|
|
4
|
-
|
|
5
|
-
export const router = Router();
|
|
6
|
-
|
|
7
|
-
router.get('/', async (_req: Request, res: Response<CredentialAccount>) => {
|
|
8
|
-
const account: CredentialAccount = {
|
|
9
|
-
id: 'me',
|
|
10
|
-
displayName: 'Me',
|
|
11
|
-
emails: [],
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
res.json(account);
|
|
15
|
-
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { Request, Response, Router } from 'express';
|
|
2
|
-
|
|
3
|
-
import { Item } from '@unito/integration-api';
|
|
4
|
-
|
|
5
|
-
export const router = Router();
|
|
6
|
-
|
|
7
|
-
router.get('/', (_req: Request, res: Response<Item>) => {
|
|
8
|
-
return res.send({
|
|
9
|
-
fields: {},
|
|
10
|
-
relations: [],
|
|
11
|
-
});
|
|
12
|
-
});
|