attio 0.0.1-experimental.20240926.1 → 0.0.1-experimental.20240927

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.
@@ -5,7 +5,14 @@ const serverErrorSchema = z.object({
5
5
  export async function handleError(response) {
6
6
  if (response.ok)
7
7
  return;
8
- const json = await response.json();
8
+ const text = await response.text();
9
+ let json;
10
+ try {
11
+ json = JSON.parse(text);
12
+ }
13
+ catch (error) {
14
+ throw new Error(`Error parsing JSON: ${JSON.stringify(text)}`);
15
+ }
9
16
  const error = serverErrorSchema.parse(json);
10
17
  throw new Error(error.message);
11
18
  }
@@ -1,9 +1,8 @@
1
1
  import { API } from "../env.js";
2
2
  import { handleError } from "./handle-error.js";
3
3
  import { makeHeaders } from "./make-headers.js";
4
- export async function removeConnectionDefinition({ token, devSlug, appId, }) {
5
- const major = 1;
6
- const response = await fetch(`${API}/developer-portal/accounts/${devSlug}/apps/${appId}/versions/${major}/connection-definitions`, {
4
+ export async function removeConnectionDefinition({ token, devSlug, appId, slug, major, }) {
5
+ const response = await fetch(`${API}/developer-portal/accounts/${devSlug}/apps/${appId}/versions/${major}/connection-definitions/${slug}`, {
7
6
  method: "DELETE",
8
7
  headers: makeHeaders(token),
9
8
  });
@@ -0,0 +1,52 @@
1
+ import { useMachine } from "@xstate/react";
2
+ import { Box, Text } from "ink";
3
+ import Spinner from "ink-spinner";
4
+ import { option } from "pastel";
5
+ import React from "react";
6
+ import { z } from "zod";
7
+ import { InitialInstructions } from "../../components/InitialInstructions.js";
8
+ import { Logo } from "../../components/Logo.js";
9
+ import { Table } from "../../components/Table.js";
10
+ import { listConnectionsMachine } from "../../machines/list-connections-machine.js";
11
+ export const description = "List all connections for the current major version of your Attio app";
12
+ export const options = z.object({
13
+ dev: z
14
+ .boolean()
15
+ .default(false)
16
+ .describe(option({ description: "Run in development mode (additional debugging info)" })),
17
+ });
18
+ const connectionTypeNames = {
19
+ "oauth2-code": "OAuth 2.0",
20
+ "secret": "Secret",
21
+ };
22
+ export default function ListConnections({ options: { dev } }) {
23
+ const [snapshot] = useMachine(listConnectionsMachine);
24
+ return (React.createElement(Box, { flexDirection: "column" },
25
+ dev && (React.createElement(Box, null,
26
+ React.createElement(Text, null, JSON.stringify(snapshot.value, null, 2)))),
27
+ React.createElement(Logo, null),
28
+ snapshot.matches("Show config instructions") && (React.createElement(InitialInstructions, { reason: snapshot.context.configError })),
29
+ snapshot.matches("No Connections") && (React.createElement(Box, { flexDirection: "column" },
30
+ React.createElement(Box, null,
31
+ React.createElement(Text, { color: "redBright" }, "This app has no connections."),
32
+ React.createElement(Text, null, " To add one, use:")),
33
+ React.createElement(Box, { flexDirection: "column", borderStyle: "round", width: 27, paddingX: 1, marginBottom: 1 },
34
+ React.createElement(Text, null, "attio connection add")))),
35
+ snapshot.matches("Loading Connections") && (React.createElement(Box, { flexDirection: "column" },
36
+ React.createElement(Box, null,
37
+ React.createElement(Text, null, "Loading connections... "),
38
+ React.createElement(Text, { color: "green" },
39
+ React.createElement(Spinner, { type: "dots" }))))),
40
+ snapshot.matches("Error") && (React.createElement(Box, null,
41
+ React.createElement(Text, { color: "red" }, snapshot.context.error))),
42
+ snapshot.matches("Display Connections") &&
43
+ snapshot.context.connections &&
44
+ snapshot.context.connections && (React.createElement(Box, null,
45
+ React.createElement(Table, { rows: Object.entries(snapshot.context.connections).map(([slug, connection]) => ({
46
+ "Slug": slug,
47
+ "Label": connection.label,
48
+ "Global": connection.global ? "Yes" : "No",
49
+ "Allow Multiple": connection.allow_multiple ? "Yes" : "No",
50
+ "Type": connectionTypeNames[connection.connection_type],
51
+ })) })))));
52
+ }
@@ -6,6 +6,7 @@ import React from "react";
6
6
  import { z } from "zod";
7
7
  import { InitialInstructions } from "../../components/InitialInstructions.js";
8
8
  import { Logo } from "../../components/Logo.js";
9
+ import { Select } from "../../components/Select.js";
9
10
  import { removeConnectionMachine } from "../../machines/remove-connection-machine.js";
10
11
  export const description = "Remove a connection from your Attio app";
11
12
  export const options = z.object({
@@ -15,27 +16,53 @@ export const options = z.object({
15
16
  .describe(option({ description: "Run in development mode (additional debugging info)" })),
16
17
  });
17
18
  export default function RemoveConnection({ options: { dev } }) {
18
- const [snapshot] = useMachine(removeConnectionMachine);
19
+ const [snapshot, send] = useMachine(removeConnectionMachine);
19
20
  return (React.createElement(Box, { flexDirection: "column" },
20
21
  dev && (React.createElement(Box, null,
21
22
  React.createElement(Text, null, JSON.stringify(snapshot.value, null, 2)))),
22
23
  React.createElement(Logo, null),
23
24
  snapshot.matches("Show config instructions") && (React.createElement(InitialInstructions, { reason: snapshot.context.configError })),
24
- snapshot.matches("No connection to remove") && (React.createElement(Box, { flexDirection: "column" },
25
+ snapshot.matches("No Connections") && (React.createElement(Box, { flexDirection: "column" },
25
26
  React.createElement(Box, null,
26
27
  React.createElement(Text, { color: "redBright" }, "This app has no connections to remove."),
27
28
  React.createElement(Text, null, " To add one, use:")),
28
29
  React.createElement(Box, { flexDirection: "column", borderStyle: "round", width: 27, paddingX: 1, marginBottom: 1 },
29
30
  React.createElement(Text, null, "attio connection add")))),
30
- snapshot.matches("Removing connection definition") && (React.createElement(Box, { flexDirection: "column" },
31
+ snapshot.matches("Loading Connections") && (React.createElement(Box, { flexDirection: "column" },
31
32
  React.createElement(Box, null,
32
- React.createElement(Text, null, "Removing connection definition..."),
33
+ React.createElement(Text, null, "Loading connections... "),
33
34
  React.createElement(Text, { color: "green" },
34
35
  React.createElement(Spinner, { type: "dots" }))))),
35
- snapshot.matches("Success") && (React.createElement(Box, { flexDirection: "column" },
36
+ snapshot.matches("Choosing a Connection") && snapshot.context.connections && (React.createElement(Box, { flexDirection: "column" },
37
+ React.createElement(Box, null,
38
+ React.createElement(Text, null, "Which connection would you like to remove?")),
36
39
  React.createElement(Box, null,
37
- React.createElement(Text, { color: "green" }, "SUCCESS!! \uD83C\uDF89 Your connection has been removed.")))),
38
- snapshot.matches("Error") && (React.createElement(Box, { flexDirection: "column" },
40
+ React.createElement(Select, { items: Object.entries(snapshot.context.connections).map(([slug, value]) => ({
41
+ value: slug,
42
+ label: value.label,
43
+ })), onSelect: (slug) => send({ type: "Connection Chosen", slug }) })))),
44
+ snapshot.matches("Confirm Removal") &&
45
+ snapshot.context.chosenConnectionSlug &&
46
+ snapshot.context.connections && (React.createElement(Box, { flexDirection: "column" },
39
47
  React.createElement(Box, null,
40
- React.createElement(Text, { color: "red" }, snapshot.context.error))))));
48
+ React.createElement(Text, null,
49
+ "Are you sure you want to remove \"",
50
+ snapshot.context.connections[snapshot.context.chosenConnectionSlug].label,
51
+ "\"?")),
52
+ React.createElement(Box, null,
53
+ React.createElement(Select, { items: [
54
+ { value: "Confirm", label: "Yes, remove it" },
55
+ { value: "Cancel", label: "Cancel" },
56
+ ], onSelect: (type) => send({ type }) })))),
57
+ snapshot.matches("Removing connection definition") && (React.createElement(Box, { flexDirection: "column" },
58
+ React.createElement(Box, null,
59
+ React.createElement(Text, null, "Removing connection definition..."),
60
+ React.createElement(Text, { color: "green" },
61
+ React.createElement(Spinner, { type: "dots" }))))),
62
+ snapshot.matches("Cancelled") && (React.createElement(Box, null,
63
+ React.createElement(Text, null, "No connections have been removed."))),
64
+ snapshot.matches("Success") && (React.createElement(Box, null,
65
+ React.createElement(Text, { color: "green" }, "SUCCESS!! \uD83C\uDF89 Your connection has been removed."))),
66
+ snapshot.matches("Error") && (React.createElement(Box, null,
67
+ React.createElement(Text, { color: "red" }, snapshot.context.error)))));
41
68
  }
@@ -0,0 +1,119 @@
1
+ import { assign, setup, fromCallback } from "xstate";
2
+ import { fetchConnections } from "../api/fetch-connections.js";
3
+ import { emptyConfig } from "../schema.js";
4
+ import { loadAppConfig, loadDeveloperConfig } from "./actors.js";
5
+ export const listConnectionsMachine = setup({
6
+ types: {
7
+ context: {},
8
+ events: {},
9
+ },
10
+ actors: {
11
+ loadDeveloperConfig,
12
+ loadAppConfig,
13
+ loadConnections: fromCallback(({ sendBack, input }) => {
14
+ fetchConnections({
15
+ token: input.developer.token,
16
+ devSlug: input.developer.slug,
17
+ appId: input.config.id,
18
+ major: input.config.major,
19
+ })
20
+ .then((connections) => sendBack({ type: "Connections Loaded", connections }))
21
+ .catch((error) => sendBack({ type: "Error", error: error.message }));
22
+ }),
23
+ },
24
+ actions: {
25
+ setError: assign({
26
+ error: (_, params) => params.error,
27
+ }),
28
+ setDeveloperConfig: assign({
29
+ developer: (_, params) => params,
30
+ }),
31
+ setConfigError: assign({
32
+ configError: (_, params) => params.configError,
33
+ }),
34
+ setConfig: assign({
35
+ config: (_, params) => params.config,
36
+ }),
37
+ setConnections: assign({
38
+ connections: (_, params) => params.connections,
39
+ }),
40
+ },
41
+ guards: {
42
+ "have connections": (_, params) => Boolean(params.connections && Object.keys(params.connections).length > 0),
43
+ },
44
+ }).createMachine({
45
+ context: ({ input }) => ({
46
+ developer: { slug: "", token: "" },
47
+ config: emptyConfig,
48
+ ...input,
49
+ }),
50
+ id: "List Connections Machine",
51
+ states: {
52
+ "Loading Developer Config": {
53
+ invoke: {
54
+ src: "loadDeveloperConfig",
55
+ },
56
+ on: {
57
+ "Developer Config Loaded": {
58
+ target: "Loading App Config",
59
+ actions: { type: "setDeveloperConfig", params: ({ event }) => event },
60
+ },
61
+ "No Developer Config": {
62
+ target: "Show config instructions",
63
+ actions: { type: "setConfigError", params: ({ event }) => event },
64
+ },
65
+ },
66
+ },
67
+ "Show config instructions": {
68
+ type: "final",
69
+ },
70
+ "Loading App Config": {
71
+ invoke: {
72
+ src: "loadAppConfig",
73
+ },
74
+ on: {
75
+ "Error": {
76
+ target: "Error",
77
+ actions: { type: "setError", params: ({ event }) => event },
78
+ },
79
+ "App Config Loaded": {
80
+ target: "Loading Connections",
81
+ actions: { type: "setConfig", params: ({ event }) => event },
82
+ },
83
+ },
84
+ },
85
+ "Error": {
86
+ type: "final",
87
+ },
88
+ "Loading Connections": {
89
+ invoke: {
90
+ src: "loadConnections",
91
+ input: ({ context }) => context,
92
+ },
93
+ on: {
94
+ "Connections Loaded": [
95
+ {
96
+ target: "Display Connections",
97
+ actions: { type: "setConnections", params: ({ event }) => event },
98
+ guard: {
99
+ type: "have connections",
100
+ params: ({ event }) => event,
101
+ },
102
+ },
103
+ "No Connections",
104
+ ],
105
+ "Error": {
106
+ target: "Error",
107
+ actions: { type: "setError", params: ({ event }) => event },
108
+ },
109
+ },
110
+ },
111
+ "No Connections": {
112
+ type: "final",
113
+ },
114
+ "Display Connections": {
115
+ type: "final",
116
+ },
117
+ },
118
+ initial: "Loading Developer Config",
119
+ });
@@ -1,4 +1,5 @@
1
1
  import { assign, setup, fromCallback } from "xstate";
2
+ import { fetchConnections } from "../api/fetch-connections.js";
2
3
  import { removeConnectionDefinition } from "../api/remove-connection-definition.js";
3
4
  import { emptyConfig } from "../schema.js";
4
5
  import { updateAppConfig } from "../util/app-config.js";
@@ -16,6 +17,8 @@ export const removeConnectionMachine = setup({
16
17
  token: input.developer.token,
17
18
  devSlug: input.developer.slug,
18
19
  appId: input.config.id,
20
+ slug: input.chosenConnectionSlug,
21
+ major: input.config.major,
19
22
  });
20
23
  updateAppConfig((config) => ({
21
24
  ...config,
@@ -31,6 +34,16 @@ export const removeConnectionMachine = setup({
31
34
  }),
32
35
  loadDeveloperConfig,
33
36
  loadAppConfig,
37
+ loadConnections: fromCallback(({ sendBack, input }) => {
38
+ fetchConnections({
39
+ token: input.developer.token,
40
+ devSlug: input.developer.slug,
41
+ appId: input.config.id,
42
+ major: input.config.major,
43
+ })
44
+ .then((connections) => sendBack({ type: "Connections Loaded", connections }))
45
+ .catch((error) => sendBack({ type: "Error", error: error.message }));
46
+ }),
34
47
  },
35
48
  actions: {
36
49
  clearError: assign({
@@ -45,12 +58,22 @@ export const removeConnectionMachine = setup({
45
58
  setConfigError: assign({
46
59
  configError: (_, params) => params.configError,
47
60
  }),
48
- setAppConfig: assign({
61
+ setConfig: assign({
49
62
  config: (_, params) => params.config,
50
63
  }),
64
+ setConnections: assign({
65
+ connections: (_, params) => params.connections,
66
+ }),
67
+ setConnection: assign({
68
+ chosenConnectionSlug: (_, params) => params.slug,
69
+ }),
70
+ setOnlyConnection: assign({
71
+ chosenConnectionSlug: (_, params) => Object.keys(params.connections)[0],
72
+ }),
51
73
  },
52
74
  guards: {
53
- "have a connection": (_, params) => Boolean(params.config.connection),
75
+ "have more than one connection": (_, params) => Boolean(params.connections && Object.keys(params.connections).length > 1),
76
+ "have only one connection": (_, params) => Boolean(params.connections && Object.keys(params.connections).length === 1),
54
77
  },
55
78
  }).createMachine({
56
79
  context: ({ input }) => ({
@@ -88,9 +111,8 @@ export const removeConnectionMachine = setup({
88
111
  actions: { type: "setError", params: ({ event }) => event },
89
112
  },
90
113
  "App Config Loaded": {
91
- target: "Do we have a connection?",
92
- actions: { type: "setAppConfig", params: ({ event }) => event },
93
- reenter: true,
114
+ target: "Loading Connections",
115
+ actions: { type: "setConfig", params: ({ event }) => event },
94
116
  },
95
117
  },
96
118
  },
@@ -100,7 +122,11 @@ export const removeConnectionMachine = setup({
100
122
  "Removing connection definition": {
101
123
  invoke: {
102
124
  src: "removeConnectionDefinition",
103
- input: ({ context }) => context,
125
+ input: ({ context }) => ({
126
+ developer: context.developer,
127
+ config: context.config,
128
+ chosenConnectionSlug: context.chosenConnectionSlug,
129
+ }),
104
130
  },
105
131
  on: {
106
132
  Success: "Success",
@@ -113,32 +139,67 @@ export const removeConnectionMachine = setup({
113
139
  "Success": {
114
140
  type: "final",
115
141
  },
116
- "Do we have a connection?": {
117
- always: [
118
- {
119
- target: "Removing connection definition",
120
- guard: { type: "have a connection", params: ({ context }) => context },
121
- reenter: true,
122
- },
123
- {
124
- target: "Removing connection definition (just in case)",
125
- reenter: true,
142
+ "Loading Connections": {
143
+ invoke: {
144
+ src: "loadConnections",
145
+ input: ({ context }) => context,
146
+ },
147
+ on: {
148
+ "Connections Loaded": [
149
+ {
150
+ target: "Choosing a Connection",
151
+ actions: { type: "setConnections", params: ({ event }) => event },
152
+ guard: {
153
+ type: "have more than one connection",
154
+ params: ({ event }) => event,
155
+ },
156
+ },
157
+ {
158
+ target: "Confirm Removal",
159
+ guard: {
160
+ type: "have only one connection",
161
+ params: ({ event }) => event,
162
+ },
163
+ reenter: true,
164
+ actions: [
165
+ {
166
+ type: "setConnections",
167
+ params: ({ event }) => event,
168
+ },
169
+ {
170
+ type: "setOnlyConnection",
171
+ params: ({ event }) => event,
172
+ },
173
+ ],
174
+ },
175
+ "No Connections",
176
+ ],
177
+ "Error": {
178
+ target: "Error",
179
+ actions: { type: "setError", params: ({ event }) => event },
126
180
  },
127
- ],
181
+ },
128
182
  },
129
- "No connection to remove": {
183
+ "No Connections": {
130
184
  type: "final",
131
185
  },
132
- "Removing connection definition (just in case)": {
133
- invoke: {
134
- src: "removeConnectionDefinition",
135
- input: ({ context }) => context,
186
+ "Choosing a Connection": {
187
+ on: {
188
+ "Connection Chosen": {
189
+ target: "Confirm Removal",
190
+ actions: { type: "setConnection", params: ({ event }) => event },
191
+ reenter: true,
192
+ },
136
193
  },
194
+ },
195
+ "Confirm Removal": {
137
196
  on: {
138
- Error: "No connection to remove",
139
- Success: "Success",
197
+ Confirm: "Removing connection definition",
198
+ Cancel: "Cancelled",
140
199
  },
141
- description: `In case our local config is out of date with the server, let's go ahead and send the removal request to the server just in case.`,
200
+ },
201
+ "Cancelled": {
202
+ type: "final",
142
203
  },
143
204
  },
144
205
  initial: "Loading Developer Config",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "attio",
3
- "version": "0.0.1-experimental.20240926.1",
3
+ "version": "0.0.1-experimental.20240927",
4
4
  "bin": "lib/attio.js",
5
5
  "type": "module",
6
6
  "files": [