@integration-app/react 0.2.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. package/dist/index.d.ts +396 -165
  2. package/dist/index.js +527 -234
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.module.d.ts +396 -165
  5. package/dist/index.module.mjs +515 -235
  6. package/dist/index.module.mjs.map +1 -1
  7. package/dist/index.umd.d.ts +396 -165
  8. package/dist/index.umd.js +527 -238
  9. package/dist/index.umd.js.map +1 -1
  10. package/package.json +5 -2
  11. package/src/actions/useAction.ts +35 -0
  12. package/src/actions/useActionInstance.ts +56 -0
  13. package/src/actions/useActionInstances.ts +11 -0
  14. package/src/actions/useActions.ts +11 -0
  15. package/src/app-events/useAppEventSubscription.ts +6 -6
  16. package/src/app-events/useAppEventSubscriptions.ts +5 -4
  17. package/src/app-events/useAppEventType.ts +10 -7
  18. package/src/app-events/useAppEventTypes.ts +2 -4
  19. package/src/app-events/useAppEvents.ts +2 -4
  20. package/src/contexts/index.tsx +4 -0
  21. package/src/customers/useCustomer.ts +19 -0
  22. package/src/customers/useCustomers.ts +11 -0
  23. package/src/data-collections/useDataCollectionSpec.ts +26 -0
  24. package/src/data-links/useDataLinkTable.ts +18 -0
  25. package/src/data-links/useDataLinkTableInstance.ts +39 -0
  26. package/src/data-links/useDataLinkTableInstances.ts +19 -0
  27. package/src/data-links/useDataLinkTables.ts +11 -0
  28. package/src/data-sources/useDataSource.ts +29 -6
  29. package/src/data-sources/useDataSourceEvents.ts +2 -4
  30. package/src/data-sources/useDataSourceInstance.ts +120 -26
  31. package/src/data-sources/useDataSourceInstanceCollection.ts +14 -4
  32. package/src/data-sources/useDataSourceInstanceLocations.ts +17 -6
  33. package/src/data-sources/useDataSourceInstances.ts +5 -4
  34. package/src/data-sources/useDataSources.ts +2 -4
  35. package/src/field-mappings/useFieldMapping.ts +29 -8
  36. package/src/field-mappings/useFieldMappingInstance.ts +35 -12
  37. package/src/field-mappings/useFieldMappingInstances.ts +5 -4
  38. package/src/field-mappings/useFieldMappings.ts +2 -4
  39. package/src/flows/useFlow.ts +29 -8
  40. package/src/flows/useFlowInstance.ts +62 -5
  41. package/src/flows/useFlowInstances.ts +2 -4
  42. package/src/flows/useFlowRun.ts +18 -6
  43. package/src/flows/useFlowRuns.ts +5 -5
  44. package/src/flows/useFlows.ts +5 -5
  45. package/src/hooks/useElement.tsx +137 -149
  46. package/src/hooks/useElements.tsx +44 -73
  47. package/src/hooks/useIntegrationAppSWR.tsx +13 -0
  48. package/src/index.tsx +30 -14
  49. package/src/integrations/useConnection.ts +14 -5
  50. package/src/integrations/useConnections.ts +3 -4
  51. package/src/integrations/useConnectorSpec.ts +9 -18
  52. package/src/integrations/useIntegration.ts +11 -7
  53. package/src/integrations/useIntegrations.ts +3 -4
  54. package/src/screens/useScreen.ts +19 -0
  55. package/src/flows/useFlowTemplate.ts +0 -0
  56. package/src/flows/useFlowTemplates.ts +0 -0
  57. package/src/hooks/useGetter.tsx +0 -38
@@ -1,187 +1,175 @@
1
- import {
2
- IntegrationAppClient,
3
- ElementAccessor,
4
- ElementInstanceAccessor,
5
- } from '@integration-app/sdk'
6
- import { useEffect, useState } from 'react'
1
+ import { IntegrationAppClient } from '@integration-app/sdk'
2
+ import AwesomeDebouncePromise from 'awesome-debounce-promise'
7
3
  import { useIntegrationApp } from 'contexts/integration-app-context'
4
+ import useSWR from 'swr'
8
5
 
9
- interface ElementHookResult<
10
- ElementInterface,
11
- ElementAccessor,
12
- CreateRequest,
13
- UpdateRequest,
14
- > {
15
- data: ElementInterface | undefined
16
- loading: boolean
17
- error?: Error
18
- create: (data: CreateRequest) => Promise<ElementInterface>
19
- patch: (data: UpdateRequest) => Promise<ElementInterface>
20
- put: (data: CreateRequest) => Promise<ElementInterface>
21
- archive: () => Promise<void>
22
- refresh: () => void
23
- accessor: ElementAccessor
6
+ interface ElementState {
7
+ debouncedPut: (data) => Promise<void>
8
+ updatedLocally: boolean
9
+ savingToServer: boolean
24
10
  }
25
11
 
26
- type ElementAccessorGetter<SelectorInterface, ElementAccessor> = (
27
- params: SelectorInterface | string,
28
- ) => ElementAccessor
12
+ const elementStateCache = new Map<string, ElementState>()
13
+
14
+ interface BaseElementAccessor<Element, UpdateRequest, CreateRequest> {
15
+ get(): Promise<Element>
29
16
 
30
- export type ElementAccessorGenerator<SelectorInterface, ElementAccessor> = (
31
- integrationApp: IntegrationAppClient,
32
- ) => ElementAccessorGetter<SelectorInterface, ElementAccessor>
17
+ patch?(data: Partial<UpdateRequest>): Promise<Element>
18
+
19
+ put?(data: UpdateRequest): Promise<Element>
33
20
 
34
- interface BaseElementAccessor<ElementInterface, CreateRequest, UpdateRequest> {
35
- get(): Promise<ElementInterface>
36
- create?: (data: CreateRequest) => Promise<ElementInterface>
37
- patch?(data: UpdateRequest): Promise<ElementInterface>
38
- put?(data: CreateRequest): Promise<ElementInterface>
39
21
  archive?(): Promise<void>
40
- }
41
22
 
42
- type ExtractElementGenerics<T, Interface> = T extends ElementAccessor<
43
- infer _InterfaceType,
44
- infer CreateRequestType,
45
- infer UpdateRequestType
46
- >
47
- ? { CreateRequest: CreateRequestType; UpdateRequest: UpdateRequestType }
48
- : T extends ElementInstanceAccessor<
49
- infer _ElementInstanceType,
50
- infer _SelectorType,
51
- infer CreateRequestType,
52
- infer UpdateRequestType
53
- >
54
- ? { CreateRequest: CreateRequestType; UpdateRequest: UpdateRequestType }
55
- : { CreateRequest: Partial<Interface>; UpdateRequest: Partial<Interface> }
23
+ create?(data: CreateRequest): Promise<Element>
24
+ }
56
25
 
57
26
  export function useElement<
58
- ElementInterface,
59
- SelectorInterface,
27
+ Element,
28
+ UpdateRequest,
29
+ CreateRequest,
60
30
  ElementAccessor extends BaseElementAccessor<
61
- ElementInterface,
62
- CreateRequest,
63
- UpdateRequest
31
+ Element,
32
+ UpdateRequest,
33
+ CreateRequest
64
34
  >,
65
- CreateRequest extends Partial<ElementInterface> = ExtractElementGenerics<
66
- ElementAccessor,
67
- ElementInterface
68
- >['CreateRequest'],
69
- UpdateRequest extends Partial<ElementInterface> = ExtractElementGenerics<
70
- ElementAccessor,
71
- ElementInterface
72
- >['UpdateRequest'],
73
35
  >(
74
- props: SelectorInterface | { id: string },
75
- accessorGenerator: ElementAccessorGenerator<
76
- SelectorInterface,
77
- ElementAccessor
78
- >,
79
- ): ElementHookResult<
80
- ElementInterface,
81
- ElementAccessor,
82
- CreateRequest,
83
- UpdateRequest
84
- > {
36
+ selector: any,
37
+ accessorGenerator: (
38
+ integrationAppClient: IntegrationAppClient,
39
+ ) => ElementAccessor,
40
+ ) {
85
41
  const integrationApp = useIntegrationApp()
86
- const [data, setData] = useState<ElementInterface>()
87
- const [loading, setLoading] = useState<boolean>(true)
88
- const [error, setError] = useState<Error>(null)
89
- const [refreshCounter, setRefreshCounter] = useState(0)
90
-
91
- function updateDataWith(newData) {
92
- if (data !== undefined) {
93
- setData({
94
- ...data,
95
- ...newData,
96
- })
97
- }
42
+
43
+ // To make sure all instances of useElement with the same IntegrationAppClient and selector
44
+ // are synchronized, we cache them using selector and token as a key
45
+ const elementKeyData: any = {
46
+ token: integrationApp.token,
47
+ selector,
48
+ }
49
+ const elementKey = JSON.stringify(elementKeyData)
50
+
51
+ if (!elementStateCache.has(elementKey)) {
52
+ elementStateCache.set(elementKey, {
53
+ updatedLocally: false,
54
+ savingToServer: false,
55
+ debouncedPut: AwesomeDebouncePromise(async (data) => {
56
+ elementState.updatedLocally = false
57
+ elementState.savingToServer = true
58
+
59
+ try {
60
+ const result = await accessor?.put(data)
61
+ if (!elementState.updatedLocally) {
62
+ // Update savingToSever so that cached versions in each hook are updated when they react to 'mutate' below
63
+ // Yes, this duplicates the one in `finally`, but but the time that one is called it's too late since `mutate` already did its job.
64
+ elementState.savingToServer = false
65
+ // When we received updated state of the element,
66
+ // apply it to the context as long as we didn't make any other changes locally
67
+ // meanwhile.
68
+ await mutate(result, false)
69
+ }
70
+ } catch (e) {
71
+ elementState.updatedLocally = true
72
+ throw e
73
+ } finally {
74
+ elementState.savingToServer = false
75
+ }
76
+ }, 500),
77
+ })
98
78
  }
99
79
 
100
- function replaceDataWith(newData) {
101
- setData(newData)
80
+ const elementState = elementStateCache.get(elementKey)
81
+
82
+ const accessor = integrationApp ? accessorGenerator(integrationApp) : null
83
+
84
+ const swrKey = accessor && selector ? `element:${elementKey}` : null // do not fetch anything if selector or accessor is not defined
85
+
86
+ const {
87
+ data: item,
88
+ mutate,
89
+ error,
90
+ isLoading,
91
+ isValidating,
92
+ } = useSWR(swrKey, () => accessor?.get(), {
93
+ // pause revalidation if update is in progress to not overwrite local changes
94
+ isPaused: () => elementState.updatedLocally || elementState.savingToServer,
95
+ })
96
+
97
+ const loading = isLoading
98
+ const refreshing = isValidating
99
+
100
+ async function refresh(): Promise<Element> {
101
+ return await mutate()
102
102
  }
103
103
 
104
- const selector = (props as any)?.id
105
- ? (props as { id: string }).id
106
- : (props as SelectorInterface)
107
-
108
- const accessor = integrationApp
109
- ? accessorGenerator(integrationApp)(selector)
110
- : null
111
-
112
- useEffect(() => {
113
- setLoading(true)
114
- setError(null)
115
- if (integrationApp && selector) {
116
- accessor
117
- .get()
118
- .then(setData)
119
- .catch(setError)
120
- .finally(() => setLoading(false))
121
- } else {
122
- setError(
123
- new Error(
124
- 'IntegrationApp not found. Was this component wrapped in <IntegrationAppProvider>?',
125
- ),
104
+ async function put(data: UpdateRequest) {
105
+ if (!accessor?.put) {
106
+ throw new Error(
107
+ `"put method is not supported for accessor ${accessor.constructor.name}`,
126
108
  )
127
109
  }
128
- }, [
129
- integrationApp,
130
- JSON.stringify(selector),
131
- JSON.stringify(props),
132
- refreshCounter,
133
- ])
134
-
135
- async function create(createData: CreateRequest) {
136
- // Because `createData` do not contain all critical fields
137
- // we do not update state with `createData` to avoid problem
138
- // with missing critical fields like `id`
139
-
140
- const returnedData = await accessor.create(createData)
141
- replaceDataWith(returnedData)
142
- return returnedData
143
- }
144
-
145
- function refresh() {
146
- setRefreshCounter(refreshCounter + 1)
147
- }
148
110
 
149
- async function patch(patch: UpdateRequest) {
150
- if (typeof patch === 'object') {
151
- updateDataWith(patch ?? {})
111
+ elementState.updatedLocally = true
152
112
 
153
- // TODO: PL-3550
154
- // PATCH could response with modified fields that do not exist in `patch`
155
- // but this data could be outdated because of other methods calls
156
- return accessor.patch(patch)
157
- } else {
158
- return data
113
+ // We don't know which fields are "innate" for the element and should stay
114
+ // and which should be removed by put, so we'll do a simple patch.
115
+ // We'll update data with actual value from the server after put is done.
116
+ const newLocalData = {
117
+ ...item,
118
+ ...data,
159
119
  }
120
+
121
+ await mutate(newLocalData, false)
122
+
123
+ await elementState.debouncedPut(data)
160
124
  }
161
125
 
162
- async function put(putData: CreateRequest) {
163
- updateDataWith(putData)
126
+ async function patch(data: Partial<UpdateRequest>) {
127
+ const newData = {
128
+ ...item,
129
+ ...data,
130
+ } as UpdateRequest
164
131
 
165
- // TODO: PL-3550
166
- // PUT could response with modified fields that do not exist in `patch`
167
- // but this data could be outdated because of other methods calls
168
- return await accessor.put(putData)
132
+ return put(newData)
169
133
  }
170
134
 
171
135
  async function archive() {
172
- setData(null)
173
- return accessor.archive()
136
+ if (!accessor?.archive) {
137
+ return
138
+ }
139
+
140
+ await mutate({ ...item, archivedAt: new Date().toISOString() }, false)
141
+ await accessor?.archive()
142
+ await mutate()
143
+ }
144
+
145
+ async function create(data: CreateRequest) {
146
+ if (!accessor?.create) {
147
+ throw new Error(
148
+ `"create method is not supported for accessor ${accessor.constructor.name}`,
149
+ )
150
+ }
151
+
152
+ const result = await accessor?.create(data)
153
+
154
+ return await mutate(result)
174
155
  }
175
156
 
176
157
  return {
177
- data,
158
+ accessor,
159
+
160
+ item,
161
+
162
+ loading,
163
+ saving: elementState.updatedLocally || elementState.savingToServer,
164
+
165
+ error,
166
+
167
+ refresh,
168
+ refreshing,
169
+
178
170
  create,
179
171
  patch,
180
172
  put,
181
173
  archive,
182
- refresh,
183
- loading,
184
- error,
185
- accessor,
186
174
  }
187
175
  }
@@ -1,98 +1,69 @@
1
1
  import { useIntegrationApp } from '../contexts/integration-app-context'
2
- import { useEffect, useRef, useState } from 'react'
3
- import { IntegrationAppClient } from '@integration-app/sdk'
4
- import { PaginationQuery, PaginationResponse } from '@integration-app/sdk'
5
-
6
- export interface ElementsHookResult<Element> {
7
- items: Element[]
8
- loading: boolean
9
- error?: Error
10
- refresh(): Promise<void>
11
- loadMore(): Promise<void>
12
- }
13
-
14
- interface ElementsAccessor<Element, FindQuery> {
15
- find(query: FindQuery): Promise<PaginationResponse<Element>>
16
- }
2
+ import { useState } from 'react'
3
+ import { PaginationResponse } from '@integration-app/sdk'
4
+ import useSWRInfinite from 'swr/infinite'
5
+ import qs from 'query-string'
17
6
 
18
- type InstanceAccessorGenerator<Element, FindQuery> = (
19
- integrationApp: IntegrationAppClient,
20
- ) => ElementsAccessor<Element, FindQuery>
7
+ const LIMIT = 25
21
8
 
22
- export function useElements<Element, FindQuery extends PaginationQuery>(
23
- initialQuery: FindQuery,
24
- accessorGenerator: InstanceAccessorGenerator<Element, FindQuery>,
25
- ): ElementsHookResult<Element> {
9
+ export function useElements<Item>(route: string, query = {}) {
26
10
  const integrationApp = useIntegrationApp()
27
11
 
28
- const refreshId = useRef<number>(0)
29
- const [items, setItems] = useState<Element[]>([])
30
- const [nextCursor, setNextCursor] = useState<any>(undefined)
31
- const [loading, setLoading] = useState<boolean>(false)
32
- const [error, setError] = useState<Error>(null)
12
+ function getKey(
13
+ page: number,
14
+ previousPageData: PaginationResponse<Item> | null,
15
+ ) {
16
+ // first page, we don't have `previousPageData`
17
+ if (page === 0)
18
+ return `/${route}?${qs.stringify({
19
+ ...query,
20
+ limit: LIMIT,
21
+ })}`
22
+
23
+ // reached the end
24
+ if (previousPageData.items?.length < LIMIT) return null
25
+
26
+ return `/${route}?${qs.stringify({
27
+ ...query,
28
+ limit: LIMIT,
29
+ cursor: previousPageData.cursor,
30
+ })}`
31
+ }
33
32
 
34
- async function loadMore() {
35
- const startingRefreshId = refreshId.current
36
- const isFirstPage = !nextCursor
37
-
38
- function setStateIfCurrentRefresh(stateSetter, valueGetter: (arg) => any) {
39
- // If the refreshId has changed since the beginning of this function,
40
- // it means refresh() was called while we were loading more results.
41
- // Don't change the state in that case - the latest refresh will take care of that.
42
- stateSetter((value) =>
43
- startingRefreshId === refreshId.current ? valueGetter(value) : value,
44
- )
45
- }
33
+ const [loadingMore, setIsLoadingMore] = useState(false)
46
34
 
47
- setStateIfCurrentRefresh(setError, () => null)
48
- setStateIfCurrentRefresh(setLoading, () => true)
35
+ const { data, size, setSize, isLoading, error, mutate, isValidating } =
36
+ useSWRInfinite<PaginationResponse<Item>>(getKey, (url) =>
37
+ integrationApp.get(url),
38
+ )
49
39
 
50
- const queryParams = {
51
- ...initialQuery,
52
- }
40
+ const items = data ? data.map((page) => page.items).flat() : []
53
41
 
54
- if (nextCursor) queryParams.cursor = nextCursor
42
+ const loading = isLoading
43
+ const refreshing = isValidating
55
44
 
56
- try {
57
- const data = await accessorGenerator(integrationApp).find(queryParams)
45
+ async function loadMore() {
46
+ const hasMoreToLoad = data[size - 1]?.items?.length === LIMIT
58
47
 
59
- setStateIfCurrentRefresh(setNextCursor, () => data.cursor)
60
- setStateIfCurrentRefresh(setItems, (items) =>
61
- isFirstPage ? data.items : [...items, ...data.items],
62
- )
63
- } catch (e) {
64
- setStateIfCurrentRefresh(setError, () => e)
65
- } finally {
66
- setStateIfCurrentRefresh(setLoading, () => false)
48
+ if (hasMoreToLoad) {
49
+ setIsLoadingMore(true)
50
+ await setSize(size + 1)
51
+ setIsLoadingMore(false)
67
52
  }
68
53
  }
69
54
 
70
- // refresh on initialQuery change
71
- useEffect(() => {
72
- if (!integrationApp) {
73
- setError(
74
- new Error(
75
- 'IntegrationApp not found. Was this component wrapped in <IntegrationAppProvider>?',
76
- ),
77
- )
78
-
79
- return
80
- }
81
-
82
- refresh()
83
- }, [integrationApp, JSON.stringify(initialQuery)])
84
-
85
55
  async function refresh() {
86
- refreshId.current += 1
87
- setNextCursor(undefined)
88
- await loadMore()
56
+ await mutate()
89
57
  }
90
58
 
91
59
  return {
92
60
  items,
93
61
 
94
62
  refresh,
63
+ refreshing,
64
+
95
65
  loadMore,
66
+ loadingMore,
96
67
 
97
68
  loading,
98
69
 
@@ -0,0 +1,13 @@
1
+ import useSWR from 'swr'
2
+ import { useIntegrationApp } from '../contexts/integration-app-context'
3
+
4
+ export function useIntegrationAppSWR(path: string, options?: any) {
5
+ const client = useIntegrationApp()
6
+
7
+ const fetcher = async () => {
8
+ const response = await client.get(path, options)
9
+ return response
10
+ }
11
+
12
+ return useSWR(client ? path : undefined, fetcher, options)
13
+ }
package/src/index.tsx CHANGED
@@ -1,32 +1,31 @@
1
- export {
2
- useIntegrationApp,
3
- IntegrationAppProvider,
4
- } from './contexts/integration-app-context.js'
1
+ export * from './contexts/index.js'
5
2
 
3
+ export { useIntegrationAppSWR } from './hooks/useIntegrationAppSWR.js'
4
+
5
+ export { useConnection } from './integrations/useConnection.js'
6
+ export { useConnections } from './integrations/useConnections.js'
6
7
  export { useConnectorSpec } from './integrations/useConnectorSpec.js'
7
8
  export { useIntegration } from './integrations/useIntegration.js'
8
9
  export { useIntegrations } from './integrations/useIntegrations.js'
9
- export { useConnection } from './integrations/useConnection.js'
10
- export { useConnections } from './integrations/useConnections.js'
11
10
 
12
11
  export { useFieldMapping } from './field-mappings/useFieldMapping.js'
13
- export { useFieldMappings } from './field-mappings/useFieldMappings.js'
14
12
  export { useFieldMappingInstance } from './field-mappings/useFieldMappingInstance.js'
15
13
  export { useFieldMappingInstances } from './field-mappings/useFieldMappingInstances.js'
14
+ export { useFieldMappings } from './field-mappings/useFieldMappings.js'
16
15
 
17
16
  export { useDataSource } from './data-sources/useDataSource.js'
18
- export { useDataSources } from './data-sources/useDataSources.js'
17
+ export { useDataSourceEvents } from './data-sources/useDataSourceEvents.js'
19
18
  export { useDataSourceInstance } from './data-sources/useDataSourceInstance.js'
19
+ export { useDataSourceInstanceCollection } from './data-sources/useDataSourceInstanceCollection.js'
20
+ export { useDataSourceInstanceLocations } from './data-sources/useDataSourceInstanceLocations.js'
20
21
  export { useDataSourceInstances } from './data-sources/useDataSourceInstances.js'
21
- export { useDataSourceInstanceCollection as useDataSourceCollection } from './data-sources/useDataSourceInstanceCollection.js'
22
- export { useDataSourceInstanceLocations as useDataSourceLocations } from './data-sources/useDataSourceInstanceLocations.js'
23
- export { useDataSourceEvents } from './data-sources/useDataSourceEvents.js'
22
+ export { useDataSources } from './data-sources/useDataSources.js'
24
23
 
25
- export { useAppEvents } from './app-events/useAppEvents.js'
26
- export { useAppEventType } from './app-events/useAppEventType.js'
27
- export { useAppEventTypes } from './app-events/useAppEventTypes.js'
28
24
  export { useAppEventSubscription } from './app-events/useAppEventSubscription.js'
29
25
  export { useAppEventSubscriptions } from './app-events/useAppEventSubscriptions.js'
26
+ export { useAppEventType } from './app-events/useAppEventType.js'
27
+ export { useAppEventTypes } from './app-events/useAppEventTypes.js'
28
+ export { useAppEvents } from './app-events/useAppEvents.js'
30
29
 
31
30
  export { useFlow } from './flows/useFlow.js'
32
31
  export { useFlows } from './flows/useFlows.js'
@@ -37,4 +36,21 @@ export { useFlowInstances } from './flows/useFlowInstances.js'
37
36
  export { useFlowRun } from './flows/useFlowRun.js'
38
37
  export { useFlowRuns } from './flows/useFlowRuns.js'
39
38
 
39
+ export { useDataLinkTable } from './data-links/useDataLinkTable.js'
40
+ export { useDataLinkTableInstance } from './data-links/useDataLinkTableInstance.js'
41
+ export { useDataLinkTableInstances } from './data-links/useDataLinkTableInstances.js'
42
+ export { useDataLinkTables } from './data-links/useDataLinkTables.js'
43
+
44
+ export { useAction } from './actions/useAction.js'
45
+ export { useActionInstance } from './actions/useActionInstance.js'
46
+ export { useActionInstances } from './actions/useActionInstances.js'
47
+ export { useActions } from './actions/useActions.js'
48
+
49
+ export { useScreen } from './screens/useScreen.js'
50
+
51
+ export { useCustomer } from './customers/useCustomer.js'
52
+ export { useCustomers } from './customers/useCustomers.js'
53
+
54
+ export { useDataCollectionSpec } from './data-collections/useDataCollectionSpec.js'
55
+
40
56
  export { DataForm } from '@integration-app/sdk'
@@ -1,12 +1,21 @@
1
- import { Connection, ConnectionAccessor } from '@integration-app/sdk'
1
+ import {
2
+ Connection,
3
+ ConnectionAccessor,
4
+ CreateConnectionRequest,
5
+ UpdateConnectionRequest,
6
+ } from '@integration-app/sdk'
2
7
  import { useElement } from '../hooks/useElement'
3
8
 
4
9
  export function useConnection(id: string) {
5
- const { data: connection, ...rest } = useElement<
10
+ const { item: connection, ...rest } = useElement<
6
11
  Connection,
7
- string,
12
+ UpdateConnectionRequest,
13
+ CreateConnectionRequest,
8
14
  ConnectionAccessor
9
- >(id, (integrationApp) => integrationApp.connection.bind(integrationApp))
15
+ >(id, (integrationApp) => integrationApp.connection(id))
10
16
 
11
- return { connection, ...rest }
17
+ return {
18
+ connection,
19
+ ...rest,
20
+ }
12
21
  }
@@ -2,12 +2,11 @@ import { Connection, FindConnectionsQuery } from '@integration-app/sdk'
2
2
  import { useElements } from '../hooks/useElements'
3
3
 
4
4
  export function useConnections(query?: FindConnectionsQuery) {
5
- const { ...rest } = useElements<Connection, FindConnectionsQuery>(
6
- query,
7
- (integrationApp) => integrationApp.connections,
8
- )
5
+ const { ...rest } = useElements<Connection>('connections', query)
9
6
 
10
7
  return {
8
+ connections: rest.items,
9
+
11
10
  ...rest,
12
11
  }
13
12
  }
@@ -1,25 +1,16 @@
1
1
  import { ConnectionSpec } from '@integration-app/sdk'
2
- import { useEffect, useState } from 'react'
2
+ import useSWR from 'swr'
3
3
  import { useIntegrationApp } from '../contexts/integration-app-context'
4
4
 
5
- export function useConnectorSpec(integrationKey: string) {
5
+ export function useConnectorSpec(integrationIdOrKey: string) {
6
6
  const integrationApp = useIntegrationApp()
7
- const [data, setData] = useState<ConnectionSpec | null>(null)
8
- const [loading, setLoading] = useState(true)
9
- const [error, setError] = useState<Error>(null)
10
7
 
11
- useEffect(() => {
12
- if (!integrationApp) {
13
- return
14
- }
8
+ const { data, isLoading, error } = useSWR<ConnectionSpec>(
9
+ integrationIdOrKey
10
+ ? `/integrations/${integrationIdOrKey}/connector-spec`
11
+ : undefined,
12
+ () => integrationApp.integration(integrationIdOrKey).getConnectorSpec(),
13
+ )
15
14
 
16
- integrationApp
17
- .integration(integrationKey)
18
- .getConnectorSpec()
19
- .then(setData)
20
- .catch(setError)
21
- .finally(() => setLoading(false))
22
- }, [integrationApp, integrationKey])
23
-
24
- return { data, loading, error }
15
+ return { data, loading: isLoading, error }
25
16
  }
@@ -1,14 +1,18 @@
1
- import { Integration, IntegrationAccessor } from '@integration-app/sdk'
1
+ import {
2
+ CreateIntegrationRequest,
3
+ Integration,
4
+ IntegrationAccessor,
5
+ UpdateIntegrationRequest,
6
+ } from '@integration-app/sdk'
2
7
  import { useElement } from '../hooks/useElement'
3
8
 
4
- export function useIntegration(idOrKey: string) {
5
- const { data: integration, ...rest } = useElement<
9
+ export function useIntegration(id: string) {
10
+ const { item: integration, ...rest } = useElement<
6
11
  Integration,
7
- string,
12
+ UpdateIntegrationRequest,
13
+ CreateIntegrationRequest,
8
14
  IntegrationAccessor
9
- >(idOrKey, (integrationApp) =>
10
- integrationApp.integration.bind(integrationApp),
11
- )
15
+ >(id, (integrationApp) => integrationApp.integration(id))
12
16
 
13
17
  return { integration, ...rest }
14
18
  }
@@ -2,12 +2,11 @@ import { FindIntegrationsQuery, Integration } from '@integration-app/sdk'
2
2
  import { useElements } from '../hooks/useElements'
3
3
 
4
4
  export function useIntegrations(query?: FindIntegrationsQuery) {
5
- const { ...rest } = useElements<Integration, FindIntegrationsQuery>(
6
- query,
7
- (integrationApp) => integrationApp.integrations,
8
- )
5
+ const { ...rest } = useElements<Integration>('integrations', query)
9
6
 
10
7
  return {
8
+ integrations: rest.items,
9
+
11
10
  ...rest,
12
11
  }
13
12
  }