@integration-app/react 0.1.26 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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 -28
  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 -8
  83. package/data-sources/useDataSources.js +0 -12
  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 -61
  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
+ }