290 lines
8.2 KiB
TypeScript
290 lines
8.2 KiB
TypeScript
import {
|
|
getDescriptionForSchema,
|
|
getSchemaFromResult,
|
|
Lookup,
|
|
LookupResult,
|
|
} from '@nx/nx-dev/data-access-packages';
|
|
import { JsonSchema, JsonSchema1 } from '@nx/nx-dev/models-package';
|
|
import { ParameterView } from './parameter-view';
|
|
import { shouldShowInStage, Stage } from './stage';
|
|
import { Type } from './types/type';
|
|
|
|
interface PropertySchema extends JsonSchema1 {
|
|
alias?: string;
|
|
'x-deprecated'?: boolean;
|
|
'x-priority'?: 'important' | 'internal';
|
|
}
|
|
|
|
interface PropertyModel {
|
|
alias: string;
|
|
initialSchema: PropertySchema;
|
|
isRequired: boolean;
|
|
lookupResult: LookupResult;
|
|
propertyName: string;
|
|
propertyReference: string;
|
|
}
|
|
|
|
const isPropertyDeprecated = (schema: PropertySchema | JsonSchema): boolean =>
|
|
typeof schema === 'boolean' ? false : !!schema['x-deprecated'];
|
|
const getPropertyAlias = (schema: PropertySchema | JsonSchema): string => {
|
|
if (typeof schema === 'boolean' || schema.alias === undefined) return '';
|
|
return String(schema.alias);
|
|
};
|
|
|
|
function getViewModel(
|
|
schema: JsonSchema1,
|
|
lookup: Lookup,
|
|
reference: string
|
|
): PropertyModel[] {
|
|
const properties = schema.properties || {};
|
|
return Object.keys(properties)
|
|
.sort((a, b) => a[0].localeCompare(b[0])) // Sort properties alphabetically
|
|
.map((propertyName) => {
|
|
const propertySchema = properties[propertyName] as PropertySchema;
|
|
const lookupResult = lookup.getSchema(propertySchema);
|
|
return {
|
|
propertyName,
|
|
alias: propertySchema['alias'] ?? '',
|
|
initialSchema: propertySchema,
|
|
isRequired:
|
|
typeof schema.required !== 'undefined' &&
|
|
!!schema.required.find((n) => n === propertyName),
|
|
lookupResult,
|
|
propertyReference:
|
|
lookupResult?.baseReference ||
|
|
`${reference}/properties/${propertyName}`,
|
|
};
|
|
});
|
|
}
|
|
|
|
function extractPropertiesByImportance(properties: PropertyModel[]): {
|
|
deprecated: PropertyModel[];
|
|
important: PropertyModel[];
|
|
internal: PropertyModel[];
|
|
required: PropertyModel[];
|
|
rest: PropertyModel[];
|
|
} {
|
|
const result: {
|
|
deprecated: PropertyModel[];
|
|
important: PropertyModel[];
|
|
internal: PropertyModel[];
|
|
required: PropertyModel[];
|
|
rest: PropertyModel[];
|
|
} = {
|
|
deprecated: [],
|
|
important: [],
|
|
internal: [],
|
|
required: [],
|
|
rest: [],
|
|
};
|
|
for (const property of properties) {
|
|
if (property.isRequired) {
|
|
result.required.push(property);
|
|
continue;
|
|
}
|
|
if (isPropertyDeprecated(property.initialSchema)) {
|
|
result.deprecated.push(property);
|
|
continue;
|
|
}
|
|
if (
|
|
property.initialSchema['x-priority'] &&
|
|
property.initialSchema['x-priority'] === 'important'
|
|
) {
|
|
result.important.push(property);
|
|
continue;
|
|
}
|
|
if (
|
|
property.initialSchema['x-priority'] &&
|
|
property.initialSchema['x-priority'] === 'internal'
|
|
) {
|
|
result.internal.push(property);
|
|
continue;
|
|
}
|
|
result.rest.push(property);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
export function SchemaViewer({
|
|
schema,
|
|
reference,
|
|
lookup,
|
|
stage,
|
|
}: {
|
|
schema: JsonSchema1;
|
|
reference: string;
|
|
lookup: Lookup;
|
|
stage: Stage;
|
|
}): JSX.Element {
|
|
const properties = getViewModel(schema, lookup, reference).filter((p) => {
|
|
if (p.lookupResult === undefined) {
|
|
return true;
|
|
}
|
|
return shouldShowInStage(stage, p.lookupResult.schema);
|
|
});
|
|
|
|
const categorizedProperties = extractPropertiesByImportance(properties);
|
|
|
|
function renderProps(properties: any[]): JSX.Element[] {
|
|
return properties.map((p) => {
|
|
if (p.lookupResult) {
|
|
return (
|
|
<ParameterView
|
|
key={p.propertyName}
|
|
alias={p.alias}
|
|
name={p.propertyName}
|
|
description={getDescriptionForSchema(p.lookupResult.schema)}
|
|
required={p.isRequired}
|
|
deprecated={isPropertyDeprecated(p.lookupResult.schema)}
|
|
schema={p.lookupResult.schema}
|
|
reference={p.propertyReference}
|
|
lookup={lookup}
|
|
/>
|
|
);
|
|
} else {
|
|
return (
|
|
<ParameterView
|
|
key={p.propertyName}
|
|
alias={p.alias}
|
|
deprecated={isPropertyDeprecated(p.initialSchema)}
|
|
name={p.propertyName}
|
|
description={getDescriptionForSchema(p.initialSchema)}
|
|
required={p.isRequired}
|
|
schema={p.initialSchema}
|
|
reference={p.propertyReference}
|
|
lookup={lookup}
|
|
/>
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
const additionalProperties = new Array<JSX.Element>();
|
|
if (typeof schema.additionalProperties === 'boolean') {
|
|
if (schema.additionalProperties) {
|
|
additionalProperties.push(
|
|
<ParameterView
|
|
key="schema-additional-properties"
|
|
name="Additional Properties"
|
|
description="Extra properties of any type may be provided to this object."
|
|
required={false}
|
|
alias={''}
|
|
deprecated={false}
|
|
schema={{}}
|
|
reference={`${reference}/additionalProperties`}
|
|
lookup={lookup}
|
|
/>
|
|
);
|
|
}
|
|
} else if (schema.additionalProperties !== undefined) {
|
|
const additionalPropertiesResult = lookup.getSchema(
|
|
schema.additionalProperties
|
|
);
|
|
if (additionalPropertiesResult !== undefined) {
|
|
const resolvedReference =
|
|
additionalPropertiesResult.baseReference ||
|
|
`${reference}/additionalProperties`;
|
|
additionalProperties.push(
|
|
<ParameterView
|
|
key="schema-additional-properties"
|
|
name="Additional Properties"
|
|
description={getDescriptionForSchema(additionalPropertiesResult)}
|
|
required={false}
|
|
alias={getPropertyAlias(additionalPropertiesResult.schema)}
|
|
deprecated={isPropertyDeprecated(additionalPropertiesResult.schema)}
|
|
schema={additionalPropertiesResult.schema}
|
|
reference={resolvedReference}
|
|
lookup={lookup}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
const patternProperties = schema.patternProperties || {};
|
|
const renderedPatternProperties = Object.keys(patternProperties).map(
|
|
(pattern, i) => {
|
|
const lookupResult = lookup.getSchema(patternProperties[pattern]);
|
|
const currentSchema =
|
|
getSchemaFromResult(lookupResult) || patternProperties[pattern];
|
|
return (
|
|
<ParameterView
|
|
key={`pattern-properties-${i}`}
|
|
name={`/${pattern}/ (keys of pattern)`}
|
|
alias={getPropertyAlias(currentSchema)}
|
|
deprecated={isPropertyDeprecated(currentSchema)}
|
|
description={getDescriptionForSchema(schema)}
|
|
required={false}
|
|
schema={currentSchema}
|
|
reference={
|
|
lookupResult?.baseReference ||
|
|
`${reference}/patternProperties/${pattern}`
|
|
}
|
|
lookup={lookup}
|
|
/>
|
|
);
|
|
}
|
|
);
|
|
|
|
const hasProperties =
|
|
Object.keys(properties).length > 0 ||
|
|
renderedPatternProperties.length > 0 ||
|
|
additionalProperties.length > 0;
|
|
|
|
const { anyOf, allOf, oneOf, not } = schema;
|
|
const compositeOnlyType: JsonSchema1 = { anyOf, allOf, oneOf, not };
|
|
let mixinProps = <></>;
|
|
if (
|
|
Object.keys(compositeOnlyType).some(
|
|
(key) => compositeOnlyType[key] !== undefined
|
|
)
|
|
) {
|
|
mixinProps = (
|
|
<>
|
|
<h3 key="mixins-header">Mixins</h3>
|
|
{hasProperties ? (
|
|
<p key="mixins-description">
|
|
This type has all of the properties below, but must also match this
|
|
type:
|
|
</p>
|
|
) : (
|
|
<p key="mixins-description">
|
|
This object must match the following conditions:
|
|
</p>
|
|
)}
|
|
<Type
|
|
key="mixins-type"
|
|
s={compositeOnlyType}
|
|
lookup={lookup}
|
|
reference={reference}
|
|
/>
|
|
</>
|
|
);
|
|
}
|
|
|
|
let allRenderedProperties = <></>;
|
|
const renderedProps = [
|
|
renderProps([
|
|
...categorizedProperties.required,
|
|
...categorizedProperties.important,
|
|
...categorizedProperties.rest,
|
|
]),
|
|
...renderedPatternProperties,
|
|
...additionalProperties,
|
|
renderProps([
|
|
...categorizedProperties.internal,
|
|
...categorizedProperties.deprecated,
|
|
]),
|
|
];
|
|
if (hasProperties) {
|
|
allRenderedProperties = <>{renderedProps}</>;
|
|
}
|
|
|
|
return (
|
|
<div key="schema-explorer">
|
|
{mixinProps}
|
|
{allRenderedProperties}
|
|
</div>
|
|
);
|
|
}
|