attio 0.0.1-experimental.20240925 → 0.0.1-experimental.20240926.1
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/lib/api/add-connection-definition.js +9 -5
- package/lib/api/fetch-connections.js +21 -0
- package/lib/api/start-graphql-server.js +32 -0
- package/lib/client/forms/build.d.ts +1 -1
- package/lib/client/forms/connection.d.ts +17 -0
- package/lib/client/forms/index.d.ts +1 -0
- package/lib/client/forms/path.d.ts +7 -3
- package/lib/client/index.d.ts +1 -0
- package/lib/commands/connection/add.js +46 -15
- package/lib/commands/dev.js +3 -1
- package/lib/machines/add-connection-machine.js +213 -80
- package/lib/machines/dev-machine.js +50 -0
- package/lib/schema.graphql +536 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/util/find-available-port.js +21 -0
- package/package.json +8 -2
- package/schema.graphql +536 -0
|
@@ -7,14 +7,18 @@ const addConnectionSchema = z.object({
|
|
|
7
7
|
app_id: z.string(),
|
|
8
8
|
connection_definition_id: z.string(),
|
|
9
9
|
});
|
|
10
|
-
export async function addConnectionDefinition({ token, devSlug, appId,
|
|
10
|
+
export async function addConnectionDefinition({ token, devSlug, appId, slug, label, description, allowMultiple, global, connectionType, clientId, clientSecret, authorizeUrl, accessTokenUrl, major, scopes, }) {
|
|
11
11
|
const connectionDefinitionId = uuid();
|
|
12
12
|
const body = {
|
|
13
|
-
connection_type:
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
connection_type: connectionType,
|
|
14
|
+
slug,
|
|
15
|
+
label,
|
|
16
|
+
description,
|
|
17
|
+
allow_multiple: allowMultiple,
|
|
18
|
+
global,
|
|
19
|
+
major,
|
|
16
20
|
};
|
|
17
|
-
if (
|
|
21
|
+
if (connectionType === "oauth2-code") {
|
|
18
22
|
body.authorize_url = authorizeUrl;
|
|
19
23
|
body.access_token_url = accessTokenUrl;
|
|
20
24
|
body.client_id = clientId;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { API } from "../env.js";
|
|
3
|
+
import { handleError } from "./handle-error.js";
|
|
4
|
+
import { makeHeaders } from "./make-headers.js";
|
|
5
|
+
const connectionDefinitionsResponseSchema = z.object({
|
|
6
|
+
connection_definitions: z.record(z.string(), z.object({
|
|
7
|
+
label: z.string(),
|
|
8
|
+
connection_type: z.enum(["oauth2-code", "secret"]),
|
|
9
|
+
description: z.string().nullable(),
|
|
10
|
+
global: z.boolean(),
|
|
11
|
+
allow_multiple: z.boolean(),
|
|
12
|
+
})),
|
|
13
|
+
});
|
|
14
|
+
export async function fetchConnections({ token, devSlug, appId, major, }) {
|
|
15
|
+
const response = await fetch(`${API}/developer-portal/accounts/${devSlug}/apps/${appId}/versions/${major}/connection-definitions`, {
|
|
16
|
+
method: "GET",
|
|
17
|
+
headers: makeHeaders(token),
|
|
18
|
+
});
|
|
19
|
+
await handleError(response);
|
|
20
|
+
return connectionDefinitionsResponseSchema.parse(await response.json()).connection_definitions;
|
|
21
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { graphqlServer } from "@hono/graphql-server";
|
|
2
|
+
import { serve } from "@hono/node-server";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import { buildSchema } from "graphql";
|
|
5
|
+
import { Hono } from "hono";
|
|
6
|
+
import path, { dirname } from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
import { findAvailablePort } from "../util/find-available-port.js";
|
|
9
|
+
export function startGraphqlServer(sendBack) {
|
|
10
|
+
let server = null;
|
|
11
|
+
const startServer = async () => {
|
|
12
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
13
|
+
const currentDirPath = dirname(currentFilePath);
|
|
14
|
+
const schemaPath = path.resolve(currentDirPath, "..", "schema.graphql");
|
|
15
|
+
const schemaString = fs.readFileSync(schemaPath, "utf8");
|
|
16
|
+
const port = await findAvailablePort(8700);
|
|
17
|
+
const schema = buildSchema(schemaString);
|
|
18
|
+
const app = new Hono();
|
|
19
|
+
const rootResolver = () => {
|
|
20
|
+
return {};
|
|
21
|
+
};
|
|
22
|
+
app.use("/graphql", graphqlServer({ schema, rootResolver, graphiql: true }));
|
|
23
|
+
server = serve({ fetch: app.fetch, port });
|
|
24
|
+
sendBack({ type: "GraphQL Server Started", port });
|
|
25
|
+
};
|
|
26
|
+
startServer();
|
|
27
|
+
return () => {
|
|
28
|
+
if (!server)
|
|
29
|
+
return;
|
|
30
|
+
server.close();
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A connection form value.
|
|
3
|
+
*/
|
|
4
|
+
export interface FormConnection {
|
|
5
|
+
type: "connection";
|
|
6
|
+
isOptional: boolean;
|
|
7
|
+
optional(): FormConnection;
|
|
8
|
+
}
|
|
9
|
+
export interface Connection {
|
|
10
|
+
connectionId: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Create a new connection form value.
|
|
14
|
+
*/
|
|
15
|
+
export declare function connection({ connectionSlug }: {
|
|
16
|
+
connectionSlug: string;
|
|
17
|
+
}): FormConnection;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Prettify } from "../util/prettify";
|
|
2
2
|
import { FormArray } from "./array";
|
|
3
|
+
import { FormConnection, Connection } from "./connection";
|
|
3
4
|
import { FormNumber } from "./number";
|
|
4
5
|
import { FormString } from "./string";
|
|
5
6
|
type Assert<TCat, TAnimal> = TCat extends TAnimal ? TCat : never;
|
|
6
7
|
type Join<A extends string | number | null, B extends string | number> = A extends null ? B : `${A}.${B}`;
|
|
7
|
-
export type ValueType = "string" | "number" | "array";
|
|
8
|
-
export type SchemaNode = FormString | FormNumber | FormArray<any> | {
|
|
8
|
+
export type ValueType = "string" | "number" | "array" | "connection";
|
|
9
|
+
export type SchemaNode = FormString | FormNumber | FormConnection | FormArray<any> | {
|
|
9
10
|
type: "object";
|
|
10
11
|
};
|
|
11
12
|
type PathWithTypeRecursive<TState, TRoot extends string | number | null> = TState extends FormString ? {
|
|
@@ -14,6 +15,9 @@ type PathWithTypeRecursive<TState, TRoot extends string | number | null> = TStat
|
|
|
14
15
|
} : TState extends FormNumber ? {
|
|
15
16
|
path: TRoot;
|
|
16
17
|
type: "number";
|
|
18
|
+
} : TState extends FormConnection ? {
|
|
19
|
+
path: TRoot;
|
|
20
|
+
type: "connection";
|
|
17
21
|
} : TState extends FormArray<infer TElement> ? {
|
|
18
22
|
path: TRoot;
|
|
19
23
|
type: "array";
|
|
@@ -28,7 +32,7 @@ export type Path<TState> = PathWithType<TState>["path"];
|
|
|
28
32
|
export type PathTo<TState, TType extends ValueType> = Extract<PathWithType<TState>, {
|
|
29
33
|
type: TType;
|
|
30
34
|
}>["path"];
|
|
31
|
-
export type ValueOf<TState> = TState extends FormString ? string : TState extends FormNumber ? number : TState extends FormArray<infer TElement> ? Array<ValueOf<TElement>> : {
|
|
35
|
+
export type ValueOf<TState> = TState extends FormString ? string : TState extends FormNumber ? number : TState extends FormConnection ? Connection : TState extends FormArray<infer TElement> ? Array<ValueOf<TElement>> : {
|
|
32
36
|
[K in keyof TState]: K extends string ? ValueOf<TState[K]> : never;
|
|
33
37
|
};
|
|
34
38
|
export {};
|
package/lib/client/index.d.ts
CHANGED
|
@@ -4,5 +4,6 @@ export * as Forms from "./forms/index.js";
|
|
|
4
4
|
export { FormArray } from "./forms/array.js";
|
|
5
5
|
export { FormNumber } from "./forms/number.js";
|
|
6
6
|
export { FormString } from "./forms/string.js";
|
|
7
|
+
export { FormConnection, Connection } from "./forms/connection.js";
|
|
7
8
|
export * from "./forms/path.js";
|
|
8
9
|
export { Form } from "./forms/build.js";
|
|
@@ -8,17 +8,27 @@ import { z } from "zod";
|
|
|
8
8
|
import { InitialInstructions } from "../../components/InitialInstructions.js";
|
|
9
9
|
import { Logo } from "../../components/Logo.js";
|
|
10
10
|
import { Select } from "../../components/Select.js";
|
|
11
|
-
import { addConnectionMachine,
|
|
11
|
+
import { addConnectionMachine, connectionTypes } from "../../machines/add-connection-machine.js";
|
|
12
12
|
export const description = "Create a new connection for your Attio app";
|
|
13
13
|
export const options = z.object({
|
|
14
|
+
slug: z
|
|
15
|
+
.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe(option({ description: "A unique slug for your connection" })),
|
|
18
|
+
label: z
|
|
19
|
+
.string()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe(option({ description: "The label for your connection that will be displayed to users" })),
|
|
22
|
+
description: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe(option({
|
|
26
|
+
description: "A more detailed description of your connection that will be displayed to users",
|
|
27
|
+
})),
|
|
14
28
|
type: z
|
|
15
29
|
.enum(["secret", "oauth2-code"])
|
|
16
30
|
.optional()
|
|
17
31
|
.describe(option({ description: "The type of connection to create" })),
|
|
18
|
-
audience: z
|
|
19
|
-
.enum(["workspace", "workspace-member"])
|
|
20
|
-
.optional()
|
|
21
|
-
.describe(option({ description: "The audience for the connection" })),
|
|
22
32
|
authorizeUrl: z
|
|
23
33
|
.string()
|
|
24
34
|
.url("Invalid URL, e.g. https://authorization-server.com/authorize")
|
|
@@ -51,11 +61,13 @@ export const options = z.object({
|
|
|
51
61
|
.default(false)
|
|
52
62
|
.describe(option({ description: "Run in development mode (additional debugging info)" })),
|
|
53
63
|
});
|
|
54
|
-
export default function AddConnection({ options: { type: connectionType,
|
|
64
|
+
export default function AddConnection({ options: { slug, label, description, type: connectionType, authorizeUrl, accessTokenUrl, scopes, clientId, clientSecret, dev, }, }) {
|
|
55
65
|
const [snapshot, send] = useMachine(addConnectionMachine, {
|
|
56
66
|
input: {
|
|
67
|
+
slug,
|
|
68
|
+
label,
|
|
69
|
+
description,
|
|
57
70
|
connectionType,
|
|
58
|
-
audience,
|
|
59
71
|
authorizeUrl,
|
|
60
72
|
accessTokenUrl,
|
|
61
73
|
scopes,
|
|
@@ -69,22 +81,41 @@ export default function AddConnection({ options: { type: connectionType, audienc
|
|
|
69
81
|
React.createElement(Logo, null),
|
|
70
82
|
React.createElement(Box, { flexDirection: "column" },
|
|
71
83
|
snapshot.matches("Show config instructions") && (React.createElement(InitialInstructions, { reason: snapshot.context.configError })),
|
|
72
|
-
snapshot.matches("
|
|
84
|
+
snapshot.matches("Ask for slug") && (React.createElement(React.Fragment, null,
|
|
73
85
|
React.createElement(Box, null,
|
|
74
|
-
React.createElement(Text,
|
|
75
|
-
React.createElement(
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
React.createElement(Text, null, "Unique Slug: "),
|
|
87
|
+
React.createElement(TextInput, { value: snapshot.context.slug ?? "", placeholder: "my-access-token", onChange: (slug) => send({ type: "Update Slug", slug }), onSubmit: () => send({ type: "Submit" }) })))),
|
|
88
|
+
snapshot.matches("Ask for label") && (React.createElement(React.Fragment, null,
|
|
89
|
+
React.createElement(Box, null,
|
|
90
|
+
React.createElement(Text, null, "Provide a label for your connection. This will be displayed to users. (You can edit this later)")),
|
|
91
|
+
React.createElement(Box, null,
|
|
92
|
+
React.createElement(TextInput, { value: snapshot.context.label ?? "", placeholder: "My Awesome Connection", onChange: (label) => send({ type: "Update Label", label }), onSubmit: () => send({ type: "Submit" }) })))),
|
|
93
|
+
snapshot.matches("Ask for description") && (React.createElement(React.Fragment, null,
|
|
94
|
+
React.createElement(Box, null,
|
|
95
|
+
React.createElement(Text, null, "Provide an optional detailed description for your connection. This will be displayed to users. (You can edit this later)")),
|
|
96
|
+
React.createElement(Box, null,
|
|
97
|
+
React.createElement(TextInput, { value: snapshot.context.description ?? "", onChange: (description) => send({ type: "Update Description", description }), onSubmit: () => send({ type: "Submit" }) })))),
|
|
78
98
|
snapshot.matches("Ask for connection type") && (React.createElement(React.Fragment, null,
|
|
79
99
|
React.createElement(Box, null,
|
|
80
100
|
React.createElement(Text, null, "What type of connection will this be?")),
|
|
81
101
|
React.createElement(Box, null,
|
|
82
102
|
React.createElement(Select, { items: connectionTypes, onSelect: (connectionType) => send({ type: "Choose Connection Type", connectionType }) })))),
|
|
83
|
-
snapshot.matches("Ask for
|
|
103
|
+
snapshot.matches("Ask for global") && (React.createElement(React.Fragment, null,
|
|
104
|
+
React.createElement(Box, null,
|
|
105
|
+
React.createElement(Text, null, "Should users of your app be allowed to add multiple connections?")),
|
|
106
|
+
React.createElement(Box, null,
|
|
107
|
+
React.createElement(Select, { items: [
|
|
108
|
+
{ value: true, label: "Yes" },
|
|
109
|
+
{ value: false, label: "No" },
|
|
110
|
+
], onSelect: (global) => send({ type: "Choose Global", global }) })))),
|
|
111
|
+
snapshot.matches("Ask for allow multiple") && (React.createElement(React.Fragment, null,
|
|
84
112
|
React.createElement(Box, null,
|
|
85
|
-
React.createElement(Text, null, "
|
|
113
|
+
React.createElement(Text, null, "Should this connection be allowed to be used multiple times?")),
|
|
86
114
|
React.createElement(Box, null,
|
|
87
|
-
React.createElement(Select, { items:
|
|
115
|
+
React.createElement(Select, { items: [
|
|
116
|
+
{ value: true, label: "Yes" },
|
|
117
|
+
{ value: false, label: "No" },
|
|
118
|
+
], onSelect: (allowMultiple) => send({ type: "Choose Allow Multiple", allowMultiple }) })))),
|
|
88
119
|
snapshot.matches("Ask for Authorize URL") && (React.createElement(React.Fragment, null,
|
|
89
120
|
React.createElement(Box, null,
|
|
90
121
|
React.createElement(Text, null, "OAuth Authorize URL: "),
|
package/lib/commands/dev.js
CHANGED
|
@@ -88,7 +88,9 @@ export default function Dev({ options: { debug } }) {
|
|
|
88
88
|
React.createElement(Text, { color: "redBright" }, "\u274C Upload Error"),
|
|
89
89
|
React.createElement(Text, null,
|
|
90
90
|
"\u2013 ",
|
|
91
|
-
snapshot.context.uploadError?.message))))
|
|
91
|
+
snapshot.context.uploadError?.message)))),
|
|
92
|
+
snapshot.matches({ Watching: { Graphql: "Started" } }) && (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
|
|
93
|
+
React.createElement(Text, null, "Press \"o\" to open GraphQL Explorer")))),
|
|
92
94
|
hasErrors && (React.createElement(ScrollBox, { borderStyle: "round", padding: 1 },
|
|
93
95
|
jsError && renderBuildErrors(jsError),
|
|
94
96
|
tsErrors && renderTypeScriptErrors(tsErrors)))));
|