Skip to content

Commit

Permalink
feat: Generate openrpc json
Browse files Browse the repository at this point in the history
  • Loading branch information
ksentak committed Dec 11, 2024
1 parent 596c707 commit 33764e5
Show file tree
Hide file tree
Showing 5 changed files with 857 additions and 491 deletions.
1 change: 1 addition & 0 deletions src/cli.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const knownOpts = {
'language': [path],
'examples': [path, Array],
'as-path': [Boolean],
'bidirectional': [Boolean],
'pass-throughs': [Boolean]
}

Expand Down
2 changes: 1 addition & 1 deletion src/openrpc/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ const run = async ({

// Add schemas
mergedOpenRpc.components && Object.assign(platformApiOpenRpc.components.schemas, mergedOpenRpc.components.schemas)
appApiOpenRpc.components && Object.assign(appApiOpenRpc.components.schemas, mergedOpenRpc.components.schemas)
appApiOpenRpc?.components && Object.assign(appApiOpenRpc.components.schemas, mergedOpenRpc.components.schemas)

// Add externally referenced schemas that are in our shared schemas path
platformApiOpenRpc = addExternalSchemas(platformApiOpenRpc, sharedSchemas)
Expand Down
61 changes: 56 additions & 5 deletions src/shared/json-schema.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,29 @@ const isNull = schema => {
return (schema.type === 'null' || schema.const === null)
}

const isSchema = element => element.$ref || element.type || element.const || element.oneOf || element.anyOf || element.allOf
const isSchema = element => element.$ref || element.type || element.const || element.oneOf || element.anyOf || element.allOf || element.$id

const pathToArray = (ref, json) => {
//let path = ref.split('#').pop().substr(1).split('/')

const ids = []
if (json) {
ids.push(...getAllValuesForName("$id", json)) // add all $ids but the first one
}

const subschema = ids.find(id => ref.indexOf(id) >= 0)

let path = ref.split('#').pop().substring(1)

if (subschema) {
path = [].concat(...path.split('/'+subschema+'/').map(n => [n.split('/'), subschema])).slice(0, -1).flat()
}
else {
path = path.split('/')
}

return path.map(x => x.match(/^[0-9]+$/) ? parseInt(x) : x)
}

const refToPath = ref => {
let path = ref.split('#').pop().substr(1).split('/')
Expand All @@ -36,6 +58,9 @@ const objectPaths = obj => {
const addDelimiter = (a, b) => a ? `${a}/${b}` : b;

const paths = (obj = {}, head = '#') => {
if (obj && isObject(obj) && obj.$id && head !== '#') {
head = obj.$id
}
return obj ? Object.entries(obj)
.reduce((product, [key, value]) => {
let fullPath = addDelimiter(head, key)
Expand All @@ -47,17 +72,37 @@ const objectPaths = obj => {
return paths(obj);
}

const getAllValuesForName = (name, obj) => {
const isObject = val => typeof val === 'object'

const values = (name, obj = {}) => {
return obj ? Object.entries(obj)
.reduce((product, [key, value]) => {
if (isObject(value)) {
return product.concat(values(name, value))
}
else if (key === name) {
return product.concat(value)
}
else {
return product
}
}, []) : []
}
return [...new Set(values(name, obj))];
}

const getExternalSchemaPaths = obj => {
return objectPaths(obj)
.filter(x => /\/\$ref$/.test(x))
.map(refToPath)
.map(x => pathToArray(x, obj))
.filter(x => !/^#/.test(getPathOr(null, x, obj)))
}

const getLocalSchemaPaths = obj => {
return objectPaths(obj)
.filter(x => /\/\$ref$/.test(x))
.map(refToPath)
.map(x => pathToArray(x, obj))
.filter(x => /^#.+/.test(getPathOr(null, x, obj)))
}

Expand Down Expand Up @@ -456,11 +501,16 @@ const getLocalSchemas = (json = {}) => {
}

const isDefinitionReferencedBySchema = (name = '', moduleJson = {}) => {
let subSchema = false
if (name.indexOf("/https://") >= 0) {
name = name.substring(name.indexOf('/https://')+1)
subSchema = true
}
const refs = objectPaths(moduleJson)
.filter(x => /\/\$ref$/.test(x))
.map(refToPath)
.map(x => pathToArray(x, moduleJson))
.map(x => getPathOr(null, x, moduleJson))
.filter(x => x === name)
.filter(x => subSchema ? x.startsWith(name) : x === name)

return (refs.length > 0)
}
Expand Down Expand Up @@ -622,4 +672,5 @@ export {
dereferenceAndMergeAllOfs,
flattenMultipleOfs,
namespaceRefs,
getAllValuesForName,
}
39 changes: 39 additions & 0 deletions src/shared/methods.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2021 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

const tag = (method, name) => method.tags.find(tag => tag.name === name)
export const extension = (method, name) => (method.tags.find(t => t[name]) || {})[name]

export const capabilities = method => tag(method, 'capabilities')
export const isProvider = method => capabilities(method)['x-provides']
export const isPusher = method => capabilities(method)['x-push']
export const isNotifier = method => method.tags.find(t => t.name === 'notifier')
export const isEvent = method => tag(method, 'event')
export const isRegistration = method => tag(method, 'registration')
export const isProviderInterface = method => isProvider(method) && !isRegistration(method) && !isPusher(method)

export const name = method => method.name.split('.').pop()
export const rename = (method, renamer) => method.name.split('.').map((x, i, arr) => i === (arr.length-1) ? renamer(x) : x).join('.')

export const getNotifier = (method, client) => client.methods.find(m => m.name === method.tags.find(t => t.name === "event")['x-notifier'])
export const getEvent = (method, server) => server.methods.find(m => m.name === method.tags.find(t => t.name === "notifier")['x-event'])
export const getCapability = (method) => Object.values(capabilities(method)).map(v => Array.isArray(v) ? v.shift() : v).filter(v => typeof v === 'string').find(x => (x.startsWith('xrn:firebolt:capability')))
export const getRole = (method) => Object.keys(capabilities(method)).find(x => ['x-uses', 'x-provides', 'x-manages'].includes(x))

export const provides = (method) => extension(method, 'x-provides')

Loading

0 comments on commit 33764e5

Please sign in to comment.