Json Schema for Message Docs

This commit is contained in:
simonredfern 2026-01-16 12:50:14 +01:00
parent 4ace47d1ab
commit da698bb095
6 changed files with 150 additions and 14 deletions

1
components.d.ts vendored
View File

@ -45,6 +45,7 @@ declare module 'vue' {
GlossarySearchNav: typeof import('./src/components/GlossarySearchNav.vue')['default']
HeaderNav: typeof import('./src/components/HeaderNav.vue')['default']
Menu: typeof import('./src/components/Menu.vue')['default']
MessageDocsJsonSchemaSearchNav: typeof import('./src/components/MessageDocsJsonSchemaSearchNav.vue')['default']
MessageDocsSearchNav: typeof import('./src/components/MessageDocsSearchNav.vue')['default']
Preview: typeof import('./src/components/Preview.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']

View File

@ -36,7 +36,7 @@ import {
HEADER_LINKS_HOVER_COLOR as headerLinksHoverColorSetting,
HEADER_LINKS_BACKGROUND_COLOR as headerLinksBackgroundColorSetting
} from '../obp/style-setting'
import { obpApiActiveVersionsKey, obpGroupedMessageDocsKey, obpMyCollectionsEndpointKey } from '@/obp/keys'
import { obpApiActiveVersionsKey, obpGroupedMessageDocsKey, obpGroupedMessageDocsJsonSchemaKey, obpMyCollectionsEndpointKey } from '@/obp/keys'
import SvelteDropdown from './SvelteDropdown.vue'
const route = useRoute()
@ -51,6 +51,14 @@ const loginUsername = ref('')
const logoffurl = ref('')
const obpApiVersions = ref(inject(obpApiActiveVersionsKey) || [])
const obpMessageDocs = ref(Object.keys(inject(obpGroupedMessageDocsKey) || {}))
const obpMessageDocsJsonSchema = ref(Object.keys(inject(obpGroupedMessageDocsJsonSchemaKey) || {}))
// Combine message docs with JSON Schema items (with "J Schema" postfix)
const combinedMessageDocs = computed(() => {
const regularDocs = obpMessageDocs.value || []
const jsonSchemaDocs = (obpMessageDocsJsonSchema.value || []).map(connector => `${connector} J Schema`)
return [...regularDocs, ...jsonSchemaDocs]
})
// Debug menu items
const debugMenuItems = ref(['/debug/providers-status', '/debug/oidc'])
@ -189,8 +197,8 @@ const setActive = (target: HTMLElement | null) => {
}
}
const handleMore = (command: string) => {
console.log('handleMore called with command:', command)
const handleMore = (command: string, source?: string) => {
console.log('handleMore called with command:', command, 'source:', source)
// Ignore divider
if (command === '---') {
@ -201,11 +209,22 @@ const handleMore = (command: string) => {
if (element !== null) {
element.textContent = command;
}
if (command === '/message-docs') {
// Check if command ends with " J Schema" - if so, it's a JSON Schema message doc
if (command.endsWith(' J Schema')) {
const connector = command.replace(' J Schema', '')
console.log('Navigating to message docs JSON schema:', connector)
router.push({ name: 'message-docs-json-schema', params: { id: connector } })
} else if (command === '/message-docs') {
// Navigate to message docs list
console.log('Navigating to message docs list')
router.push({ name: 'message-docs-list' })
} else if (command === '/message-docs-json-schema') {
// Navigate to message docs JSON schema list
console.log('Navigating to message docs JSON schema list')
router.push({ name: 'message-docs-json-schema-list' })
} else if (command.includes('_')) {
// Regular message docs (connector names contain underscores)
console.log('Navigating to message docs:', command)
router.push({ name: 'message-docs', params: { id: command } })
} else if (command.startsWith('/debug/')) {
@ -292,7 +311,7 @@ const getCurrentPath = () => {
class="menu-right"
id="header-nav-message-docs"
label="Message Docs"
:items="obpMessageDocs"
:items="combinedMessageDocs"
:hover-color="headerLinksHoverColor"
:background-color="headerLinksBackgroundColor"
@select="handleMore"

View File

@ -38,7 +38,12 @@ import { createI18n } from 'vue-i18n'
import { languages, defaultLocale } from './language'
import { cache as cacheResourceDocs, cacheDoc as cacheResourceDocsDoc } from './obp/resource-docs'
import { cache as cacheMessageDocs, cacheDoc as cacheMessageDocsDoc } from './obp/message-docs'
import {
cache as cacheMessageDocs,
cacheDoc as cacheMessageDocsDoc,
cacheJsonSchema as cacheMessageDocsJsonSchema,
cacheDocJsonSchema as cacheMessageDocsJsonSchemaDoc
} from './obp/message-docs'
import { OBP_API_VERSION, getMyAPICollections, getMyAPICollectionsEndpoint } from './obp'
import { getOBPGlossary } from './obp/glossary'
@ -47,16 +52,18 @@ import './assets/main.css'
import '@fontsource/roboto/300.css'
import '@fontsource/roboto/400.css'
import '@fontsource/roboto/700.css'
import { getCacheStorageInfo } from './obp/common-functions'
import {
obpApiActiveVersionsKey,
obpApiHostKey,
obpGlossaryKey,
obpGroupedMessageDocsKey,
obpGroupedMessageDocsJsonSchemaKey,
obpGroupedResourceDocsKey,
obpMyCollectionsEndpointKey,
obpResourceDocsKey
} from './obp/keys'
import { getCacheStorageInfo } from './obp/common-functions'
;(async () => {
const app = createApp(App)
const router = await appRouter()
@ -272,6 +279,13 @@ async function setupData(app: App<Element>, worker: Worker) {
const cacheStorageOfMessageDocs = await caches.open('obp-message-docs-cache') // Please note: The global 'caches' read-only property returns the 'CacheStorage' object associated with the current context.
// 'match': Checks if a given Request is a key in any of the Cache objects that the CacheStorage object tracks, and returns a Promise that resolves to that match.
const cachedResponseOfMessageDocs = await cacheStorageOfMessageDocs.match('/')
// 'open': Returns a Promise that resolves to the Cache object matching the cacheName(obp-message-docs-json-schema-cache) (a new cache is created if it doesn't already exist.)
const cacheStorageOfMessageDocsJsonSchema = await caches.open(
'obp-message-docs-json-schema-cache'
) // Please note: The global 'caches' read-only property returns the 'CacheStorage' object associated with the current context.
// 'match': Checks if a given Request is a key in any of the Cache objects that the CacheStorage object tracks, and returns a Promise that resolves to that match.
const cachedResponseOfMessageDocsJsonSchema =
await cacheStorageOfMessageDocsJsonSchema.match('/')
// Listen to Web worker
worker.onmessage = async (event) => {
@ -286,6 +300,10 @@ async function setupData(app: App<Element>, worker: Worker) {
await cacheMessageDocsDoc(cacheStorageOfMessageDocs)
console.log('Message Docs cache was updated.')
}
if (event.data === 'update-message-docs-json-schema') {
await cacheMessageDocsJsonSchemaDoc(cacheStorageOfMessageDocsJsonSchema)
console.log('Message Docs JSON Schema cache was updated.')
}
}
const { resourceDocs, groupedDocs } = await cacheResourceDocs(
@ -298,6 +316,11 @@ async function setupData(app: App<Element>, worker: Worker) {
cachedResponseOfMessageDocs,
worker
)
const messageDocsJsonSchema = await cacheMessageDocsJsonSchema(
cacheStorageOfMessageDocsJsonSchema,
cachedResponseOfMessageDocsJsonSchema,
worker
)
// Provide data to a component's descendants
// App-level provides are available to all components rendered in the app
@ -306,6 +329,7 @@ async function setupData(app: App<Element>, worker: Worker) {
app.provide(obpApiActiveVersionsKey, Object.keys(resourceDocs).sort())
app.provide(obpGroupedResourceDocsKey, groupedDocs)
app.provide(obpGroupedMessageDocsKey, messageDocs)
app.provide(obpGroupedMessageDocsJsonSchemaKey, messageDocsJsonSchema)
app.provide(obpApiHostKey, import.meta.env.VITE_OBP_API_HOST)
const glossary = await getOBPGlossary()
app.provide(obpGlossaryKey, glossary)

View File

@ -31,6 +31,9 @@ export const obpResourceDocsKey = Symbol('OBP-ResourceDocs') as InjectionKey<any
export const obpApiActiveVersionsKey = Symbol('OBP-APIActiveVersions') as InjectionKey<any>
export const obpGroupedResourceDocsKey = Symbol('OBP-GroupedResourceDocs') as InjectionKey<any>
export const obpGroupedMessageDocsKey = Symbol('OBP-GroupedMessageDocs') as InjectionKey<any> // This cause an issue
export const obpGroupedMessageDocsJsonSchemaKey = Symbol(
'OBP-GroupedMessageDocsJsonSchema'
) as InjectionKey<any>
export const obpApiHostKey = Symbol('OBP-API-Host') as InjectionKey<any>
export const obpGlossaryKey = Symbol('OBP-Glossary') as InjectionKey<any>
export const obpMyCollectionsEndpointKey = Symbol('OBP-MyCollectionsEndpoint') as InjectionKey<any>
export const obpMyCollectionsEndpointKey = Symbol('OBP-MyCollectionsEndpoint') as InjectionKey<any>

View File

@ -43,7 +43,15 @@ export async function getOBPMessageDocs(item: string): Promise<any> {
return await get(`obp/${OBP_API_VERSION}/message-docs/${item}`)
}
export function getGroupedMessageDocs(docs: any): Promise<any> {
// Get Message Docs JSON Schema
export async function getOBPMessageDocsJsonSchema(item: string): Promise<any> {
const logMessage = `Loading message docs JSON schema { connector: ${item} }`
console.log(logMessage)
updateLoadingInfoMessage(logMessage)
return await get(`obp/v6.0.0/message-docs/${item}/json-schema`)
}
export function getGroupedMessageDocs(docs: any): any {
return docs.message_docs.reduce((values: any, doc: any) => {
const tag = doc.adapter_implementation.group.replace('-', '').trim()
;(values[tag] = values[tag] || []).push(doc)
@ -51,6 +59,39 @@ export function getGroupedMessageDocs(docs: any): Promise<any> {
}, {})
}
export function getGroupedMessageDocsJsonSchema(docs: any): any {
if (!docs.definitions || typeof docs.definitions !== 'object') {
return {}
}
// Convert definitions object to array format and group by InBound/OutBound prefix
const grouped: any = {}
Object.keys(docs.definitions).forEach((methodName: string) => {
const schema = docs.definitions[methodName]
// Determine category based on method name prefix
let category = 'Uncategorized'
if (methodName.startsWith('InBound')) {
category = 'Inbound Methods'
} else if (methodName.startsWith('OutBound')) {
category = 'Outbound Methods'
}
if (!grouped[category]) {
grouped[category] = []
}
grouped[category].push({
method_name: methodName,
category: category,
request_schema: schema,
response_schema: schema
})
})
return grouped
}
export async function cacheDoc(cacheStorageOfMessageDocs: any): Promise<any> {
const messageDocs = await connectors.reduce(async (agroup: any, connector: any) => {
const logMessage = `Caching message docs { connector: ${connector} }`
@ -71,11 +112,30 @@ async function getCacheDoc(cacheStorageOfMessageDocs: any): Promise<any> {
return await cacheDoc(cacheStorageOfMessageDocs)
}
export async function cache(
cacheStorage: any,
cachedResponse: any,
worker: any
): Promise<any> {
export async function cacheDocJsonSchema(cacheStorageOfMessageDocsJsonSchema: any): Promise<any> {
const messageDocsJsonSchema = await connectors.reduce(async (agroup: any, connector: any) => {
const logMessage = `Caching message docs JSON schema { connector: ${connector} }`
console.log(logMessage)
updateLoadingInfoMessage(logMessage)
const group = await agroup
const docs = await getOBPMessageDocsJsonSchema(connector)
if (!Object.keys(docs).includes('code')) {
group[connector] = getGroupedMessageDocsJsonSchema(docs)
}
return group
}, Promise.resolve({}))
await cacheStorageOfMessageDocsJsonSchema.put(
'/',
new Response(JSON.stringify(messageDocsJsonSchema))
)
return messageDocsJsonSchema
}
async function getCacheDocJsonSchema(cacheStorageOfMessageDocsJsonSchema: any): Promise<any> {
return await cacheDocJsonSchema(cacheStorageOfMessageDocsJsonSchema)
}
export async function cache(cacheStorage: any, cachedResponse: any, worker: any): Promise<any> {
try {
worker.postMessage('update-message-docs')
return await cachedResponse.json()
@ -87,3 +147,20 @@ export async function cache(
return await getCacheDoc(cacheStorage)
}
}
export async function cacheJsonSchema(
cacheStorage: any,
cachedResponse: any,
worker: any
): Promise<any> {
try {
worker.postMessage('update-message-docs-json-schema')
return await cachedResponse.json()
} catch (error) {
console.warn('No message docs JSON schema cache or malformed cache.')
console.log('Caching message docs JSON schema...')
const isServerActive = await isServerUp()
if (!isServerActive) throw new Error('API Server is not responding.')
return await getCacheDocJsonSchema(cacheStorage)
}
}

View File

@ -30,6 +30,8 @@ import GlossaryView from '../views/GlossaryView.vue'
import HelpView from '../views/HelpView.vue'
import MessageDocsView from '../views/MessageDocsView.vue'
import MessageDocsListView from '../views/MessageDocsListView.vue'
import MessageDocsJsonSchemaView from '../views/MessageDocsJsonSchemaView.vue'
import MessageDocsJsonSchemaListView from '../views/MessageDocsJsonSchemaListView.vue'
import BodyView from '../views/BodyView.vue'
import Content from '../components/Content.vue'
import Preview from '../components/Preview.vue'
@ -86,6 +88,16 @@ export default async function router(): Promise<any> {
name: 'message-docs',
component: isServerActive ? MessageDocsView : InternalServerErrorView
},
{
path: '/message-docs-json-schema',
name: 'message-docs-json-schema-list',
component: isServerActive ? MessageDocsJsonSchemaListView : InternalServerErrorView
},
{
path: '/message-docs-json-schema/:id',
name: 'message-docs-json-schema',
component: isServerActive ? MessageDocsJsonSchemaView : InternalServerErrorView
},
{
path: '/resource-docs',
redirect: () => {