chore: use redocly-config package for external config schemas (#1471)

This commit is contained in:
Andrew Tatomyr
2024-03-14 10:19:05 +02:00
committed by GitHub
parent c8bca54e7b
commit aed098209d
8 changed files with 22 additions and 1261 deletions

View File

@@ -580,21 +580,7 @@ Expected type \`string\` but got \`integer\`.
Error was generated by the configuration spec rule.
[41] .redocly.yaml:192:13 at #/theme/footer/items/0/items/0/href
Property \`href\` is not expected here.
190 | items:
191 | - label: Terms of Use
192 | href: 'https://test.com/' # Not expected
| ^^^^
193 |
194 | markdown:
Error was generated by the configuration spec rule.
[42] .redocly.yaml:141:12 at #/env/some-env/mockServer/off
[41] .redocly.yaml:141:12 at #/env/some-env/mockServer/off
Expected type \`boolean\` but got \`string\`.
@@ -608,7 +594,7 @@ Expected type \`boolean\` but got \`string\`.
Error was generated by the configuration spec rule.
[43] .redocly.yaml:142:7 at #/env/some-env/mockServer/not-expected
[42] .redocly.yaml:142:7 at #/env/some-env/mockServer/not-expected
Property \`not-expected\` is not expected here.
@@ -622,7 +608,7 @@ Property \`not-expected\` is not expected here.
Error was generated by the configuration spec rule.
[44] .redocly.yaml:144:7 at #/env/some-env/apis/no-root
[43] .redocly.yaml:144:7 at #/env/some-env/apis/no-root
The field \`root\` must be present on this level.
@@ -636,7 +622,7 @@ The field \`root\` must be present on this level.
Error was generated by the configuration spec rule.
[45] .redocly.yaml:148:15 at #/env/some-env/apis/wrong-root/root
[44] .redocly.yaml:148:15 at #/env/some-env/apis/wrong-root/root
Expected type \`string\` but got \`integer\`.
@@ -650,7 +636,7 @@ Expected type \`string\` but got \`integer\`.
Error was generated by the configuration spec rule.
[46] env.yaml:2:5 at #/ignore/0
[45] env.yaml:2:5 at #/ignore/0
Expected type \`string\` but got \`integer\`.
@@ -665,7 +651,7 @@ referenced from .redocly.yaml:151:5 at #/env/referenced-env
Error was generated by the configuration spec rule.
❌ Your config has 46 errors.
❌ Your config has 45 errors.
`;

12
package-lock.json generated
View File

@@ -3501,6 +3501,11 @@
"resolved": "packages/cli",
"link": true
},
"node_modules/@redocly/config": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.1.1.tgz",
"integrity": "sha512-nliB8VES4OED+HtfYf0Z4U72i2fj++xfqkDcYEinU3bjDERL2gvUgeqCUoavEmLU39+CKo1fV7saTSze6trBNA=="
},
"node_modules/@redocly/openapi-core": {
"resolved": "packages/core",
"link": true
@@ -12944,6 +12949,7 @@
"license": "MIT",
"dependencies": {
"@redocly/ajv": "^8.11.0",
"@redocly/config": "^0.1.1",
"colorette": "^1.2.0",
"js-levenshtein": "^1.1.6",
"js-yaml": "^4.1.0",
@@ -15598,10 +15604,16 @@
}
}
},
"@redocly/config": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.1.1.tgz",
"integrity": "sha512-nliB8VES4OED+HtfYf0Z4U72i2fj++xfqkDcYEinU3bjDERL2gvUgeqCUoavEmLU39+CKo1fV7saTSze6trBNA=="
},
"@redocly/openapi-core": {
"version": "file:packages/core",
"requires": {
"@redocly/ajv": "^8.11.0",
"@redocly/config": "^0.1.1",
"@types/js-levenshtein": "^1.1.0",
"@types/js-yaml": "^4.0.3",
"@types/lodash.isequal": "^4.5.5",

View File

@@ -35,6 +35,7 @@
],
"dependencies": {
"@redocly/ajv": "^8.11.0",
"@redocly/config": "^0.1.1",
"colorette": "^1.2.0",
"js-levenshtein": "^1.1.6",
"js-yaml": "^4.1.0",

View File

@@ -6,7 +6,7 @@ import { BaseResolver } from '../resolve';
import { loadConfig } from '../config/load';
import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../__tests__/utils';
import { detectSpec } from '../oas-types';
import { themeConfigSchema } from '../types/theme-config';
import { rootRedoclyConfigSchema } from '@redocly/config';
import { createConfigTypes } from '../types/redocly-yaml';
const testPortalConfig = parseYamlToDocument(
@@ -1124,20 +1124,6 @@ describe('lint', () => {
"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": [
@@ -1204,7 +1190,7 @@ describe('lint', () => {
document,
externalConfigTypes: createConfigTypes({
type: 'object',
properties: { theme: themeConfigSchema },
properties: { theme: rootRedoclyConfigSchema.properties.theme },
additionalProperties: false,
}),
});
@@ -1379,20 +1365,6 @@ describe('lint', () => {
"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": [

View File

@@ -243,16 +243,3 @@ export type RulesFields =
| 'oas3_0Decorators'
| 'oas3_1Decorators'
| 'async2Decorators';
export enum AuthProviderType {
OIDC = 'OIDC',
SAML2 = 'SAML2',
BASIC = 'BASIC',
}
export enum ApigeeDevOnboardingIntegrationAuthType {
SERVICE_ACCOUNT = 'SERVICE_ACCOUNT',
OAUTH2 = 'OAUTH2',
}
export const DEFAULT_TEAM_CLAIM_NAME = 'https://redocly.com/sso/teams';

View File

@@ -1,416 +0,0 @@
import {
ApigeeDevOnboardingIntegrationAuthType,
AuthProviderType,
DEFAULT_TEAM_CLAIM_NAME,
} from '../config';
import { themeConfigSchema } from './theme-config';
import type { FromSchema } from 'json-schema-to-ts';
import type { ThemeConfig } from './theme-config';
const oidcIssuerMetadataSchema = {
type: 'object',
properties: {
end_session_endpoint: { type: 'string' },
token_endpoint: { type: 'string' },
authorization_endpoint: { type: 'string' },
jwks_uri: { type: 'string' },
},
required: ['token_endpoint', 'authorization_endpoint'],
additionalProperties: true,
} as const;
const oidcProviderConfigSchema = {
type: 'object',
properties: {
type: { type: 'string', const: AuthProviderType.OIDC },
title: { type: 'string' },
pkce: { type: 'boolean', default: false },
configurationUrl: { type: 'string', minLength: 1 },
configuration: oidcIssuerMetadataSchema,
clientId: { type: 'string', minLength: 1 },
clientSecret: { type: 'string', minLength: 0 },
teamsClaimName: { type: 'string' },
teamsClaimMap: { type: 'object', additionalProperties: { type: 'string' } },
defaultTeams: { type: 'array', items: { type: 'string' } },
scopes: { type: 'array', items: { type: 'string' } },
tokenExpirationTime: { type: 'number' },
authorizationRequestCustomParams: { type: 'object', additionalProperties: { type: 'string' } },
tokenRequestCustomParams: { type: 'object', additionalProperties: { type: 'string' } },
audience: { type: 'array', items: { type: 'string' } },
},
required: ['type', 'clientId'],
oneOf: [{ required: ['configurationUrl'] }, { required: ['configuration'] }],
additionalProperties: false,
} as const;
const saml2ProviderConfigSchema = {
type: 'object',
properties: {
type: { type: 'string', const: AuthProviderType.SAML2 },
title: { type: 'string' },
issuerId: { type: 'string' },
entityId: { type: 'string' },
ssoUrl: { type: 'string' },
x509PublicCert: { type: 'string' },
teamsAttributeName: { type: 'string', default: DEFAULT_TEAM_CLAIM_NAME },
teamsAttributeMap: { type: 'object', additionalProperties: { type: 'string' } },
defaultTeams: { type: 'array', items: { type: 'string' } },
},
additionalProperties: false,
required: ['type', 'issuerId', 'ssoUrl', 'x509PublicCert'],
} as const;
const basicAuthProviderConfigSchema = {
type: 'object',
properties: {
type: { type: 'string', const: AuthProviderType.BASIC },
title: { type: 'string' },
credentials: {
type: 'array',
items: {
type: 'object',
properties: {
username: { type: 'string' },
password: { type: 'string' },
passwordHash: { type: 'string' },
teams: { type: 'array', items: { type: 'string' } },
},
required: ['username'],
additionalProperties: false,
},
},
},
required: ['type', 'credentials'],
additionalProperties: false,
} as const;
const authProviderConfigSchema = {
oneOf: [oidcProviderConfigSchema, saml2ProviderConfigSchema, basicAuthProviderConfigSchema],
discriminator: { propertyName: 'type' },
} as const;
const ssoOnPremConfigSchema = {
type: 'object',
additionalProperties: authProviderConfigSchema,
} 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 = {
type: 'object',
properties: {
to: { type: 'string' },
type: { type: 'number', default: 301 },
},
additionalProperties: false,
} as const;
const redirectsConfigSchema = {
type: 'object',
additionalProperties: redirectConfigSchema,
default: {},
} as const;
const apiConfigSchema = {
type: 'object',
properties: {
root: { type: 'string' },
output: { type: 'string', pattern: '(.ya?ml|.json)$' },
rbac: { type: 'object', additionalProperties: true },
theme: {
type: 'object',
properties: {
openapi: themeConfigSchema.properties.openapi,
graphql: themeConfigSchema.properties.graphql,
},
additionalProperties: false,
},
title: { type: 'string' },
metadata: { type: 'object', additionalProperties: true },
rules: { type: 'object', additionalProperties: true },
decorators: { type: 'object', additionalProperties: true },
},
required: ['root'],
} as const;
const metadataConfigSchema = {
type: 'object',
additionalProperties: true,
} as const;
const seoConfigSchema = {
type: 'object',
properties: {
title: { type: 'string' },
description: { type: 'string' },
siteUrl: { type: 'string' },
image: { type: 'string' },
keywords: {
oneOf: [{ type: 'array', items: { type: 'string' } }, { type: 'string' }],
},
lang: { type: 'string' },
jsonLd: { type: 'object' },
meta: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
content: { type: 'string' },
},
required: ['name', 'content'],
additionalProperties: false,
},
},
},
additionalProperties: false,
} as const;
const rbacScopeItemsSchema = { type: 'object', additionalProperties: { type: 'string' } } as const;
const rbacConfigSchema = {
type: 'object',
properties: {
cms: rbacScopeItemsSchema,
content: {
type: 'object',
properties: {
'**': rbacScopeItemsSchema,
},
additionalProperties: rbacScopeItemsSchema,
},
},
additionalProperties: rbacScopeItemsSchema,
} as const;
const graviteeAdapterConfigSchema = {
type: 'object',
properties: {
type: { type: 'string', const: 'GRAVITEE' },
apiBaseUrl: { type: 'string' },
env: { type: 'string' },
allowApiProductsOutsideCatalog: { type: 'boolean', default: false },
stage: { type: 'string', default: 'non-production' },
auth: { type: 'object', properties: { static: { type: 'string' } } },
},
additionalProperties: false,
required: ['type', 'apiBaseUrl'],
} as const;
const apigeeAdapterAuthOauth2Schema = {
type: 'object',
properties: {
type: { type: 'string', const: ApigeeDevOnboardingIntegrationAuthType.OAUTH2 },
tokenEndpoint: { type: 'string' },
clientId: { type: 'string' },
clientSecret: { type: 'string' },
},
additionalProperties: false,
required: ['type', 'tokenEndpoint', 'clientId', 'clientSecret'],
} as const;
const apigeeAdapterAuthServiceAccountSchema = {
type: 'object',
properties: {
type: { type: 'string', const: ApigeeDevOnboardingIntegrationAuthType.SERVICE_ACCOUNT },
serviceAccountEmail: { type: 'string' },
serviceAccountPrivateKey: { type: 'string' },
},
additionalProperties: false,
required: ['type', 'serviceAccountEmail', 'serviceAccountPrivateKey'],
} as const;
const apigeeXAdapterConfigSchema = {
type: 'object',
properties: {
type: { type: 'string', const: 'APIGEE_X' },
apiUrl: { type: 'string' },
stage: { type: 'string', default: 'non-production' },
organizationName: { type: 'string' },
ignoreApiProducts: { type: 'array', items: { type: 'string' } },
allowApiProductsOutsideCatalog: { type: 'boolean', default: false },
auth: {
type: 'object',
oneOf: [apigeeAdapterAuthOauth2Schema, apigeeAdapterAuthServiceAccountSchema],
discriminator: { propertyName: 'type' },
},
},
additionalProperties: false,
required: ['type', 'organizationName', 'auth'],
} as const;
const apigeeEdgeAdapterConfigSchema = {
...apigeeXAdapterConfigSchema,
properties: {
...apigeeXAdapterConfigSchema.properties,
type: { type: 'string', const: 'APIGEE_EDGE' },
},
} as const;
const devOnboardingAdapterConfigSchema = {
type: 'object',
oneOf: [apigeeXAdapterConfigSchema, apigeeEdgeAdapterConfigSchema, graviteeAdapterConfigSchema],
discriminator: { propertyName: 'type' },
} as const;
const devOnboardingConfigSchema = {
type: 'object',
required: ['adapters'],
additionalProperties: false,
properties: {
adapters: {
type: 'array',
items: devOnboardingAdapterConfigSchema,
},
},
} as const;
const i18ConfigSchema = {
type: 'object',
properties: {
defaultLocale: {
type: 'string',
},
locales: {
type: 'array',
items: {
type: 'object',
properties: {
code: {
type: 'string',
},
name: {
type: 'string',
},
},
required: ['code'],
},
},
},
additionalProperties: false,
required: ['defaultLocale'],
} as const;
const responseHeaderSchema = {
type: 'object',
properties: {
name: { type: 'string' },
value: { type: 'string' },
},
additionalProperties: false,
required: ['name', 'value'],
} as const;
const redoclyConfigSchema = {
type: 'object',
properties: {
licenseKey: { type: 'string' },
redirects: redirectsConfigSchema,
seo: seoConfigSchema,
rbac: rbacConfigSchema,
responseHeaders: {
type: 'object',
additionalProperties: {
type: 'array',
items: responseHeaderSchema,
},
},
mockServer: {
type: 'object',
properties: {
off: { type: 'boolean', default: false },
position: { type: 'string', enum: ['first', 'last', 'replace', 'off'], default: 'first' },
strictExamples: { type: 'boolean', default: false },
errorIfForcedExampleNotFound: { type: 'boolean', default: false },
description: { type: 'string' },
},
},
apis: {
type: 'object',
additionalProperties: apiConfigSchema,
},
ssoOnPrem: ssoOnPremConfigSchema,
sso: ssoConfigSchema,
residency: { type: 'string' },
developerOnboarding: devOnboardingConfigSchema,
i18n: i18ConfigSchema,
metadata: metadataConfigSchema,
ignore: {
type: 'array',
items: {
type: 'string',
},
},
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',
additionalProperties: environmentSchema, // TODO: if we want full validation we need to override apis, theme and the root
},
},
default: {},
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>;

View File

@@ -1,4 +1,4 @@
import { rootRedoclyConfigSchema } from './portal-config-schema';
import { rootRedoclyConfigSchema } from '@redocly/config';
import { listOf } from '.';
import { omitObjectProps, pickObjectProps, isCustomRuleId } from '../utils';
import { getNodeTypesFromJSONSchema } from './json-schema-adapter';

View File

@@ -1,781 +0,0 @@
import type { FromSchema } from 'json-schema-to-ts';
const logoConfigSchema = {
type: 'object',
properties: {
image: { type: 'string' },
srcSet: { type: 'string' },
altText: { type: 'string' },
link: { type: 'string' },
favicon: { type: 'string' },
},
additionalProperties: false,
} as const;
const hideConfigSchema = {
type: 'object',
properties: {
hide: { type: 'boolean' },
},
additionalProperties: false,
} as const;
const scriptConfigSchema = {
type: 'object',
properties: {
src: { type: 'string' },
async: { type: 'boolean' },
crossorigin: { type: 'string' },
defer: { type: 'boolean' },
fetchpriority: { type: 'string' },
integrity: { type: 'string' },
module: { type: 'boolean' },
nomodule: { type: 'boolean' },
nonce: { type: 'string' },
referrerpolicy: { type: 'string' },
type: { type: 'string' },
},
required: ['src'],
additionalProperties: true,
} as const;
const linksConfigSchema = {
type: 'object',
properties: {
href: { type: 'string' },
as: { type: 'string' },
crossorigin: { type: 'string' },
fetchpriority: { type: 'string' },
hreflang: { type: 'string' },
imagesizes: { type: 'string' },
imagesrcset: { type: 'string' },
integrity: { type: 'string' },
media: { type: 'string' },
prefetch: { type: 'string' },
referrerpolicy: { type: 'string' },
rel: { type: 'string' },
sizes: { type: 'string' },
title: { type: 'string' },
type: { type: 'string' },
},
required: ['href'],
additionalProperties: true,
} as const;
const markdownConfigSchema = {
type: 'object',
properties: {
frontMatterKeysToResolve: {
type: 'array',
items: { type: 'string' },
default: ['image', 'links'],
},
partialsFolders: {
type: 'array',
items: { type: 'string' },
default: ['_partials'],
},
lastUpdatedBlock: {
type: 'object',
properties: {
format: {
type: 'string',
enum: ['timeago', 'iso', 'long', 'short'],
default: 'timeago',
},
locale: { type: 'string' },
...hideConfigSchema.properties,
},
additionalProperties: false,
default: {},
},
toc: {
type: 'object',
properties: {
header: { type: 'string', default: 'On this page' },
depth: { type: 'integer', default: 3, minimum: 1 },
...hideConfigSchema.properties,
},
additionalProperties: false,
default: {},
},
editPage: {
type: 'object',
properties: {
baseUrl: { type: 'string' },
...hideConfigSchema.properties,
},
additionalProperties: false,
default: {},
},
},
additionalProperties: false,
default: {},
} as const;
const amplitudeAnalyticsConfigSchema = {
type: 'object',
properties: {
includeInDevelopment: { type: 'boolean' },
apiKey: { type: 'string' },
head: { type: 'boolean' },
respectDNT: { type: 'boolean' },
exclude: { type: 'array', items: { type: 'string' } },
outboundClickEventName: { type: 'string' },
pageViewEventName: { type: 'string' },
amplitudeConfig: { type: 'object', additionalProperties: true },
},
additionalProperties: false,
required: ['apiKey'],
} as const;
const fullstoryAnalyticsConfigSchema = {
type: 'object',
properties: {
includeInDevelopment: { type: 'boolean' },
orgId: { type: 'string' },
},
additionalProperties: false,
required: ['orgId'],
} as const;
const heapAnalyticsConfigSchema = {
type: 'object',
properties: {
includeInDevelopment: { type: 'boolean' },
appId: { type: 'string' },
},
additionalProperties: false,
required: ['appId'],
} as const;
const rudderstackAnalyticsConfigSchema = {
type: 'object',
properties: {
includeInDevelopment: { type: 'boolean' },
writeKey: { type: 'string', minLength: 10 },
trackPage: { type: 'boolean' },
dataPlaneUrl: { type: 'string' },
controlPlaneUrl: { type: 'string' },
sdkUrl: { type: 'string' },
loadOptions: { type: 'object', additionalProperties: true },
},
additionalProperties: false,
required: ['writeKey'],
} as const;
const segmentAnalyticsConfigSchema = {
type: 'object',
properties: {
includeInDevelopment: { type: 'boolean' },
writeKey: { type: 'string', minLength: 10 },
trackPage: { type: 'boolean' },
includeTitleInPageCall: { type: 'boolean' },
host: { type: 'string' },
},
additionalProperties: false,
required: ['writeKey'],
} as const;
const gtmAnalyticsConfigSchema = {
type: 'object',
properties: {
includeInDevelopment: { type: 'boolean' },
trackingId: { type: 'string' },
gtmAuth: { type: 'string' },
gtmPreview: { type: 'string' },
defaultDataLayer: {},
dataLayerName: { type: 'string' },
enableWebVitalsTracking: { type: 'boolean' },
selfHostedOrigin: { type: 'string' },
pageViewEventName: { type: 'string' },
},
additionalProperties: false,
required: ['trackingId'],
} as const;
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 = {
type: 'object',
properties: {
includeInDevelopment: { type: 'boolean' },
trackingId: { type: 'string' },
conversionId: { type: 'string' },
floodlightId: { type: 'string' },
head: { type: 'boolean' },
respectDNT: { type: 'boolean' },
exclude: { type: 'array', items: { type: 'string' } },
optimizeId: { type: 'string' },
anonymizeIp: { type: 'boolean' },
cookieExpires: { type: 'number' },
// All enabled tracking configs
trackers: {
type: 'object',
additionalProperties: productGoogleAnalyticsConfigSchema,
},
},
additionalProperties: false,
required: ['trackingId'],
} as const;
const adobeAnalyticsConfigSchema = {
type: 'object',
properties: {
includeInDevelopment: { type: 'boolean' },
scriptUrl: { type: 'string' },
pageViewEventName: { type: 'string' },
},
additionalProperties: false,
required: ['scriptUrl'],
} as const;
const navItemSchema = {
type: 'object',
properties: {
page: { type: 'string' },
directory: { type: 'string' },
disconnect: { type: 'boolean', default: false },
group: { type: 'string' },
label: { type: 'string' },
separator: { type: 'string' },
separatorLine: { type: 'boolean' },
linePosition: {
type: 'string',
enum: ['top', 'bottom'],
default: 'top',
},
version: { type: 'string' },
menuStyle: { type: 'string', enum: ['drilldown'] },
expanded: { type: 'string', const: 'always' },
selectFirstItemOnExpand: { type: 'boolean' },
flatten: { type: 'boolean' },
linkedSidebars: {
type: 'array',
items: { type: 'string' },
},
},
} as const;
const navItemsSchema = {
type: 'array',
items: {
...navItemSchema,
properties: {
...navItemSchema.properties,
items: { type: 'array', items: navItemSchema },
},
},
} as const;
const productConfigSchema = {
type: 'object',
properties: {
name: { type: 'string' },
icon: { type: 'string' },
folder: { type: 'string' },
},
additionalProperties: false,
required: ['name', 'folder'],
} as const;
const suggestedPageSchema = {
type: 'object',
properties: {
page: { type: 'string' },
label: { type: 'string' },
labelTranslationKey: { type: 'string' },
},
required: ['page'],
} as const;
const catalogFilterSchema = {
type: 'object',
additionalProperties: false,
required: ['title', 'property'],
properties: {
type: { type: 'string', enum: ['select', 'checkboxes', 'date-range'] },
title: { type: 'string' },
titleTranslationKey: { type: 'string' },
property: { type: 'string' },
parentFilter: { type: 'string' },
valuesMapping: { type: 'object', additionalProperties: { type: 'string' } },
missingCategoryName: { type: 'string' },
missingCategoryNameTranslationKey: { type: 'string' },
options: { type: 'array', items: { type: 'string' } },
},
} as const;
const scorecardConfigSchema = {
type: 'object',
additionalProperties: true,
required: [],
properties: {
ignoreNonCompliant: { type: 'boolean', default: false },
teamMetadataProperty: {
type: 'object',
properties: {
property: { type: 'string' },
label: { type: 'string' },
default: { type: 'string' },
},
},
levels: {
type: 'array',
items: {
type: 'object',
required: ['name'],
properties: {
name: { type: 'string' },
color: { type: 'string' },
extends: { type: 'array', items: { type: 'string' } },
rules: {
type: 'object',
additionalProperties: {
oneOf: [{ type: 'string' }, { type: 'object' }],
},
},
},
additionalProperties: false,
},
},
targets: {
type: 'array',
items: {
type: 'object',
required: ['where'],
properties: {
minimumLevel: { type: 'string' },
where: {
type: 'object',
required: ['metadata'],
properties: {
metadata: { type: 'object', additionalProperties: { type: 'string' } },
},
additionalProperties: false,
},
},
additionalProperties: false,
},
},
},
} as const;
const catalogSchema = {
type: 'object',
additionalProperties: true,
required: ['slug', 'items'],
properties: {
slug: { type: 'string' },
filters: { type: 'array', items: catalogFilterSchema },
groupByFirstFilter: { type: 'boolean' },
filterValuesCasing: {
type: 'string',
enum: ['sentence', 'original', 'lowercase', 'uppercase'],
},
items: navItemsSchema,
requiredPermission: { type: 'string' },
separateVersions: { type: 'boolean' },
title: { type: 'string' },
titleTranslationKey: { type: 'string' },
description: { type: 'string' },
descriptionTranslationKey: { type: 'string' },
},
} as const;
const catalogsConfigSchema = {
type: 'object',
patternProperties: {
'.*': catalogSchema,
},
} as const;
export const themeConfigSchema = {
type: 'object',
properties: {
imports: {
type: 'array',
items: { type: 'string' },
default: [],
},
logo: logoConfigSchema,
navbar: {
type: 'object',
properties: {
items: navItemsSchema,
...hideConfigSchema.properties,
},
additionalProperties: false,
},
products: {
type: 'object',
additionalProperties: productConfigSchema,
},
footer: {
type: 'object',
properties: {
items: navItemsSchema,
copyrightText: { type: 'string' },
logo: hideConfigSchema,
...hideConfigSchema.properties,
},
additionalProperties: false,
},
sidebar: {
type: 'object',
properties: {
separatorLine: { type: 'boolean' },
linePosition: {
type: 'string',
enum: ['top', 'bottom'],
default: 'bottom',
},
...hideConfigSchema.properties,
},
additionalProperties: false,
},
scripts: {
type: 'object',
properties: {
head: { type: 'array', items: scriptConfigSchema },
body: { type: 'array', items: scriptConfigSchema },
},
additionalProperties: false,
},
links: { type: 'array', items: linksConfigSchema },
feedback: {
type: 'object',
properties: {
hide: {
type: 'boolean',
default: false,
},
type: {
type: 'string',
enum: ['rating', 'sentiment', 'comment', 'reasons', 'mood', 'scale'],
default: 'sentiment',
},
settings: {
type: 'object',
properties: {
label: { type: 'string' },
submitText: { type: 'string' },
buttonText: { type: 'string' },
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: {
hide: {
type: 'boolean',
default: false,
},
component: {
type: 'string',
enum: ['radio', 'checkbox'],
default: 'checkbox',
},
label: { type: 'string' },
items: { type: 'array', items: { type: 'string' } },
},
additionalProperties: false,
},
comment: {
type: 'object',
properties: {
hide: {
type: 'boolean',
default: false,
},
label: { type: 'string' },
likeLabel: { type: 'string' },
dislikeLabel: { type: 'string' },
satisfiedLabel: { type: 'string' },
neutralLabel: { type: 'string' },
dissatisfiedLabel: { type: 'string' },
},
additionalProperties: false,
},
},
additionalProperties: false,
...hideConfigSchema.properties,
},
},
additionalProperties: false,
default: {},
},
search: {
type: 'object',
properties: {
placement: {
type: 'string',
default: 'navbar',
},
shortcuts: {
type: 'array',
items: { type: 'string' },
default: ['/'],
},
suggestedPages: {
type: 'array',
items: suggestedPageSchema,
},
...hideConfigSchema.properties,
},
additionalProperties: false,
default: {},
},
colorMode: {
type: 'object',
properties: {
ignoreDetection: { type: 'boolean' },
modes: {
type: 'array',
items: { type: 'string' },
default: ['light', 'dark'],
},
...hideConfigSchema.properties,
},
additionalProperties: false,
default: {},
},
navigation: {
type: 'object',
properties: {
nextButton: {
type: 'object',
properties: {
text: { type: 'string', default: 'Next to {{label}}' },
...hideConfigSchema.properties,
},
additionalProperties: false,
default: {},
},
previousButton: {
type: 'object',
properties: {
text: { type: 'string', default: 'Back to {{label}}' },
...hideConfigSchema.properties,
},
additionalProperties: false,
default: {},
},
},
additionalProperties: false,
default: {},
},
codeSnippet: {
type: 'object',
properties: {
elementFormat: { type: 'string', default: 'icon' },
copy: {
type: 'object',
properties: {
...hideConfigSchema.properties,
},
additionalProperties: false,
default: { hide: false },
},
report: {
type: 'object',
properties: {
tooltipText: { type: 'string' },
buttonText: { type: 'string' },
label: { type: 'string' },
...hideConfigSchema.properties,
},
additionalProperties: false,
default: { hide: false },
},
expand: {
type: 'object',
properties: {
...hideConfigSchema.properties,
},
additionalProperties: false,
default: { hide: false },
},
collapse: {
type: 'object',
properties: {
...hideConfigSchema.properties,
},
additionalProperties: false,
default: { hide: false },
},
},
additionalProperties: false,
default: {},
},
markdown: markdownConfigSchema,
openapi: { type: 'object', additionalProperties: true }, // TODO: put the real schema here @Viacheslav
graphql: { type: 'object', additionalProperties: true },
analytics: {
type: 'object',
properties: {
adobe: adobeAnalyticsConfigSchema,
amplitude: amplitudeAnalyticsConfigSchema,
fullstory: fullstoryAnalyticsConfigSchema,
heap: heapAnalyticsConfigSchema,
rudderstack: rudderstackAnalyticsConfigSchema,
segment: segmentAnalyticsConfigSchema,
gtm: gtmAnalyticsConfigSchema,
ga: googleAnalyticsConfigSchema,
},
},
userProfile: {
type: 'object',
properties: {
loginLabel: { type: 'string', default: 'Login' },
logoutLabel: { type: 'string', default: 'Logout' },
menu: {
type: 'array',
items: {
type: 'object',
properties: {
label: { type: 'string' },
external: { type: 'boolean' },
link: { type: 'string' },
separatorLine: { type: 'boolean' },
},
additionalProperties: true,
},
default: [],
},
...hideConfigSchema.properties,
},
additionalProperties: false,
default: {},
},
versionPicker: {
type: 'object',
properties: {
hide: { type: 'boolean' },
showForUnversioned: {
type: 'boolean',
},
},
},
breadcrumbs: {
type: 'object',
properties: {
hide: { type: 'boolean' },
prefixItems: {
type: 'array',
items: {
type: 'object',
properties: {
label: { type: 'string' },
labelTranslationKey: { type: 'string' },
page: { type: 'string' },
},
additionalProperties: false,
default: {},
},
},
},
additionalProperties: false,
default: {},
},
catalog: catalogsConfigSchema,
scorecard: scorecardConfigSchema,
},
additionalProperties: true,
default: {},
} as const;
export const productThemeOverrideSchema = {
type: 'object',
properties: {
logo: themeConfigSchema.properties.logo,
navbar: themeConfigSchema.properties.navbar,
footer: themeConfigSchema.properties.footer,
sidebar: themeConfigSchema.properties.sidebar,
search: themeConfigSchema.properties.search,
codeSnippet: themeConfigSchema.properties.codeSnippet,
breadcrumbs: themeConfigSchema.properties.breadcrumbs,
analytics: {
type: 'object',
properties: {
ga: productGoogleAnalyticsConfigSchema,
},
},
},
additionalProperties: true,
default: {},
} as const;
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>;