@integration-app/react 0.2.0 → 0.3.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.
Files changed (57) hide show
  1. package/README.md +49 -34
  2. package/dist/index.d.ts +377 -160
  3. package/dist/index.js +517 -226
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.module.d.ts +377 -160
  6. package/dist/index.module.mjs +506 -227
  7. package/dist/index.module.mjs.map +1 -1
  8. package/dist/index.umd.d.ts +377 -160
  9. package/dist/index.umd.js +517 -230
  10. package/dist/index.umd.js.map +1 -1
  11. package/package.json +16 -9
  12. package/src/actions/useAction.ts +35 -0
  13. package/src/actions/useActionInstance.ts +56 -0
  14. package/src/actions/useActionInstances.ts +11 -0
  15. package/src/actions/useActions.ts +11 -0
  16. package/src/app-events/useAppEventSubscription.ts +6 -6
  17. package/src/app-events/useAppEventSubscriptions.ts +5 -4
  18. package/src/app-events/useAppEventType.ts +10 -7
  19. package/src/app-events/useAppEventTypes.ts +2 -4
  20. package/src/app-events/useAppEvents.ts +2 -4
  21. package/src/contexts/index.tsx +4 -0
  22. package/src/contexts/integration-app-context.tsx +11 -2
  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 +17 -0
  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 +37 -14
  37. package/src/field-mappings/useFieldMappingInstances.ts +6 -5
  38. package/src/field-mappings/useFieldMappings.ts +2 -4
  39. package/src/flows/useFlow.ts +28 -6
  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 +142 -136
  46. package/src/hooks/useElements.tsx +44 -73
  47. package/src/hooks/useIntegrationAppSWR.tsx +13 -0
  48. package/src/index.tsx +29 -16
  49. package/src/integrations/useConnection.ts +14 -5
  50. package/src/integrations/useConnections.ts +3 -4
  51. package/src/integrations/useConnectorSpec.ts +14 -0
  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/rollup.config.mjs +0 -64
  56. package/src/hooks/useGetter.tsx +0 -38
  57. package/src/integrations/useConnectionSpec.ts +0 -25
@@ -1,169 +1,175 @@
1
- import {
2
- IntegrationAppClient,
3
- ElementAccessor,
4
- ElementInstanceAccessor,
5
- } from '@integration-app/sdk'
6
- import { useEffect, useState } from 'react'
7
- import { useIntegrationApp } from '../contexts/integration-app-context'
8
-
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
1
+ import { IntegrationAppClient } from '@integration-app/sdk'
2
+ import AwesomeDebouncePromise from 'awesome-debounce-promise'
3
+ import { useIntegrationApp } from 'contexts/integration-app-context'
4
+ import useSWR from 'swr'
5
+
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
- const selector = (props as any)?.id
92
- ? (props as { id: string }).id
93
- : (props as SelectorInterface)
94
-
95
- const accessor = integrationApp
96
- ? accessorGenerator(integrationApp)(selector)
97
- : null
98
-
99
- useEffect(() => {
100
- setLoading(true)
101
- setError(null)
102
- if (integrationApp) {
103
- accessor
104
- .get()
105
- .then(setData)
106
- .catch(setError)
107
- .finally(() => setLoading(false))
108
- } else {
109
- setError(
110
- new Error(
111
- 'IntegrationApp not found. Was this component wrapped in <IntegrationAppProvider>?',
112
- ),
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
+ })
78
+ }
79
+
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
+ }
103
+
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}`,
113
108
  )
114
109
  }
115
- }, [integrationApp, JSON.stringify(props), refreshCounter])
116
-
117
- async function create(createData: CreateRequest) {
118
- if (data !== undefined) {
119
- setData({
120
- ...data,
121
- ...createData,
122
- })
110
+
111
+ elementState.updatedLocally = true
112
+
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,
123
119
  }
124
- return accessorGenerator(integrationApp)(selector).create(createData)
120
+
121
+ await mutate(newLocalData, false)
122
+
123
+ await elementState.debouncedPut(data)
125
124
  }
126
125
 
127
- function refresh() {
128
- setRefreshCounter(refreshCounter + 1)
126
+ async function patch(data: Partial<UpdateRequest>) {
127
+ const newData = {
128
+ ...item,
129
+ ...data,
130
+ } as UpdateRequest
131
+
132
+ return put(newData)
129
133
  }
130
134
 
131
- async function patch(patch: UpdateRequest) {
132
- if (typeof patch === 'object') {
133
- setData({
134
- ...data,
135
- ...(patch ?? {}),
136
- })
137
- return accessorGenerator(integrationApp)(selector).patch(patch)
138
- } else {
139
- return data
135
+ async function archive() {
136
+ if (!accessor?.archive) {
137
+ return
140
138
  }
139
+
140
+ await mutate({ ...item, archivedAt: new Date().toISOString() }, false)
141
+ await accessor?.archive()
142
+ await mutate()
141
143
  }
142
144
 
143
- async function put(putData: CreateRequest) {
144
- if (data !== undefined) {
145
- setData({
146
- ...data,
147
- ...putData,
148
- })
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
+ )
149
150
  }
150
- return accessorGenerator(integrationApp)(selector).put(putData)
151
- }
152
151
 
153
- async function archive() {
154
- setData(null)
155
- return accessorGenerator(integrationApp)(selector).archive()
152
+ const result = await accessor?.create(data)
153
+
154
+ return await mutate(result)
156
155
  }
157
156
 
158
157
  return {
159
- data,
158
+ accessor,
159
+
160
+ item,
161
+
162
+ loading,
163
+ saving: elementState.updatedLocally || elementState.savingToServer,
164
+
165
+ error,
166
+
167
+ refresh,
168
+ refreshing,
169
+
160
170
  create,
161
171
  patch,
162
172
  put,
163
173
  archive,
164
- refresh,
165
- loading,
166
- error,
167
- accessor,
168
174
  }
169
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'
2
+
3
+ export { useIntegrationAppSWR } from './hooks/useIntegrationAppSWR.js'
5
4
 
6
- export { useConnectorSpec } from './integrations/useConnectionSpec.js'
7
- export { useIntegration } from './integrations/useIntegration.js'
8
- export { useIntegrations } from './integrations/useIntegrations.js'
9
5
  export { useConnection } from './integrations/useConnection.js'
10
6
  export { useConnections } from './integrations/useConnections.js'
7
+ export { useConnectorSpec } from './integrations/useConnectorSpec.js'
8
+ export { useIntegration } from './integrations/useIntegration.js'
9
+ export { useIntegrations } from './integrations/useIntegrations.js'
11
10
 
12
11
  export { useFieldMapping } from './field-mappings/useFieldMapping.js'
13
- export { useFieldMappings } from './field-mappings/useFieldMappings.js'
14
-
15
12
  export { useFieldMappingInstance } from './field-mappings/useFieldMappingInstance.js'
16
13
  export { useFieldMappingInstances } from './field-mappings/useFieldMappingInstances.js'
14
+ export { useFieldMappings } from './field-mappings/useFieldMappings.js'
17
15
 
18
16
  export { useDataSource } from './data-sources/useDataSource.js'
19
- export { useDataSources } from './data-sources/useDataSources.js'
20
- export { useDataSourceInstance } from './data-sources/useDataSourceInstance.js'
21
- export { useDataSourceInstanceCollection as useDataSourceCollection } from './data-sources/useDataSourceInstanceCollection.js'
22
- export { useDataSourceInstanceLocations as useDataSourceLocations } from './data-sources/useDataSourceInstanceLocations.js'
23
17
  export { useDataSourceEvents } from './data-sources/useDataSourceEvents.js'
18
+ export { useDataSourceInstance } from './data-sources/useDataSourceInstance.js'
19
+ export { useDataSourceInstanceCollection } from './data-sources/useDataSourceInstanceCollection.js'
20
+ export { useDataSourceInstanceLocations } from './data-sources/useDataSourceInstanceLocations.js'
21
+ export { useDataSourceInstances } from './data-sources/useDataSourceInstances.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,18 @@ 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 { useDataCollectionSpec } from './data-collections/useDataCollectionSpec.js'
52
+
40
53
  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
  }
@@ -0,0 +1,14 @@
1
+ import { ConnectionSpec } from '@integration-app/sdk'
2
+ import { useIntegrationApp } from '../contexts/integration-app-context'
3
+ import useSWR from 'swr'
4
+
5
+ export function useConnectorSpec(integrationIdOrKey: string) {
6
+ const integrationApp = useIntegrationApp()
7
+
8
+ const { data, isLoading, error } = useSWR<ConnectionSpec>(
9
+ `/integrations/${integrationIdOrKey}/connector-spec`,
10
+ () => integrationApp.integration(integrationIdOrKey).getConnectorSpec(),
11
+ )
12
+
13
+ return { data, loading: isLoading, error }
14
+ }
@@ -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
  }
@@ -0,0 +1,19 @@
1
+ import {
2
+ CreateScreenRequest,
3
+ Screen,
4
+ ScreenAccessor,
5
+ ScreenSelector,
6
+ UpdateScreenRequest,
7
+ } from '@integration-app/sdk'
8
+ import { useElement } from '../hooks/useElement'
9
+
10
+ export function useScreen(selector: ScreenSelector | string) {
11
+ const { item: screen, ...rest } = useElement<
12
+ Screen,
13
+ UpdateScreenRequest,
14
+ CreateScreenRequest,
15
+ ScreenAccessor
16
+ >(selector, (integrationApp) => integrationApp.screen(selector))
17
+
18
+ return { screen, ...rest }
19
+ }