Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] Add recursive convert function #1

Merged
merged 13 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ The implemented functions are:
- `getLocalizedDescription`:
- takes in parameters a schema with localized descriptions, the locale for which we want the description and optionnaly the locale of the default description (default value is `'en'`)
- returns the description for the wanted locale, or `undefined` if not found
- `renderJsonSchema`:
- takes in parameters a schema with localized descriptions, the locale for which we want the description and optionnaly the locale of the default description (default value is `'en'`)
CGNonofr marked this conversation as resolved.
Show resolved Hide resolved
- returns the `JSONSchema6` equivalent with the selected locale
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const DEFAULT_LOCALE_DESCRIPTION = 'en'
export const DEFAULT_DESCRIPTION_LOCALE = 'en'
85 changes: 73 additions & 12 deletions src/description.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,82 @@
import { DEFAULT_LOCALE_DESCRIPTION } from './constants'
import { Descriptions, LocalizedJSONSchema } from './types'
import { JSONSchema6, JSONSchema6Definition } from 'json-schema'
import { DEFAULT_DESCRIPTION_LOCALE } from './constants'
import { LocalizedJSONSchema, LocalizedJSONSchemaDefinition } from './types'
import { isNotEmpty } from './utils'

export function getDescriptions (schema: LocalizedJSONSchema, localeDescription: string = DEFAULT_LOCALE_DESCRIPTION): Descriptions {
function getDescription (schema: LocalizedJSONSchema, locale: string = DEFAULT_DESCRIPTION_LOCALE): string | undefined {
const description = isNotEmpty(schema.description) ? schema.description : undefined
const descriptions = schema.descriptions
return schema.descriptions?.[locale] ?? description
}

function renderArrayOfJsonSchema (locale: string, array?: LocalizedJSONSchemaDefinition[]): JSONSchema6Definition[] | undefined {
return array?.map(schema => renderJsonSchema(locale, schema)).filter((schema): schema is JSONSchema6Definition => schema != null)
}

function renderObjectOfJsonSchema (locale: string, object?: { [key: string]: LocalizedJSONSchemaDefinition }): { [key: string]: JSONSchema6Definition } | undefined {
return object == null
? undefined
: Object.entries(object).reduce((newSchema, [key, schema]) => ({
...newSchema,
[key]: renderJsonSchema(locale, schema)
}), {})
}

if (descriptions == null) {
return description != null ? { [localeDescription]: description } : {}
function renderJsonSchema (locale: string, schema: LocalizedJSONSchema): JSONSchema6
function renderJsonSchema (locale: string, schema: boolean): boolean
function renderJsonSchema (locale: string, schema: undefined): undefined
function renderJsonSchema (locale: string, schema?: LocalizedJSONSchemaDefinition): JSONSchema6Definition | undefined
function renderJsonSchema (locale: string, schema?: LocalizedJSONSchemaDefinition): JSONSchema6Definition | undefined {
if (schema == null) {
return undefined
} else if (typeof schema === 'boolean') {
return schema
}

if (description != null && !Object.keys(descriptions).includes(localeDescription)) {
descriptions[localeDescription] = description
const description = getDescription(schema, locale)
const {
descriptions,
items,
additionalItems,
contains,
properties,
patternProperties,
additionalProperties,
dependencies,
propertyNames,
allOf,
anyOf,
oneOf,
not,
definitions,
...jsonSchema
} = schema

const newDependencies = dependencies == null
? undefined
: Object.entries(dependencies).reduce((newSchema, [key, schema]) => ({
...newSchema,
[key]: Array.isArray(schema) ? schema : renderJsonSchema(locale, schema)
}), {})

return {
...jsonSchema,
items: Array.isArray(items)
? renderArrayOfJsonSchema(locale, items)
: renderJsonSchema(locale, items),
additionalItems: renderJsonSchema(locale, additionalItems),
contains: renderJsonSchema(locale, contains),
properties: renderObjectOfJsonSchema(locale, properties),
patternProperties: renderObjectOfJsonSchema(locale, patternProperties),
additionalProperties: renderJsonSchema(locale, additionalProperties),
dependencies: newDependencies,
propertyNames: renderJsonSchema(locale, propertyNames),
allOf: renderArrayOfJsonSchema(locale, allOf),
anyOf: renderArrayOfJsonSchema(locale, anyOf),
oneOf: renderArrayOfJsonSchema(locale, oneOf),
not: renderJsonSchema(locale, not),
definitions: renderObjectOfJsonSchema(locale, definitions),
description: description ?? jsonSchema.description
}
return descriptions
}

export function getLocalizedDescription (schema: LocalizedJSONSchema, locale: string, localeDescription?: string): string | undefined {
return getDescriptions(schema, localeDescription)[locale]
}
export { getDescription, renderJsonSchema }
6 changes: 3 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { getDescriptions, getLocalizedDescription } from './description'
import { renderJsonSchema, getDescription } from './description'
import { Descriptions, LocalizedJSONSchema } from './types'

export {
Descriptions,
LocalizedJSONSchema,

getDescriptions,
getLocalizedDescription
getDescription,
renderJsonSchema
}
90 changes: 70 additions & 20 deletions src/test/description.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, test } from '@jest/globals'
import { Descriptions, LocalizedJSONSchema } from '../types'
import { getDescriptions, getLocalizedDescription } from '..'
import { renderJsonSchema, getDescription } from '../description'

const description: string = 'test description'
const englishDescription: string = 'english description'
Expand All @@ -19,42 +19,92 @@ const schemaWithoutDescription: LocalizedJSONSchema = {
const schemaWithoutDescriptions: LocalizedJSONSchema = {
description
}
const nestedSchemas: LocalizedJSONSchema = {
description,
descriptions,
items: [fullSchema, schemaWithoutDescription],
additionalItems: false,
contains: fullSchema,
properties: {
test1: schemaWithoutDescriptions,
test2: fullSchema
}
}

describe('Full schema', () => {
test('Descriptions', () => {
const localizedDescriptions: Descriptions = getDescriptions(fullSchema)
expect(localizedDescriptions).toMatchObject(descriptions)
})
test('Localized description', () => {
const english = getLocalizedDescription(fullSchema, 'en')
const french = getLocalizedDescription(fullSchema, 'fr')
const english = getDescription(fullSchema, 'en')
const french = getDescription(fullSchema, 'fr')
expect(english).toMatch(englishDescription)
expect(french).toMatch(frenchDescription)
})
test('Render schema', () => {
const englishSchema = renderJsonSchema('en', fullSchema)
const frenchSchema = renderJsonSchema('fr', fullSchema)
expect(englishSchema).toMatchObject({ description: englishDescription })
expect(frenchSchema).toMatchObject({ description: frenchDescription })
})
})

describe('Schema without description', () => {
test('Descriptions', () => {
const localizedDescriptions: Descriptions = getDescriptions(schemaWithoutDescription)
expect(localizedDescriptions).toMatchObject(descriptions)
})
test('Localized description', () => {
const english = getLocalizedDescription(schemaWithoutDescription, 'en')
const french = getLocalizedDescription(schemaWithoutDescription, 'fr')
const english = getDescription(schemaWithoutDescription, 'en')
const french = getDescription(schemaWithoutDescription, 'fr')
expect(english).toMatch(englishDescription)
expect(french).toMatch(frenchDescription)
})
test('Render schema', () => {
const englishSchema = renderJsonSchema('en', schemaWithoutDescription)
const frenchSchema = renderJsonSchema('fr', schemaWithoutDescription)
expect(englishSchema).toMatchObject({ description: englishDescription })
expect(frenchSchema).toMatchObject({ description: frenchDescription })
})
})

describe('Schema without descriptions', () => {
test('Descriptions', () => {
const localizedDescriptions: Descriptions = getDescriptions(schemaWithoutDescriptions)
expect(localizedDescriptions).toMatchObject({ en: description })
})
test('Localized description', () => {
const english = getLocalizedDescription(schemaWithoutDescriptions, 'en')
const french = getLocalizedDescription(schemaWithoutDescriptions, 'fr')
const english = getDescription(schemaWithoutDescriptions, 'en')
const french = getDescription(schemaWithoutDescriptions, 'fr')
expect(english).toMatch(description)
expect(french).toBeUndefined()
expect(french).toMatch(description)
})
test('Render schema', () => {
const englishSchema = renderJsonSchema('en', schemaWithoutDescriptions)
const frenchSchema = renderJsonSchema('fr', schemaWithoutDescriptions)
expect(englishSchema).toMatchObject({ description })
expect(frenchSchema).toMatchObject({ description })
})
})

describe('Nested schemas', () => {
test('Localized description', () => {
const english = getDescription(nestedSchemas, 'en')
const french = getDescription(nestedSchemas, 'fr')
expect(english).toMatch(englishDescription)
expect(french).toMatch(frenchDescription)
})
test('Render schema', () => {
const englishSchema = renderJsonSchema('en', nestedSchemas)
const frenchSchema = renderJsonSchema('fr', nestedSchemas)
expect(englishSchema).toMatchObject({
description: englishDescription,
items: [{ description: englishDescription }, { description: englishDescription }],
additionalItems: false,
contains: { description: englishDescription },
properties: {
test1: { description },
test2: { description: englishDescription }
}
})
expect(frenchSchema).toMatchObject({
description: frenchDescription,
items: [{ description: frenchDescription }, { description: frenchDescription }],
additionalItems: false,
contains: { description: frenchDescription },
properties: {
test1: { description },
test2: { description: frenchDescription }
}
})
})
})
23 changes: 23 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@ export type Descriptions = {
[key: string]: string
}

export type LocalizedJSONSchemaDefinition = LocalizedJSONSchema | boolean

export interface LocalizedJSONSchema extends JSONSchema6 {
descriptions?: Descriptions
items?: LocalizedJSONSchemaDefinition | LocalizedJSONSchemaDefinition[]
additionalItems?: LocalizedJSONSchemaDefinition
contains?: LocalizedJSONSchemaDefinition
properties?: {
[k: string]: LocalizedJSONSchemaDefinition
}
patternProperties?: {
[k: string]: LocalizedJSONSchemaDefinition
}
additionalProperties?: LocalizedJSONSchemaDefinition
dependencies?: {
[k: string]: LocalizedJSONSchemaDefinition | string[]
}
propertyNames?: LocalizedJSONSchemaDefinition
allOf?: LocalizedJSONSchemaDefinition[]
anyOf?: LocalizedJSONSchemaDefinition[]
oneOf?: LocalizedJSONSchemaDefinition[]
not?: LocalizedJSONSchemaDefinition
definitions?: {
[k: string]: LocalizedJSONSchemaDefinition
}
}
Loading