@integration-app/react 0.1.26 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }