@quazardous/qdadm 1.19.3
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.
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/package.json +101 -0
- package/src/assets/logo.svg +6 -0
- package/src/auth/SessionAuthAdapter.ts +459 -0
- package/src/auth/index.ts +15 -0
- package/src/chain/ActiveStack.ts +154 -0
- package/src/chain/StackHydrator.ts +292 -0
- package/src/chain/index.ts +25 -0
- package/src/chain/useActiveStack.ts +120 -0
- package/src/chain/useStackHydrator.ts +155 -0
- package/src/components/BoolCell.vue +34 -0
- package/src/components/InfoBanner.vue +73 -0
- package/src/components/QdadmRoot.vue +52 -0
- package/src/components/SeverityTag.vue +86 -0
- package/src/components/dialogs/BulkStatusDialog.vue +60 -0
- package/src/components/dialogs/MultiStepDialog.vue +331 -0
- package/src/components/dialogs/SimpleDialog.vue +122 -0
- package/src/components/dialogs/UnsavedChangesDialog.vue +80 -0
- package/src/components/display/BannerZone.vue +65 -0
- package/src/components/display/CardsGrid.vue +72 -0
- package/src/components/display/CopyableId.vue +84 -0
- package/src/components/display/EmptyState.vue +62 -0
- package/src/components/display/IntensityBar.vue +190 -0
- package/src/components/display/RichCardsGrid.vue +101 -0
- package/src/components/edit/FormActions.vue +78 -0
- package/src/components/edit/FormField.vue +82 -0
- package/src/components/edit/FormInput.vue +158 -0
- package/src/components/edit/FormPage.vue +260 -0
- package/src/components/edit/FormTab.vue +217 -0
- package/src/components/edit/FormTabs.vue +120 -0
- package/src/components/edit/LookupField.vue +406 -0
- package/src/components/edit/LookupPickerDialog.vue +230 -0
- package/src/components/editors/JsonEditorFoldable.vue +470 -0
- package/src/components/editors/JsonStructuredField.vue +264 -0
- package/src/components/editors/JsonViewer.vue +51 -0
- package/src/components/editors/KeyValueEditor.vue +314 -0
- package/src/components/editors/LanguageEditor.vue +259 -0
- package/src/components/editors/PermissionEditor.vue +558 -0
- package/src/components/editors/ScopeEditor.vue +372 -0
- package/src/components/editors/VanillaJsonEditor.vue +183 -0
- package/src/components/index.ts +83 -0
- package/src/components/item/FieldGroups.vue +392 -0
- package/src/components/layout/AppLayout.vue +1032 -0
- package/src/components/layout/BaseLayout.vue +190 -0
- package/src/components/layout/Breadcrumb.vue +55 -0
- package/src/components/layout/DashboardLayout.vue +104 -0
- package/src/components/layout/FormLayout.vue +256 -0
- package/src/components/layout/ListLayout.vue +278 -0
- package/src/components/layout/PageHeader.vue +156 -0
- package/src/components/layout/PageLayout.vue +126 -0
- package/src/components/layout/PageNav.vue +192 -0
- package/src/components/layout/SidebarBox.vue +87 -0
- package/src/components/layout/Zone.vue +182 -0
- package/src/components/layout/defaults/DefaultBreadcrumb.vue +149 -0
- package/src/components/layout/defaults/DefaultFooter.vue +89 -0
- package/src/components/layout/defaults/DefaultFormActions.vue +67 -0
- package/src/components/layout/defaults/DefaultHeader.vue +78 -0
- package/src/components/layout/defaults/DefaultMenu.vue +188 -0
- package/src/components/layout/defaults/DefaultPagination.vue +75 -0
- package/src/components/layout/defaults/DefaultTable.vue +137 -0
- package/src/components/layout/defaults/DefaultUserInfo.vue +111 -0
- package/src/components/layout/defaults/index.ts +16 -0
- package/src/components/lists/ActionButtons.vue +49 -0
- package/src/components/lists/ActionColumn.vue +40 -0
- package/src/components/lists/FilterBar.vue +54 -0
- package/src/components/lists/ListPage.vue +467 -0
- package/src/components/pages/LoginPage.vue +334 -0
- package/src/components/pages/NotFoundPage.vue +98 -0
- package/src/components/show/ShowDisplay.vue +343 -0
- package/src/components/show/ShowField.vue +67 -0
- package/src/components/show/ShowPage.vue +431 -0
- package/src/composables/index.ts +155 -0
- package/src/composables/useApp.ts +67 -0
- package/src/composables/useAuth.ts +98 -0
- package/src/composables/useBareForm.ts +319 -0
- package/src/composables/useBreadcrumb.ts +252 -0
- package/src/composables/useChildPage.ts +113 -0
- package/src/composables/useCurrentEntity.ts +60 -0
- package/src/composables/useDeferred.ts +94 -0
- package/src/composables/useDirtyState.ts +138 -0
- package/src/composables/useEntityItemFormPage.ts +942 -0
- package/src/composables/useEntityItemFormPage.types.ts +451 -0
- package/src/composables/useEntityItemPage.ts +438 -0
- package/src/composables/useEntityItemShowPage.ts +722 -0
- package/src/composables/useFieldManager.ts +692 -0
- package/src/composables/useForm.ts +544 -0
- package/src/composables/useGuardStore.ts +53 -0
- package/src/composables/useHooks.ts +56 -0
- package/src/composables/useInfoBanner.ts +217 -0
- package/src/composables/useJsonSyntax.ts +122 -0
- package/src/composables/useLayoutResolver.ts +329 -0
- package/src/composables/useListPage.ts +1535 -0
- package/src/composables/useListPage.types.ts +437 -0
- package/src/composables/useListPage.utils.ts +101 -0
- package/src/composables/useNavContext.ts +439 -0
- package/src/composables/useNavigation.ts +216 -0
- package/src/composables/useOptionsLookup.ts +323 -0
- package/src/composables/usePageTitle.ts +84 -0
- package/src/composables/useSSEBridge.ts +142 -0
- package/src/composables/useSemanticBreadcrumb.ts +222 -0
- package/src/composables/useSidebarState.ts +24 -0
- package/src/composables/useSignals.ts +52 -0
- package/src/composables/useUnsavedChangesGuard.ts +155 -0
- package/src/composables/useUserImpersonator.ts +317 -0
- package/src/composables/useZoneRegistry.ts +252 -0
- package/src/core/bundles.ts +558 -0
- package/src/core/decorator.ts +420 -0
- package/src/core/extension.ts +470 -0
- package/src/core/index.ts +59 -0
- package/src/deferred/DeferredRegistry.ts +353 -0
- package/src/deferred/index.ts +15 -0
- package/src/editors/index.ts +12 -0
- package/src/entity/EntityManager.cache.ts +530 -0
- package/src/entity/EntityManager.crud.ts +615 -0
- package/src/entity/EntityManager.interface.ts +78 -0
- package/src/entity/EntityManager.query.ts +221 -0
- package/src/entity/EntityManager.relations.ts +151 -0
- package/src/entity/EntityManager.ts +944 -0
- package/src/entity/EntityManager.types.ts +288 -0
- package/src/entity/auth/CompositeAuthAdapter.ts +194 -0
- package/src/entity/auth/EntityAuthAdapter.ts +119 -0
- package/src/entity/auth/PermissiveAdapter.ts +43 -0
- package/src/entity/auth/RoleHierarchy.ts +128 -0
- package/src/entity/auth/SecurityChecker.ts +202 -0
- package/src/entity/auth/factory.ts +203 -0
- package/src/entity/auth/index.ts +47 -0
- package/src/entity/factory.test.js +189 -0
- package/src/entity/factory.ts +146 -0
- package/src/entity/index.ts +49 -0
- package/src/entity/storage/ApiStorage.ts +228 -0
- package/src/entity/storage/IStorage.ts +100 -0
- package/src/entity/storage/LocalStorage.ts +200 -0
- package/src/entity/storage/MemoryStorage.ts +184 -0
- package/src/entity/storage/MockApiStorage.ts +318 -0
- package/src/entity/storage/SdkStorage.ts +377 -0
- package/src/entity/storage/factory.test.js +159 -0
- package/src/entity/storage/factory.ts +209 -0
- package/src/entity/storage/index.ts +75 -0
- package/src/gen/FieldMapper.ts +122 -0
- package/src/gen/StorageProfileFactory.ts +93 -0
- package/src/gen/connectors/BaseConnector.ts +150 -0
- package/src/gen/connectors/ManualConnector.test.js +499 -0
- package/src/gen/connectors/ManualConnector.ts +414 -0
- package/src/gen/connectors/OpenAPIConnector.test.js +836 -0
- package/src/gen/connectors/OpenAPIConnector.ts +693 -0
- package/src/gen/connectors/__fixtures__/sample-openapi.json +311 -0
- package/src/gen/connectors/index.ts +27 -0
- package/src/gen/createManagers.ts +245 -0
- package/src/gen/decorators.ts +134 -0
- package/src/gen/generateManagers.test.js +540 -0
- package/src/gen/generateManagers.ts +398 -0
- package/src/gen/index.ts +66 -0
- package/src/gen/schema.ts +248 -0
- package/src/gen/vite-plugin.ts +114 -0
- package/src/generated/managers/testManager.js +45 -0
- package/src/generated/managers/testManager.ts +46 -0
- package/src/hooks/HookRegistry.ts +16 -0
- package/src/hooks/index.ts +15 -0
- package/src/i18n/I18n.ts +344 -0
- package/src/i18n/IncrementalDomainProvider.ts +153 -0
- package/src/i18n/InlineTranslationProvider.ts +4 -0
- package/src/i18n/LazyTranslationProvider.ts +102 -0
- package/src/i18n/MessagesRegistry.ts +4 -0
- package/src/i18n/Resolver.ts +4 -0
- package/src/i18n/__tests__/I18n.test.ts +169 -0
- package/src/i18n/__tests__/IncrementalDomainProvider.test.ts +146 -0
- package/src/i18n/__tests__/LazyTranslationProvider.test.ts +100 -0
- package/src/i18n/__tests__/Resolver.test.ts +271 -0
- package/src/i18n/defaults/DefaultCoreProvider.ts +28 -0
- package/src/i18n/defaults/core.en.yml +55 -0
- package/src/i18n/defaults/core.fr.yml +55 -0
- package/src/i18n/index.ts +55 -0
- package/src/i18n/loaders/raw-modules.d.ts +15 -0
- package/src/i18n/loaders/yaml.ts +35 -0
- package/src/i18n/strategies.ts +4 -0
- package/src/i18n/types.ts +34 -0
- package/src/i18n/useI18n.ts +34 -0
- package/src/index.ts +453 -0
- package/src/kernel/EventRouter.ts +23 -0
- package/src/kernel/Kernel.api.ts +79 -0
- package/src/kernel/Kernel.auth.ts +120 -0
- package/src/kernel/Kernel.i18n.ts +29 -0
- package/src/kernel/Kernel.modules.ts +170 -0
- package/src/kernel/Kernel.registries.ts +237 -0
- package/src/kernel/Kernel.routing.ts +334 -0
- package/src/kernel/Kernel.ts +460 -0
- package/src/kernel/Kernel.types.ts +263 -0
- package/src/kernel/Kernel.vue.ts +304 -0
- package/src/kernel/KernelContext.entities.ts +80 -0
- package/src/kernel/KernelContext.events.ts +57 -0
- package/src/kernel/KernelContext.i18n.ts +37 -0
- package/src/kernel/KernelContext.permissions.ts +38 -0
- package/src/kernel/KernelContext.routing.ts +280 -0
- package/src/kernel/KernelContext.ts +252 -0
- package/src/kernel/KernelContext.types.ts +173 -0
- package/src/kernel/KernelContext.zones.ts +54 -0
- package/src/kernel/Module.ts +169 -0
- package/src/kernel/ModuleLoader.ts +645 -0
- package/src/kernel/SSEBridge.ts +11 -0
- package/src/kernel/SignalBus.ts +48 -0
- package/src/kernel/index.ts +56 -0
- package/src/module/index.ts +33 -0
- package/src/module/moduleRegistry.ts +492 -0
- package/src/modules/debug/AuthCollector.ts +501 -0
- package/src/modules/debug/Collector.ts +20 -0
- package/src/modules/debug/DebugBridge.ts +14 -0
- package/src/modules/debug/DebugModule.ts +220 -0
- package/src/modules/debug/EntitiesCollector.ts +839 -0
- package/src/modules/debug/ErrorCollector.ts +4 -0
- package/src/modules/debug/I18nCollector.ts +9 -0
- package/src/modules/debug/LocalStorageAdapter.ts +7 -0
- package/src/modules/debug/RouterCollector.ts +449 -0
- package/src/modules/debug/SignalCollector.ts +4 -0
- package/src/modules/debug/ToastCollector.ts +4 -0
- package/src/modules/debug/ZonesCollector.ts +447 -0
- package/src/modules/debug/components/DebugBar.vue +31 -0
- package/src/modules/debug/components/adminPanelsConfig.ts +42 -0
- package/src/modules/debug/components/index.ts +9 -0
- package/src/modules/debug/components/panels/AuthPanel.vue +267 -0
- package/src/modules/debug/components/panels/EntitiesPanel.vue +572 -0
- package/src/modules/debug/components/panels/I18nPanel.vue +738 -0
- package/src/modules/debug/components/panels/RouterPanel.vue +440 -0
- package/src/modules/debug/components/panels/ZonesPanel.vue +133 -0
- package/src/modules/debug/components/panels/index.ts +16 -0
- package/src/modules/debug/index.ts +58 -0
- package/src/modules/debug/styles.scss +2671 -0
- package/src/notifications/NotificationBadge.vue +35 -0
- package/src/notifications/NotificationListener.vue +71 -0
- package/src/notifications/NotificationModule.ts +76 -0
- package/src/notifications/NotificationPanel.vue +478 -0
- package/src/notifications/NotificationStore.ts +288 -0
- package/src/notifications/index.ts +42 -0
- package/src/notifications/styles.scss +32 -0
- package/src/orchestrator/Orchestrator.ts +427 -0
- package/src/orchestrator/index.ts +8 -0
- package/src/orchestrator/useOrchestrator.ts +80 -0
- package/src/plugin.ts +251 -0
- package/src/query/FilterQuery.ts +306 -0
- package/src/query/QueryExecutor.ts +341 -0
- package/src/query/index.ts +16 -0
- package/src/security/EntityRolesProvider.ts +385 -0
- package/src/security/PermissionMatcher.ts +152 -0
- package/src/security/PermissionRegistry.ts +273 -0
- package/src/security/PersistableRolesProvider.ts +584 -0
- package/src/security/RolesManager.ts +136 -0
- package/src/security/RolesProvider.ts +167 -0
- package/src/security/RolesProviderStorage.ts +211 -0
- package/src/security/SecurityModule.ts +94 -0
- package/src/security/StaticRolesProvider.ts +117 -0
- package/src/security/UsersManager.ts +163 -0
- package/src/security/index.ts +77 -0
- package/src/security/pages/RoleForm.vue +276 -0
- package/src/security/pages/RoleList.vue +123 -0
- package/src/styles/_alerts.scss +95 -0
- package/src/styles/_buttons.scss +391 -0
- package/src/styles/_cards.scss +241 -0
- package/src/styles/_code.scss +78 -0
- package/src/styles/_dialogs.scss +55 -0
- package/src/styles/_filter-bar.scss +70 -0
- package/src/styles/_forms.scss +434 -0
- package/src/styles/_helpers.scss +153 -0
- package/src/styles/_lists.scss +225 -0
- package/src/styles/_main.scss +356 -0
- package/src/styles/_markdown.scss +88 -0
- package/src/styles/_responsive.scss +255 -0
- package/src/styles/_show-pages.scss +186 -0
- package/src/styles/_sidebar.scss +380 -0
- package/src/styles/_states.scss +115 -0
- package/src/styles/_stats.scss +47 -0
- package/src/styles/_variables.scss +224 -0
- package/src/styles/index.scss +38 -0
- package/src/styles/theme/_components.scss +331 -0
- package/src/styles/theme/_index.scss +10 -0
- package/src/styles/theme/_tokens.scss +147 -0
- package/src/styles/theme/_utilities.scss +175 -0
- package/src/toast/ToastBridgeModule.ts +68 -0
- package/src/toast/ToastListener.vue +55 -0
- package/src/toast/index.ts +26 -0
- package/src/toast/useSignalToast.ts +154 -0
- package/src/types/index.ts +285 -0
- package/src/types/pluralize.d.ts +19 -0
- package/src/types/quarkernel.d.ts +50 -0
- package/src/utils/formatters.ts +174 -0
- package/src/utils/index.ts +35 -0
- package/src/utils/transformers.ts +131 -0
- package/src/vite/qdadmDebugPlugin.ts +401 -0
- package/src/vite-env.d.ts +50 -0
- package/src/zones/ZoneRegistry.ts +934 -0
- package/src/zones/index.ts +29 -0
- package/src/zones/zones.ts +191 -0
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAPI Connector
|
|
3
|
+
*
|
|
4
|
+
* Parses OpenAPI 3.x specifications into UnifiedEntitySchema format.
|
|
5
|
+
* Configurable path patterns, data wrapper, and operation filtering.
|
|
6
|
+
*
|
|
7
|
+
* @module gen/connectors/OpenAPIConnector
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { BaseConnector, type ConnectorOptions, type ParseResult, type ParseWarning } from './BaseConnector'
|
|
11
|
+
import { getDefaultType, type CustomMappings } from '../FieldMapper'
|
|
12
|
+
import type { UnifiedEntitySchema, UnifiedFieldSchema, UnifiedFieldType } from '../schema'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Default path patterns for entity extraction
|
|
16
|
+
* Matches common REST patterns: /api/entities and /api/entities/{id}
|
|
17
|
+
*/
|
|
18
|
+
const DEFAULT_PATH_PATTERNS: RegExp[] = [
|
|
19
|
+
/^\/api\/([a-z-]+)\/?$/, // /api/users/ or /api/users
|
|
20
|
+
/^\/api\/([a-z-]+)\/\{[^}]+\}$/, // /api/users/{id}
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* HTTP methods that indicate CRUD operations
|
|
25
|
+
*/
|
|
26
|
+
const CRUD_METHODS: Readonly<Record<string, string>> = Object.freeze({
|
|
27
|
+
get: 'read',
|
|
28
|
+
post: 'create',
|
|
29
|
+
put: 'update',
|
|
30
|
+
patch: 'update',
|
|
31
|
+
delete: 'delete',
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Filter function for OpenAPI operations
|
|
36
|
+
*/
|
|
37
|
+
export type OperationFilter = (path: string, method: string, operation: OpenAPIOperation) => boolean
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* OpenAPI Connector specific options
|
|
41
|
+
*/
|
|
42
|
+
export interface OpenAPIConnectorOptions extends ConnectorOptions {
|
|
43
|
+
/** Path patterns to match entities (default: DEFAULT_PATH_PATTERNS) */
|
|
44
|
+
pathPatterns?: RegExp[]
|
|
45
|
+
/** Property name wrapping response data (default: 'data') */
|
|
46
|
+
dataWrapper?: string
|
|
47
|
+
/** Filter function for operations */
|
|
48
|
+
operationFilter?: OperationFilter | null
|
|
49
|
+
/** Custom type mappings for FieldMapper */
|
|
50
|
+
customMappings?: CustomMappings
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Working data structure for entity extraction
|
|
55
|
+
*/
|
|
56
|
+
interface EntityWorkingData {
|
|
57
|
+
/** Entity name */
|
|
58
|
+
name: string
|
|
59
|
+
/** Collection endpoint */
|
|
60
|
+
endpoint: string
|
|
61
|
+
/** Detected ID field from path parameter */
|
|
62
|
+
idField: string | null
|
|
63
|
+
/** Merged raw JSON Schema */
|
|
64
|
+
rawSchema: OpenAPISchema | null
|
|
65
|
+
/** Extracted fields */
|
|
66
|
+
fields: Map<string, UnifiedFieldSchema>
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* OpenAPI schema type
|
|
71
|
+
*/
|
|
72
|
+
interface OpenAPISchema {
|
|
73
|
+
type?: string
|
|
74
|
+
format?: string
|
|
75
|
+
properties?: Record<string, OpenAPISchema>
|
|
76
|
+
items?: OpenAPISchema
|
|
77
|
+
required?: string[]
|
|
78
|
+
$ref?: string
|
|
79
|
+
description?: string
|
|
80
|
+
enum?: string[]
|
|
81
|
+
default?: unknown
|
|
82
|
+
readOnly?: boolean
|
|
83
|
+
[key: string]: unknown
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* OpenAPI operation type
|
|
88
|
+
*/
|
|
89
|
+
interface OpenAPIOperation {
|
|
90
|
+
responses?: Record<
|
|
91
|
+
string,
|
|
92
|
+
{
|
|
93
|
+
content?: Record<
|
|
94
|
+
string,
|
|
95
|
+
{
|
|
96
|
+
schema?: OpenAPISchema
|
|
97
|
+
}
|
|
98
|
+
>
|
|
99
|
+
}
|
|
100
|
+
>
|
|
101
|
+
requestBody?: {
|
|
102
|
+
content?: Record<
|
|
103
|
+
string,
|
|
104
|
+
{
|
|
105
|
+
schema?: OpenAPISchema
|
|
106
|
+
}
|
|
107
|
+
>
|
|
108
|
+
}
|
|
109
|
+
tags?: string[]
|
|
110
|
+
[key: string]: unknown
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* OpenAPI path item type
|
|
115
|
+
*/
|
|
116
|
+
interface OpenAPIPathItem {
|
|
117
|
+
get?: OpenAPIOperation
|
|
118
|
+
post?: OpenAPIOperation
|
|
119
|
+
put?: OpenAPIOperation
|
|
120
|
+
patch?: OpenAPIOperation
|
|
121
|
+
delete?: OpenAPIOperation
|
|
122
|
+
[key: string]: unknown
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* OpenAPI specification type
|
|
127
|
+
*/
|
|
128
|
+
interface OpenAPISpec {
|
|
129
|
+
openapi?: string
|
|
130
|
+
paths: Record<string, OpenAPIPathItem>
|
|
131
|
+
components?: {
|
|
132
|
+
schemas?: Record<string, OpenAPISchema>
|
|
133
|
+
[key: string]: unknown
|
|
134
|
+
}
|
|
135
|
+
[key: string]: unknown
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* OpenAPI Connector for parsing OpenAPI 3.x specs into UnifiedEntitySchema.
|
|
140
|
+
*
|
|
141
|
+
* @extends BaseConnector
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* // Basic usage with defaults
|
|
145
|
+
* const connector = new OpenAPIConnector()
|
|
146
|
+
* const schemas = connector.parse(openapiSpec)
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* // Custom path patterns for versioned API
|
|
150
|
+
* const connector = new OpenAPIConnector({
|
|
151
|
+
* pathPatterns: [
|
|
152
|
+
* /^\/api\/v1\/([a-z-]+)\/?$/,
|
|
153
|
+
* /^\/api\/v1\/([a-z-]+)\/\{[^}]+\}$/
|
|
154
|
+
* ]
|
|
155
|
+
* })
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* // Filter to only public operations
|
|
159
|
+
* const connector = new OpenAPIConnector({
|
|
160
|
+
* operationFilter: (path, method, op) => !op.tags?.includes('internal')
|
|
161
|
+
* })
|
|
162
|
+
*/
|
|
163
|
+
export class OpenAPIConnector extends BaseConnector {
|
|
164
|
+
/** Path patterns to match entities */
|
|
165
|
+
pathPatterns: RegExp[]
|
|
166
|
+
|
|
167
|
+
/** Property name wrapping response data */
|
|
168
|
+
dataWrapper: string
|
|
169
|
+
|
|
170
|
+
/** Filter function for operations */
|
|
171
|
+
operationFilter: OperationFilter | null
|
|
172
|
+
|
|
173
|
+
/** Custom type mappings */
|
|
174
|
+
customMappings: CustomMappings
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Create a new OpenAPI connector
|
|
178
|
+
*
|
|
179
|
+
* @param options - Connector options
|
|
180
|
+
*/
|
|
181
|
+
constructor(options: OpenAPIConnectorOptions = {}) {
|
|
182
|
+
super(options)
|
|
183
|
+
|
|
184
|
+
this.pathPatterns = options.pathPatterns || DEFAULT_PATH_PATTERNS
|
|
185
|
+
this.dataWrapper = options.dataWrapper ?? 'data'
|
|
186
|
+
this.operationFilter = options.operationFilter || null
|
|
187
|
+
this.customMappings = options.customMappings || {}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Parse OpenAPI spec into UnifiedEntitySchema array
|
|
192
|
+
*
|
|
193
|
+
* @param source - OpenAPI 3.x specification object
|
|
194
|
+
* @returns Parsed entity schemas
|
|
195
|
+
* @throws If source is invalid or parsing fails in strict mode
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* const schemas = connector.parse({
|
|
199
|
+
* openapi: '3.0.0',
|
|
200
|
+
* paths: { '/api/users': { get: { ... } } }
|
|
201
|
+
* })
|
|
202
|
+
*/
|
|
203
|
+
parse(source: unknown): UnifiedEntitySchema[] {
|
|
204
|
+
const spec = source as OpenAPISpec
|
|
205
|
+
this._validateSource(spec)
|
|
206
|
+
const entities = this._extractEntities(spec)
|
|
207
|
+
return Array.from(entities.values())
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Parse with warnings for detailed feedback
|
|
212
|
+
*
|
|
213
|
+
* @param source - OpenAPI 3.x specification object
|
|
214
|
+
* @returns Schemas and warnings
|
|
215
|
+
*/
|
|
216
|
+
parseWithWarnings(source: unknown): ParseResult {
|
|
217
|
+
const warnings: ParseWarning[] = []
|
|
218
|
+
const spec = source as OpenAPISpec
|
|
219
|
+
|
|
220
|
+
this._validateSource(spec)
|
|
221
|
+
const entities = this._extractEntities(spec, warnings)
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
schemas: Array.from(entities.values()),
|
|
225
|
+
warnings,
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Validate OpenAPI source object
|
|
231
|
+
*
|
|
232
|
+
* @private
|
|
233
|
+
* @param source - Source to validate
|
|
234
|
+
* @throws If source is invalid
|
|
235
|
+
*/
|
|
236
|
+
private _validateSource(source: OpenAPISpec): void {
|
|
237
|
+
if (!source || typeof source !== 'object') {
|
|
238
|
+
throw new Error(`${this.name}: source must be an object`)
|
|
239
|
+
}
|
|
240
|
+
if (!source.paths || typeof source.paths !== 'object') {
|
|
241
|
+
throw new Error(`${this.name}: source must have paths object`)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Extract all entities from OpenAPI spec
|
|
247
|
+
*
|
|
248
|
+
* @private
|
|
249
|
+
* @param source - OpenAPI spec
|
|
250
|
+
* @param warnings - Warning collector
|
|
251
|
+
* @returns Entity map
|
|
252
|
+
*/
|
|
253
|
+
private _extractEntities(
|
|
254
|
+
source: OpenAPISpec,
|
|
255
|
+
warnings: ParseWarning[] = []
|
|
256
|
+
): Map<string, UnifiedEntitySchema> {
|
|
257
|
+
const entityData: Map<string, EntityWorkingData> = new Map()
|
|
258
|
+
|
|
259
|
+
for (const [path, pathItem] of Object.entries(source.paths)) {
|
|
260
|
+
const entityName = this._extractEntityName(path)
|
|
261
|
+
if (!entityName) continue
|
|
262
|
+
|
|
263
|
+
if (!entityData.has(entityName)) {
|
|
264
|
+
entityData.set(entityName, {
|
|
265
|
+
name: entityName,
|
|
266
|
+
endpoint: this._buildEndpoint(path),
|
|
267
|
+
idField: null,
|
|
268
|
+
rawSchema: null,
|
|
269
|
+
fields: new Map(),
|
|
270
|
+
})
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Detect idField from path parameter (e.g., /users/{uuid} → 'uuid')
|
|
274
|
+
const data = entityData.get(entityName)!
|
|
275
|
+
const detectedIdField = this._extractIdField(path)
|
|
276
|
+
if (detectedIdField && !data.idField) {
|
|
277
|
+
data.idField = detectedIdField
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
this._processPath(source, path, pathItem, data, warnings)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Convert working data to UnifiedEntitySchema
|
|
284
|
+
return this._convertToSchemas(entityData)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Extract entity name from path using configured patterns
|
|
289
|
+
*
|
|
290
|
+
* @private
|
|
291
|
+
* @param path - API path
|
|
292
|
+
* @returns Entity name or null
|
|
293
|
+
*/
|
|
294
|
+
private _extractEntityName(path: string): string | null {
|
|
295
|
+
for (const pattern of this.pathPatterns) {
|
|
296
|
+
const match = path.match(pattern)
|
|
297
|
+
if (match && match[1]) {
|
|
298
|
+
return match[1]
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return null
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Build endpoint from path (use collection path, not item path)
|
|
306
|
+
*
|
|
307
|
+
* @private
|
|
308
|
+
* @param path - API path
|
|
309
|
+
* @returns Collection endpoint
|
|
310
|
+
*/
|
|
311
|
+
private _buildEndpoint(path: string): string {
|
|
312
|
+
// Remove path parameter suffix to get collection endpoint
|
|
313
|
+
return path.replace(/\/\{[^}]+\}$/, '')
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Extract idField from path parameter (e.g., /users/{uuid} → 'uuid')
|
|
318
|
+
*
|
|
319
|
+
* @private
|
|
320
|
+
* @param path - API path
|
|
321
|
+
* @returns Parameter name or null
|
|
322
|
+
*/
|
|
323
|
+
private _extractIdField(path: string): string | null {
|
|
324
|
+
const match = path.match(/\/\{([^}]+)\}$/)
|
|
325
|
+
return match?.[1] ?? null
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Process a path and extract schema info
|
|
330
|
+
*
|
|
331
|
+
* @private
|
|
332
|
+
* @param spec - Full OpenAPI spec (for $ref resolution)
|
|
333
|
+
* @param path - API path
|
|
334
|
+
* @param pathItem - OpenAPI path item
|
|
335
|
+
* @param entity - Working entity data
|
|
336
|
+
* @param warnings - Warning collector
|
|
337
|
+
*/
|
|
338
|
+
private _processPath(
|
|
339
|
+
spec: OpenAPISpec,
|
|
340
|
+
path: string,
|
|
341
|
+
pathItem: OpenAPIPathItem,
|
|
342
|
+
entity: EntityWorkingData,
|
|
343
|
+
warnings: ParseWarning[]
|
|
344
|
+
): void {
|
|
345
|
+
for (const [method, operation] of Object.entries(pathItem)) {
|
|
346
|
+
if (!CRUD_METHODS[method]) continue
|
|
347
|
+
if (typeof operation !== 'object') continue
|
|
348
|
+
|
|
349
|
+
const op = operation as OpenAPIOperation
|
|
350
|
+
|
|
351
|
+
// Apply operation filter
|
|
352
|
+
if (this.operationFilter && !this.operationFilter(path, method, op)) {
|
|
353
|
+
continue
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const isList = this._isListOperation(path, method, op, spec)
|
|
357
|
+
|
|
358
|
+
// Extract response schema
|
|
359
|
+
const responseSchema = this._extractResponseSchema(op, spec)
|
|
360
|
+
if (responseSchema) {
|
|
361
|
+
const itemSchema = isList ? this._extractArrayItemSchema(responseSchema, spec) : responseSchema
|
|
362
|
+
|
|
363
|
+
if (itemSchema) {
|
|
364
|
+
entity.rawSchema = this._mergeSchemas(entity.rawSchema, itemSchema)
|
|
365
|
+
this._extractFields(itemSchema, entity.fields, spec, warnings, path)
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Extract request body schema
|
|
370
|
+
if (op.requestBody) {
|
|
371
|
+
const requestSchema = this._extractRequestSchema(op, spec)
|
|
372
|
+
if (requestSchema) {
|
|
373
|
+
this._extractFields(requestSchema, entity.fields, spec, warnings, path)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Check if operation returns a list
|
|
381
|
+
*
|
|
382
|
+
* @private
|
|
383
|
+
* @param path - API path
|
|
384
|
+
* @param method - HTTP method
|
|
385
|
+
* @param operation - OpenAPI operation
|
|
386
|
+
* @param spec - Full spec for ref resolution
|
|
387
|
+
* @returns True if list operation
|
|
388
|
+
*/
|
|
389
|
+
private _isListOperation(
|
|
390
|
+
path: string,
|
|
391
|
+
method: string,
|
|
392
|
+
operation: OpenAPIOperation,
|
|
393
|
+
spec: OpenAPISpec
|
|
394
|
+
): boolean {
|
|
395
|
+
// GET on collection path (no path parameter) is typically list
|
|
396
|
+
if (method === 'get' && !path.includes('{')) {
|
|
397
|
+
return true
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Check if response schema is array
|
|
401
|
+
const schema = this._extractResponseSchema(operation, spec)
|
|
402
|
+
if (schema?.type === 'array') {
|
|
403
|
+
return true
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Check for pagination indicators in full response
|
|
407
|
+
const fullSchema = this._extractFullResponseSchema(operation, spec)
|
|
408
|
+
if (fullSchema?.properties?.pagination || fullSchema?.properties?.meta) {
|
|
409
|
+
return true
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return false
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Extract response schema (unwrap from data wrapper)
|
|
417
|
+
*
|
|
418
|
+
* @private
|
|
419
|
+
* @param operation - OpenAPI operation
|
|
420
|
+
* @param spec - Full spec for ref resolution
|
|
421
|
+
* @returns Extracted schema
|
|
422
|
+
*/
|
|
423
|
+
private _extractResponseSchema(
|
|
424
|
+
operation: OpenAPIOperation,
|
|
425
|
+
spec: OpenAPISpec
|
|
426
|
+
): OpenAPISchema | null {
|
|
427
|
+
const fullSchema = this._extractFullResponseSchema(operation, spec)
|
|
428
|
+
if (!fullSchema) return null
|
|
429
|
+
|
|
430
|
+
// Unwrap from configured data wrapper
|
|
431
|
+
if (this.dataWrapper) {
|
|
432
|
+
const wrapped = fullSchema.properties?.[this.dataWrapper]
|
|
433
|
+
if (wrapped) {
|
|
434
|
+
return wrapped.$ref ? this._resolveRef(wrapped.$ref, spec) : wrapped
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return fullSchema
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Extract full response schema without unwrapping
|
|
443
|
+
*
|
|
444
|
+
* @private
|
|
445
|
+
* @param operation - OpenAPI operation
|
|
446
|
+
* @param spec - Full spec for ref resolution
|
|
447
|
+
* @returns Full response schema
|
|
448
|
+
*/
|
|
449
|
+
private _extractFullResponseSchema(
|
|
450
|
+
operation: OpenAPIOperation,
|
|
451
|
+
spec: OpenAPISpec
|
|
452
|
+
): OpenAPISchema | null {
|
|
453
|
+
const response = operation.responses?.['200'] || operation.responses?.['201']
|
|
454
|
+
if (!response) return null
|
|
455
|
+
|
|
456
|
+
const content = response.content?.['application/json']
|
|
457
|
+
if (!content) return null
|
|
458
|
+
|
|
459
|
+
if (content.schema?.$ref) {
|
|
460
|
+
return this._resolveRef(content.schema.$ref, spec)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return content.schema || null
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Extract request body schema
|
|
468
|
+
*
|
|
469
|
+
* @private
|
|
470
|
+
* @param operation - OpenAPI operation
|
|
471
|
+
* @param spec - Full spec for ref resolution
|
|
472
|
+
* @returns Request schema
|
|
473
|
+
*/
|
|
474
|
+
private _extractRequestSchema(
|
|
475
|
+
operation: OpenAPIOperation,
|
|
476
|
+
spec: OpenAPISpec
|
|
477
|
+
): OpenAPISchema | null {
|
|
478
|
+
const content = operation.requestBody?.content?.['application/json']
|
|
479
|
+
if (!content) return null
|
|
480
|
+
|
|
481
|
+
if (content.schema?.$ref) {
|
|
482
|
+
return this._resolveRef(content.schema.$ref, spec)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return content.schema || null
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Resolve a $ref to its schema
|
|
490
|
+
*
|
|
491
|
+
* @private
|
|
492
|
+
* @param ref - Reference string (e.g., '#/components/schemas/User')
|
|
493
|
+
* @param spec - Full spec
|
|
494
|
+
* @returns Resolved schema
|
|
495
|
+
*/
|
|
496
|
+
private _resolveRef(ref: string, spec: OpenAPISpec): OpenAPISchema | null {
|
|
497
|
+
if (!ref.startsWith('#/')) return null
|
|
498
|
+
|
|
499
|
+
const parts = ref.slice(2).split('/')
|
|
500
|
+
let current: unknown = spec
|
|
501
|
+
|
|
502
|
+
for (const part of parts) {
|
|
503
|
+
if (!current || typeof current !== 'object') return null
|
|
504
|
+
current = (current as Record<string, unknown>)[part]
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return (current as OpenAPISchema) || null
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Extract item schema from array schema
|
|
512
|
+
*
|
|
513
|
+
* @private
|
|
514
|
+
* @param schema - Array schema
|
|
515
|
+
* @param spec - Full spec for ref resolution
|
|
516
|
+
* @returns Item schema
|
|
517
|
+
*/
|
|
518
|
+
private _extractArrayItemSchema(schema: OpenAPISchema, spec: OpenAPISpec): OpenAPISchema | null {
|
|
519
|
+
if (schema.type === 'array' && schema.items) {
|
|
520
|
+
if (schema.items.$ref) {
|
|
521
|
+
return this._resolveRef(schema.items.$ref, spec)
|
|
522
|
+
}
|
|
523
|
+
return schema.items
|
|
524
|
+
}
|
|
525
|
+
return schema
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Extract fields from JSON Schema and add to field map
|
|
530
|
+
*
|
|
531
|
+
* @private
|
|
532
|
+
* @param schema - JSON Schema object
|
|
533
|
+
* @param fields - Field map to populate
|
|
534
|
+
* @param spec - Full spec for ref resolution
|
|
535
|
+
* @param warnings - Warning collector
|
|
536
|
+
* @param sourcePath - Source path for warnings
|
|
537
|
+
* @param prefix - Prefix for nested field names
|
|
538
|
+
*/
|
|
539
|
+
private _extractFields(
|
|
540
|
+
schema: OpenAPISchema,
|
|
541
|
+
fields: Map<string, UnifiedFieldSchema>,
|
|
542
|
+
spec: OpenAPISpec,
|
|
543
|
+
warnings: ParseWarning[],
|
|
544
|
+
sourcePath: string,
|
|
545
|
+
prefix: string = ''
|
|
546
|
+
): void {
|
|
547
|
+
if (!schema || !schema.properties) {
|
|
548
|
+
return
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const required = new Set(schema.required || [])
|
|
552
|
+
|
|
553
|
+
for (const [name, propSchema] of Object.entries(schema.properties)) {
|
|
554
|
+
const fieldName = prefix ? `${prefix}.${name}` : name
|
|
555
|
+
|
|
556
|
+
// Resolve $ref in property schema
|
|
557
|
+
const resolvedSchema = propSchema.$ref ? this._resolveRef(propSchema.$ref, spec) : propSchema
|
|
558
|
+
|
|
559
|
+
if (!resolvedSchema) {
|
|
560
|
+
warnings.push({
|
|
561
|
+
path: `${sourcePath}#${fieldName}`,
|
|
562
|
+
message: `Could not resolve schema for field: ${fieldName}`,
|
|
563
|
+
code: 'UNRESOLVED_REF',
|
|
564
|
+
})
|
|
565
|
+
continue
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Use FieldMapper for type conversion
|
|
569
|
+
const type = getDefaultType(resolvedSchema, this.customMappings)
|
|
570
|
+
|
|
571
|
+
const field: UnifiedFieldSchema = {
|
|
572
|
+
name: fieldName,
|
|
573
|
+
type: type as UnifiedFieldType,
|
|
574
|
+
required: required.has(name),
|
|
575
|
+
readOnly: resolvedSchema.readOnly || false,
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Add optional properties
|
|
579
|
+
if (resolvedSchema.description) {
|
|
580
|
+
field.label = resolvedSchema.description
|
|
581
|
+
}
|
|
582
|
+
if (resolvedSchema.format) {
|
|
583
|
+
field.format = resolvedSchema.format
|
|
584
|
+
}
|
|
585
|
+
if (resolvedSchema.enum) {
|
|
586
|
+
field.enum = resolvedSchema.enum
|
|
587
|
+
}
|
|
588
|
+
if (resolvedSchema.default !== undefined) {
|
|
589
|
+
field.default = resolvedSchema.default
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Merge with existing field (may have more info from other operations)
|
|
593
|
+
if (fields.has(fieldName)) {
|
|
594
|
+
const existing = fields.get(fieldName)!
|
|
595
|
+
this._mergeField(existing, field)
|
|
596
|
+
} else {
|
|
597
|
+
fields.set(fieldName, field)
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Handle nested objects (one level only to avoid complexity)
|
|
601
|
+
if (resolvedSchema.type === 'object' && resolvedSchema.properties && !prefix) {
|
|
602
|
+
this._extractFields(resolvedSchema, fields, spec, warnings, sourcePath, fieldName)
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Merge field info from secondary source into primary
|
|
609
|
+
*
|
|
610
|
+
* @private
|
|
611
|
+
* @param primary - Primary field to merge into
|
|
612
|
+
* @param secondary - Secondary field with additional info
|
|
613
|
+
*/
|
|
614
|
+
private _mergeField(primary: UnifiedFieldSchema, secondary: UnifiedFieldSchema): void {
|
|
615
|
+
if (!primary.label && secondary.label) {
|
|
616
|
+
primary.label = secondary.label
|
|
617
|
+
}
|
|
618
|
+
if (!primary.enum && secondary.enum) {
|
|
619
|
+
primary.enum = secondary.enum
|
|
620
|
+
}
|
|
621
|
+
if (!primary.required && secondary.required) {
|
|
622
|
+
primary.required = true
|
|
623
|
+
}
|
|
624
|
+
if (primary.default === undefined && secondary.default !== undefined) {
|
|
625
|
+
primary.default = secondary.default
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Merge two JSON Schemas
|
|
631
|
+
*
|
|
632
|
+
* @private
|
|
633
|
+
* @param base - Base schema
|
|
634
|
+
* @param other - Schema to merge
|
|
635
|
+
* @returns Merged schema
|
|
636
|
+
*/
|
|
637
|
+
private _mergeSchemas(base: OpenAPISchema | null, other: OpenAPISchema): OpenAPISchema {
|
|
638
|
+
if (!base) return other
|
|
639
|
+
if (!other) return base
|
|
640
|
+
|
|
641
|
+
const merged: OpenAPISchema = { ...base }
|
|
642
|
+
|
|
643
|
+
if (other.properties) {
|
|
644
|
+
merged.properties = {
|
|
645
|
+
...(base.properties || {}),
|
|
646
|
+
...other.properties,
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
if (other.required) {
|
|
651
|
+
merged.required = [...new Set([...(base.required || []), ...other.required])]
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
return merged
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Convert working entity data to UnifiedEntitySchema
|
|
659
|
+
*
|
|
660
|
+
* @private
|
|
661
|
+
* @param entityData - Working data
|
|
662
|
+
* @returns Final schemas
|
|
663
|
+
*/
|
|
664
|
+
private _convertToSchemas(
|
|
665
|
+
entityData: Map<string, EntityWorkingData>
|
|
666
|
+
): Map<string, UnifiedEntitySchema> {
|
|
667
|
+
const schemas: Map<string, UnifiedEntitySchema> = new Map()
|
|
668
|
+
|
|
669
|
+
for (const [name, data] of entityData) {
|
|
670
|
+
const schema: UnifiedEntitySchema = {
|
|
671
|
+
name: data.name,
|
|
672
|
+
endpoint: data.endpoint,
|
|
673
|
+
fields: Object.fromEntries(data.fields),
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Add idField if detected from path parameter
|
|
677
|
+
if (data.idField) {
|
|
678
|
+
schema.idField = data.idField
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// Add extensions if configured
|
|
682
|
+
if (Object.keys(this.extensions).length > 0) {
|
|
683
|
+
schema.extensions = { ...this.extensions }
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
schemas.set(name, schema)
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return schemas
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
export default OpenAPIConnector
|