@redocly/openapi-core 1.8.2 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/lib/bundle.d.ts +1 -1
  3. package/lib/config/all.js +1 -0
  4. package/lib/config/minimal.js +1 -0
  5. package/lib/config/recommended-strict.js +1 -0
  6. package/lib/config/recommended.js +1 -0
  7. package/lib/lint.d.ts +1 -0
  8. package/lib/lint.js +1 -1
  9. package/lib/oas-types.d.ts +1 -1
  10. package/lib/ref-utils.js +4 -4
  11. package/lib/resolve.js +9 -1
  12. package/lib/rules/common/no-required-schema-properties-undefined.d.ts +2 -0
  13. package/lib/rules/common/no-required-schema-properties-undefined.js +37 -0
  14. package/lib/rules/oas2/index.js +2 -0
  15. package/lib/rules/oas3/index.js +2 -0
  16. package/lib/types/index.d.ts +7 -7
  17. package/lib/types/json-schema-adapter.d.ts +3 -0
  18. package/lib/types/json-schema-adapter.js +173 -0
  19. package/lib/types/oas2.d.ts +3 -2
  20. package/lib/types/oas3.d.ts +3 -2
  21. package/lib/types/oas3_1.d.ts +3 -2
  22. package/lib/types/portal-config-schema.d.ts +5261 -52
  23. package/lib/types/portal-config-schema.js +71 -55
  24. package/lib/types/redocly-yaml.d.ts +14 -2
  25. package/lib/types/redocly-yaml.js +102 -39
  26. package/lib/types/theme-config.d.ts +819 -36
  27. package/lib/types/theme-config.js +67 -29
  28. package/lib/utils.d.ts +2 -2
  29. package/lib/visitors.js +1 -1
  30. package/lib/walk.js +7 -1
  31. package/package.json +1 -1
  32. package/src/__tests__/lint.test.ts +1218 -36
  33. package/src/__tests__/ref-utils.test.ts +22 -0
  34. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +2 -0
  35. package/src/config/__tests__/load.test.ts +13 -13
  36. package/src/config/all.ts +1 -0
  37. package/src/config/minimal.ts +1 -0
  38. package/src/config/recommended-strict.ts +1 -0
  39. package/src/config/recommended.ts +1 -0
  40. package/src/decorators/oas2/remove-unused-components.ts +3 -2
  41. package/src/decorators/oas3/remove-unused-components.ts +3 -2
  42. package/src/lint.ts +2 -1
  43. package/src/ref-utils.ts +4 -4
  44. package/src/resolve.ts +13 -1
  45. package/src/rules/common/__tests__/no-required-schema-properties-undefined.test.ts +550 -0
  46. package/src/rules/common/no-required-schema-properties-undefined.ts +53 -0
  47. package/src/rules/oas2/index.ts +2 -0
  48. package/src/rules/oas3/index.ts +2 -0
  49. package/src/types/index.ts +7 -12
  50. package/src/types/json-schema-adapter.ts +217 -0
  51. package/src/types/oas2.ts +5 -2
  52. package/src/types/oas3.ts +6 -2
  53. package/src/types/oas3_1.ts +5 -2
  54. package/src/types/portal-config-schema.ts +111 -61
  55. package/src/types/redocly-yaml.ts +119 -43
  56. package/src/types/theme-config.ts +125 -27
  57. package/src/utils.ts +2 -2
  58. package/src/visitors.ts +1 -1
  59. package/src/walk.ts +7 -1
  60. package/tsconfig.tsbuildinfo +1 -1
@@ -6,6 +6,276 @@ import { BaseResolver } from '../resolve';
6
6
  import { loadConfig } from '../config/load';
7
7
  import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../__tests__/utils';
8
8
  import { detectSpec } from '../oas-types';
9
+ import { themeConfigSchema } from '../types/theme-config';
10
+ import { createConfigTypes } from '../types/redocly-yaml';
11
+
12
+ const testPortalConfig = parseYamlToDocument(
13
+ outdent`
14
+ licenseKey: 123 # Must be a string
15
+
16
+ apis:
17
+ without-root:
18
+ foo: Not expected!
19
+ output: file.json
20
+ with-wrong-root:
21
+ root: 456 # Must be a string
22
+ with-theme:
23
+ root: ./openapi.yaml
24
+ theme:
25
+ openapi: wrong, must be an object
26
+ not-expected: Must fail
27
+
28
+ seo:
29
+ keywords: 789 # Must be an array
30
+
31
+ redirects:
32
+ some-redirect:
33
+ t1o: Wrong name, should be 'two'
34
+ type: wrong type, must be a number
35
+
36
+ rbac:
37
+ 'team-b.md':
38
+ TeamB: read
39
+ team-c.md:
40
+ TeamC: read
41
+ /blog/*:
42
+ anonymous: none
43
+ authenticated: read
44
+ /blogpost/:
45
+ TeamD: none
46
+ '**/*.md':
47
+ TeamA: none
48
+ authenticated: none
49
+ '*': read
50
+ 'blog/april-2022.md':
51
+ TeamA: none
52
+ TeamC: read
53
+ test.md:
54
+ TeamC: none
55
+ TeamB: none
56
+ authenticated: none
57
+ '*': read
58
+ test/**:
59
+ TeamB: read
60
+ TeamC: read
61
+ authenticated: read
62
+ anonymous: read
63
+ additional-property:
64
+ something: 123 # Must be a string
65
+ content:
66
+ '**':
67
+ additionalProp: 456 # Must be a stirng
68
+ foo:
69
+ additionalProp2: 789 # Must be a stirng
70
+
71
+ responseHeaders:
72
+ some-header: wrong, must be an array
73
+ some-header2:
74
+ - wrong, must be an object
75
+ - unexpected-property: Should fail
76
+ # name: Must be reported as a missing required prop
77
+ value: 123 # Must be a string
78
+
79
+ ssoOnPrem:
80
+ oidc:
81
+ title: 456 # Must be a string
82
+ type: OIDC
83
+ configurationUrl: http://localhost/oidc/.well-known/openid-configuration
84
+ clientId: '{{ process.env.public }}'
85
+ clientSecret: '{{ process.env.secret }}'
86
+ teamsClaimName: https://test.com
87
+ scopes:
88
+ - openid
89
+ audience:
90
+ - default
91
+ authorizationRequestCustomParams:
92
+ login_hint: 789 # Must be a string
93
+ prompt: login
94
+ configuration:
95
+ token_endpoint: 123 # Must be a string
96
+ # authorization_endpoint: Must be reported as a missing required prop
97
+ additional-propery: Must be allowed
98
+ defaultTeams:
99
+ - 456 # Must be a string
100
+
101
+ sso-config-schema-without-configurationUrl:
102
+ type: OIDC
103
+ # clientId: Must be reported as a missing required prop
104
+ # configurationUrl: Must be reported as a missing required prop
105
+ clientSecret: '{{ process.env.secret }}'
106
+
107
+ basic:
108
+ type: BASIC
109
+ credentials:
110
+ - teams:
111
+ - 789 # Must be a string
112
+ - correct
113
+ # username: Must be reported as a missing required prop
114
+
115
+ sso:
116
+ - WRONG # Does not match allowed options
117
+
118
+ developerOnboarding:
119
+ wrong: A not allowed field
120
+ adapters:
121
+ - should be object
122
+ - type: 123 # Must be a string
123
+ - type: APIGEE_X
124
+ # organizationName: Must be reported as a missing required prop
125
+ auth:
126
+ type: OAUTH2
127
+ # tokenEndpoint: Must be reported as a missing required prop
128
+ clientId: 456 # Must be a string
129
+ clientSecret: '{{ process.env.secret }}'
130
+ not-expected: Must fail
131
+ - type: APIGEE_X
132
+ organizationName: Test
133
+ auth:
134
+ type: SERVICE_ACCOUNT
135
+ # serviceAccountPrivateKey: Must be reported as a missing required prop
136
+ serviceAccountEmail: 789 # Must be a string
137
+
138
+ i18n:
139
+ defaultLocale: en-US
140
+ locales:
141
+ - code: 123 # Must be a string
142
+ name: English
143
+ - code: es-ES
144
+ name: Spanish
145
+
146
+ metadata:
147
+ test: anything
148
+
149
+ not-listed-filed: Must be reported as not expected
150
+
151
+ env:
152
+ some-env:
153
+ mockServer:
154
+ off: must be boolean
155
+ not-expected: Must fail
156
+ apis:
157
+ no-root:
158
+ # root: Must be defined
159
+ rules: {}
160
+ wrong-root:
161
+ root: 789 # Must be a string
162
+
163
+ theme:
164
+ breadcrumbs:
165
+ hide: false
166
+ prefixItems:
167
+ - label: Home
168
+ page: '/'
169
+ imports:
170
+ - '@redocly/theme-experimental'
171
+
172
+ logo:
173
+ srcSet: './images/redocly-black-logo.svg light, ./images/redocly-brand-logo.svg dark'
174
+ altText: Test
175
+ link: /
176
+ asyncapi:
177
+ hideInfo: false
178
+ expandSchemas:
179
+ root: true
180
+ elements: true
181
+ navbar:
182
+ items:
183
+ - label: Markdown
184
+ page: /markdown/
185
+
186
+ search:
187
+ shortcuts:
188
+ - ctrl+f
189
+ - cmd+k
190
+ - /
191
+ suggestedPages:
192
+ - label: TSX page
193
+ page: tsx.page.tsx
194
+ - page: /my-catalog/
195
+
196
+ footer:
197
+ copyrightText: Copyright © Test 2019-2020.
198
+ items:
199
+ - group: Legal
200
+ items:
201
+ - label: Terms of Use
202
+ href: 'https://test.com/' # Not expected
203
+
204
+ markdown:
205
+ lastUpdatedBlock:
206
+ format: 'long'
207
+ editPage:
208
+ baseUrl: https://test.com
209
+ graphql:
210
+ pagination: section
211
+ menu:
212
+ {
213
+ initialLoadState: 'default',
214
+ requireExactGroups: false,
215
+ groups:
216
+ [
217
+ {
218
+ name: 'GraphQL custom group',
219
+ directives: { includeByName: ['cacheControl', 'typeDirective'] },
220
+ },
221
+ ],
222
+ otherItemsGroupName: 'Other',
223
+ }
224
+ sidebar:
225
+ separatorLine: true
226
+ linePosition: top
227
+ catalog:
228
+ main:
229
+ title: API Catalog
230
+ description: 'This is a description of the API Catalog'
231
+ slug: /my-catalog/
232
+ filters:
233
+ - title: Domain
234
+ property: domain
235
+ missingCategoryName: Other
236
+ - title: API Category
237
+ property: category
238
+ missingCategoryName: Other
239
+ groupByFirstFilter: false
240
+ items:
241
+ - directory: ./
242
+ flatten: true
243
+ includeByMetadata:
244
+ type: [openapi]
245
+ scorecard:
246
+ ignoreNonCompliant: true
247
+ levels:
248
+ - name: Baseline
249
+ extends:
250
+ - minimal
251
+ - name: Silver
252
+ extends:
253
+ - recommended
254
+ rules:
255
+ info-description: off
256
+
257
+ - name: Gold
258
+ rules:
259
+ rule/path-item-get-required:
260
+ severity: warn
261
+ subject:
262
+ type: PathItem
263
+ message: Every path item must have a GET operation.
264
+ assertions:
265
+ required:
266
+ - get
267
+
268
+ operation-4xx-response: warn
269
+ targets:
270
+ - where:
271
+ metadata:
272
+ l0: Distribution
273
+ publishDateRange: 2021-01-01T00:00:00Z/2022-01-01
274
+ minimumLevel: Silver
275
+
276
+ `,
277
+ ''
278
+ );
9
279
 
10
280
  describe('lint', () => {
11
281
  it('lintFromString should work', async () => {
@@ -97,28 +367,28 @@ describe('lint', () => {
97
367
  assertions:
98
368
  local/checkWordsCount:
99
369
  min: 3
100
- theme:
101
- openapi:
102
- showConsole: true
103
- layout:
104
- scope: section
105
- routingStrategy: browser
106
- theme:
107
- rightPanel:
108
- backgroundColor: '#263238'
109
- links:
110
- color: '#6CC496'
370
+ theme:
371
+ openapi:
372
+ showConsole: true
373
+ layout:
374
+ scope: section
375
+ routingStrategy: browser
111
376
  theme:
112
- openapi:
113
- showConsole: true
114
- layout:
115
- scope: section
116
- routingStrategy: browser
377
+ rightPanel:
378
+ backgroundColor: '#263238'
379
+ links:
380
+ color: '#6CC496'
117
381
  theme:
118
- rightPanel:
119
- backgroundColor: '#263238'
120
- links:
121
- color: '#6CC496'
382
+ openapi:
383
+ showConsole: true
384
+ layout:
385
+ scope: section
386
+ routingStrategy: browser
387
+ theme:
388
+ rightPanel:
389
+ backgroundColor: '#263238'
390
+ links:
391
+ color: '#6CC496'
122
392
  `,
123
393
  ''
124
394
  );
@@ -130,31 +400,26 @@ describe('lint', () => {
130
400
  "from": undefined,
131
401
  "location": [
132
402
  {
133
- "pointer": "#/eme",
134
- "reportOnKey": true,
403
+ "pointer": "#/apis",
404
+ "reportOnKey": false,
135
405
  "source": "",
136
406
  },
137
407
  ],
138
- "message": "Property \`eme\` is not expected here.",
408
+ "message": "Expected type \`ConfigApis\` (object) but got \`string\`",
139
409
  "ruleId": "configuration spec",
140
410
  "severity": "error",
141
- "suggest": [
142
- "env",
143
- "theme",
144
- "seo",
145
- "sso",
146
- ],
411
+ "suggest": [],
147
412
  },
148
413
  {
149
414
  "from": undefined,
150
415
  "location": [
151
416
  {
152
- "pointer": "#/openapi",
153
- "reportOnKey": true,
417
+ "pointer": "#/theme/openapi/layout",
418
+ "reportOnKey": false,
154
419
  "source": "",
155
420
  },
156
421
  ],
157
- "message": "Property \`openapi\` is not expected here.",
422
+ "message": "\`layout\` can be one of the following only: "stacked", "three-panel".",
158
423
  "ruleId": "configuration spec",
159
424
  "severity": "error",
160
425
  "suggest": [],
@@ -163,15 +428,18 @@ describe('lint', () => {
163
428
  "from": undefined,
164
429
  "location": [
165
430
  {
166
- "pointer": "#/apis",
167
- "reportOnKey": false,
431
+ "pointer": "#/theme/openapi/theme/theme",
432
+ "reportOnKey": true,
168
433
  "source": "",
169
434
  },
170
435
  ],
171
- "message": "Expected type \`ConfigApis\` (object) but got \`string\`",
436
+ "message": "Property \`theme\` is not expected here.",
172
437
  "ruleId": "configuration spec",
173
438
  "severity": "error",
174
- "suggest": [],
439
+ "suggest": [
440
+ "schema",
441
+ "shape",
442
+ ],
175
443
  },
176
444
  ]
177
445
  `);
@@ -285,6 +553,920 @@ describe('lint', () => {
285
553
  `);
286
554
  });
287
555
 
556
+ it('lintConfig should detect wrong fields in the default configuration after merging with the portal config schema', async () => {
557
+ const document = testPortalConfig;
558
+ const results = await lintConfig({ document });
559
+
560
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
561
+ [
562
+ {
563
+ "from": undefined,
564
+ "location": [
565
+ {
566
+ "pointer": "#/licenseKey",
567
+ "reportOnKey": false,
568
+ "source": "",
569
+ },
570
+ ],
571
+ "message": "Expected type \`string\` but got \`integer\`.",
572
+ "ruleId": "configuration spec",
573
+ "severity": "error",
574
+ "suggest": [],
575
+ },
576
+ {
577
+ "from": undefined,
578
+ "location": [
579
+ {
580
+ "pointer": "#/sso/0",
581
+ "reportOnKey": false,
582
+ "source": "",
583
+ },
584
+ ],
585
+ "message": "\`sso\` can be one of the following only: "REDOCLY", "CORPORATE", "GUEST".",
586
+ "ruleId": "configuration spec",
587
+ "severity": "error",
588
+ "suggest": [],
589
+ },
590
+ {
591
+ "from": undefined,
592
+ "location": [
593
+ {
594
+ "pointer": "#/not-listed-filed",
595
+ "reportOnKey": true,
596
+ "source": "",
597
+ },
598
+ ],
599
+ "message": "Property \`not-listed-filed\` is not expected here.",
600
+ "ruleId": "configuration spec",
601
+ "severity": "error",
602
+ "suggest": [],
603
+ },
604
+ {
605
+ "from": undefined,
606
+ "location": [
607
+ {
608
+ "pointer": "#/redirects/some-redirect/t1o",
609
+ "reportOnKey": true,
610
+ "source": "",
611
+ },
612
+ ],
613
+ "message": "Property \`t1o\` is not expected here.",
614
+ "ruleId": "configuration spec",
615
+ "severity": "error",
616
+ "suggest": [
617
+ "to",
618
+ "type",
619
+ ],
620
+ },
621
+ {
622
+ "from": undefined,
623
+ "location": [
624
+ {
625
+ "pointer": "#/redirects/some-redirect/type",
626
+ "reportOnKey": false,
627
+ "source": "",
628
+ },
629
+ ],
630
+ "message": "Expected type \`number\` but got \`string\`.",
631
+ "ruleId": "configuration spec",
632
+ "severity": "error",
633
+ "suggest": [],
634
+ },
635
+ {
636
+ "from": undefined,
637
+ "location": [
638
+ {
639
+ "pointer": "#/seo/keywords",
640
+ "reportOnKey": false,
641
+ "source": "",
642
+ },
643
+ ],
644
+ "message": "Expected type \`array\` but got \`integer\`.",
645
+ "ruleId": "configuration spec",
646
+ "severity": "error",
647
+ "suggest": [],
648
+ },
649
+ {
650
+ "from": undefined,
651
+ "location": [
652
+ {
653
+ "pointer": "#/rbac/content/**/additionalProp",
654
+ "reportOnKey": false,
655
+ "source": "",
656
+ },
657
+ ],
658
+ "message": "Expected type \`string\` but got \`integer\`.",
659
+ "ruleId": "configuration spec",
660
+ "severity": "error",
661
+ "suggest": [],
662
+ },
663
+ {
664
+ "from": undefined,
665
+ "location": [
666
+ {
667
+ "pointer": "#/rbac/content/foo/additionalProp2",
668
+ "reportOnKey": false,
669
+ "source": "",
670
+ },
671
+ ],
672
+ "message": "Expected type \`string\` but got \`integer\`.",
673
+ "ruleId": "configuration spec",
674
+ "severity": "error",
675
+ "suggest": [],
676
+ },
677
+ {
678
+ "from": undefined,
679
+ "location": [
680
+ {
681
+ "pointer": "#/rbac/additional-property/something",
682
+ "reportOnKey": false,
683
+ "source": "",
684
+ },
685
+ ],
686
+ "message": "Expected type \`string\` but got \`integer\`.",
687
+ "ruleId": "configuration spec",
688
+ "severity": "error",
689
+ "suggest": [],
690
+ },
691
+ {
692
+ "from": undefined,
693
+ "location": [
694
+ {
695
+ "pointer": "#/responseHeaders/some-header",
696
+ "reportOnKey": false,
697
+ "source": "",
698
+ },
699
+ ],
700
+ "message": "Expected type \`rootRedoclyConfigSchema.responseHeaders_additionalProperties\` (array) but got \`string\`",
701
+ "ruleId": "configuration spec",
702
+ "severity": "error",
703
+ "suggest": [],
704
+ },
705
+ {
706
+ "from": undefined,
707
+ "location": [
708
+ {
709
+ "pointer": "#/responseHeaders/some-header2/0",
710
+ "reportOnKey": false,
711
+ "source": "",
712
+ },
713
+ ],
714
+ "message": "Expected type \`rootRedoclyConfigSchema.responseHeaders_additionalProperties_items\` (object) but got \`string\`",
715
+ "ruleId": "configuration spec",
716
+ "severity": "error",
717
+ "suggest": [],
718
+ },
719
+ {
720
+ "from": undefined,
721
+ "location": [
722
+ {
723
+ "pointer": "#/responseHeaders/some-header2/1",
724
+ "reportOnKey": true,
725
+ "source": "",
726
+ },
727
+ ],
728
+ "message": "The field \`name\` must be present on this level.",
729
+ "ruleId": "configuration spec",
730
+ "severity": "error",
731
+ "suggest": [],
732
+ },
733
+ {
734
+ "from": undefined,
735
+ "location": [
736
+ {
737
+ "pointer": "#/responseHeaders/some-header2/1/unexpected-property",
738
+ "reportOnKey": true,
739
+ "source": "",
740
+ },
741
+ ],
742
+ "message": "Property \`unexpected-property\` is not expected here.",
743
+ "ruleId": "configuration spec",
744
+ "severity": "error",
745
+ "suggest": [],
746
+ },
747
+ {
748
+ "from": undefined,
749
+ "location": [
750
+ {
751
+ "pointer": "#/responseHeaders/some-header2/1/value",
752
+ "reportOnKey": false,
753
+ "source": "",
754
+ },
755
+ ],
756
+ "message": "Expected type \`string\` but got \`integer\`.",
757
+ "ruleId": "configuration spec",
758
+ "severity": "error",
759
+ "suggest": [],
760
+ },
761
+ {
762
+ "from": undefined,
763
+ "location": [
764
+ {
765
+ "pointer": "#/apis/without-root",
766
+ "reportOnKey": true,
767
+ "source": "",
768
+ },
769
+ ],
770
+ "message": "The field \`root\` must be present on this level.",
771
+ "ruleId": "configuration spec",
772
+ "severity": "error",
773
+ "suggest": [],
774
+ },
775
+ {
776
+ "from": undefined,
777
+ "location": [
778
+ {
779
+ "pointer": "#/apis/without-root/foo",
780
+ "reportOnKey": true,
781
+ "source": "",
782
+ },
783
+ ],
784
+ "message": "Property \`foo\` is not expected here.",
785
+ "ruleId": "configuration spec",
786
+ "severity": "error",
787
+ "suggest": [
788
+ "root",
789
+ ],
790
+ },
791
+ {
792
+ "from": undefined,
793
+ "location": [
794
+ {
795
+ "pointer": "#/apis/with-wrong-root/root",
796
+ "reportOnKey": false,
797
+ "source": "",
798
+ },
799
+ ],
800
+ "message": "Expected type \`string\` but got \`integer\`.",
801
+ "ruleId": "configuration spec",
802
+ "severity": "error",
803
+ "suggest": [],
804
+ },
805
+ {
806
+ "from": undefined,
807
+ "location": [
808
+ {
809
+ "pointer": "#/apis/with-theme/theme/openapi",
810
+ "reportOnKey": false,
811
+ "source": "",
812
+ },
813
+ ],
814
+ "message": "Expected type \`object\` but got \`string\`.",
815
+ "ruleId": "configuration spec",
816
+ "severity": "error",
817
+ "suggest": [],
818
+ },
819
+ {
820
+ "from": undefined,
821
+ "location": [
822
+ {
823
+ "pointer": "#/apis/with-theme/theme/not-expected",
824
+ "reportOnKey": true,
825
+ "source": "",
826
+ },
827
+ ],
828
+ "message": "Property \`not-expected\` is not expected here.",
829
+ "ruleId": "configuration spec",
830
+ "severity": "error",
831
+ "suggest": [],
832
+ },
833
+ {
834
+ "from": undefined,
835
+ "location": [
836
+ {
837
+ "pointer": "#/ssoOnPrem/oidc/title",
838
+ "reportOnKey": false,
839
+ "source": "",
840
+ },
841
+ ],
842
+ "message": "Expected type \`string\` but got \`integer\`.",
843
+ "ruleId": "configuration spec",
844
+ "severity": "error",
845
+ "suggest": [],
846
+ },
847
+ {
848
+ "from": undefined,
849
+ "location": [
850
+ {
851
+ "pointer": "#/ssoOnPrem/oidc/defaultTeams/0",
852
+ "reportOnKey": false,
853
+ "source": "",
854
+ },
855
+ ],
856
+ "message": "Expected type \`string\` but got \`integer\`.",
857
+ "ruleId": "configuration spec",
858
+ "severity": "error",
859
+ "suggest": [],
860
+ },
861
+ {
862
+ "from": undefined,
863
+ "location": [
864
+ {
865
+ "pointer": "#/ssoOnPrem/oidc/configuration",
866
+ "reportOnKey": true,
867
+ "source": "",
868
+ },
869
+ ],
870
+ "message": "The field \`authorization_endpoint\` must be present on this level.",
871
+ "ruleId": "configuration spec",
872
+ "severity": "error",
873
+ "suggest": [],
874
+ },
875
+ {
876
+ "from": undefined,
877
+ "location": [
878
+ {
879
+ "pointer": "#/ssoOnPrem/oidc/configuration/token_endpoint",
880
+ "reportOnKey": false,
881
+ "source": "",
882
+ },
883
+ ],
884
+ "message": "Expected type \`string\` but got \`integer\`.",
885
+ "ruleId": "configuration spec",
886
+ "severity": "error",
887
+ "suggest": [],
888
+ },
889
+ {
890
+ "from": undefined,
891
+ "location": [
892
+ {
893
+ "pointer": "#/ssoOnPrem/oidc/authorizationRequestCustomParams/login_hint",
894
+ "reportOnKey": false,
895
+ "source": "",
896
+ },
897
+ ],
898
+ "message": "Expected type \`string\` but got \`integer\`.",
899
+ "ruleId": "configuration spec",
900
+ "severity": "error",
901
+ "suggest": [],
902
+ },
903
+ {
904
+ "from": undefined,
905
+ "location": [
906
+ {
907
+ "pointer": "#/ssoOnPrem/sso-config-schema-without-configurationUrl",
908
+ "reportOnKey": true,
909
+ "source": "",
910
+ },
911
+ ],
912
+ "message": "The field \`clientId\` must be present on this level.",
913
+ "ruleId": "configuration spec",
914
+ "severity": "error",
915
+ "suggest": [],
916
+ },
917
+ {
918
+ "from": undefined,
919
+ "location": [
920
+ {
921
+ "pointer": "#/ssoOnPrem/sso-config-schema-without-configurationUrl",
922
+ "reportOnKey": true,
923
+ "source": "",
924
+ },
925
+ ],
926
+ "message": "The field \`configurationUrl\` must be present on this level.",
927
+ "ruleId": "configuration spec",
928
+ "severity": "error",
929
+ "suggest": [],
930
+ },
931
+ {
932
+ "from": undefined,
933
+ "location": [
934
+ {
935
+ "pointer": "#/ssoOnPrem/basic/credentials/0",
936
+ "reportOnKey": true,
937
+ "source": "",
938
+ },
939
+ ],
940
+ "message": "The field \`username\` must be present on this level.",
941
+ "ruleId": "configuration spec",
942
+ "severity": "error",
943
+ "suggest": [],
944
+ },
945
+ {
946
+ "from": undefined,
947
+ "location": [
948
+ {
949
+ "pointer": "#/ssoOnPrem/basic/credentials/0/teams/0",
950
+ "reportOnKey": false,
951
+ "source": "",
952
+ },
953
+ ],
954
+ "message": "Expected type \`string\` but got \`integer\`.",
955
+ "ruleId": "configuration spec",
956
+ "severity": "error",
957
+ "suggest": [],
958
+ },
959
+ {
960
+ "from": undefined,
961
+ "location": [
962
+ {
963
+ "pointer": "#/developerOnboarding/wrong",
964
+ "reportOnKey": true,
965
+ "source": "",
966
+ },
967
+ ],
968
+ "message": "Property \`wrong\` is not expected here.",
969
+ "ruleId": "configuration spec",
970
+ "severity": "error",
971
+ "suggest": [],
972
+ },
973
+ {
974
+ "from": undefined,
975
+ "location": [
976
+ {
977
+ "pointer": "#/developerOnboarding/adapters/0",
978
+ "reportOnKey": false,
979
+ "source": "",
980
+ },
981
+ ],
982
+ "message": "Expected type \`APIGEE_X\` (object) but got \`string\`",
983
+ "ruleId": "configuration spec",
984
+ "severity": "error",
985
+ "suggest": [],
986
+ },
987
+ {
988
+ "from": undefined,
989
+ "location": [
990
+ {
991
+ "pointer": "#/developerOnboarding/adapters/1",
992
+ "reportOnKey": true,
993
+ "source": "",
994
+ },
995
+ ],
996
+ "message": "The field \`organizationName\` must be present on this level.",
997
+ "ruleId": "configuration spec",
998
+ "severity": "error",
999
+ "suggest": [],
1000
+ },
1001
+ {
1002
+ "from": undefined,
1003
+ "location": [
1004
+ {
1005
+ "pointer": "#/developerOnboarding/adapters/1",
1006
+ "reportOnKey": true,
1007
+ "source": "",
1008
+ },
1009
+ ],
1010
+ "message": "The field \`auth\` must be present on this level.",
1011
+ "ruleId": "configuration spec",
1012
+ "severity": "error",
1013
+ "suggest": [],
1014
+ },
1015
+ {
1016
+ "from": undefined,
1017
+ "location": [
1018
+ {
1019
+ "pointer": "#/developerOnboarding/adapters/1/type",
1020
+ "reportOnKey": false,
1021
+ "source": "",
1022
+ },
1023
+ ],
1024
+ "message": "Expected type \`string\` but got \`integer\`.",
1025
+ "ruleId": "configuration spec",
1026
+ "severity": "error",
1027
+ "suggest": [],
1028
+ },
1029
+ {
1030
+ "from": undefined,
1031
+ "location": [
1032
+ {
1033
+ "pointer": "#/developerOnboarding/adapters/2",
1034
+ "reportOnKey": true,
1035
+ "source": "",
1036
+ },
1037
+ ],
1038
+ "message": "The field \`organizationName\` must be present on this level.",
1039
+ "ruleId": "configuration spec",
1040
+ "severity": "error",
1041
+ "suggest": [],
1042
+ },
1043
+ {
1044
+ "from": undefined,
1045
+ "location": [
1046
+ {
1047
+ "pointer": "#/developerOnboarding/adapters/2/auth",
1048
+ "reportOnKey": true,
1049
+ "source": "",
1050
+ },
1051
+ ],
1052
+ "message": "The field \`tokenEndpoint\` must be present on this level.",
1053
+ "ruleId": "configuration spec",
1054
+ "severity": "error",
1055
+ "suggest": [],
1056
+ },
1057
+ {
1058
+ "from": undefined,
1059
+ "location": [
1060
+ {
1061
+ "pointer": "#/developerOnboarding/adapters/2/auth/clientId",
1062
+ "reportOnKey": false,
1063
+ "source": "",
1064
+ },
1065
+ ],
1066
+ "message": "Expected type \`string\` but got \`integer\`.",
1067
+ "ruleId": "configuration spec",
1068
+ "severity": "error",
1069
+ "suggest": [],
1070
+ },
1071
+ {
1072
+ "from": undefined,
1073
+ "location": [
1074
+ {
1075
+ "pointer": "#/developerOnboarding/adapters/2/auth/not-expected",
1076
+ "reportOnKey": true,
1077
+ "source": "",
1078
+ },
1079
+ ],
1080
+ "message": "Property \`not-expected\` is not expected here.",
1081
+ "ruleId": "configuration spec",
1082
+ "severity": "error",
1083
+ "suggest": [],
1084
+ },
1085
+ {
1086
+ "from": undefined,
1087
+ "location": [
1088
+ {
1089
+ "pointer": "#/developerOnboarding/adapters/3/auth",
1090
+ "reportOnKey": true,
1091
+ "source": "",
1092
+ },
1093
+ ],
1094
+ "message": "The field \`serviceAccountPrivateKey\` must be present on this level.",
1095
+ "ruleId": "configuration spec",
1096
+ "severity": "error",
1097
+ "suggest": [],
1098
+ },
1099
+ {
1100
+ "from": undefined,
1101
+ "location": [
1102
+ {
1103
+ "pointer": "#/developerOnboarding/adapters/3/auth/serviceAccountEmail",
1104
+ "reportOnKey": false,
1105
+ "source": "",
1106
+ },
1107
+ ],
1108
+ "message": "Expected type \`string\` but got \`integer\`.",
1109
+ "ruleId": "configuration spec",
1110
+ "severity": "error",
1111
+ "suggest": [],
1112
+ },
1113
+ {
1114
+ "from": undefined,
1115
+ "location": [
1116
+ {
1117
+ "pointer": "#/i18n/locales/0/code",
1118
+ "reportOnKey": false,
1119
+ "source": "",
1120
+ },
1121
+ ],
1122
+ "message": "Expected type \`string\` but got \`integer\`.",
1123
+ "ruleId": "configuration spec",
1124
+ "severity": "error",
1125
+ "suggest": [],
1126
+ },
1127
+ {
1128
+ "from": undefined,
1129
+ "location": [
1130
+ {
1131
+ "pointer": "#/theme/footer/items/0/items/0/href",
1132
+ "reportOnKey": true,
1133
+ "source": "",
1134
+ },
1135
+ ],
1136
+ "message": "Property \`href\` is not expected here.",
1137
+ "ruleId": "configuration spec",
1138
+ "severity": "error",
1139
+ "suggest": [],
1140
+ },
1141
+ {
1142
+ "from": undefined,
1143
+ "location": [
1144
+ {
1145
+ "pointer": "#/env/some-env/mockServer/off",
1146
+ "reportOnKey": false,
1147
+ "source": "",
1148
+ },
1149
+ ],
1150
+ "message": "Expected type \`boolean\` but got \`string\`.",
1151
+ "ruleId": "configuration spec",
1152
+ "severity": "error",
1153
+ "suggest": [],
1154
+ },
1155
+ {
1156
+ "from": undefined,
1157
+ "location": [
1158
+ {
1159
+ "pointer": "#/env/some-env/mockServer/not-expected",
1160
+ "reportOnKey": true,
1161
+ "source": "",
1162
+ },
1163
+ ],
1164
+ "message": "Property \`not-expected\` is not expected here.",
1165
+ "ruleId": "configuration spec",
1166
+ "severity": "error",
1167
+ "suggest": [],
1168
+ },
1169
+ {
1170
+ "from": undefined,
1171
+ "location": [
1172
+ {
1173
+ "pointer": "#/env/some-env/apis/no-root",
1174
+ "reportOnKey": true,
1175
+ "source": "",
1176
+ },
1177
+ ],
1178
+ "message": "The field \`root\` must be present on this level.",
1179
+ "ruleId": "configuration spec",
1180
+ "severity": "error",
1181
+ "suggest": [],
1182
+ },
1183
+ {
1184
+ "from": undefined,
1185
+ "location": [
1186
+ {
1187
+ "pointer": "#/env/some-env/apis/wrong-root/root",
1188
+ "reportOnKey": false,
1189
+ "source": "",
1190
+ },
1191
+ ],
1192
+ "message": "Expected type \`string\` but got \`integer\`.",
1193
+ "ruleId": "configuration spec",
1194
+ "severity": "error",
1195
+ "suggest": [],
1196
+ },
1197
+ ]
1198
+ `);
1199
+ });
1200
+
1201
+ it('lintConfig should alternate its behavior when supplied externalConfigTypes', async () => {
1202
+ const document = testPortalConfig;
1203
+ const results = await lintConfig({
1204
+ document,
1205
+ externalConfigTypes: createConfigTypes({
1206
+ type: 'object',
1207
+ properties: { theme: themeConfigSchema },
1208
+ additionalProperties: false,
1209
+ }),
1210
+ });
1211
+
1212
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
1213
+ [
1214
+ {
1215
+ "from": undefined,
1216
+ "location": [
1217
+ {
1218
+ "pointer": "#/licenseKey",
1219
+ "reportOnKey": true,
1220
+ "source": "",
1221
+ },
1222
+ ],
1223
+ "message": "Property \`licenseKey\` is not expected here.",
1224
+ "ruleId": "configuration spec",
1225
+ "severity": "error",
1226
+ "suggest": [],
1227
+ },
1228
+ {
1229
+ "from": undefined,
1230
+ "location": [
1231
+ {
1232
+ "pointer": "#/seo",
1233
+ "reportOnKey": true,
1234
+ "source": "",
1235
+ },
1236
+ ],
1237
+ "message": "Property \`seo\` is not expected here.",
1238
+ "ruleId": "configuration spec",
1239
+ "severity": "error",
1240
+ "suggest": [],
1241
+ },
1242
+ {
1243
+ "from": undefined,
1244
+ "location": [
1245
+ {
1246
+ "pointer": "#/redirects",
1247
+ "reportOnKey": true,
1248
+ "source": "",
1249
+ },
1250
+ ],
1251
+ "message": "Property \`redirects\` is not expected here.",
1252
+ "ruleId": "configuration spec",
1253
+ "severity": "error",
1254
+ "suggest": [],
1255
+ },
1256
+ {
1257
+ "from": undefined,
1258
+ "location": [
1259
+ {
1260
+ "pointer": "#/rbac",
1261
+ "reportOnKey": true,
1262
+ "source": "",
1263
+ },
1264
+ ],
1265
+ "message": "Property \`rbac\` is not expected here.",
1266
+ "ruleId": "configuration spec",
1267
+ "severity": "error",
1268
+ "suggest": [],
1269
+ },
1270
+ {
1271
+ "from": undefined,
1272
+ "location": [
1273
+ {
1274
+ "pointer": "#/responseHeaders",
1275
+ "reportOnKey": true,
1276
+ "source": "",
1277
+ },
1278
+ ],
1279
+ "message": "Property \`responseHeaders\` is not expected here.",
1280
+ "ruleId": "configuration spec",
1281
+ "severity": "error",
1282
+ "suggest": [],
1283
+ },
1284
+ {
1285
+ "from": undefined,
1286
+ "location": [
1287
+ {
1288
+ "pointer": "#/ssoOnPrem",
1289
+ "reportOnKey": true,
1290
+ "source": "",
1291
+ },
1292
+ ],
1293
+ "message": "Property \`ssoOnPrem\` is not expected here.",
1294
+ "ruleId": "configuration spec",
1295
+ "severity": "error",
1296
+ "suggest": [],
1297
+ },
1298
+ {
1299
+ "from": undefined,
1300
+ "location": [
1301
+ {
1302
+ "pointer": "#/sso",
1303
+ "reportOnKey": true,
1304
+ "source": "",
1305
+ },
1306
+ ],
1307
+ "message": "Property \`sso\` is not expected here.",
1308
+ "ruleId": "configuration spec",
1309
+ "severity": "error",
1310
+ "suggest": [],
1311
+ },
1312
+ {
1313
+ "from": undefined,
1314
+ "location": [
1315
+ {
1316
+ "pointer": "#/developerOnboarding",
1317
+ "reportOnKey": true,
1318
+ "source": "",
1319
+ },
1320
+ ],
1321
+ "message": "Property \`developerOnboarding\` is not expected here.",
1322
+ "ruleId": "configuration spec",
1323
+ "severity": "error",
1324
+ "suggest": [],
1325
+ },
1326
+ {
1327
+ "from": undefined,
1328
+ "location": [
1329
+ {
1330
+ "pointer": "#/i18n",
1331
+ "reportOnKey": true,
1332
+ "source": "",
1333
+ },
1334
+ ],
1335
+ "message": "Property \`i18n\` is not expected here.",
1336
+ "ruleId": "configuration spec",
1337
+ "severity": "error",
1338
+ "suggest": [],
1339
+ },
1340
+ {
1341
+ "from": undefined,
1342
+ "location": [
1343
+ {
1344
+ "pointer": "#/metadata",
1345
+ "reportOnKey": true,
1346
+ "source": "",
1347
+ },
1348
+ ],
1349
+ "message": "Property \`metadata\` is not expected here.",
1350
+ "ruleId": "configuration spec",
1351
+ "severity": "error",
1352
+ "suggest": [],
1353
+ },
1354
+ {
1355
+ "from": undefined,
1356
+ "location": [
1357
+ {
1358
+ "pointer": "#/not-listed-filed",
1359
+ "reportOnKey": true,
1360
+ "source": "",
1361
+ },
1362
+ ],
1363
+ "message": "Property \`not-listed-filed\` is not expected here.",
1364
+ "ruleId": "configuration spec",
1365
+ "severity": "error",
1366
+ "suggest": [],
1367
+ },
1368
+ {
1369
+ "from": undefined,
1370
+ "location": [
1371
+ {
1372
+ "pointer": "#/env",
1373
+ "reportOnKey": true,
1374
+ "source": "",
1375
+ },
1376
+ ],
1377
+ "message": "Property \`env\` is not expected here.",
1378
+ "ruleId": "configuration spec",
1379
+ "severity": "error",
1380
+ "suggest": [],
1381
+ },
1382
+ {
1383
+ "from": undefined,
1384
+ "location": [
1385
+ {
1386
+ "pointer": "#/theme/footer/items/0/items/0/href",
1387
+ "reportOnKey": true,
1388
+ "source": "",
1389
+ },
1390
+ ],
1391
+ "message": "Property \`href\` is not expected here.",
1392
+ "ruleId": "configuration spec",
1393
+ "severity": "error",
1394
+ "suggest": [],
1395
+ },
1396
+ {
1397
+ "from": undefined,
1398
+ "location": [
1399
+ {
1400
+ "pointer": "#/apis/without-root/foo",
1401
+ "reportOnKey": true,
1402
+ "source": "",
1403
+ },
1404
+ ],
1405
+ "message": "Property \`foo\` is not expected here.",
1406
+ "ruleId": "configuration spec",
1407
+ "severity": "error",
1408
+ "suggest": [],
1409
+ },
1410
+ {
1411
+ "from": undefined,
1412
+ "location": [
1413
+ {
1414
+ "pointer": "#/apis/without-root/output",
1415
+ "reportOnKey": true,
1416
+ "source": "",
1417
+ },
1418
+ ],
1419
+ "message": "Property \`output\` is not expected here.",
1420
+ "ruleId": "configuration spec",
1421
+ "severity": "error",
1422
+ "suggest": [],
1423
+ },
1424
+ {
1425
+ "from": undefined,
1426
+ "location": [
1427
+ {
1428
+ "pointer": "#/apis/with-wrong-root/root",
1429
+ "reportOnKey": true,
1430
+ "source": "",
1431
+ },
1432
+ ],
1433
+ "message": "Property \`root\` is not expected here.",
1434
+ "ruleId": "configuration spec",
1435
+ "severity": "error",
1436
+ "suggest": [],
1437
+ },
1438
+ {
1439
+ "from": undefined,
1440
+ "location": [
1441
+ {
1442
+ "pointer": "#/apis/with-theme/root",
1443
+ "reportOnKey": true,
1444
+ "source": "",
1445
+ },
1446
+ ],
1447
+ "message": "Property \`root\` is not expected here.",
1448
+ "ruleId": "configuration spec",
1449
+ "severity": "error",
1450
+ "suggest": [],
1451
+ },
1452
+ {
1453
+ "from": undefined,
1454
+ "location": [
1455
+ {
1456
+ "pointer": "#/apis/with-theme/theme",
1457
+ "reportOnKey": true,
1458
+ "source": "",
1459
+ },
1460
+ ],
1461
+ "message": "Property \`theme\` is not expected here.",
1462
+ "ruleId": "configuration spec",
1463
+ "severity": "error",
1464
+ "suggest": [],
1465
+ },
1466
+ ]
1467
+ `);
1468
+ });
1469
+
288
1470
  it("'const' can have any type", async () => {
289
1471
  const document = parseYamlToDocument(
290
1472
  outdent`