@integration-app/react 0.1.25 → 0.2.0

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 (140) hide show
  1. package/README.md +68 -0
  2. package/dist/index.d.ts +291 -0
  3. package/dist/index.js +411 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.module.d.ts +291 -0
  6. package/dist/index.module.mjs +379 -0
  7. package/dist/index.module.mjs.map +1 -0
  8. package/dist/index.umd.d.ts +291 -0
  9. package/dist/index.umd.js +413 -0
  10. package/dist/index.umd.js.map +1 -0
  11. package/package.json +46 -27
  12. package/rollup.config.mjs +64 -0
  13. package/src/app-events/useAppEventSubscription.ts +20 -0
  14. package/src/app-events/useAppEventSubscriptions.ts +18 -0
  15. package/src/app-events/useAppEventType.ts +15 -0
  16. package/src/app-events/useAppEventTypes.ts +13 -0
  17. package/src/app-events/useAppEvents.ts +13 -0
  18. package/src/contexts/integration-app-context.tsx +46 -0
  19. package/src/data-form/index.tsx +3 -0
  20. package/src/data-sources/useDataSource.ts +12 -0
  21. package/src/data-sources/useDataSourceEvents.ts +16 -0
  22. package/src/data-sources/useDataSourceInstance.ts +48 -0
  23. package/src/data-sources/useDataSourceInstanceCollection.ts +19 -0
  24. package/src/data-sources/useDataSourceInstanceLocations.ts +30 -0
  25. package/src/data-sources/useDataSources.ts +13 -0
  26. package/src/field-mappings/useFieldMapping.ts +14 -0
  27. package/src/field-mappings/useFieldMappingInstance.ts +31 -0
  28. package/src/field-mappings/useFieldMappingInstances.ts +18 -0
  29. package/src/field-mappings/useFieldMappings.ts +13 -0
  30. package/src/flows/useFlow.ts +11 -0
  31. package/src/flows/useFlowInstance.ts +16 -0
  32. package/src/flows/useFlowInstances.ts +13 -0
  33. package/src/flows/useFlowRun.ts +12 -0
  34. package/src/flows/useFlowRuns.ts +11 -0
  35. package/src/flows/useFlows.ts +11 -0
  36. package/src/hooks/useElement.tsx +169 -0
  37. package/src/hooks/useElements.tsx +101 -0
  38. package/src/hooks/useGetter.tsx +38 -0
  39. package/{index.d.ts → src/index.tsx} +40 -27
  40. package/src/integrations/useConnection.ts +12 -0
  41. package/src/integrations/useConnectionSpec.ts +25 -0
  42. package/src/integrations/useConnections.ts +13 -0
  43. package/src/integrations/useIntegration.ts +14 -0
  44. package/src/integrations/useIntegrations.ts +13 -0
  45. package/tsconfig.json +40 -0
  46. package/app-events/useAppEventSubscription.d.ts +0 -14
  47. package/app-events/useAppEventSubscription.js +0 -10
  48. package/app-events/useAppEventSubscription.js.map +0 -1
  49. package/app-events/useAppEventSubscriptions.d.ts +0 -8
  50. package/app-events/useAppEventSubscriptions.js +0 -12
  51. package/app-events/useAppEventSubscriptions.js.map +0 -1
  52. package/app-events/useAppEventType.d.ts +0 -13
  53. package/app-events/useAppEventType.js +0 -10
  54. package/app-events/useAppEventType.js.map +0 -1
  55. package/app-events/useAppEventTypes.d.ts +0 -8
  56. package/app-events/useAppEventTypes.js +0 -12
  57. package/app-events/useAppEventTypes.js.map +0 -1
  58. package/app-events/useAppEvents.d.ts +0 -8
  59. package/app-events/useAppEvents.js +0 -12
  60. package/app-events/useAppEvents.js.map +0 -1
  61. package/contexts/integration-app-context.d.ts +0 -11
  62. package/contexts/integration-app-context.js +0 -22
  63. package/contexts/integration-app-context.js.map +0 -1
  64. package/data-form/index.d.ts +0 -1
  65. package/data-form/index.js +0 -8
  66. package/data-form/index.js.map +0 -1
  67. package/data-sources/useDataSource.d.ts +0 -12
  68. package/data-sources/useDataSource.js +0 -10
  69. package/data-sources/useDataSource.js.map +0 -1
  70. package/data-sources/useDataSourceEvents.d.ts +0 -8
  71. package/data-sources/useDataSourceEvents.js +0 -12
  72. package/data-sources/useDataSourceEvents.js.map +0 -1
  73. package/data-sources/useDataSourceInstance.d.ts +0 -28
  74. package/data-sources/useDataSourceInstance.js +0 -28
  75. package/data-sources/useDataSourceInstance.js.map +0 -1
  76. package/data-sources/useDataSourceInstanceCollection.d.ts +0 -7
  77. package/data-sources/useDataSourceInstanceCollection.js +0 -16
  78. package/data-sources/useDataSourceInstanceCollection.js.map +0 -1
  79. package/data-sources/useDataSourceInstanceLocations.d.ts +0 -10
  80. package/data-sources/useDataSourceInstanceLocations.js +0 -21
  81. package/data-sources/useDataSourceInstanceLocations.js.map +0 -1
  82. package/data-sources/useDataSources.d.ts +0 -2
  83. package/data-sources/useDataSources.js +0 -9
  84. package/data-sources/useDataSources.js.map +0 -1
  85. package/field-mappings/useFieldMapping.d.ts +0 -12
  86. package/field-mappings/useFieldMapping.js +0 -10
  87. package/field-mappings/useFieldMapping.js.map +0 -1
  88. package/field-mappings/useFieldMappingInstance.d.ts +0 -17
  89. package/field-mappings/useFieldMappingInstance.js +0 -17
  90. package/field-mappings/useFieldMappingInstance.js.map +0 -1
  91. package/field-mappings/useFieldMappingInstances.d.ts +0 -8
  92. package/field-mappings/useFieldMappingInstances.js +0 -12
  93. package/field-mappings/useFieldMappingInstances.js.map +0 -1
  94. package/field-mappings/useFieldMappings.d.ts +0 -8
  95. package/field-mappings/useFieldMappings.js +0 -12
  96. package/field-mappings/useFieldMappings.js.map +0 -1
  97. package/flows/useFlow.d.ts +0 -12
  98. package/flows/useFlow.js +0 -10
  99. package/flows/useFlow.js.map +0 -1
  100. package/flows/useFlowInstance.d.ts +0 -14
  101. package/flows/useFlowInstance.js +0 -10
  102. package/flows/useFlowInstance.js.map +0 -1
  103. package/flows/useFlowInstances.d.ts +0 -8
  104. package/flows/useFlowInstances.js +0 -12
  105. package/flows/useFlowInstances.js.map +0 -1
  106. package/flows/useFlowRun.d.ts +0 -12
  107. package/flows/useFlowRun.js +0 -10
  108. package/flows/useFlowRun.js.map +0 -1
  109. package/flows/useFlowRuns.d.ts +0 -8
  110. package/flows/useFlowRuns.js +0 -10
  111. package/flows/useFlowRuns.js.map +0 -1
  112. package/flows/useFlows.d.ts +0 -8
  113. package/flows/useFlows.js +0 -10
  114. package/flows/useFlows.js.map +0 -1
  115. package/hooks/useElement.d.ts +0 -25
  116. package/hooks/useElement.js +0 -71
  117. package/hooks/useElement.js.map +0 -1
  118. package/hooks/useElements.d.ts +0 -15
  119. package/hooks/useElements.js +0 -59
  120. package/hooks/useElements.js.map +0 -1
  121. package/hooks/useGetter.d.ts +0 -6
  122. package/hooks/useGetter.js +0 -32
  123. package/hooks/useGetter.js.map +0 -1
  124. package/index.js +0 -59
  125. package/index.js.map +0 -1
  126. package/integrations/useConnection.d.ts +0 -12
  127. package/integrations/useConnection.js +0 -10
  128. package/integrations/useConnection.js.map +0 -1
  129. package/integrations/useConnectionSpec.d.ts +0 -5
  130. package/integrations/useConnectionSpec.js +0 -25
  131. package/integrations/useConnectionSpec.js.map +0 -1
  132. package/integrations/useConnections.d.ts +0 -8
  133. package/integrations/useConnections.js +0 -12
  134. package/integrations/useConnections.js.map +0 -1
  135. package/integrations/useIntegration.d.ts +0 -12
  136. package/integrations/useIntegration.js +0 -10
  137. package/integrations/useIntegration.js.map +0 -1
  138. package/integrations/useIntegrations.d.ts +0 -8
  139. package/integrations/useIntegrations.js +0 -12
  140. package/integrations/useIntegrations.js.map +0 -1
@@ -0,0 +1,46 @@
1
+ import { IntegrationAppClient } from '@integration-app/sdk'
2
+ import { createContext, ReactNode, useContext, useMemo } from 'react'
3
+
4
+ const IntegrationAppContext = createContext<IntegrationAppClient>(null)
5
+
6
+ IntegrationAppContext.displayName = 'IntegrationAppClientContext'
7
+
8
+ /**
9
+ * @interface
10
+ */
11
+ interface IntegrationAppProviderProps {
12
+ token: string
13
+ apiUri?: string
14
+ uiUri?: string
15
+ children: ReactNode
16
+ }
17
+
18
+ /**
19
+ * Provides an IntegrationAppClient instance to the React tree.
20
+ */
21
+ export const IntegrationAppProvider = ({
22
+ token,
23
+ apiUri = null,
24
+ uiUri = null,
25
+ children,
26
+ }: IntegrationAppProviderProps) => {
27
+ const client = useMemo(
28
+ () =>
29
+ new IntegrationAppClient({
30
+ token,
31
+ apiUri,
32
+ uiUri,
33
+ }),
34
+ [token, apiUri, uiUri],
35
+ )
36
+
37
+ return (
38
+ <IntegrationAppContext.Provider value={client}>
39
+ {children}
40
+ </IntegrationAppContext.Provider>
41
+ )
42
+ }
43
+
44
+ export function useIntegrationApp(): IntegrationAppClient {
45
+ return useContext(IntegrationAppContext)
46
+ }
@@ -0,0 +1,3 @@
1
+ export default function DataForm() {
2
+ return <p>Data Form</p>
3
+ }
@@ -0,0 +1,12 @@
1
+ import { DataSource, DataSourceAccessor } from '@integration-app/sdk'
2
+ import { useElement } from '../hooks/useElement'
3
+
4
+ export function useDataSource(idOrKey: string) {
5
+ const { data: dataSource, ...rest } = useElement<
6
+ DataSource,
7
+ string,
8
+ DataSourceAccessor
9
+ >(idOrKey, (integrationApp) => integrationApp.dataSource.bind(integrationApp))
10
+
11
+ return { dataSource, ...rest }
12
+ }
@@ -0,0 +1,16 @@
1
+ import {
2
+ DataSourceEvent,
3
+ FindDataSourceEventsQuery,
4
+ } from '@integration-app/sdk'
5
+ import { useElements } from '../hooks/useElements'
6
+
7
+ export function useDataSourceEvents(query?: FindDataSourceEventsQuery) {
8
+ const { ...rest } = useElements<DataSourceEvent, FindDataSourceEventsQuery>(
9
+ query,
10
+ (integrationApp) => integrationApp.dataSourceEvents,
11
+ )
12
+
13
+ return {
14
+ ...rest,
15
+ }
16
+ }
@@ -0,0 +1,48 @@
1
+ import {
2
+ DataCollectionCreateRequest,
3
+ DataCollectionFindRequest,
4
+ DataCollectionUpdateRequest,
5
+ DataSourceInstanceAccessor,
6
+ DataSourceInstanceSelector,
7
+ OpenDataSourceConfigurationOptions,
8
+ DataSourceInstance,
9
+ } from '@integration-app/sdk'
10
+ import { useElement } from '../hooks/useElement'
11
+
12
+ export function useDataSourceInstance(
13
+ selector: DataSourceInstanceSelector | { id: string },
14
+ ) {
15
+ const { data: dataSourceInstance, ...rest } = useElement<
16
+ DataSourceInstance,
17
+ DataSourceInstanceSelector,
18
+ DataSourceInstanceAccessor
19
+ >(selector, (integrationApp) =>
20
+ integrationApp.dataSourceInstance.bind(integrationApp),
21
+ )
22
+
23
+ const accessor = rest.accessor
24
+
25
+ return {
26
+ dataSourceInstance,
27
+ setup: () => accessor.setup(),
28
+ subscribe: (eventType) => accessor.subscribe(eventType),
29
+ resubscribe: (eventType) => accessor.resubscribe(eventType),
30
+ unsubscribe: (eventType) => accessor.unsubscribe(eventType),
31
+ pullUpdates: () => accessor.pullUpdates(),
32
+ fullSync: () => accessor.fullSync(),
33
+ reset: () => accessor.reset(),
34
+ openConfiguration: (options?: OpenDataSourceConfigurationOptions) =>
35
+ accessor.openConfiguration(options),
36
+ findRecords: (request?: DataCollectionFindRequest) =>
37
+ accessor.findRecords(request),
38
+ findRecordById: (id: string) => accessor.findRecordById(id),
39
+ createRecord: (request?: DataCollectionCreateRequest) =>
40
+ accessor.createRecord(request),
41
+ updateRecord: (request?: DataCollectionUpdateRequest) =>
42
+ accessor.updateRecord(request),
43
+ deleteRecord: (id?: string) => accessor.deleteRecord(id),
44
+ unifiedFieldsToNative: (unifiedFields: any) =>
45
+ accessor.unifiedFieldsToNative(unifiedFields),
46
+ ...rest,
47
+ }
48
+ }
@@ -0,0 +1,19 @@
1
+ import { DataCollectionSpec, DataSourceInstance } from '@integration-app/sdk'
2
+ import { useIntegrationApp } from '../contexts/integration-app-context'
3
+ import useGetter from '../hooks/useGetter'
4
+
5
+ export function useDataSourceInstanceCollection(
6
+ dataSourceInstance: DataSourceInstance,
7
+ ) {
8
+ const integrationApp = useIntegrationApp()
9
+ const { data: collection, ...rest } = useGetter<DataCollectionSpec>(
10
+ dataSourceInstance?.id,
11
+ () =>
12
+ integrationApp.dataSourceInstance(dataSourceInstance.id).getCollection(),
13
+ )
14
+
15
+ return {
16
+ collection,
17
+ ...rest,
18
+ }
19
+ }
@@ -0,0 +1,30 @@
1
+ import {
2
+ DataDirectoryListResponse,
3
+ DataSourceInstance,
4
+ } from '@integration-app/sdk'
5
+ import { useIntegrationApp } from '../contexts/integration-app-context'
6
+ import useGetter from '../hooks/useGetter'
7
+
8
+ export function useDataSourceInstanceLocations(
9
+ dataSourceInstance: DataSourceInstance,
10
+ args?: {
11
+ path?: string
12
+ cursor?: string
13
+ },
14
+ ) {
15
+ const integrationApp = useIntegrationApp()
16
+ const { data, ...rest } = useGetter<DataDirectoryListResponse>(
17
+ dataSourceInstance
18
+ ? `${dataSourceInstance.id}/${JSON.stringify(args)}`
19
+ : undefined,
20
+ () =>
21
+ integrationApp
22
+ .dataSourceInstance(dataSourceInstance.id)
23
+ .getLocations(args),
24
+ )
25
+
26
+ return {
27
+ locations: data?.locations ?? [],
28
+ ...rest,
29
+ }
30
+ }
@@ -0,0 +1,13 @@
1
+ import { DataSource, FindDataSourcesQuery } from '@integration-app/sdk'
2
+ import { useElements } from '../hooks/useElements'
3
+
4
+ export function useDataSources(query?: FindDataSourcesQuery) {
5
+ const { ...rest } = useElements<DataSource, FindDataSourcesQuery>(
6
+ query,
7
+ (integrationApp) => integrationApp.dataSources,
8
+ )
9
+
10
+ return {
11
+ ...rest,
12
+ }
13
+ }
@@ -0,0 +1,14 @@
1
+ import { FieldMapping, FieldMappingAccessor } from '@integration-app/sdk'
2
+ import { useElement } from '../hooks/useElement'
3
+
4
+ export function useFieldMapping(idOrKey: string) {
5
+ const { data: fieldMapping, ...rest } = useElement<
6
+ FieldMapping,
7
+ string,
8
+ FieldMappingAccessor
9
+ >(idOrKey, (integrationApp) =>
10
+ integrationApp.fieldMapping.bind(integrationApp),
11
+ )
12
+
13
+ return { fieldMapping, ...rest }
14
+ }
@@ -0,0 +1,31 @@
1
+ import {
2
+ FieldMappingInstanceAccessor,
3
+ FieldMappingInstanceSelector,
4
+ IFieldMappingInstance,
5
+ OpenFieldMappingInstanceConfigurationOptions,
6
+ } from '@integration-app/sdk'
7
+ import { useElement } from '../hooks/useElement'
8
+
9
+ export function useFieldMappingInstance(
10
+ selector: FieldMappingInstanceSelector | { id: string },
11
+ ) {
12
+ const { data: fieldMappingInstance, ...rest } = useElement<
13
+ IFieldMappingInstance,
14
+ FieldMappingInstanceSelector,
15
+ FieldMappingInstanceAccessor
16
+ >(selector, (integrationApp) =>
17
+ integrationApp.fieldMappingInstance.bind(integrationApp),
18
+ )
19
+
20
+ const accessor = rest.accessor
21
+
22
+ return {
23
+ fieldMappingInstance,
24
+ setup: () => accessor.setup(),
25
+ reset: () => accessor.reset(),
26
+ openConfiguration: (
27
+ options?: OpenFieldMappingInstanceConfigurationOptions,
28
+ ) => accessor.openConfiguration(options),
29
+ ...rest,
30
+ }
31
+ }
@@ -0,0 +1,18 @@
1
+ import {
2
+ FindFieldMappingInstancesQuery,
3
+ IFieldMappingInstance,
4
+ } from '@integration-app/sdk'
5
+ import { useElements } from '../hooks/useElements'
6
+
7
+ export function useFieldMappingInstances(
8
+ query?: FindFieldMappingInstancesQuery,
9
+ ) {
10
+ const { ...rest } = useElements<
11
+ IFieldMappingInstance,
12
+ FindFieldMappingInstancesQuery
13
+ >(query, (integrationApp) => integrationApp.fieldMappingInstances)
14
+
15
+ return {
16
+ ...rest,
17
+ }
18
+ }
@@ -0,0 +1,13 @@
1
+ import { FieldMapping, FindFieldMappingsQuery } from '@integration-app/sdk'
2
+ import { useElements } from '../hooks/useElements'
3
+
4
+ export function useFieldMappings(query?: FindFieldMappingsQuery) {
5
+ const { ...rest } = useElements<FieldMapping, FindFieldMappingsQuery>(
6
+ query,
7
+ (integrationApp) => integrationApp.fieldMappings,
8
+ )
9
+
10
+ return {
11
+ ...rest,
12
+ }
13
+ }
@@ -0,0 +1,11 @@
1
+ import { Flow, FlowAccessor } from '@integration-app/sdk'
2
+ import { useElement } from '../hooks/useElement'
3
+
4
+ export function useFlow(idOrKey: string) {
5
+ const { data: flow, ...rest } = useElement<Flow, string, FlowAccessor>(
6
+ idOrKey,
7
+ (integrationApp) => integrationApp.flow.bind(integrationApp),
8
+ )
9
+
10
+ return { flow, ...rest }
11
+ }
@@ -0,0 +1,16 @@
1
+ import {
2
+ FlowInstance,
3
+ FlowInstanceAccessor,
4
+ FlowInstanceSelector,
5
+ } from '@integration-app/sdk'
6
+ import { useElement } from '../hooks/useElement'
7
+
8
+ export function useFlowInstance(props: FlowInstanceSelector | { id: string }) {
9
+ const { data: flowInstance, ...rest } = useElement<
10
+ FlowInstance,
11
+ FlowInstanceSelector,
12
+ FlowInstanceAccessor
13
+ >(props, (integrationApp) => integrationApp.flowInstance.bind(integrationApp))
14
+
15
+ return { flowInstance, ...rest }
16
+ }
@@ -0,0 +1,13 @@
1
+ import { FindFlowInstancesQuery, FlowInstance } from '@integration-app/sdk'
2
+ import { useElements } from '../hooks/useElements'
3
+
4
+ export function useFlowInstances(query?: FindFlowInstancesQuery) {
5
+ const { ...rest } = useElements<FlowInstance, FindFlowInstancesQuery>(
6
+ query,
7
+ (integrationApp) => integrationApp.flowInstances,
8
+ )
9
+
10
+ return {
11
+ ...rest,
12
+ }
13
+ }
@@ -0,0 +1,12 @@
1
+ import { FlowRun, FlowRunAccessor } from '@integration-app/sdk'
2
+ import { useElement } from '../hooks/useElement'
3
+
4
+ export function useFlowRun(id: string) {
5
+ const { data: flowRun, ...rest } = useElement<
6
+ FlowRun,
7
+ string,
8
+ FlowRunAccessor
9
+ >(id, (integrationApp) => integrationApp.flowRun.bind(integrationApp))
10
+
11
+ return { flowRun, ...rest }
12
+ }
@@ -0,0 +1,11 @@
1
+ import { FindFlowRunsRequest, FlowRun } from '@integration-app/sdk'
2
+ import { useElements } from '../hooks/useElements'
3
+
4
+ export function useFlowRuns(query?: FindFlowRunsRequest) {
5
+ const { ...rest } = useElements<FlowRun, FindFlowRunsRequest>(
6
+ query,
7
+ (integrationApp) => integrationApp.flowRuns,
8
+ )
9
+
10
+ return { ...rest }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { useElements } from '../hooks/useElements'
2
+ import { FindFlowsRequest, Flow } from '@integration-app/sdk'
3
+
4
+ export function useFlows(query?: FindFlowsRequest) {
5
+ const { ...rest } = useElements<Flow, FindFlowsRequest>(
6
+ query,
7
+ (integrationApp) => integrationApp.flows,
8
+ )
9
+
10
+ return { ...rest }
11
+ }
@@ -0,0 +1,169 @@
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
24
+ }
25
+
26
+ type ElementAccessorGetter<SelectorInterface, ElementAccessor> = (
27
+ params: SelectorInterface | string,
28
+ ) => ElementAccessor
29
+
30
+ export type ElementAccessorGenerator<SelectorInterface, ElementAccessor> = (
31
+ integrationApp: IntegrationAppClient,
32
+ ) => ElementAccessorGetter<SelectorInterface, ElementAccessor>
33
+
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
+ archive?(): Promise<void>
40
+ }
41
+
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> }
56
+
57
+ export function useElement<
58
+ ElementInterface,
59
+ SelectorInterface,
60
+ ElementAccessor extends BaseElementAccessor<
61
+ ElementInterface,
62
+ CreateRequest,
63
+ UpdateRequest
64
+ >,
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
+ >(
74
+ props: SelectorInterface | { id: string },
75
+ accessorGenerator: ElementAccessorGenerator<
76
+ SelectorInterface,
77
+ ElementAccessor
78
+ >,
79
+ ): ElementHookResult<
80
+ ElementInterface,
81
+ ElementAccessor,
82
+ CreateRequest,
83
+ UpdateRequest
84
+ > {
85
+ 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
+ ),
113
+ )
114
+ }
115
+ }, [integrationApp, JSON.stringify(props), refreshCounter])
116
+
117
+ async function create(createData: CreateRequest) {
118
+ if (data !== undefined) {
119
+ setData({
120
+ ...data,
121
+ ...createData,
122
+ })
123
+ }
124
+ return accessorGenerator(integrationApp)(selector).create(createData)
125
+ }
126
+
127
+ function refresh() {
128
+ setRefreshCounter(refreshCounter + 1)
129
+ }
130
+
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
140
+ }
141
+ }
142
+
143
+ async function put(putData: CreateRequest) {
144
+ if (data !== undefined) {
145
+ setData({
146
+ ...data,
147
+ ...putData,
148
+ })
149
+ }
150
+ return accessorGenerator(integrationApp)(selector).put(putData)
151
+ }
152
+
153
+ async function archive() {
154
+ setData(null)
155
+ return accessorGenerator(integrationApp)(selector).archive()
156
+ }
157
+
158
+ return {
159
+ data,
160
+ create,
161
+ patch,
162
+ put,
163
+ archive,
164
+ refresh,
165
+ loading,
166
+ error,
167
+ accessor,
168
+ }
169
+ }
@@ -0,0 +1,101 @@
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
+ }
17
+
18
+ type InstanceAccessorGenerator<Element, FindQuery> = (
19
+ integrationApp: IntegrationAppClient,
20
+ ) => ElementsAccessor<Element, FindQuery>
21
+
22
+ export function useElements<Element, FindQuery extends PaginationQuery>(
23
+ initialQuery: FindQuery,
24
+ accessorGenerator: InstanceAccessorGenerator<Element, FindQuery>,
25
+ ): ElementsHookResult<Element> {
26
+ const integrationApp = useIntegrationApp()
27
+
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)
33
+
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
+ }
46
+
47
+ setStateIfCurrentRefresh(setError, () => null)
48
+ setStateIfCurrentRefresh(setLoading, () => true)
49
+
50
+ const queryParams = {
51
+ ...initialQuery,
52
+ }
53
+
54
+ if (nextCursor) queryParams.cursor = nextCursor
55
+
56
+ try {
57
+ const data = await accessorGenerator(integrationApp).find(queryParams)
58
+
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)
67
+ }
68
+ }
69
+
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
+ async function refresh() {
86
+ refreshId.current += 1
87
+ setNextCursor(undefined)
88
+ await loadMore()
89
+ }
90
+
91
+ return {
92
+ items,
93
+
94
+ refresh,
95
+ loadMore,
96
+
97
+ loading,
98
+
99
+ error,
100
+ }
101
+ }
@@ -0,0 +1,38 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { useIntegrationApp } from '../contexts/integration-app-context'
3
+
4
+ export default function useGetter<Type = any>(
5
+ key: string,
6
+ getter: () => Promise<Type>,
7
+ ) {
8
+ const integrationApp = useIntegrationApp()
9
+ const [data, setData] = useState<Type>()
10
+ const [loading, setLoading] = useState<boolean>(true)
11
+ const [error, setError] = useState<Error>(null)
12
+ const [refreshCounter, setRefreshCounter] = useState<number>(0)
13
+
14
+ function refresh() {
15
+ setRefreshCounter(refreshCounter + 1)
16
+ }
17
+
18
+ useEffect(() => {
19
+ if (key !== undefined) {
20
+ setLoading(true)
21
+ setError(null)
22
+ if (integrationApp) {
23
+ getter()
24
+ .then(setData)
25
+ .catch(setError)
26
+ .finally(() => setLoading(false))
27
+ } else {
28
+ setError(
29
+ new Error(
30
+ 'IntegrationApp not found. Was this component wrapped in <IntegrationAppProvider>?',
31
+ ),
32
+ )
33
+ }
34
+ }
35
+ }, [integrationApp, key, refreshCounter])
36
+
37
+ return { data, loading, error, refresh }
38
+ }