Skip to content

Commit fc1dd44

Browse files
committed
Added the new integrations system into the mono-repo
1 parent bacd76c commit fc1dd44

54 files changed

Lines changed: 34544 additions & 115 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.vscode/launch.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@
1717
"name": "Chrome webapp",
1818
"url": "http://localhost:3000",
1919
"webRoot": "${workspaceFolder}/apps/webapp/app"
20+
},
21+
{
22+
"type": "node",
23+
"request": "launch",
24+
"name": "Debug Current Test File",
25+
"autoAttachChildProcesses": true,
26+
"skipFiles": ["<node_internals>/**", "**/node_modules/**"],
27+
"program": "${workspaceRoot}/node_modules/vitest/vitest.mjs",
28+
"args": ["run", "${relativeFile}"],
29+
"smartStep": true,
30+
"console": "integratedTerminal"
2031
}
2132
]
2233
}

apps/integrations/.eslintrc.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"extends": [
3+
"eslint:recommended",
4+
"plugin:@typescript-eslint/recommended",
5+
"prettier"
6+
],
7+
"parser": "@typescript-eslint/parser",
8+
"plugins": ["@typescript-eslint"],
9+
"root": true,
10+
"rules": {
11+
"@typescript-eslint/explicit-function-return-type": "off",
12+
"@typescript-eslint/no-explicit-any": "off",
13+
"@typescript-eslint/no-unused-vars": "off",
14+
"no-console": "off"
15+
}
16+
}

apps/integrations/.gitignore

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
lerna-debug.log*
8+
9+
# Diagnostic reports (https://nodejs.org/api/report.html)
10+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11+
12+
# Runtime data
13+
pids
14+
*.pid
15+
*.seed
16+
*.pid.lock
17+
18+
# Directory for instrumented libs generated by jscoverage/JSCover
19+
lib-cov
20+
21+
# Coverage directory used by tools like istanbul
22+
coverage
23+
*.lcov
24+
25+
# nyc test coverage
26+
.nyc_output
27+
28+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29+
.grunt
30+
31+
# Bower dependency directory (https://bower.io/)
32+
bower_components
33+
34+
# node-waf configuration
35+
.lock-wscript
36+
37+
# Compiled binary addons (https://nodejs.org/api/addons.html)
38+
build/Release
39+
40+
# Dependency directories
41+
node_modules/
42+
jspm_packages/
43+
44+
# TypeScript v1 declaration files
45+
typings/
46+
47+
# TypeScript cache
48+
*.tsbuildinfo
49+
50+
# Optional npm cache directory
51+
.npm
52+
53+
# Optional eslint cache
54+
.eslintcache
55+
56+
# Microbundle cache
57+
.rpt2_cache/
58+
.rts2_cache_cjs/
59+
.rts2_cache_es/
60+
.rts2_cache_umd/
61+
62+
# Optional REPL history
63+
.node_repl_history
64+
65+
# Output of 'npm pack'
66+
*.tgz
67+
68+
# Yarn Integrity file
69+
.yarn-integrity
70+
71+
# dotenv environment variables file
72+
.env
73+
.env.test
74+
75+
# parcel-bundler cache (https://parceljs.org/)
76+
.cache
77+
78+
# Next.js build output
79+
.next
80+
81+
# Nuxt.js build / generate output
82+
.nuxt
83+
dist
84+
85+
# Gatsby files
86+
.cache/
87+
# Comment in the public line in if your project uses Gatsby and *not* Next.js
88+
# https://nextjs.org/blog/next-9-1#public-directory-support
89+
# public
90+
91+
# vuepress build output
92+
.vuepress/dist
93+
94+
# Serverless directories
95+
.serverless/
96+
97+
# FuseBox cache
98+
.fusebox/
99+
100+
# DynamoDB Local files
101+
.dynamodb/
102+
103+
# TernJS port file
104+
.tern-port
105+
.DS_Store

apps/integrations/.prettierignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
3+
/build
4+
.env

apps/integrations/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# trigger-integrations
2+
You can create API integrations using this data and system

apps/integrations/package.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"private": true,
3+
"name": "integrations",
4+
"version": "1.0.0",
5+
"description": "API integrations in a format that can be used to generate clients",
6+
"main": "src/index.ts",
7+
"scripts": {
8+
"test": "vitest",
9+
"lint": "eslint src/**",
10+
"lint-fix": "eslint --fix src/**",
11+
"generate-sdks": "tsx src/trigger/sdk/generate.ts"
12+
},
13+
"dependencies": {
14+
"@cfworker/json-schema": "^1.12.5",
15+
"@trigger.dev/sdk": "^0.2.13",
16+
"json-pointer": "^0.6.2",
17+
"json-schema-deref-sync": "^0.14.0",
18+
"json-schema-to-typescript": "^11.0.3",
19+
"node-fetch": "^3.3.0"
20+
},
21+
"devDependencies": {
22+
"@types/eslint": "^8.4.6",
23+
"@types/json-pointer": "^1.0.31",
24+
"@types/node": "^18.13.0",
25+
"@typescript-eslint/eslint-plugin": "^5.51.0",
26+
"eslint": "^8.34.0",
27+
"eslint-config-prettier": "^8.5.0",
28+
"eslint-config-standard-with-typescript": "^34.0.0",
29+
"eslint-plugin-import": "^2.27.5",
30+
"eslint-plugin-n": "^15.6.1",
31+
"eslint-plugin-promise": "^6.1.1",
32+
"rimraf": "^4.1.2",
33+
"ts-morph": "^17.0.1",
34+
"tsup": "^6.6.2",
35+
"tsx": "^3.12.3",
36+
"typescript": "^4.9.5",
37+
"vite": "^4.1.1",
38+
"vite-tsconfig-paths": "^4.0.5",
39+
"vitest": "^0.28.4"
40+
}
41+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { CacheService } from "core/cache/types";
2+
import { Endpoint } from "core/endpoint/types";
3+
import { RequestData } from "core/request/types";
4+
import { Metadata } from "./types";
5+
import { makeInputSpec, makeOutputSpec } from "./utilities";
6+
7+
export const makeRequestAction = (endpoint: Endpoint) => {
8+
const action = async (
9+
data: RequestData,
10+
cache?: CacheService,
11+
metadata?: Metadata
12+
) => {
13+
//a simple request doesn't use the cache or metadata
14+
return await endpoint.request(data);
15+
};
16+
17+
return {
18+
name: endpoint.spec.endpointSpec.metadata.name,
19+
description: endpoint.spec.endpointSpec.metadata.description,
20+
path: endpoint.spec.endpointSpec.path,
21+
method: endpoint.spec.endpointSpec.method,
22+
spec: {
23+
input: makeInputSpec(endpoint),
24+
output: makeOutputSpec(endpoint),
25+
},
26+
action,
27+
};
28+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { CacheService } from "core/cache/types";
2+
import { EndpointSpec, HTTPMethod } from "core/endpoint/types";
3+
import { RequestData, RequestResponse } from "core/request/types";
4+
5+
export type InputSpec = {
6+
security?: EndpointSpec["security"];
7+
parameters?: EndpointSpec["parameters"];
8+
body?: NonNullable<EndpointSpec["request"]["body"]>["schema"];
9+
};
10+
11+
export type OutputSpec = {
12+
responses: EndpointSpec["responses"];
13+
};
14+
15+
export type Metadata = Record<string, string>;
16+
17+
export type Action = {
18+
name: string;
19+
description: string;
20+
path: string;
21+
method: HTTPMethod;
22+
spec: {
23+
input: InputSpec;
24+
output: OutputSpec;
25+
};
26+
action: (
27+
/** The data to be sent to the endpoint */
28+
data: RequestData,
29+
/** The cache service to use for caching */
30+
cache?: CacheService,
31+
/** Additional metadata that can be used to modify the request */
32+
metadata?: Metadata
33+
) => Promise<RequestResponse>;
34+
};
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Endpoint } from "core/endpoint/types";
2+
3+
export function makeInputSpec(endpoint: Endpoint) {
4+
return {
5+
security: endpoint.spec.endpointSpec.security,
6+
parameters: endpoint.spec.endpointSpec.parameters,
7+
body: endpoint.spec.endpointSpec.request.body?.schema,
8+
};
9+
}
10+
11+
export function makeOutputSpec(endpoint: Endpoint) {
12+
return {
13+
responses: endpoint.spec.endpointSpec.responses,
14+
};
15+
}
16+
17+
export function combineSecurityScopes(
18+
securities: (Record<string, string[]> | undefined)[]
19+
): Record<string, string[]> | undefined {
20+
const securityA = securities[0];
21+
const securityB = securities[1];
22+
23+
//where a key already exists, concatenate the scope arrays together
24+
//where a key does not exist, add it to the object
25+
let combined: Record<string, string[]> = {};
26+
27+
if (securityA) {
28+
combined = {
29+
...securityA,
30+
};
31+
}
32+
33+
if (securityB) {
34+
for (const key in securityB) {
35+
if (combined[key]) {
36+
combined[key] = [...combined[key], ...securityB[key]];
37+
} else {
38+
combined[key] = securityB[key];
39+
}
40+
}
41+
}
42+
43+
if (securities.length > 2) {
44+
return combineSecurityScopes([combined, ...securities.slice(2)]);
45+
}
46+
47+
return combined;
48+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { EndpointSpec } from "core/endpoint/types";
2+
import { FetchConfig } from "core/request/types";
3+
import { expect, test } from "vitest";
4+
import { checkRequiredScopes, applyCredentials } from "./credentials";
5+
import { AuthCredentials, IntegrationAuthentication } from "./types";
6+
7+
test("Required scopes present", async () => {
8+
const credentials: AuthCredentials = {
9+
type: "oauth2",
10+
name: "authName",
11+
accessToken: "token",
12+
scopes: ["scope1", "scope2"],
13+
};
14+
15+
const requiredScopes = ["scope1", "scope2"];
16+
17+
const result = checkRequiredScopes(requiredScopes, credentials);
18+
expect(result.success).toEqual(true);
19+
});
20+
21+
test("Required scopes missing", async () => {
22+
const credentials: AuthCredentials = {
23+
type: "oauth2",
24+
name: "authName",
25+
accessToken: "token",
26+
scopes: ["scope1", "scope2"],
27+
};
28+
29+
const requiredScopes = ["scope1", "scope2", "scope3"];
30+
31+
const result = checkRequiredScopes(requiredScopes, credentials);
32+
expect(result.success).toEqual(false);
33+
if (result.success) throw new Error("Should not be success");
34+
expect(result.missingScopes).toEqual(["scope3"]);
35+
});
36+
37+
test("Applied credentials", async () => {
38+
const credentials: AuthCredentials = {
39+
type: "oauth2",
40+
name: "authName",
41+
accessToken: "123456",
42+
scopes: ["scope1", "scope2"],
43+
};
44+
const endpointSecurity: EndpointSpec["security"] = {
45+
authName: ["scope1", "scope2"],
46+
};
47+
48+
const integrationAuthentication: IntegrationAuthentication = {
49+
authName: {
50+
type: "oauth2",
51+
placement: {
52+
in: "header",
53+
type: "bearer",
54+
key: "Authorization",
55+
},
56+
authorizationUrl: "https://example.com",
57+
tokenUrl: "https://example.com",
58+
flow: "accessCode",
59+
scopes: {
60+
scope1: "scope1",
61+
scope2: "scope2",
62+
},
63+
},
64+
};
65+
66+
const existingFetch: FetchConfig = {
67+
url: "https://example.com",
68+
method: "GET",
69+
headers: {
70+
"Content-Type": "application/json",
71+
},
72+
};
73+
74+
const fetchConfig = applyCredentials(existingFetch, {
75+
endpointSecurity,
76+
authentication: integrationAuthentication,
77+
credentials,
78+
});
79+
80+
expect(fetchConfig.headers.Authorization).toEqual("Bearer 123456");
81+
expect(fetchConfig.headers["Content-Type"]).toEqual("application/json");
82+
});

0 commit comments

Comments
 (0)