@integration-app/react 0.1.26 → 0.2.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 (144) hide show
  1. package/README.md +83 -0
  2. package/dist/index.d.ts +302 -0
  3. package/dist/index.js +426 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.module.d.ts +302 -0
  6. package/dist/index.module.mjs +393 -0
  7. package/dist/index.module.mjs.map +1 -0
  8. package/dist/index.umd.d.ts +302 -0
  9. package/dist/index.umd.js +428 -0
  10. package/dist/index.umd.js.map +1 -0
  11. package/package.json +50 -27
  12. package/rollup.dts.config.mjs +21 -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 +55 -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/useDataSourceInstances.ts +16 -0
  26. package/src/data-sources/useDataSources.ts +13 -0
  27. package/src/field-mappings/useFieldMapping.ts +14 -0
  28. package/src/field-mappings/useFieldMappingInstance.ts +31 -0
  29. package/src/field-mappings/useFieldMappingInstances.ts +18 -0
  30. package/src/field-mappings/useFieldMappings.ts +13 -0
  31. package/src/flows/useFlow.ts +12 -0
  32. package/src/flows/useFlowInstance.ts +16 -0
  33. package/src/flows/useFlowInstances.ts +13 -0
  34. package/src/flows/useFlowRun.ts +12 -0
  35. package/src/flows/useFlowRuns.ts +11 -0
  36. package/src/flows/useFlowTemplate.ts +0 -0
  37. package/src/flows/useFlowTemplates.ts +0 -0
  38. package/src/flows/useFlows.ts +11 -0
  39. package/src/hooks/useElement.tsx +187 -0
  40. package/src/hooks/useElements.tsx +101 -0
  41. package/src/hooks/useGetter.tsx +38 -0
  42. package/src/index.tsx +40 -0
  43. package/src/integrations/useConnection.ts +12 -0
  44. package/src/integrations/useConnections.ts +13 -0
  45. package/src/integrations/useConnectorSpec.ts +25 -0
  46. package/src/integrations/useIntegration.ts +14 -0
  47. package/src/integrations/useIntegrations.ts +13 -0
  48. package/tsconfig.json +40 -0
  49. package/app-events/useAppEventSubscription.d.ts +0 -14
  50. package/app-events/useAppEventSubscription.js +0 -10
  51. package/app-events/useAppEventSubscription.js.map +0 -1
  52. package/app-events/useAppEventSubscriptions.d.ts +0 -8
  53. package/app-events/useAppEventSubscriptions.js +0 -12
  54. package/app-events/useAppEventSubscriptions.js.map +0 -1
  55. package/app-events/useAppEventType.d.ts +0 -13
  56. package/app-events/useAppEventType.js +0 -10
  57. package/app-events/useAppEventType.js.map +0 -1
  58. package/app-events/useAppEventTypes.d.ts +0 -8
  59. package/app-events/useAppEventTypes.js +0 -12
  60. package/app-events/useAppEventTypes.js.map +0 -1
  61. package/app-events/useAppEvents.d.ts +0 -8
  62. package/app-events/useAppEvents.js +0 -12
  63. package/app-events/useAppEvents.js.map +0 -1
  64. package/contexts/integration-app-context.d.ts +0 -11
  65. package/contexts/integration-app-context.js +0 -22
  66. package/contexts/integration-app-context.js.map +0 -1
  67. package/data-form/index.d.ts +0 -1
  68. package/data-form/index.js +0 -8
  69. package/data-form/index.js.map +0 -1
  70. package/data-sources/useDataSource.d.ts +0 -12
  71. package/data-sources/useDataSource.js +0 -10
  72. package/data-sources/useDataSource.js.map +0 -1
  73. package/data-sources/useDataSourceEvents.d.ts +0 -8
  74. package/data-sources/useDataSourceEvents.js +0 -12
  75. package/data-sources/useDataSourceEvents.js.map +0 -1
  76. package/data-sources/useDataSourceInstance.d.ts +0 -28
  77. package/data-sources/useDataSourceInstance.js +0 -28
  78. package/data-sources/useDataSourceInstance.js.map +0 -1
  79. package/data-sources/useDataSourceInstanceCollection.d.ts +0 -7
  80. package/data-sources/useDataSourceInstanceCollection.js +0 -16
  81. package/data-sources/useDataSourceInstanceCollection.js.map +0 -1
  82. package/data-sources/useDataSourceInstanceLocations.d.ts +0 -10
  83. package/data-sources/useDataSourceInstanceLocations.js +0 -21
  84. package/data-sources/useDataSourceInstanceLocations.js.map +0 -1
  85. package/data-sources/useDataSources.d.ts +0 -8
  86. package/data-sources/useDataSources.js +0 -12
  87. package/data-sources/useDataSources.js.map +0 -1
  88. package/field-mappings/useFieldMapping.d.ts +0 -12
  89. package/field-mappings/useFieldMapping.js +0 -10
  90. package/field-mappings/useFieldMapping.js.map +0 -1
  91. package/field-mappings/useFieldMappingInstance.d.ts +0 -17
  92. package/field-mappings/useFieldMappingInstance.js +0 -17
  93. package/field-mappings/useFieldMappingInstance.js.map +0 -1
  94. package/field-mappings/useFieldMappingInstances.d.ts +0 -8
  95. package/field-mappings/useFieldMappingInstances.js +0 -12
  96. package/field-mappings/useFieldMappingInstances.js.map +0 -1
  97. package/field-mappings/useFieldMappings.d.ts +0 -8
  98. package/field-mappings/useFieldMappings.js +0 -12
  99. package/field-mappings/useFieldMappings.js.map +0 -1
  100. package/flows/useFlow.d.ts +0 -12
  101. package/flows/useFlow.js +0 -10
  102. package/flows/useFlow.js.map +0 -1
  103. package/flows/useFlowInstance.d.ts +0 -14
  104. package/flows/useFlowInstance.js +0 -10
  105. package/flows/useFlowInstance.js.map +0 -1
  106. package/flows/useFlowInstances.d.ts +0 -8
  107. package/flows/useFlowInstances.js +0 -12
  108. package/flows/useFlowInstances.js.map +0 -1
  109. package/flows/useFlowRun.d.ts +0 -12
  110. package/flows/useFlowRun.js +0 -10
  111. package/flows/useFlowRun.js.map +0 -1
  112. package/flows/useFlowRuns.d.ts +0 -8
  113. package/flows/useFlowRuns.js +0 -10
  114. package/flows/useFlowRuns.js.map +0 -1
  115. package/flows/useFlows.d.ts +0 -8
  116. package/flows/useFlows.js +0 -10
  117. package/flows/useFlows.js.map +0 -1
  118. package/hooks/useElement.d.ts +0 -25
  119. package/hooks/useElement.js +0 -71
  120. package/hooks/useElement.js.map +0 -1
  121. package/hooks/useElements.d.ts +0 -15
  122. package/hooks/useElements.js +0 -59
  123. package/hooks/useElements.js.map +0 -1
  124. package/hooks/useGetter.d.ts +0 -6
  125. package/hooks/useGetter.js +0 -32
  126. package/hooks/useGetter.js.map +0 -1
  127. package/index.d.ts +0 -28
  128. package/index.js +0 -61
  129. package/index.js.map +0 -1
  130. package/integrations/useConnection.d.ts +0 -12
  131. package/integrations/useConnection.js +0 -10
  132. package/integrations/useConnection.js.map +0 -1
  133. package/integrations/useConnectionSpec.d.ts +0 -5
  134. package/integrations/useConnectionSpec.js +0 -25
  135. package/integrations/useConnectionSpec.js.map +0 -1
  136. package/integrations/useConnections.d.ts +0 -8
  137. package/integrations/useConnections.js +0 -12
  138. package/integrations/useConnections.js.map +0 -1
  139. package/integrations/useIntegration.d.ts +0 -12
  140. package/integrations/useIntegration.js +0 -10
  141. package/integrations/useIntegration.js.map +0 -1
  142. package/integrations/useIntegrations.d.ts +0 -8
  143. package/integrations/useIntegrations.js +0 -12
  144. package/integrations/useIntegrations.js.map +0 -1
@@ -0,0 +1,55 @@
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
+ fetchToken?: () => Promise<string>
14
+ credentials?: any
15
+ fetchCredentials?: () => Promise<any>
16
+ apiUri?: string
17
+ uiUri?: string
18
+ children: ReactNode
19
+ }
20
+
21
+ /**
22
+ * Provides an IntegrationAppClient instance to the React tree.
23
+ */
24
+ export const IntegrationAppProvider = ({
25
+ token,
26
+ fetchToken,
27
+ credentials,
28
+ fetchCredentials,
29
+ apiUri = null,
30
+ uiUri = null,
31
+ children,
32
+ }: IntegrationAppProviderProps) => {
33
+ const client = useMemo(
34
+ () =>
35
+ new IntegrationAppClient({
36
+ token,
37
+ fetchToken,
38
+ credentials,
39
+ fetchCredentials,
40
+ apiUri,
41
+ uiUri,
42
+ }),
43
+ [token, JSON.stringify(credentials), apiUri, uiUri],
44
+ )
45
+
46
+ return (
47
+ <IntegrationAppContext.Provider value={client}>
48
+ {children}
49
+ </IntegrationAppContext.Provider>
50
+ )
51
+ }
52
+
53
+ export function useIntegrationApp(): IntegrationAppClient {
54
+ return useContext(IntegrationAppContext)
55
+ }
@@ -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,16 @@
1
+ import {
2
+ FindDataSourceInstancesQuery,
3
+ DataSourceInstance,
4
+ } from '@integration-app/sdk'
5
+ import { useElements } from '../hooks/useElements'
6
+
7
+ export function useDataSourceInstances(query?: FindDataSourceInstancesQuery) {
8
+ const { ...rest } = useElements<
9
+ DataSourceInstance,
10
+ FindDataSourceInstancesQuery
11
+ >(query, (integrationApp) => integrationApp.dataSourceInstances)
12
+
13
+ return {
14
+ ...rest,
15
+ }
16
+ }
@@ -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
+ FieldMappingInstance,
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
+ FieldMappingInstance,
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
+ FieldMappingInstance,
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
+ FieldMappingInstance,
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,12 @@
1
+ import { Flow, FlowAccessor, FlowSelector } from '@integration-app/sdk'
2
+ import { useElement } from '../hooks/useElement'
3
+
4
+ export function useFlow(idOrSelector: string | FlowSelector) {
5
+ const { data: flow, ...rest } = useElement<
6
+ Flow,
7
+ string | FlowSelector,
8
+ FlowAccessor
9
+ >(idOrSelector, (integrationApp) => integrationApp.flow.bind(integrationApp))
10
+
11
+ return { flow, ...rest }
12
+ }
@@ -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
+ }
File without changes
File without changes
@@ -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,187 @@
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
+ function updateDataWith(newData) {
92
+ if (data !== undefined) {
93
+ setData({
94
+ ...data,
95
+ ...newData,
96
+ })
97
+ }
98
+ }
99
+
100
+ function replaceDataWith(newData) {
101
+ setData(newData)
102
+ }
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
+ ),
126
+ )
127
+ }
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
+
149
+ async function patch(patch: UpdateRequest) {
150
+ if (typeof patch === 'object') {
151
+ updateDataWith(patch ?? {})
152
+
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
159
+ }
160
+ }
161
+
162
+ async function put(putData: CreateRequest) {
163
+ updateDataWith(putData)
164
+
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)
169
+ }
170
+
171
+ async function archive() {
172
+ setData(null)
173
+ return accessor.archive()
174
+ }
175
+
176
+ return {
177
+ data,
178
+ create,
179
+ patch,
180
+ put,
181
+ archive,
182
+ refresh,
183
+ loading,
184
+ error,
185
+ accessor,
186
+ }
187
+ }
@@ -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
+ }