@redocly/openapi-core
Advanced tools
Comparing version 1.9.0 to 1.9.1
# @redocly/openapi-core | ||
## 1.9.1 | ||
### Patch Changes | ||
- Fixed a bug with resolving $refs to file names that contain the hash symbol. | ||
## 1.9.0 | ||
@@ -4,0 +10,0 @@ |
@@ -50,2 +50,2 @@ import { BaseResolver } from './resolve'; | ||
}): Promise<BundleResult>; | ||
export declare function mapTypeToComponent(typeName: string, version: SpecMajorVersion): "responses" | "parameters" | "examples" | "headers" | "schemas" | "requestBodies" | "securitySchemes" | "links" | "callbacks" | "definitions" | null; | ||
export declare function mapTypeToComponent(typeName: string, version: SpecMajorVersion): "parameters" | "examples" | "headers" | "schemas" | "responses" | "requestBodies" | "securitySchemes" | "links" | "callbacks" | "definitions" | null; |
@@ -28,2 +28,3 @@ import { BaseResolver } from './resolve'; | ||
externalRefResolver?: BaseResolver; | ||
externalConfigTypes?: Record<string, NodeType>; | ||
}): Promise<import("./walk").NormalizedProblem[]>; |
@@ -99,3 +99,3 @@ "use strict"; | ||
}); | ||
const types = (0, types_1.normalizeTypes)(redocly_yaml_1.ConfigTypes, config); | ||
const types = (0, types_1.normalizeTypes)(opts.externalConfigTypes || redocly_yaml_1.ConfigTypes, config); | ||
const rules = [ | ||
@@ -102,0 +102,0 @@ { |
@@ -27,2 +27,2 @@ import { Oas3Rule, Oas3Preprocessor, Oas2Rule, Oas2Preprocessor, Async2Preprocessor, Async2Rule } from './visitors'; | ||
export declare function getMajorSpecVersion(version: SpecVersion): SpecMajorVersion; | ||
export declare function getTypes(spec: SpecVersion): Record<string, import("./types").NodeType>; | ||
export declare function getTypes(spec: SpecVersion): Record<string, import("./types").NodeType> | Record<"Root" | "Tag" | "ExternalDocs" | "SecurityRequirement" | "Info" | "Contact" | "License" | "Paths" | "PathItem" | "Parameter" | "Operation" | "Example" | "Header" | "Responses" | "Response" | "Schema" | "Xml" | "SchemaProperties" | "NamedSchemas" | "NamedResponses" | "NamedParameters" | "NamedSecuritySchemes" | "SecurityScheme" | "Examples" | "ExamplesMap" | "TagList" | "SecurityRequirementList" | "ParameterList" | "ParameterItems" | "TagGroup" | "TagGroups" | "EnumDescriptions" | "Logo" | "XCodeSample" | "XCodeSampleList" | "XServer" | "XServerList", import("./types").NodeType> | Record<"Root" | "Tag" | "ExternalDocs" | "Server" | "ServerVariable" | "SecurityRequirement" | "Info" | "Contact" | "License" | "Paths" | "PathItem" | "Callback" | "CallbacksMap" | "Parameter" | "Operation" | "RequestBody" | "MediaTypesMap" | "MediaType" | "Example" | "Encoding" | "Header" | "Responses" | "Response" | "Link" | "Schema" | "Xml" | "SchemaProperties" | "DiscriminatorMapping" | "Discriminator" | "Components" | "NamedSchemas" | "NamedResponses" | "NamedParameters" | "NamedExamples" | "NamedRequestBodies" | "NamedHeaders" | "NamedSecuritySchemes" | "NamedLinks" | "NamedCallbacks" | "ImplicitFlow" | "PasswordFlow" | "ClientCredentials" | "AuthorizationCode" | "OAuth2Flows" | "SecurityScheme" | "ServerVariablesMap" | "ExamplesMap" | "EncodingMap" | "HeadersMap" | "LinksMap" | "WebhooksMap" | "TagList" | "SecurityRequirementList" | "ParameterList" | "TagGroup" | "TagGroups" | "EnumDescriptions" | "Logo" | "XCodeSample" | "XCodeSampleList" | "ServerList" | "XUsePkce", import("./types").NodeType> | Record<"Root" | "Info" | "License" | "Operation" | "Schema" | "SchemaProperties" | "Components" | "SecurityScheme" | "NamedPathItems", import("./types").NodeType>; |
@@ -42,5 +42,5 @@ "use strict"; | ||
function parseRef(ref) { | ||
const [uri, pointer = ''] = ref.split('#'); | ||
const [uri, pointer = ''] = ref.split('#/'); | ||
return { | ||
uri: uri || null, | ||
uri: (uri.endsWith('#') ? uri.slice(0, -1) : uri) || null, | ||
pointer: parsePointer(pointer), | ||
@@ -47,0 +47,0 @@ }; |
@@ -214,4 +214,12 @@ "use strict"; | ||
} | ||
const isTypeAFunction = typeof itemsType === 'function'; | ||
for (let i = 0; i < node.length; i++) { | ||
walk(node[i], itemsType || unknownType, (0, ref_utils_1.joinPointer)(nodeAbsoluteRef, i)); | ||
const itemType = isTypeAFunction | ||
? itemsType(node[i], (0, ref_utils_1.joinPointer)(nodeAbsoluteRef, i)) | ||
: itemsType; | ||
// we continue resolving unknown types, but stop early on known scalars | ||
if (itemType === undefined && type !== unknownType && type !== types_1.SpecExtension) { | ||
continue; | ||
} | ||
walk(node[i], (0, types_1.isNamedType)(itemType) ? itemType : unknownType, (0, ref_utils_1.joinPointer)(nodeAbsoluteRef, i)); | ||
} | ||
@@ -218,0 +226,0 @@ return; |
@@ -22,3 +22,3 @@ export type ScalarSchema = { | ||
additionalProperties?: PropType | ResolveTypeFn; | ||
items?: string; | ||
items?: PropType | ResolveTypeFn; | ||
required?: string[] | ((value: any, key: string | number | undefined) => string[]); | ||
@@ -29,4 +29,4 @@ requiredOneOf?: string[]; | ||
}; | ||
type PropType = string | NodeType | ScalarSchema | undefined | null; | ||
type ResolveTypeFn = (value: any, key: string) => string | PropType; | ||
export type PropType = string | NodeType | ScalarSchema | undefined | null; | ||
export type ResolveTypeFn = (value: any, key: string) => string | PropType; | ||
export type NormalizedNodeType = { | ||
@@ -36,3 +36,3 @@ name: string; | ||
additionalProperties?: NormalizedPropType | NormalizedResolveTypeFn; | ||
items?: NormalizedNodeType; | ||
items?: NormalizedPropType | NormalizedResolveTypeFn; | ||
required?: string[] | ((value: any, key: string | number | undefined) => string[]); | ||
@@ -43,4 +43,4 @@ requiredOneOf?: string[]; | ||
}; | ||
type NormalizedPropType = NormalizedNodeType | NormalizedScalarSchema | undefined | null; | ||
type NormalizedResolveTypeFn = (value: any, key: string) => NormalizedNodeType | NormalizedScalarSchema | undefined | null; | ||
type NormalizedPropType = NormalizedNodeType | NormalizedScalarSchema | null | undefined; | ||
type NormalizedResolveTypeFn = (value: any, key: string) => NormalizedPropType; | ||
export declare function listOf(typeName: string): { | ||
@@ -60,3 +60,3 @@ name: string; | ||
}): Record<string, NormalizedNodeType>; | ||
export declare function isNamedType(t: NormalizedNodeType | NormalizedScalarSchema | null | undefined): t is NormalizedNodeType; | ||
export declare function isNamedType(t: NormalizedPropType): t is NormalizedNodeType; | ||
export {}; |
@@ -1,2 +0,3 @@ | ||
import { NodeType } from '.'; | ||
export declare const Oas2Types: Record<string, NodeType>; | ||
import type { NodeType } from '.'; | ||
import type { Oas2NodeType } from './redocly-yaml'; | ||
export declare const Oas2Types: Record<Oas2NodeType, NodeType>; |
@@ -1,2 +0,3 @@ | ||
import { NodeType } from '.'; | ||
export declare const Oas3_1Types: Record<string, NodeType>; | ||
import type { NodeType } from '.'; | ||
import type { Oas3_1NodeType } from './redocly-yaml'; | ||
export declare const Oas3_1Types: Record<Oas3_1NodeType, NodeType>; |
@@ -1,2 +0,3 @@ | ||
import { NodeType } from '.'; | ||
export declare const Oas3Types: Record<string, NodeType>; | ||
import type { NodeType } from '.'; | ||
import type { Oas3NodeType } from './redocly-yaml'; | ||
export declare const Oas3Types: Record<Oas3NodeType, NodeType>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.rootRedoclyConfigSchema = exports.environmentSchema = exports.redoclyConfigSchema = exports.PortalConfigNodeTypes = exports.apiConfigSchema = exports.ssoConfigSchema = void 0; | ||
exports.rootRedoclyConfigSchema = void 0; | ||
const config_1 = require("../config"); | ||
const theme_config_1 = require("./theme-config"); | ||
const oidcIssuerMetadataSchema = { | ||
@@ -21,6 +22,7 @@ type: 'object', | ||
title: { type: 'string' }, | ||
pkce: { type: 'boolean', default: false }, | ||
configurationUrl: { type: 'string', minLength: 1 }, | ||
configuration: oidcIssuerMetadataSchema, | ||
clientId: { type: 'string', minLength: 1 }, | ||
clientSecret: { type: 'string', minLength: 1 }, | ||
clientSecret: { type: 'string', minLength: 0 }, | ||
teamsClaimName: { type: 'string' }, | ||
@@ -35,3 +37,3 @@ teamsClaimMap: { type: 'object', additionalProperties: { type: 'string' } }, | ||
}, | ||
required: ['type', 'clientId', 'clientSecret'], | ||
required: ['type', 'clientId'], | ||
oneOf: [{ required: ['configurationUrl'] }, { required: ['configuration'] }], | ||
@@ -83,7 +85,22 @@ additionalProperties: false, | ||
}; | ||
exports.ssoConfigSchema = { | ||
const ssoOnPremConfigSchema = { | ||
type: 'object', | ||
properties: {}, | ||
additionalProperties: authProviderConfigSchema, | ||
}; | ||
const ssoConfigSchema = { | ||
oneOf: [ | ||
{ | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
enum: ['REDOCLY', 'CORPORATE', 'GUEST'], | ||
}, | ||
uniqueItems: true, | ||
}, | ||
{ | ||
type: 'string', | ||
enum: ['REDOCLY', 'CORPORATE', 'GUEST'], | ||
}, | ||
], | ||
}; | ||
const redirectConfigSchema = { | ||
@@ -95,14 +112,14 @@ type: 'object', | ||
}, | ||
required: ['to'], | ||
additionalProperties: false, | ||
}; | ||
const redirectsConfigSchema = { | ||
type: 'object', | ||
properties: {}, | ||
additionalProperties: 'redirectConfigSchema', | ||
additionalProperties: redirectConfigSchema, | ||
default: {}, | ||
}; | ||
exports.apiConfigSchema = { | ||
const apiConfigSchema = { | ||
type: 'object', | ||
properties: { | ||
root: { type: 'string' }, | ||
output: { type: 'string', pattern: '(.ya?ml|.json)$' }, | ||
rbac: { type: 'object', additionalProperties: true }, | ||
@@ -112,3 +129,4 @@ theme: { | ||
properties: { | ||
openapi: { type: 'object', additionalProperties: true }, | ||
openapi: theme_config_1.themeConfigSchema.properties.openapi, | ||
graphql: theme_config_1.themeConfigSchema.properties.graphql, | ||
}, | ||
@@ -119,4 +137,5 @@ additionalProperties: false, | ||
metadata: { type: 'object', additionalProperties: true }, | ||
rules: { type: 'object', additionalProperties: true }, | ||
decorators: { type: 'object', additionalProperties: true }, | ||
}, | ||
additionalProperties: true, | ||
required: ['root'], | ||
@@ -135,3 +154,5 @@ }; | ||
image: { type: 'string' }, | ||
keywords: { type: 'array', items: { type: 'string' } }, | ||
keywords: { | ||
oneOf: [{ type: 'array', items: { type: 'string' } }, { type: 'string' }], | ||
}, | ||
lang: { type: 'string' }, | ||
@@ -152,12 +173,16 @@ jsonLd: { type: 'object' }, | ||
}, | ||
additionalProperties: false, | ||
}; | ||
const rbacScopeItemsSchema = { | ||
type: 'object', | ||
properties: {}, | ||
additionalProperties: { type: 'string' }, | ||
}; | ||
const rbacScopeItemsSchema = { type: 'object', additionalProperties: { type: 'string' } }; | ||
const rbacConfigSchema = { | ||
type: 'object', | ||
properties: { | ||
defaults: 'rbacScopeItemsSchema', | ||
cms: rbacScopeItemsSchema, | ||
content: { | ||
type: 'object', | ||
properties: { | ||
'**': rbacScopeItemsSchema, | ||
}, | ||
additionalProperties: rbacScopeItemsSchema, | ||
}, | ||
}, | ||
@@ -227,2 +252,3 @@ additionalProperties: rbacScopeItemsSchema, | ||
required: ['adapters'], | ||
additionalProperties: false, | ||
properties: { | ||
@@ -257,3 +283,4 @@ adapters: { | ||
}, | ||
required: ['defaultLocale', 'locales'], | ||
additionalProperties: false, | ||
required: ['defaultLocale'], | ||
}; | ||
@@ -269,20 +296,9 @@ const responseHeaderSchema = { | ||
}; | ||
exports.PortalConfigNodeTypes = { | ||
seoConfigSchema, | ||
rbacConfigSchema, | ||
rbacScopeItemsSchema, | ||
ssoConfigSchema: exports.ssoConfigSchema, | ||
devOnboardingConfigSchema, | ||
i18ConfigSchema, | ||
redirectsConfigSchema, | ||
redirectConfigSchema, | ||
// TODO: Extract other types that need to be linted in the config | ||
}; | ||
exports.redoclyConfigSchema = { | ||
const redoclyConfigSchema = { | ||
type: 'object', | ||
properties: { | ||
licenseKey: { type: 'string' }, | ||
redirects: 'redirectsConfigSchema', | ||
seo: 'seoConfigSchema', | ||
rbac: 'rbacConfigSchema', | ||
redirects: redirectsConfigSchema, | ||
seo: seoConfigSchema, | ||
rbac: rbacConfigSchema, | ||
responseHeaders: { | ||
@@ -307,28 +323,28 @@ type: 'object', | ||
type: 'object', | ||
additionalProperties: exports.apiConfigSchema, | ||
additionalProperties: apiConfigSchema, | ||
}, | ||
sso: 'ssoConfigSchema', | ||
developerOnboarding: 'devOnboardingConfigSchema', | ||
i18n: 'i18ConfigSchema', | ||
ssoOnPrem: ssoOnPremConfigSchema, | ||
sso: ssoConfigSchema, | ||
residency: { type: 'string' }, | ||
developerOnboarding: devOnboardingConfigSchema, | ||
i18n: i18ConfigSchema, | ||
metadata: metadataConfigSchema, | ||
}, | ||
default: {}, | ||
}; | ||
exports.environmentSchema = { | ||
oneOf: [ | ||
Object.assign(Object.assign({}, exports.redoclyConfigSchema), { additionalProperties: false }), | ||
{ | ||
type: 'object', | ||
properties: { | ||
$ref: { type: 'string' }, | ||
ignore: { | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
}, | ||
required: ['$ref'], | ||
additionalProperties: false, | ||
}, | ||
], | ||
theme: theme_config_1.themeConfigSchema, | ||
}, | ||
default: { redirects: {} }, | ||
additionalProperties: true, | ||
}; | ||
exports.rootRedoclyConfigSchema = Object.assign(Object.assign({}, exports.redoclyConfigSchema), { properties: Object.assign(Object.assign({}, exports.redoclyConfigSchema.properties), { env: { | ||
const environmentSchema = Object.assign(Object.assign({}, redoclyConfigSchema), { additionalProperties: false }); | ||
exports.rootRedoclyConfigSchema = Object.assign(Object.assign({}, redoclyConfigSchema), { properties: Object.assign(Object.assign({ plugins: { | ||
type: 'array', | ||
items: { type: 'string' }, | ||
} }, redoclyConfigSchema.properties), { env: { | ||
type: 'object', | ||
properties: {}, | ||
additionalProperties: exports.environmentSchema, | ||
} }), default: {}, required: ['redirects'] }); | ||
additionalProperties: environmentSchema, // TODO: if we want full validation we need to override apis, theme and the root | ||
} }), default: {}, additionalProperties: false }); |
@@ -1,2 +0,3 @@ | ||
import { NodeType } from '.'; | ||
import type { NodeType } from '.'; | ||
import type { JSONSchema } from 'json-schema-to-ts'; | ||
declare const builtInCommonRules: readonly ["spec", "info-contact", "operation-operationId", "tag-description", "tags-alphabetical"]; | ||
@@ -12,3 +13,14 @@ export type BuiltInCommonRuleId = typeof builtInCommonRules[number]; | ||
export type BuiltInAsync2RuleId = typeof builtInAsync2Rules[number]; | ||
declare const oas2NodeTypesList: readonly ["Root", "Tag", "TagList", "ExternalDocs", "SecurityRequirement", "SecurityRequirementList", "Info", "Contact", "License", "Paths", "PathItem", "Parameter", "ParameterList", "ParameterItems", "Operation", "Example", "ExamplesMap", "Examples", "Header", "Responses", "Response", "Schema", "Xml", "SchemaProperties", "NamedSchemas", "NamedResponses", "NamedParameters", "NamedSecuritySchemes", "SecurityScheme", "TagGroup", "TagGroups", "EnumDescriptions", "Logo", "XCodeSample", "XCodeSampleList", "XServer", "XServerList"]; | ||
export type Oas2NodeType = typeof oas2NodeTypesList[number]; | ||
declare const oas3NodeTypesList: readonly ["Root", "Tag", "TagList", "ExternalDocs", "Server", "ServerList", "ServerVariable", "ServerVariablesMap", "SecurityRequirement", "SecurityRequirementList", "Info", "Contact", "License", "Paths", "PathItem", "Parameter", "ParameterList", "Operation", "Callback", "CallbacksMap", "RequestBody", "MediaTypesMap", "MediaType", "Example", "ExamplesMap", "Encoding", "EncodingMap", "Header", "HeadersMap", "Responses", "Response", "Link", "LinksMap", "Schema", "Xml", "SchemaProperties", "DiscriminatorMapping", "Discriminator", "Components", "NamedSchemas", "NamedResponses", "NamedParameters", "NamedExamples", "NamedRequestBodies", "NamedHeaders", "NamedSecuritySchemes", "NamedLinks", "NamedCallbacks", "ImplicitFlow", "PasswordFlow", "ClientCredentials", "AuthorizationCode", "OAuth2Flows", "SecurityScheme", "TagGroup", "TagGroups", "EnumDescriptions", "Logo", "XCodeSample", "XCodeSampleList", "XUsePkce", "WebhooksMap"]; | ||
export type Oas3NodeType = typeof oas3NodeTypesList[number]; | ||
declare const oas3_1NodeTypesList: readonly ["Root", "Schema", "SchemaProperties", "Info", "License", "Components", "NamedPathItems", "SecurityScheme", "Operation"]; | ||
export type Oas3_1NodeType = typeof oas3_1NodeTypesList[number]; | ||
export declare const createConfigTypes: (extraSchemas: JSONSchema) => { | ||
ConfigRoot: NodeType; | ||
ConfigApisProperties: NodeType; | ||
ConfigRootTheme: NodeType; | ||
}; | ||
export declare const ConfigTypes: Record<string, NodeType>; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ConfigTypes = void 0; | ||
exports.ConfigTypes = exports.createConfigTypes = void 0; | ||
const portal_config_schema_1 = require("./portal-config-schema"); | ||
const theme_config_1 = require("./theme-config"); | ||
const _1 = require("."); | ||
const utils_1 = require("../utils"); | ||
const portal_config_schema_2 = require("./portal-config-schema"); | ||
const json_schema_adapter_1 = require("./json-schema-adapter"); | ||
const builtInCommonRules = [ | ||
@@ -84,4 +83,3 @@ 'spec', | ||
]; | ||
const nodeTypesList = [ | ||
'any', | ||
const oas2NodeTypesList = [ | ||
'Root', | ||
@@ -91,2 +89,41 @@ 'Tag', | ||
'ExternalDocs', | ||
'SecurityRequirement', | ||
'SecurityRequirementList', | ||
'Info', | ||
'Contact', | ||
'License', | ||
'Paths', | ||
'PathItem', | ||
'Parameter', | ||
'ParameterList', | ||
'ParameterItems', | ||
'Operation', | ||
'Example', | ||
'ExamplesMap', | ||
'Examples', | ||
'Header', | ||
'Responses', | ||
'Response', | ||
'Schema', | ||
'Xml', | ||
'SchemaProperties', | ||
'NamedSchemas', | ||
'NamedResponses', | ||
'NamedParameters', | ||
'NamedSecuritySchemes', | ||
'SecurityScheme', | ||
'TagGroup', | ||
'TagGroups', | ||
'EnumDescriptions', | ||
'Logo', | ||
'XCodeSample', | ||
'XCodeSampleList', | ||
'XServer', | ||
'XServerList', | ||
]; | ||
const oas3NodeTypesList = [ | ||
'Root', | ||
'Tag', | ||
'TagList', | ||
'ExternalDocs', | ||
'Server', | ||
@@ -142,8 +179,23 @@ 'ServerList', | ||
'SecurityScheme', | ||
'TagGroup', | ||
'TagGroups', | ||
'EnumDescriptions', | ||
'Logo', | ||
'XCodeSample', | ||
'XCodeSampleList', | ||
'XUsePkce', | ||
'WebhooksMap', | ||
'SpecExtension', | ||
'Message', | ||
]; | ||
const oas3_1NodeTypesList = [ | ||
'Root', | ||
'Schema', | ||
'SchemaProperties', | ||
'Info', | ||
'License', | ||
'Components', | ||
'NamedPathItems', | ||
'SecurityScheme', | ||
'Operation', | ||
]; | ||
const asyncNodeTypesList = ['Message']; | ||
const ConfigStyleguide = { | ||
@@ -174,10 +226,3 @@ properties: { | ||
}; | ||
const RootConfigStyleguide = { | ||
properties: Object.assign({ plugins: { | ||
type: 'array', | ||
items: { type: 'string' }, | ||
} }, ConfigStyleguide.properties), | ||
}; | ||
const ConfigRoot = { | ||
properties: Object.assign(Object.assign(Object.assign({}, portal_config_schema_1.rootRedoclyConfigSchema.properties), RootConfigStyleguide.properties), { apis: 'ConfigApis', theme: 'ConfigRootTheme', 'features.openapi': 'ConfigReferenceDocs', 'features.mockServer': 'ConfigMockServer', organization: { type: 'string' }, region: { enum: ['us', 'eu'] }, telemetry: { enum: ['on', 'off'] }, resolve: { | ||
const createConfigRoot = (nodeTypes) => (Object.assign(Object.assign({}, nodeTypes.rootRedoclyConfigSchema), { properties: Object.assign(Object.assign(Object.assign({}, nodeTypes.rootRedoclyConfigSchema.properties), ConfigStyleguide.properties), { apis: 'ConfigApis', theme: 'ConfigRootTheme', 'features.openapi': 'ConfigReferenceDocs', 'features.mockServer': 'ConfigMockServer', organization: { type: 'string' }, region: { enum: ['us', 'eu'] }, telemetry: { enum: ['on', 'off'] }, resolve: { | ||
properties: { | ||
@@ -192,4 +237,3 @@ http: 'ConfigHTTP', | ||
}, | ||
} }), | ||
}; | ||
} }) })); | ||
const ConfigApis = { | ||
@@ -199,15 +243,15 @@ properties: {}, | ||
}; | ||
const ConfigApisProperties = { | ||
properties: Object.assign(Object.assign(Object.assign(Object.assign({}, portal_config_schema_1.apiConfigSchema.properties), { root: { type: 'string' }, labels: { | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
}, | ||
} }), ConfigStyleguide.properties), { 'features.openapi': 'ConfigReferenceDocs', 'features.mockServer': 'ConfigMockServer', theme: 'ConfigRootTheme', files: { | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
}, | ||
} }), | ||
required: ['root'], | ||
const createConfigApisProperties = (nodeTypes) => { | ||
var _a; | ||
return (Object.assign(Object.assign({}, nodeTypes['rootRedoclyConfigSchema.apis_additionalProperties']), { properties: Object.assign(Object.assign(Object.assign(Object.assign({}, (_a = nodeTypes['rootRedoclyConfigSchema.apis_additionalProperties']) === null || _a === void 0 ? void 0 : _a.properties), { labels: { | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
}, | ||
} }), ConfigStyleguide.properties), { 'features.openapi': 'ConfigReferenceDocs', 'features.mockServer': 'ConfigMockServer', files: { | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
}, | ||
} }) })); | ||
}; | ||
@@ -224,4 +268,5 @@ const ConfigHTTP = { | ||
}; | ||
const ConfigRootTheme = { | ||
properties: Object.assign(Object.assign({}, theme_config_1.themeConfigSchema.properties), { openapi: 'ConfigReferenceDocs', mockServer: 'ConfigMockServer' }), | ||
const createConfigRootTheme = (nodeTypes) => { | ||
var _a; | ||
return (Object.assign(Object.assign({}, nodeTypes['rootRedoclyConfigSchema.theme']), { properties: Object.assign(Object.assign({}, (_a = nodeTypes['rootRedoclyConfigSchema.theme']) === null || _a === void 0 ? void 0 : _a.properties), { openapi: 'ConfigReferenceDocs' }) })); | ||
}; | ||
@@ -262,3 +307,14 @@ const Rules = { | ||
properties: { | ||
type: { enum: nodeTypesList }, | ||
type: { | ||
enum: [ | ||
...new Set([ | ||
'any', | ||
...oas2NodeTypesList, | ||
...oas3NodeTypesList, | ||
...oas3_1NodeTypesList, | ||
...asyncNodeTypesList, | ||
'SpecExtension', | ||
]), | ||
], | ||
}, | ||
property: (value) => { | ||
@@ -716,2 +772,3 @@ if (Array.isArray(value)) { | ||
const ConfigReferenceDocs = { | ||
// TODO: partially invalid @Viacheslav | ||
properties: { | ||
@@ -851,7 +908,11 @@ theme: 'ConfigTheme', | ||
}; | ||
exports.ConfigTypes = Object.assign({ Assert, | ||
ConfigRoot, | ||
const createConfigTypes = (extraSchemas) => { | ||
// Create types based on external schemas | ||
const nodeTypes = (0, json_schema_adapter_1.getNodeTypesFromJSONSchema)('rootRedoclyConfigSchema', extraSchemas); | ||
return Object.assign(Object.assign(Object.assign({}, CoreConfigTypes), { ConfigRoot: createConfigRoot(nodeTypes), ConfigApisProperties: createConfigApisProperties(nodeTypes), ConfigRootTheme: createConfigRootTheme(nodeTypes) }), nodeTypes); | ||
}; | ||
exports.createConfigTypes = createConfigTypes; | ||
const CoreConfigTypes = { | ||
Assert, | ||
ConfigApis, | ||
ConfigApisProperties, | ||
RootConfigStyleguide, | ||
ConfigStyleguide, | ||
@@ -866,3 +927,2 @@ ConfigReferenceDocs, | ||
ConfigTheme, | ||
ConfigRootTheme, | ||
AssertDefinition, | ||
@@ -916,2 +976,4 @@ ThemeColors, | ||
AssertionDefinitionAssertions, | ||
AssertionDefinitionSubject }, portal_config_schema_2.PortalConfigNodeTypes); | ||
AssertionDefinitionSubject, | ||
}; | ||
exports.ConfigTypes = (0, exports.createConfigTypes)(portal_config_schema_1.rootRedoclyConfigSchema); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ScorecardStatus = exports.productThemeOverrideSchema = exports.themeConfigSchema = void 0; | ||
exports.productThemeOverrideSchema = exports.themeConfigSchema = void 0; | ||
const logoConfigSchema = { | ||
@@ -70,2 +70,7 @@ type: 'object', | ||
}, | ||
partialsFolders: { | ||
type: 'array', | ||
items: { type: 'string' }, | ||
default: ['_partials'], | ||
}, | ||
lastUpdatedBlock: { | ||
@@ -77,3 +82,3 @@ type: 'object', | ||
default: 'timeago', | ||
}, locale: { type: 'string', default: 'en-US' } }, hideConfigSchema.properties), | ||
}, locale: { type: 'string' } }, hideConfigSchema.properties), | ||
additionalProperties: false, | ||
@@ -173,2 +178,15 @@ default: {}, | ||
}; | ||
const productGoogleAnalyticsConfigSchema = { | ||
type: 'object', | ||
properties: { | ||
includeInDevelopment: { type: 'boolean' }, | ||
trackingId: { type: 'string' }, | ||
conversionId: { type: 'string' }, | ||
floodlightId: { type: 'string' }, | ||
optimizeId: { type: 'string' }, | ||
exclude: { type: 'array', items: { type: 'string' } }, | ||
}, | ||
additionalProperties: false, | ||
required: ['trackingId'], | ||
}; | ||
const googleAnalyticsConfigSchema = { | ||
@@ -187,2 +205,7 @@ type: 'object', | ||
cookieExpires: { type: 'number' }, | ||
// All enabled tracking configs | ||
trackers: { | ||
type: 'object', | ||
additionalProperties: productGoogleAnalyticsConfigSchema, | ||
}, | ||
}, | ||
@@ -207,2 +230,3 @@ additionalProperties: false, | ||
directory: { type: 'string' }, | ||
disconnect: { type: 'boolean', default: false }, | ||
group: { type: 'string' }, | ||
@@ -240,3 +264,3 @@ label: { type: 'string' }, | ||
additionalProperties: false, | ||
required: ['name', 'icon', 'folder'], | ||
required: ['name', 'folder'], | ||
}; | ||
@@ -262,2 +286,3 @@ const suggestedPageSchema = { | ||
parentFilter: { type: 'string' }, | ||
valuesMapping: { type: 'object', additionalProperties: { type: 'string' } }, | ||
missingCategoryName: { type: 'string' }, | ||
@@ -271,5 +296,5 @@ missingCategoryNameTranslationKey: { type: 'string' }, | ||
additionalProperties: true, | ||
required: ['levels'], | ||
required: [], | ||
properties: { | ||
failBuildIfBelowMinimum: { type: 'boolean', default: false }, | ||
ignoreNonCompliant: { type: 'boolean', default: false }, | ||
teamMetadataProperty: { | ||
@@ -290,2 +315,3 @@ type: 'object', | ||
name: { type: 'string' }, | ||
color: { type: 'string' }, | ||
extends: { type: 'array', items: { type: 'string' } }, | ||
@@ -295,3 +321,3 @@ rules: { | ||
additionalProperties: { | ||
type: ['object', 'string'], | ||
oneOf: [{ type: 'string' }, { type: 'object' }], | ||
}, | ||
@@ -383,8 +409,2 @@ }, | ||
}, | ||
seo: { | ||
type: 'object', | ||
properties: { | ||
title: { type: 'string' }, | ||
}, | ||
}, | ||
scripts: { | ||
@@ -408,3 +428,3 @@ type: 'object', | ||
type: 'string', | ||
enum: ['rating', 'sentiment', 'comment', 'reasons'], | ||
enum: ['rating', 'sentiment', 'comment', 'reasons', 'mood', 'scale'], | ||
default: 'sentiment', | ||
@@ -415,11 +435,23 @@ }, | ||
submitText: { type: 'string' }, | ||
max: { type: 'number' }, | ||
buttonText: { type: 'string' }, | ||
multi: { type: 'boolean' }, | ||
component: { | ||
type: 'string', | ||
enum: ['radio', 'checkbox'], | ||
default: 'checkbox', | ||
}, | ||
items: { type: 'array', items: { type: 'string' }, minItems: 1 }, | ||
leftScaleLabel: { type: 'string' }, | ||
rightScaleLabel: { type: 'string' }, | ||
reasons: { | ||
type: 'object', | ||
properties: { | ||
enable: { type: 'boolean', default: true }, | ||
multi: { type: 'boolean' }, | ||
hide: { | ||
type: 'boolean', | ||
default: false, | ||
}, | ||
component: { | ||
type: 'string', | ||
enum: ['radio', 'checkbox'], | ||
default: 'checkbox', | ||
}, | ||
label: { type: 'string' }, | ||
@@ -433,6 +465,12 @@ items: { type: 'array', items: { type: 'string' } }, | ||
properties: { | ||
enable: { type: 'boolean', default: true }, | ||
hide: { | ||
type: 'boolean', | ||
default: false, | ||
}, | ||
label: { type: 'string' }, | ||
likeLabel: { type: 'string' }, | ||
dislikeLabel: { type: 'string' }, | ||
satisfiedLabel: { type: 'string' }, | ||
neutralLabel: { type: 'string' }, | ||
dissatisfiedLabel: { type: 'string' }, | ||
}, | ||
@@ -477,3 +515,3 @@ additionalProperties: false, | ||
type: 'object', | ||
properties: Object.assign({ text: { type: 'string', default: 'Next to {label}' } }, hideConfigSchema.properties), | ||
properties: Object.assign({ text: { type: 'string', default: 'Next to {{label}}' } }, hideConfigSchema.properties), | ||
additionalProperties: false, | ||
@@ -484,3 +522,3 @@ default: {}, | ||
type: 'object', | ||
properties: Object.assign({ text: { type: 'string', default: 'Back to {label}' } }, hideConfigSchema.properties), | ||
properties: Object.assign({ text: { type: 'string', default: 'Back to {{label}}' } }, hideConfigSchema.properties), | ||
additionalProperties: false, | ||
@@ -496,3 +534,3 @@ default: {}, | ||
properties: { | ||
controlsStyle: { type: 'string', default: 'icon' }, | ||
elementFormat: { type: 'string', default: 'icon' }, | ||
copy: { | ||
@@ -506,5 +544,5 @@ type: 'object', | ||
type: 'object', | ||
properties: Object.assign({}, hideConfigSchema.properties), | ||
properties: Object.assign({ tooltipText: { type: 'string' }, buttonText: { type: 'string' }, label: { type: 'string' } }, hideConfigSchema.properties), | ||
additionalProperties: false, | ||
default: { hide: true }, | ||
default: { hide: false }, | ||
}, | ||
@@ -608,2 +646,8 @@ expand: { | ||
breadcrumbs: exports.themeConfigSchema.properties.breadcrumbs, | ||
analytics: { | ||
type: 'object', | ||
properties: { | ||
ga: productGoogleAnalyticsConfigSchema, | ||
}, | ||
}, | ||
}, | ||
@@ -613,7 +657,1 @@ additionalProperties: true, | ||
}; | ||
var ScorecardStatus; | ||
(function (ScorecardStatus) { | ||
ScorecardStatus["BelowMinimum"] = "Below minimum"; | ||
ScorecardStatus["Highest"] = "Highest"; | ||
ScorecardStatus["Minimum"] = "Minimum"; | ||
})(ScorecardStatus || (exports.ScorecardStatus = ScorecardStatus = {})); |
@@ -18,4 +18,4 @@ import { UserContext } from './walk'; | ||
export declare function isDefined<T>(x: T | undefined): x is T; | ||
export declare function isPlainObject(value: any): value is object; | ||
export declare function isEmptyObject(value: any): value is object; | ||
export declare function isPlainObject(value: any): value is Record<string, unknown>; | ||
export declare function isEmptyObject(value: any): value is Record<string, unknown>; | ||
export declare function isEmptyArray(value: any): boolean; | ||
@@ -22,0 +22,0 @@ export declare function readFileFromUrl(url: string, config: HttpResolveConfig): Promise<{ |
@@ -64,3 +64,3 @@ "use strict"; | ||
} | ||
if (from.items) { | ||
if (from.items && typeof from.items !== 'function') { | ||
if (from.items === to) { | ||
@@ -67,0 +67,0 @@ addWeakFromStack(ruleConf, stack); |
@@ -143,4 +143,10 @@ "use strict"; | ||
if (itemsType !== undefined) { | ||
const isTypeAFunction = typeof itemsType === 'function'; | ||
for (let i = 0; i < resolvedNode.length; i++) { | ||
walkNode(resolvedNode[i], itemsType, resolvedLocation.child([i]), resolvedNode, i); | ||
const itemType = isTypeAFunction | ||
? itemsType(resolvedNode[i], resolvedLocation.child([i]).absolutePointer) | ||
: itemsType; | ||
if ((0, types_1.isNamedType)(itemType)) { | ||
walkNode(resolvedNode[i], itemType, resolvedLocation.child([i]), resolvedNode, i); | ||
} | ||
} | ||
@@ -147,0 +153,0 @@ } |
{ | ||
"name": "@redocly/openapi-core", | ||
"version": "1.9.0", | ||
"version": "1.9.1", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -9,3 +9,273 @@ import * as path from 'path'; | ||
import { detectSpec } from '../oas-types'; | ||
import { themeConfigSchema } from '../types/theme-config'; | ||
import { createConfigTypes } from '../types/redocly-yaml'; | ||
const testPortalConfig = parseYamlToDocument( | ||
outdent` | ||
licenseKey: 123 # Must be a string | ||
apis: | ||
without-root: | ||
foo: Not expected! | ||
output: file.json | ||
with-wrong-root: | ||
root: 456 # Must be a string | ||
with-theme: | ||
root: ./openapi.yaml | ||
theme: | ||
openapi: wrong, must be an object | ||
not-expected: Must fail | ||
seo: | ||
keywords: 789 # Must be an array | ||
redirects: | ||
some-redirect: | ||
t1o: Wrong name, should be 'two' | ||
type: wrong type, must be a number | ||
rbac: | ||
'team-b.md': | ||
TeamB: read | ||
team-c.md: | ||
TeamC: read | ||
/blog/*: | ||
anonymous: none | ||
authenticated: read | ||
/blogpost/: | ||
TeamD: none | ||
'**/*.md': | ||
TeamA: none | ||
authenticated: none | ||
'*': read | ||
'blog/april-2022.md': | ||
TeamA: none | ||
TeamC: read | ||
test.md: | ||
TeamC: none | ||
TeamB: none | ||
authenticated: none | ||
'*': read | ||
test/**: | ||
TeamB: read | ||
TeamC: read | ||
authenticated: read | ||
anonymous: read | ||
additional-property: | ||
something: 123 # Must be a string | ||
content: | ||
'**': | ||
additionalProp: 456 # Must be a stirng | ||
foo: | ||
additionalProp2: 789 # Must be a stirng | ||
responseHeaders: | ||
some-header: wrong, must be an array | ||
some-header2: | ||
- wrong, must be an object | ||
- unexpected-property: Should fail | ||
# name: Must be reported as a missing required prop | ||
value: 123 # Must be a string | ||
ssoOnPrem: | ||
oidc: | ||
title: 456 # Must be a string | ||
type: OIDC | ||
configurationUrl: http://localhost/oidc/.well-known/openid-configuration | ||
clientId: '{{ process.env.public }}' | ||
clientSecret: '{{ process.env.secret }}' | ||
teamsClaimName: https://test.com | ||
scopes: | ||
- openid | ||
audience: | ||
- default | ||
authorizationRequestCustomParams: | ||
login_hint: 789 # Must be a string | ||
prompt: login | ||
configuration: | ||
token_endpoint: 123 # Must be a string | ||
# authorization_endpoint: Must be reported as a missing required prop | ||
additional-propery: Must be allowed | ||
defaultTeams: | ||
- 456 # Must be a string | ||
sso-config-schema-without-configurationUrl: | ||
type: OIDC | ||
# clientId: Must be reported as a missing required prop | ||
# configurationUrl: Must be reported as a missing required prop | ||
clientSecret: '{{ process.env.secret }}' | ||
basic: | ||
type: BASIC | ||
credentials: | ||
- teams: | ||
- 789 # Must be a string | ||
- correct | ||
# username: Must be reported as a missing required prop | ||
sso: | ||
- WRONG # Does not match allowed options | ||
developerOnboarding: | ||
wrong: A not allowed field | ||
adapters: | ||
- should be object | ||
- type: 123 # Must be a string | ||
- type: APIGEE_X | ||
# organizationName: Must be reported as a missing required prop | ||
auth: | ||
type: OAUTH2 | ||
# tokenEndpoint: Must be reported as a missing required prop | ||
clientId: 456 # Must be a string | ||
clientSecret: '{{ process.env.secret }}' | ||
not-expected: Must fail | ||
- type: APIGEE_X | ||
organizationName: Test | ||
auth: | ||
type: SERVICE_ACCOUNT | ||
# serviceAccountPrivateKey: Must be reported as a missing required prop | ||
serviceAccountEmail: 789 # Must be a string | ||
i18n: | ||
defaultLocale: en-US | ||
locales: | ||
- code: 123 # Must be a string | ||
name: English | ||
- code: es-ES | ||
name: Spanish | ||
metadata: | ||
test: anything | ||
not-listed-filed: Must be reported as not expected | ||
env: | ||
some-env: | ||
mockServer: | ||
off: must be boolean | ||
not-expected: Must fail | ||
apis: | ||
no-root: | ||
# root: Must be defined | ||
rules: {} | ||
wrong-root: | ||
root: 789 # Must be a string | ||
theme: | ||
breadcrumbs: | ||
hide: false | ||
prefixItems: | ||
- label: Home | ||
page: '/' | ||
imports: | ||
- '@redocly/theme-experimental' | ||
logo: | ||
srcSet: './images/redocly-black-logo.svg light, ./images/redocly-brand-logo.svg dark' | ||
altText: Test | ||
link: / | ||
asyncapi: | ||
hideInfo: false | ||
expandSchemas: | ||
root: true | ||
elements: true | ||
navbar: | ||
items: | ||
- label: Markdown | ||
page: /markdown/ | ||
search: | ||
shortcuts: | ||
- ctrl+f | ||
- cmd+k | ||
- / | ||
suggestedPages: | ||
- label: TSX page | ||
page: tsx.page.tsx | ||
- page: /my-catalog/ | ||
footer: | ||
copyrightText: Copyright © Test 2019-2020. | ||
items: | ||
- group: Legal | ||
items: | ||
- label: Terms of Use | ||
href: 'https://test.com/' # Not expected | ||
markdown: | ||
lastUpdatedBlock: | ||
format: 'long' | ||
editPage: | ||
baseUrl: https://test.com | ||
graphql: | ||
pagination: section | ||
menu: | ||
{ | ||
initialLoadState: 'default', | ||
requireExactGroups: false, | ||
groups: | ||
[ | ||
{ | ||
name: 'GraphQL custom group', | ||
directives: { includeByName: ['cacheControl', 'typeDirective'] }, | ||
}, | ||
], | ||
otherItemsGroupName: 'Other', | ||
} | ||
sidebar: | ||
separatorLine: true | ||
linePosition: top | ||
catalog: | ||
main: | ||
title: API Catalog | ||
description: 'This is a description of the API Catalog' | ||
slug: /my-catalog/ | ||
filters: | ||
- title: Domain | ||
property: domain | ||
missingCategoryName: Other | ||
- title: API Category | ||
property: category | ||
missingCategoryName: Other | ||
groupByFirstFilter: false | ||
items: | ||
- directory: ./ | ||
flatten: true | ||
includeByMetadata: | ||
type: [openapi] | ||
scorecard: | ||
ignoreNonCompliant: true | ||
levels: | ||
- name: Baseline | ||
extends: | ||
- minimal | ||
- name: Silver | ||
extends: | ||
- recommended | ||
rules: | ||
info-description: off | ||
- name: Gold | ||
rules: | ||
rule/path-item-get-required: | ||
severity: warn | ||
subject: | ||
type: PathItem | ||
message: Every path item must have a GET operation. | ||
assertions: | ||
required: | ||
- get | ||
operation-4xx-response: warn | ||
targets: | ||
- where: | ||
metadata: | ||
l0: Distribution | ||
publishDateRange: 2021-01-01T00:00:00Z/2022-01-01 | ||
minimumLevel: Silver | ||
`, | ||
'' | ||
); | ||
describe('lint', () => { | ||
@@ -101,24 +371,24 @@ it('lintFromString should work', async () => { | ||
min: 3 | ||
theme: | ||
openapi: | ||
showConsole: true | ||
layout: | ||
scope: section | ||
routingStrategy: browser | ||
theme: | ||
rightPanel: | ||
backgroundColor: '#263238' | ||
links: | ||
color: '#6CC496' | ||
theme: | ||
openapi: | ||
showConsole: true | ||
layout: | ||
scope: section | ||
routingStrategy: browser | ||
theme: | ||
openapi: | ||
showConsole: true | ||
layout: | ||
scope: section | ||
routingStrategy: browser | ||
rightPanel: | ||
backgroundColor: '#263238' | ||
links: | ||
color: '#6CC496' | ||
theme: | ||
rightPanel: | ||
backgroundColor: '#263238' | ||
links: | ||
color: '#6CC496' | ||
openapi: | ||
showConsole: true | ||
layout: | ||
scope: section | ||
routingStrategy: browser | ||
theme: | ||
rightPanel: | ||
backgroundColor: '#263238' | ||
links: | ||
color: '#6CC496' | ||
`, | ||
@@ -135,16 +405,11 @@ '' | ||
{ | ||
"pointer": "#/eme", | ||
"reportOnKey": true, | ||
"pointer": "#/apis", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`eme\` is not expected here.", | ||
"message": "Expected type \`ConfigApis\` (object) but got \`string\`", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [ | ||
"env", | ||
"theme", | ||
"seo", | ||
"sso", | ||
], | ||
"suggest": [], | ||
}, | ||
@@ -155,8 +420,8 @@ { | ||
{ | ||
"pointer": "#/openapi", | ||
"reportOnKey": true, | ||
"pointer": "#/theme/openapi/layout", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`openapi\` is not expected here.", | ||
"message": "\`layout\` can be one of the following only: "stacked", "three-panel".", | ||
"ruleId": "configuration spec", | ||
@@ -170,11 +435,14 @@ "severity": "error", | ||
{ | ||
"pointer": "#/apis", | ||
"reportOnKey": false, | ||
"pointer": "#/theme/openapi/theme/theme", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`ConfigApis\` (object) but got \`string\`", | ||
"message": "Property \`theme\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
"suggest": [ | ||
"schema", | ||
"shape", | ||
], | ||
}, | ||
@@ -293,2 +561,916 @@ ] | ||
it('lintConfig should detect wrong fields in the default configuration after merging with the portal config schema', async () => { | ||
const document = testPortalConfig; | ||
const results = await lintConfig({ document }); | ||
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(` | ||
[ | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/licenseKey", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/sso/0", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "\`sso\` can be one of the following only: "REDOCLY", "CORPORATE", "GUEST".", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/not-listed-filed", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`not-listed-filed\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/redirects/some-redirect/t1o", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`t1o\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [ | ||
"to", | ||
"type", | ||
], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/redirects/some-redirect/type", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`number\` but got \`string\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/seo/keywords", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`array\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/rbac/content/**/additionalProp", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/rbac/content/foo/additionalProp2", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/rbac/additional-property/something", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/responseHeaders/some-header", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`rootRedoclyConfigSchema.responseHeaders_additionalProperties\` (array) but got \`string\`", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/responseHeaders/some-header2/0", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`rootRedoclyConfigSchema.responseHeaders_additionalProperties_items\` (object) but got \`string\`", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/responseHeaders/some-header2/1", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "The field \`name\` must be present on this level.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/responseHeaders/some-header2/1/unexpected-property", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`unexpected-property\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/responseHeaders/some-header2/1/value", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/apis/without-root", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "The field \`root\` must be present on this level.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/apis/without-root/foo", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`foo\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [ | ||
"root", | ||
], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/apis/with-wrong-root/root", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/apis/with-theme/theme/openapi", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`object\` but got \`string\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/apis/with-theme/theme/not-expected", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`not-expected\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/ssoOnPrem/oidc/title", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/ssoOnPrem/oidc/defaultTeams/0", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/ssoOnPrem/oidc/configuration", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "The field \`authorization_endpoint\` must be present on this level.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/ssoOnPrem/oidc/configuration/token_endpoint", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/ssoOnPrem/oidc/authorizationRequestCustomParams/login_hint", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/ssoOnPrem/sso-config-schema-without-configurationUrl", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "The field \`clientId\` must be present on this level.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/ssoOnPrem/sso-config-schema-without-configurationUrl", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "The field \`configurationUrl\` must be present on this level.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/ssoOnPrem/basic/credentials/0", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "The field \`username\` must be present on this level.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/ssoOnPrem/basic/credentials/0/teams/0", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/developerOnboarding/wrong", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`wrong\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/developerOnboarding/adapters/0", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`APIGEE_X\` (object) but got \`string\`", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/developerOnboarding/adapters/1", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "The field \`organizationName\` must be present on this level.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/developerOnboarding/adapters/1", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "The field \`auth\` must be present on this level.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/developerOnboarding/adapters/1/type", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/developerOnboarding/adapters/2", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "The field \`organizationName\` must be present on this level.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/developerOnboarding/adapters/2/auth", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "The field \`tokenEndpoint\` must be present on this level.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/developerOnboarding/adapters/2/auth/clientId", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/developerOnboarding/adapters/2/auth/not-expected", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`not-expected\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/developerOnboarding/adapters/3/auth", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "The field \`serviceAccountPrivateKey\` must be present on this level.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/developerOnboarding/adapters/3/auth/serviceAccountEmail", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/i18n/locales/0/code", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/theme/footer/items/0/items/0/href", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`href\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/env/some-env/mockServer/off", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`boolean\` but got \`string\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/env/some-env/mockServer/not-expected", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`not-expected\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/env/some-env/apis/no-root", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "The field \`root\` must be present on this level.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/env/some-env/apis/wrong-root/root", | ||
"reportOnKey": false, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Expected type \`string\` but got \`integer\`.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
] | ||
`); | ||
}); | ||
it('lintConfig should alternate its behavior when supplied externalConfigTypes', async () => { | ||
const document = testPortalConfig; | ||
const results = await lintConfig({ | ||
document, | ||
externalConfigTypes: createConfigTypes({ | ||
type: 'object', | ||
properties: { theme: themeConfigSchema }, | ||
additionalProperties: false, | ||
}), | ||
}); | ||
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(` | ||
[ | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/licenseKey", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`licenseKey\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/seo", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`seo\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/redirects", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`redirects\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/rbac", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`rbac\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/responseHeaders", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`responseHeaders\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/ssoOnPrem", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`ssoOnPrem\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/sso", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`sso\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/developerOnboarding", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`developerOnboarding\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/i18n", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`i18n\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/metadata", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`metadata\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/not-listed-filed", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`not-listed-filed\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/env", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`env\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/theme/footer/items/0/items/0/href", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`href\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/apis/without-root/foo", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`foo\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/apis/without-root/output", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`output\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/apis/with-wrong-root/root", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`root\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/apis/with-theme/root", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`root\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": undefined, | ||
"location": [ | ||
{ | ||
"pointer": "#/apis/with-theme/theme", | ||
"reportOnKey": true, | ||
"source": "", | ||
}, | ||
], | ||
"message": "Property \`theme\` is not expected here.", | ||
"ruleId": "configuration spec", | ||
"severity": "error", | ||
"suggest": [], | ||
}, | ||
] | ||
`); | ||
}); | ||
it("'const' can have any type", async () => { | ||
@@ -295,0 +1477,0 @@ const document = parseYamlToDocument( |
@@ -99,2 +99,24 @@ import outdent from 'outdent'; | ||
it('should parse a ref correctly', () => { | ||
expect(parseRef('./info.yaml#/description')).toEqual({ | ||
uri: './info.yaml', | ||
pointer: ['description'], | ||
}); | ||
}); | ||
it('should parse a ref which contain a hash in the middle', () => { | ||
// Here `info#description.md` is a file name | ||
expect(parseRef('./info#description.md')).toEqual({ | ||
uri: './info#description.md', | ||
pointer: [], | ||
}); | ||
}); | ||
it('should parse a ref which ends with a hash', () => { | ||
expect(parseRef('./info.yaml#')).toEqual({ | ||
uri: './info.yaml', | ||
pointer: [], | ||
}); | ||
}); | ||
describe('refBaseName', () => { | ||
@@ -101,0 +123,0 @@ it('returns base name for file reference', () => { |
@@ -141,2 +141,15 @@ import { loadConfig, findConfig, getConfig, createConfig } from '../load'; | ||
{ | ||
"location": [ | ||
{ | ||
"pointer": "#/theme", | ||
"reportOnKey": false, | ||
"source": "fixtures/resolve-refs-in-config/config-with-refs.yaml", | ||
}, | ||
], | ||
"message": "Can't resolve $ref: ENOENT: no such file or directory 'fixtures/resolve-refs-in-config/wrong-ref.yaml'", | ||
"ruleId": "configuration no-unresolved-refs", | ||
"severity": "warn", | ||
"suggest": [], | ||
}, | ||
{ | ||
"from": { | ||
@@ -158,15 +171,2 @@ "pointer": "#/rules", | ||
}, | ||
{ | ||
"location": [ | ||
{ | ||
"pointer": "#/theme", | ||
"reportOnKey": false, | ||
"source": "fixtures/resolve-refs-in-config/config-with-refs.yaml", | ||
}, | ||
], | ||
"message": "Can't resolve $ref: ENOENT: no such file or directory 'fixtures/resolve-refs-in-config/wrong-ref.yaml'", | ||
"ruleId": "configuration no-unresolved-refs", | ||
"severity": "warn", | ||
"suggest": [], | ||
}, | ||
] | ||
@@ -173,0 +173,0 @@ `); |
@@ -1,6 +0,7 @@ | ||
import type { Oas2Decorator } from '../../visitors'; | ||
import { Location } from '../../ref-utils'; | ||
import type { Oas2Components } from '../../typings/swagger'; | ||
import { isEmptyObject } from '../../utils'; | ||
import type { Oas2Decorator } from '../../visitors'; | ||
import type { Oas2Components } from '../../typings/swagger'; | ||
export const RemoveUnusedComponents: Oas2Decorator = () => { | ||
@@ -7,0 +8,0 @@ const components = new Map< |
@@ -1,6 +0,7 @@ | ||
import type { Oas3Decorator } from '../../visitors'; | ||
import { Location } from '../../ref-utils'; | ||
import type { Oas3Components } from '../../typings/openapi'; | ||
import { isEmptyObject } from '../../utils'; | ||
import type { Oas3Decorator } from '../../visitors'; | ||
import type { Oas3Components } from '../../typings/openapi'; | ||
export const RemoveUnusedComponents: Oas3Decorator = () => { | ||
@@ -7,0 +8,0 @@ const components = new Map< |
@@ -115,2 +115,3 @@ import { BaseResolver, resolveDocument, makeDocumentFromString } from './resolve'; | ||
externalRefResolver?: BaseResolver; | ||
externalConfigTypes?: Record<string, NodeType>; | ||
}) { | ||
@@ -130,3 +131,3 @@ const { document, severity, externalRefResolver = new BaseResolver() } = opts; | ||
const types = normalizeTypes(ConfigTypes, config); | ||
const types = normalizeTypes(opts.externalConfigTypes || ConfigTypes, config); | ||
const rules: (RuleInstanceConfig & { | ||
@@ -133,0 +134,0 @@ visitor: NestedVisitObject<unknown, Oas3Visitor | Oas3Visitor[]>; |
@@ -46,5 +46,5 @@ import { Source } from './resolve'; | ||
export function parseRef(ref: string): { uri: string | null; pointer: string[] } { | ||
const [uri, pointer = ''] = ref.split('#'); | ||
const [uri, pointer = ''] = ref.split('#/'); | ||
return { | ||
uri: uri || null, | ||
uri: (uri.endsWith('#') ? uri.slice(0, -1) : uri) || null, | ||
pointer: parsePointer(pointer), | ||
@@ -51,0 +51,0 @@ }; |
@@ -272,4 +272,16 @@ import * as fs from 'fs'; | ||
} | ||
const isTypeAFunction = typeof itemsType === 'function'; | ||
for (let i = 0; i < node.length; i++) { | ||
walk(node[i], itemsType || unknownType, joinPointer(nodeAbsoluteRef, i)); | ||
const itemType = isTypeAFunction | ||
? itemsType(node[i], joinPointer(nodeAbsoluteRef, i)) | ||
: itemsType; | ||
// we continue resolving unknown types, but stop early on known scalars | ||
if (itemType === undefined && type !== unknownType && type !== SpecExtension) { | ||
continue; | ||
} | ||
walk( | ||
node[i], | ||
isNamedType(itemType) ? itemType : unknownType, | ||
joinPointer(nodeAbsoluteRef, i) | ||
); | ||
} | ||
@@ -276,0 +288,0 @@ return; |
@@ -24,3 +24,3 @@ export type ScalarSchema = { | ||
additionalProperties?: PropType | ResolveTypeFn; | ||
items?: string; | ||
items?: PropType | ResolveTypeFn; | ||
required?: string[] | ((value: any, key: string | number | undefined) => string[]); | ||
@@ -31,4 +31,4 @@ requiredOneOf?: string[]; | ||
}; | ||
type PropType = string | NodeType | ScalarSchema | undefined | null; | ||
type ResolveTypeFn = (value: any, key: string) => string | PropType; | ||
export type PropType = string | NodeType | ScalarSchema | undefined | null; | ||
export type ResolveTypeFn = (value: any, key: string) => string | PropType; | ||
@@ -39,3 +39,3 @@ export type NormalizedNodeType = { | ||
additionalProperties?: NormalizedPropType | NormalizedResolveTypeFn; | ||
items?: NormalizedNodeType; | ||
items?: NormalizedPropType | NormalizedResolveTypeFn; | ||
required?: string[] | ((value: any, key: string | number | undefined) => string[]); | ||
@@ -47,7 +47,4 @@ requiredOneOf?: string[]; | ||
type NormalizedPropType = NormalizedNodeType | NormalizedScalarSchema | undefined | null; | ||
type NormalizedResolveTypeFn = ( | ||
value: any, | ||
key: string | ||
) => NormalizedNodeType | NormalizedScalarSchema | undefined | null; | ||
type NormalizedPropType = NormalizedNodeType | NormalizedScalarSchema | null | undefined; | ||
type NormalizedResolveTypeFn = (value: any, key: string) => NormalizedPropType; | ||
@@ -149,6 +146,4 @@ export function listOf(typeName: string) { | ||
export function isNamedType( | ||
t: NormalizedNodeType | NormalizedScalarSchema | null | undefined | ||
): t is NormalizedNodeType { | ||
export function isNamedType(t: NormalizedPropType): t is NormalizedNodeType { | ||
return typeof t?.name === 'string'; | ||
} |
@@ -1,3 +0,6 @@ | ||
import { NodeType, listOf, mapOf } from '.'; | ||
import { listOf, mapOf } from '.'; | ||
import type { NodeType } from '.'; | ||
import type { Oas2NodeType } from './redocly-yaml'; | ||
const responseCodeRegexp = /^[0-9][0-9Xx]{2}$/; | ||
@@ -440,3 +443,3 @@ | ||
export const Oas2Types: Record<string, NodeType> = { | ||
export const Oas2Types: Record<Oas2NodeType, NodeType> = { | ||
Root, | ||
@@ -443,0 +446,0 @@ Tag, |
@@ -1,4 +0,7 @@ | ||
import { NodeType, listOf, mapOf } from '.'; | ||
import { listOf, mapOf } from '.'; | ||
import { Oas3Types } from './oas3'; | ||
import type { NodeType } from '.'; | ||
import type { Oas3_1NodeType } from './redocly-yaml'; | ||
const Root: NodeType = { | ||
@@ -265,3 +268,3 @@ properties: { | ||
export const Oas3_1Types: Record<string, NodeType> = { | ||
export const Oas3_1Types: Record<Oas3_1NodeType, NodeType> = { | ||
...Oas3Types, | ||
@@ -268,0 +271,0 @@ Info, |
@@ -1,3 +0,7 @@ | ||
import { NodeType, listOf, mapOf } from '.'; | ||
import { listOf, mapOf } from '.'; | ||
import { isMappingRef } from '../ref-utils'; | ||
import type { NodeType } from '.'; | ||
import type { Oas3NodeType } from './redocly-yaml'; | ||
const responseCodeRegexp = /^[0-9][0-9Xx]{2}$/; | ||
@@ -534,3 +538,3 @@ | ||
export const Oas3Types: Record<string, NodeType> = { | ||
export const Oas3Types: Record<Oas3NodeType, NodeType> = { | ||
Root, | ||
@@ -537,0 +541,0 @@ Tag, |
@@ -6,4 +6,6 @@ import { | ||
} from '../config'; | ||
import { themeConfigSchema } from './theme-config'; | ||
import type { NodeType } from '.'; | ||
import type { FromSchema } from 'json-schema-to-ts'; | ||
import type { ThemeConfig } from './theme-config'; | ||
@@ -27,6 +29,7 @@ const oidcIssuerMetadataSchema = { | ||
title: { type: 'string' }, | ||
pkce: { type: 'boolean', default: false }, | ||
configurationUrl: { type: 'string', minLength: 1 }, | ||
configuration: oidcIssuerMetadataSchema, | ||
clientId: { type: 'string', minLength: 1 }, | ||
clientSecret: { type: 'string', minLength: 1 }, | ||
clientSecret: { type: 'string', minLength: 0 }, | ||
teamsClaimName: { type: 'string' }, | ||
@@ -41,3 +44,3 @@ teamsClaimMap: { type: 'object', additionalProperties: { type: 'string' } }, | ||
}, | ||
required: ['type', 'clientId', 'clientSecret'], | ||
required: ['type', 'clientId'], | ||
oneOf: [{ required: ['configurationUrl'] }, { required: ['configuration'] }], | ||
@@ -93,8 +96,24 @@ additionalProperties: false, | ||
export const ssoConfigSchema = { | ||
const ssoOnPremConfigSchema = { | ||
type: 'object', | ||
properties: {}, | ||
additionalProperties: authProviderConfigSchema, | ||
} as NodeType; | ||
} as const; | ||
const ssoConfigSchema = { | ||
oneOf: [ | ||
{ | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
enum: ['REDOCLY', 'CORPORATE', 'GUEST'], | ||
}, | ||
uniqueItems: true, | ||
}, | ||
{ | ||
type: 'string', | ||
enum: ['REDOCLY', 'CORPORATE', 'GUEST'], | ||
}, | ||
], | ||
} as const; | ||
const redirectConfigSchema = { | ||
@@ -106,16 +125,16 @@ type: 'object', | ||
}, | ||
required: ['to'], | ||
} as NodeType; | ||
additionalProperties: false, | ||
} as const; | ||
const redirectsConfigSchema = { | ||
type: 'object', | ||
properties: {}, | ||
additionalProperties: 'redirectConfigSchema', | ||
additionalProperties: redirectConfigSchema, | ||
default: {}, | ||
} as NodeType; | ||
} as const; | ||
export const apiConfigSchema = { | ||
const apiConfigSchema = { | ||
type: 'object', | ||
properties: { | ||
root: { type: 'string' }, | ||
output: { type: 'string', pattern: '(.ya?ml|.json)$' }, | ||
rbac: { type: 'object', additionalProperties: true }, | ||
@@ -125,3 +144,4 @@ theme: { | ||
properties: { | ||
openapi: { type: 'object', additionalProperties: true }, | ||
openapi: themeConfigSchema.properties.openapi, | ||
graphql: themeConfigSchema.properties.graphql, | ||
}, | ||
@@ -132,4 +152,5 @@ additionalProperties: false, | ||
metadata: { type: 'object', additionalProperties: true }, | ||
rules: { type: 'object', additionalProperties: true }, | ||
decorators: { type: 'object', additionalProperties: true }, | ||
}, | ||
additionalProperties: true, | ||
required: ['root'], | ||
@@ -150,3 +171,5 @@ } as const; | ||
image: { type: 'string' }, | ||
keywords: { type: 'array', items: { type: 'string' } }, | ||
keywords: { | ||
oneOf: [{ type: 'array', items: { type: 'string' } }, { type: 'string' }], | ||
}, | ||
lang: { type: 'string' }, | ||
@@ -167,9 +190,6 @@ jsonLd: { type: 'object' }, | ||
}, | ||
additionalProperties: false, | ||
} as const; | ||
const rbacScopeItemsSchema = { | ||
type: 'object', | ||
properties: {}, | ||
additionalProperties: { type: 'string' }, | ||
} as NodeType; | ||
const rbacScopeItemsSchema = { type: 'object', additionalProperties: { type: 'string' } } as const; | ||
@@ -179,6 +199,13 @@ const rbacConfigSchema = { | ||
properties: { | ||
defaults: 'rbacScopeItemsSchema', | ||
cms: rbacScopeItemsSchema, | ||
content: { | ||
type: 'object', | ||
properties: { | ||
'**': rbacScopeItemsSchema, | ||
}, | ||
additionalProperties: rbacScopeItemsSchema, | ||
}, | ||
}, | ||
additionalProperties: rbacScopeItemsSchema, | ||
} as NodeType; | ||
} as const; | ||
@@ -259,2 +286,3 @@ const graviteeAdapterConfigSchema = { | ||
required: ['adapters'], | ||
additionalProperties: false, | ||
properties: { | ||
@@ -266,3 +294,3 @@ adapters: { | ||
}, | ||
} as NodeType; | ||
} as const; | ||
@@ -291,4 +319,5 @@ const i18ConfigSchema = { | ||
}, | ||
required: ['defaultLocale', 'locales'], | ||
} as NodeType; | ||
additionalProperties: false, | ||
required: ['defaultLocale'], | ||
} as const; | ||
@@ -305,21 +334,9 @@ const responseHeaderSchema = { | ||
export const PortalConfigNodeTypes: Record<string, NodeType> = { | ||
seoConfigSchema, | ||
rbacConfigSchema, | ||
rbacScopeItemsSchema, | ||
ssoConfigSchema, | ||
devOnboardingConfigSchema, | ||
i18ConfigSchema, | ||
redirectsConfigSchema, | ||
redirectConfigSchema, | ||
// TODO: Extract other types that need to be linted in the config | ||
}; | ||
export const redoclyConfigSchema = { | ||
const redoclyConfigSchema = { | ||
type: 'object', | ||
properties: { | ||
licenseKey: { type: 'string' }, | ||
redirects: 'redirectsConfigSchema', | ||
seo: 'seoConfigSchema', | ||
rbac: 'rbacConfigSchema', | ||
redirects: redirectsConfigSchema, | ||
seo: seoConfigSchema, | ||
rbac: rbacConfigSchema, | ||
responseHeaders: { | ||
@@ -346,36 +363,69 @@ type: 'object', | ||
}, | ||
sso: 'ssoConfigSchema', | ||
developerOnboarding: 'devOnboardingConfigSchema', | ||
i18n: 'i18ConfigSchema', | ||
ssoOnPrem: ssoOnPremConfigSchema, | ||
sso: ssoConfigSchema, | ||
residency: { type: 'string' }, | ||
developerOnboarding: devOnboardingConfigSchema, | ||
i18n: i18ConfigSchema, | ||
metadata: metadataConfigSchema, | ||
}, | ||
default: {}, | ||
} as NodeType; | ||
export const environmentSchema = { | ||
oneOf: [ | ||
{ ...redoclyConfigSchema, additionalProperties: false }, | ||
{ | ||
type: 'object', | ||
properties: { | ||
$ref: { type: 'string' }, | ||
ignore: { | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
}, | ||
required: ['$ref'], | ||
additionalProperties: false, | ||
}, | ||
], | ||
theme: themeConfigSchema, | ||
}, | ||
default: { redirects: {} }, | ||
additionalProperties: true, | ||
} as const; | ||
const environmentSchema = { | ||
...redoclyConfigSchema, | ||
additionalProperties: false, | ||
} as const; | ||
export const rootRedoclyConfigSchema = { | ||
...redoclyConfigSchema, | ||
properties: { | ||
plugins: { | ||
type: 'array', | ||
items: { type: 'string' }, | ||
}, | ||
...redoclyConfigSchema.properties, | ||
env: { | ||
type: 'object', | ||
properties: {}, | ||
additionalProperties: environmentSchema, | ||
additionalProperties: environmentSchema, // TODO: if we want full validation we need to override apis, theme and the root | ||
}, | ||
}, | ||
default: {}, | ||
required: ['redirects'], | ||
additionalProperties: false, | ||
} as const; | ||
export type RedoclyConfig<T = ThemeConfig> = FromSchema<typeof rootRedoclyConfigSchema> & { | ||
theme?: T; | ||
}; | ||
export type RedirectConfig = FromSchema<typeof redirectConfigSchema>; | ||
export type RedirectsConfig = FromSchema<typeof redirectsConfigSchema>; | ||
export type AuthProviderConfig = FromSchema<typeof authProviderConfigSchema>; | ||
export type BasicAuthProviderConfig = FromSchema<typeof basicAuthProviderConfigSchema>; | ||
export type OidcProviderConfig = FromSchema<typeof oidcProviderConfigSchema>; | ||
export type Saml2ProviderConfig = FromSchema<typeof saml2ProviderConfigSchema>; | ||
export type SeoConfig = FromSchema<typeof seoConfigSchema>; | ||
export type RbacConfig = FromSchema<typeof rbacConfigSchema>; | ||
export type RbacScopeItems = FromSchema<typeof rbacScopeItemsSchema>; | ||
export type OidcIssuerMetadata = FromSchema<typeof oidcIssuerMetadataSchema>; | ||
export type DevOnboardingAdapterConfig = FromSchema<typeof devOnboardingAdapterConfigSchema>; | ||
export type GraviteeAdapterConfig = FromSchema<typeof graviteeAdapterConfigSchema>; | ||
export type ApigeeAdapterConfig = FromSchema< | ||
typeof apigeeXAdapterConfigSchema | typeof apigeeEdgeAdapterConfigSchema | ||
>; | ||
export type ApigeeAdapterAuthOauth2 = FromSchema<typeof apigeeAdapterAuthOauth2Schema>; | ||
export type ApigeeAdapterAuthServiceAccount = FromSchema< | ||
typeof apigeeAdapterAuthServiceAccountSchema | ||
>; | ||
export type SsoConfig = FromSchema<typeof ssoOnPremConfigSchema>; | ||
export type I18nConfig = FromSchema<typeof i18ConfigSchema>; | ||
export type ApiConfig = FromSchema<typeof apiConfigSchema>; |
@@ -1,7 +0,9 @@ | ||
import { rootRedoclyConfigSchema, apiConfigSchema } from './portal-config-schema'; | ||
import { themeConfigSchema } from './theme-config'; | ||
import { NodeType, listOf } from '.'; | ||
import { rootRedoclyConfigSchema } from './portal-config-schema'; | ||
import { listOf } from '.'; | ||
import { omitObjectProps, pickObjectProps, isCustomRuleId } from '../utils'; | ||
import { PortalConfigNodeTypes } from './portal-config-schema'; | ||
import { getNodeTypesFromJSONSchema } from './json-schema-adapter'; | ||
import type { NodeType } from '.'; | ||
import type { JSONSchema } from 'json-schema-to-ts'; | ||
const builtInCommonRules = [ | ||
@@ -100,4 +102,3 @@ 'spec', | ||
const nodeTypesList = [ | ||
'any', | ||
const oas2NodeTypesList = [ | ||
'Root', | ||
@@ -107,2 +108,44 @@ 'Tag', | ||
'ExternalDocs', | ||
'SecurityRequirement', | ||
'SecurityRequirementList', | ||
'Info', | ||
'Contact', | ||
'License', | ||
'Paths', | ||
'PathItem', | ||
'Parameter', | ||
'ParameterList', | ||
'ParameterItems', | ||
'Operation', | ||
'Example', | ||
'ExamplesMap', | ||
'Examples', | ||
'Header', | ||
'Responses', | ||
'Response', | ||
'Schema', | ||
'Xml', | ||
'SchemaProperties', | ||
'NamedSchemas', | ||
'NamedResponses', | ||
'NamedParameters', | ||
'NamedSecuritySchemes', | ||
'SecurityScheme', | ||
'TagGroup', | ||
'TagGroups', | ||
'EnumDescriptions', | ||
'Logo', | ||
'XCodeSample', | ||
'XCodeSampleList', | ||
'XServer', | ||
'XServerList', | ||
] as const; | ||
export type Oas2NodeType = typeof oas2NodeTypesList[number]; | ||
const oas3NodeTypesList = [ | ||
'Root', | ||
'Tag', | ||
'TagList', | ||
'ExternalDocs', | ||
'Server', | ||
@@ -158,9 +201,30 @@ 'ServerList', | ||
'SecurityScheme', | ||
'TagGroup', | ||
'TagGroups', | ||
'EnumDescriptions', | ||
'Logo', | ||
'XCodeSample', | ||
'XCodeSampleList', | ||
'XUsePkce', | ||
'WebhooksMap', | ||
'SpecExtension', | ||
'Message', | ||
]; | ||
] as const; | ||
export type Oas3NodeType = typeof oas3NodeTypesList[number]; | ||
const oas3_1NodeTypesList = [ | ||
'Root', | ||
'Schema', | ||
'SchemaProperties', | ||
'Info', | ||
'License', | ||
'Components', | ||
'NamedPathItems', | ||
'SecurityScheme', | ||
'Operation', | ||
] as const; | ||
export type Oas3_1NodeType = typeof oas3_1NodeTypesList[number]; | ||
const asyncNodeTypesList = ['Message'] as const; | ||
const ConfigStyleguide: NodeType = { | ||
@@ -192,18 +256,9 @@ properties: { | ||
const RootConfigStyleguide: NodeType = { | ||
const createConfigRoot = (nodeTypes: Record<string, NodeType>): NodeType => ({ | ||
...nodeTypes.rootRedoclyConfigSchema, | ||
properties: { | ||
plugins: { | ||
type: 'array', | ||
items: { type: 'string' }, | ||
}, | ||
...nodeTypes.rootRedoclyConfigSchema.properties, | ||
...ConfigStyleguide.properties, | ||
}, | ||
}; | ||
const ConfigRoot: NodeType = { | ||
properties: { | ||
...rootRedoclyConfigSchema.properties, | ||
...RootConfigStyleguide.properties, | ||
apis: 'ConfigApis', | ||
theme: 'ConfigRootTheme', | ||
apis: 'ConfigApis', // Override apis with internal format | ||
theme: 'ConfigRootTheme', // Override theme with internal format | ||
'features.openapi': 'ConfigReferenceDocs', // deprecated | ||
@@ -227,3 +282,3 @@ 'features.mockServer': 'ConfigMockServer', // deprecated | ||
}, | ||
}; | ||
}); | ||
@@ -235,6 +290,6 @@ const ConfigApis: NodeType = { | ||
const ConfigApisProperties: NodeType = { | ||
const createConfigApisProperties = (nodeTypes: Record<string, NodeType>): NodeType => ({ | ||
...nodeTypes['rootRedoclyConfigSchema.apis_additionalProperties'], | ||
properties: { | ||
...apiConfigSchema.properties, | ||
root: { type: 'string' }, | ||
...nodeTypes['rootRedoclyConfigSchema.apis_additionalProperties']?.properties, | ||
labels: { | ||
@@ -249,3 +304,2 @@ type: 'array', | ||
'features.mockServer': 'ConfigMockServer', // deprecated | ||
theme: 'ConfigRootTheme', | ||
files: { | ||
@@ -258,4 +312,3 @@ type: 'array', | ||
}, | ||
required: ['root'], | ||
}; | ||
}); | ||
@@ -273,9 +326,9 @@ const ConfigHTTP: NodeType = { | ||
const ConfigRootTheme: NodeType = { | ||
const createConfigRootTheme = (nodeTypes: Record<string, NodeType>): NodeType => ({ | ||
...nodeTypes['rootRedoclyConfigSchema.theme'], | ||
properties: { | ||
...themeConfigSchema.properties, | ||
openapi: 'ConfigReferenceDocs', | ||
mockServer: 'ConfigMockServer', | ||
...nodeTypes['rootRedoclyConfigSchema.theme']?.properties, | ||
openapi: 'ConfigReferenceDocs', // Override theme.openapi with internal format | ||
}, | ||
}; | ||
}); | ||
@@ -314,3 +367,14 @@ const Rules: NodeType = { | ||
properties: { | ||
type: { enum: nodeTypesList }, | ||
type: { | ||
enum: [ | ||
...new Set([ | ||
'any', | ||
...oas2NodeTypesList, | ||
...oas3NodeTypesList, | ||
...oas3_1NodeTypesList, | ||
...asyncNodeTypesList, | ||
'SpecExtension', | ||
]), | ||
], | ||
}, | ||
property: (value: unknown) => { | ||
@@ -876,4 +940,5 @@ if (Array.isArray(value)) { | ||
const ConfigReferenceDocs: NodeType = { | ||
// TODO: partially invalid @Viacheslav | ||
properties: { | ||
theme: 'ConfigTheme', | ||
theme: 'ConfigTheme', // TODO: deprecated @Viacheslav | ||
corsProxyUrl: { type: 'string' }, | ||
@@ -1009,8 +1074,18 @@ ctrlFHijack: { type: 'boolean' }, | ||
export const ConfigTypes: Record<string, NodeType> = { | ||
export const createConfigTypes = (extraSchemas: JSONSchema) => { | ||
// Create types based on external schemas | ||
const nodeTypes = getNodeTypesFromJSONSchema('rootRedoclyConfigSchema', extraSchemas); | ||
return { | ||
...CoreConfigTypes, | ||
ConfigRoot: createConfigRoot(nodeTypes), | ||
ConfigApisProperties: createConfigApisProperties(nodeTypes), | ||
ConfigRootTheme: createConfigRootTheme(nodeTypes), | ||
...nodeTypes, | ||
}; | ||
}; | ||
const CoreConfigTypes: Record<string, NodeType> = { | ||
Assert, | ||
ConfigRoot, | ||
ConfigApis, | ||
ConfigApisProperties, | ||
RootConfigStyleguide, | ||
ConfigStyleguide, | ||
@@ -1025,3 +1100,2 @@ ConfigReferenceDocs, | ||
ConfigTheme, | ||
ConfigRootTheme, | ||
AssertDefinition, | ||
@@ -1076,3 +1150,4 @@ ThemeColors, | ||
AssertionDefinitionSubject, | ||
...PortalConfigNodeTypes, | ||
}; | ||
export const ConfigTypes: Record<string, NodeType> = createConfigTypes(rootRedoclyConfigSchema); |
@@ -0,1 +1,3 @@ | ||
import type { FromSchema } from 'json-schema-to-ts'; | ||
const logoConfigSchema = { | ||
@@ -71,2 +73,7 @@ type: 'object', | ||
}, | ||
partialsFolders: { | ||
type: 'array', | ||
items: { type: 'string' }, | ||
default: ['_partials'], | ||
}, | ||
lastUpdatedBlock: { | ||
@@ -80,3 +87,3 @@ type: 'object', | ||
}, | ||
locale: { type: 'string', default: 'en-US' }, | ||
locale: { type: 'string' }, | ||
...hideConfigSchema.properties, | ||
@@ -192,2 +199,17 @@ }, | ||
const productGoogleAnalyticsConfigSchema = { | ||
type: 'object', | ||
properties: { | ||
includeInDevelopment: { type: 'boolean' }, | ||
trackingId: { type: 'string' }, | ||
conversionId: { type: 'string' }, | ||
floodlightId: { type: 'string' }, | ||
optimizeId: { type: 'string' }, | ||
exclude: { type: 'array', items: { type: 'string' } }, | ||
}, | ||
additionalProperties: false, | ||
required: ['trackingId'], | ||
} as const; | ||
const googleAnalyticsConfigSchema = { | ||
@@ -209,2 +231,8 @@ type: 'object', | ||
cookieExpires: { type: 'number' }, | ||
// All enabled tracking configs | ||
trackers: { | ||
type: 'object', | ||
additionalProperties: productGoogleAnalyticsConfigSchema, | ||
}, | ||
}, | ||
@@ -231,2 +259,3 @@ additionalProperties: false, | ||
directory: { type: 'string' }, | ||
disconnect: { type: 'boolean', default: false }, | ||
group: { type: 'string' }, | ||
@@ -272,3 +301,3 @@ label: { type: 'string' }, | ||
additionalProperties: false, | ||
required: ['name', 'icon', 'folder'], | ||
required: ['name', 'folder'], | ||
} as const; | ||
@@ -296,2 +325,3 @@ | ||
parentFilter: { type: 'string' }, | ||
valuesMapping: { type: 'object', additionalProperties: { type: 'string' } }, | ||
missingCategoryName: { type: 'string' }, | ||
@@ -306,5 +336,5 @@ missingCategoryNameTranslationKey: { type: 'string' }, | ||
additionalProperties: true, | ||
required: ['levels'], | ||
required: [], | ||
properties: { | ||
failBuildIfBelowMinimum: { type: 'boolean', default: false }, | ||
ignoreNonCompliant: { type: 'boolean', default: false }, | ||
teamMetadataProperty: { | ||
@@ -325,2 +355,3 @@ type: 'object', | ||
name: { type: 'string' }, | ||
color: { type: 'string' }, | ||
extends: { type: 'array', items: { type: 'string' } }, | ||
@@ -330,3 +361,3 @@ rules: { | ||
additionalProperties: { | ||
type: ['object', 'string'], | ||
oneOf: [{ type: 'string' }, { type: 'object' }], | ||
}, | ||
@@ -433,8 +464,2 @@ }, | ||
}, | ||
seo: { | ||
type: 'object', | ||
properties: { | ||
title: { type: 'string' }, | ||
}, | ||
}, | ||
scripts: { | ||
@@ -458,3 +483,3 @@ type: 'object', | ||
type: 'string', | ||
enum: ['rating', 'sentiment', 'comment', 'reasons'], | ||
enum: ['rating', 'sentiment', 'comment', 'reasons', 'mood', 'scale'], | ||
default: 'sentiment', | ||
@@ -467,11 +492,23 @@ }, | ||
submitText: { type: 'string' }, | ||
max: { type: 'number' }, | ||
buttonText: { type: 'string' }, | ||
multi: { type: 'boolean' }, | ||
component: { | ||
type: 'string', | ||
enum: ['radio', 'checkbox'], | ||
default: 'checkbox', | ||
}, | ||
items: { type: 'array', items: { type: 'string' }, minItems: 1 }, | ||
leftScaleLabel: { type: 'string' }, | ||
rightScaleLabel: { type: 'string' }, | ||
reasons: { | ||
type: 'object', | ||
properties: { | ||
enable: { type: 'boolean', default: true }, | ||
multi: { type: 'boolean' }, | ||
hide: { | ||
type: 'boolean', | ||
default: false, | ||
}, | ||
component: { | ||
type: 'string', | ||
enum: ['radio', 'checkbox'], | ||
default: 'checkbox', | ||
}, | ||
label: { type: 'string' }, | ||
@@ -485,6 +522,12 @@ items: { type: 'array', items: { type: 'string' } }, | ||
properties: { | ||
enable: { type: 'boolean', default: true }, | ||
hide: { | ||
type: 'boolean', | ||
default: false, | ||
}, | ||
label: { type: 'string' }, | ||
likeLabel: { type: 'string' }, | ||
dislikeLabel: { type: 'string' }, | ||
satisfiedLabel: { type: 'string' }, | ||
neutralLabel: { type: 'string' }, | ||
dissatisfiedLabel: { type: 'string' }, | ||
}, | ||
@@ -542,3 +585,3 @@ additionalProperties: false, | ||
properties: { | ||
text: { type: 'string', default: 'Next to {label}' }, | ||
text: { type: 'string', default: 'Next to {{label}}' }, | ||
...hideConfigSchema.properties, | ||
@@ -552,3 +595,3 @@ }, | ||
properties: { | ||
text: { type: 'string', default: 'Back to {label}' }, | ||
text: { type: 'string', default: 'Back to {{label}}' }, | ||
...hideConfigSchema.properties, | ||
@@ -566,3 +609,3 @@ }, | ||
properties: { | ||
controlsStyle: { type: 'string', default: 'icon' }, | ||
elementFormat: { type: 'string', default: 'icon' }, | ||
copy: { | ||
@@ -579,6 +622,9 @@ type: 'object', | ||
properties: { | ||
tooltipText: { type: 'string' }, | ||
buttonText: { type: 'string' }, | ||
label: { type: 'string' }, | ||
...hideConfigSchema.properties, | ||
}, | ||
additionalProperties: false, | ||
default: { hide: true }, | ||
default: { hide: false }, | ||
}, | ||
@@ -606,3 +652,3 @@ expand: { | ||
markdown: markdownConfigSchema, | ||
openapi: { type: 'object', additionalProperties: true }, | ||
openapi: { type: 'object', additionalProperties: true }, // TODO: put the real schema here @Viacheslav | ||
graphql: { type: 'object', additionalProperties: true }, | ||
@@ -693,2 +739,8 @@ analytics: { | ||
breadcrumbs: themeConfigSchema.properties.breadcrumbs, | ||
analytics: { | ||
type: 'object', | ||
properties: { | ||
ga: productGoogleAnalyticsConfigSchema, | ||
}, | ||
}, | ||
}, | ||
@@ -699,6 +751,52 @@ additionalProperties: true, | ||
export enum ScorecardStatus { | ||
BelowMinimum = 'Below minimum', | ||
Highest = 'Highest', | ||
Minimum = 'Minimum', | ||
} | ||
export type ThemeConfig = FromSchema<typeof themeConfigSchema>; | ||
// TODO: cannot export as it relies on external types | ||
// export type ThemeUIConfig = ThemeConfig & { | ||
// auth?: { | ||
// // used by portal dev login emulator | ||
// idpsInfo?: { | ||
// idpId: string; | ||
// type: string; // AuthProviderType | ||
// title: string | undefined; | ||
// }[]; | ||
// devLogin?: boolean; | ||
// loginUrls?: Record<string, string>; | ||
// }; | ||
// search?: { | ||
// shortcuts?: string[]; | ||
// suggestedPages?: any[]; | ||
// }; | ||
// breadcrumbs?: { | ||
// prefixItems?: ResolvedNavLinkItem[]; | ||
// }; | ||
// products?: { | ||
// [key: string]: ProductUiConfig; | ||
// }; | ||
// }; | ||
export type ProductConfig = FromSchema<typeof productConfigSchema>; | ||
export type ProductGoogleAnalyticsConfig = FromSchema<typeof productGoogleAnalyticsConfigSchema>; | ||
// TODO: cannot export as it relies on external types | ||
// export type ProductThemeOverrideConfig = Pick< | ||
// ThemeUIConfig, | ||
// 'logo' | 'navbar' | 'footer' | 'sidebar' | 'search' | 'codeSnippet' | 'breadcrumbs' | ||
// > & { analytics?: { ga?: ProductGoogleAnalyticsConfig } }; | ||
// export type ProductUiConfig = ProductConfig & { | ||
// slug: string; | ||
// link: string; | ||
// [REDOCLY_TEAMS_RBAC]?: { [key: string]: string }; | ||
// themeOverride?: ProductThemeOverrideConfig; | ||
// }; | ||
export type MarkdownConfig = FromSchema<typeof markdownConfigSchema>; | ||
export type AmplitudeAnalyticsConfig = FromSchema<typeof amplitudeAnalyticsConfigSchema>; | ||
export type RudderstackAnalyticsConfig = FromSchema<typeof rudderstackAnalyticsConfigSchema>; | ||
export type SegmentAnalyticsConfig = FromSchema<typeof segmentAnalyticsConfigSchema>; | ||
export type GtmAnalyticsConfig = FromSchema<typeof gtmAnalyticsConfigSchema>; | ||
export type GoogleAnalyticsConfig = FromSchema<typeof googleAnalyticsConfigSchema>; | ||
export type CatalogConfig = FromSchema<typeof catalogSchema>; | ||
export type CatalogFilterConfig = FromSchema<typeof catalogFilterSchema>; | ||
export type ScorecardConfig = FromSchema<typeof scorecardConfigSchema>; |
@@ -40,7 +40,7 @@ import * as fs from 'fs'; | ||
export function isPlainObject(value: any): value is object { | ||
export function isPlainObject(value: any): value is Record<string, unknown> { | ||
return value !== null && typeof value === 'object' && !Array.isArray(value); | ||
} | ||
export function isEmptyObject(value: any): value is object { | ||
export function isEmptyObject(value: any): value is Record<string, unknown> { | ||
return isPlainObject(value) && Object.keys(value).length === 0; | ||
@@ -47,0 +47,0 @@ } |
@@ -362,3 +362,3 @@ import { SpecExtension } from './types'; | ||
} | ||
if (from.items) { | ||
if (from.items && typeof from.items !== 'function') { | ||
if (from.items === to) { | ||
@@ -365,0 +365,0 @@ addWeakFromStack(ruleConf, stack); |
@@ -274,4 +274,10 @@ import { Location, isRef } from './ref-utils'; | ||
if (itemsType !== undefined) { | ||
const isTypeAFunction = typeof itemsType === 'function'; | ||
for (let i = 0; i < resolvedNode.length; i++) { | ||
walkNode(resolvedNode[i], itemsType, resolvedLocation.child([i]), resolvedNode, i); | ||
const itemType = isTypeAFunction | ||
? itemsType(resolvedNode[i], resolvedLocation.child([i]).absolutePointer) | ||
: itemsType; | ||
if (isNamedType(itemType)) { | ||
walkNode(resolvedNode[i], itemType, resolvedLocation.child([i]), resolvedNode, i); | ||
} | ||
} | ||
@@ -278,0 +284,0 @@ } |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3231405
564
56723