Skip to content

Commit c298527

Browse files
committed
CLOUDP-306580: avoid mixed refs
1 parent e594161 commit c298527

File tree

3 files changed

+53
-44
lines changed

3 files changed

+53
-44
lines changed

tools/spectral/ipa/__tests__/IPA125OneOfNoBaseTypes.test.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,9 @@ testRule('xgen-IPA-125-oneOf-no-base-types', [
7070
errors: [
7171
{
7272
code: 'xgen-IPA-125-oneOf-no-base-types',
73-
message: 'oneOf should not contain base types like integer, number, string, or boolean.',
73+
message: 'oneOf should not mix base types with references.',
7474
path: ['components', 'schemas', 'MixedType'],
75-
severity: DiagnosticSeverity.Warning,
75+
severity: DiagnosticSeverity.Error,
7676
},
7777
],
7878
},
@@ -91,12 +91,29 @@ testRule('xgen-IPA-125-oneOf-no-base-types', [
9191
errors: [
9292
{
9393
code: 'xgen-IPA-125-oneOf-no-base-types',
94-
message: 'oneOf should not contain base types like integer, number, string, or boolean.',
94+
message: 'oneOf should not contain multiple different base types.',
9595
path: ['components', 'schemas', 'BaseTypes'],
96-
severity: DiagnosticSeverity.Warning,
96+
severity: DiagnosticSeverity.Error,
9797
},
9898
],
9999
},
100+
{
101+
name: 'valid oneOf with same base type multiple times',
102+
document: {
103+
components: {
104+
schemas: {
105+
...componentSchemas,
106+
SameBaseType: {
107+
oneOf: [
108+
{ type: 'string', enum: ['one'] },
109+
{ type: 'string', enum: ['two'] },
110+
],
111+
},
112+
},
113+
},
114+
},
115+
errors: [],
116+
},
100117
{
101118
name: 'oneOf with exception',
102119
document: {

tools/spectral/ipa/rulesets/IPA-125.yaml

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -43,48 +43,23 @@ rules:
4343

4444
xgen-IPA-125-oneOf-no-base-types:
4545
description: |
46-
API producers should not use oneOf with base types like integer, string, boolean, or number.
46+
API producers should not use oneOf with different base types like integer, string, boolean, or number or references at the same time.
4747
4848
##### Implementation details
4949
Rule checks for the following conditions:
5050
- Applies to schemas with `oneOf` arrays
51-
- Ensures no element within oneOf has a type property that is a primitive/base type
51+
- Ensures no mixing of base types with references
52+
- Ensures no multiple different base types in the same oneOf
5253
- Base types considered are: integer, string, boolean, number
54+
- Using the same base type multiple times is allowed (e.g., multiple string enums)
5355
5456
##### Rationale
55-
Using oneOf with primitive types can lead to ambiguity and validation problems. Clients may not
57+
Using oneOf with multiple primitive types can lead to ambiguity and validation problems. Clients may not
5658
be able to properly determine which type to use in which context. Instead, use more specific
5759
object types with clear discriminators.
5860
59-
##### Example Violation
60-
```yaml
61-
# Incorrect - Using oneOf with base types
62-
type: object
63-
properties:
64-
value:
65-
oneOf:
66-
- type: string
67-
- type: integer
68-
```
69-
70-
##### Example Compliance
71-
```yaml
72-
# Correct - Using oneOf with object types only
73-
type: object
74-
properties:
75-
value:
76-
oneOf:
77-
- $ref: '#/components/schemas/StringValue'
78-
- $ref: '#/components/schemas/IntegerValue'
79-
discriminator:
80-
propertyName: valueType
81-
mapping:
82-
string: '#/components/schemas/StringValue'
83-
integer: '#/components/schemas/IntegerValue'
84-
```
85-
8661
message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-125-oneOf-no-base-types'
87-
severity: warn
62+
severity: error
8863
given: '$.components.schemas[*]'
8964
then:
9065
function: 'IPA125OneOfNoBaseTypes'

tools/spectral/ipa/rulesets/functions/IPA125OneOfNoBaseTypes.js

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { resolveObject } from './utils/componentUtils.js';
33
import { hasException } from './utils/exceptions.js';
44

55
const RULE_NAME = 'xgen-IPA-125-oneOf-no-base-types';
6-
const ERROR_MESSAGE = 'oneOf should not contain base types like integer, number, string, or boolean.';
6+
const ERROR_MESSAGE_MIXED = 'oneOf should not mix base types with references.';
7+
const ERROR_MESSAGE_MULTIPLE = 'oneOf should not contain multiple different base types.';
78

89
export default (input, _, { path, documentInventory }) => {
910
if (!input.oneOf || !Array.isArray(input.oneOf)) {
@@ -22,14 +23,30 @@ export default (input, _, { path, documentInventory }) => {
2223
};
2324

2425
function checkViolationsAndReturnErrors(schema, path) {
25-
// Check if any oneOf item is a base type
26-
const baseTypes = ['integer', 'number', 'string', 'boolean'];
27-
const hasBaseType = schema.oneOf.some(
28-
(item) => typeof item === 'object' && item.type && baseTypes.includes(item.type)
29-
);
30-
31-
if (hasBaseType) {
32-
return [{ path, message: ERROR_MESSAGE }];
26+
const baseTypes = ['string', 'number', 'integer', 'boolean'];
27+
const foundBaseTypes = new Set();
28+
let hasRef = false;
29+
let hasBaseType = false;
30+
31+
// Check each oneOf item
32+
for (const item of schema.oneOf) {
33+
if (item.$ref) {
34+
hasRef = true;
35+
continue;
36+
}
37+
38+
if (item.type && baseTypes.includes(item.type)) {
39+
hasBaseType = true;
40+
foundBaseTypes.add(item.type);
41+
}
42+
}
43+
44+
if (hasRef && hasBaseType) {
45+
return [{ path, message: ERROR_MESSAGE_MIXED }];
46+
}
47+
48+
if (foundBaseTypes.size > 1) {
49+
return [{ path, message: ERROR_MESSAGE_MULTIPLE }];
3350
}
3451

3552
return [];

0 commit comments

Comments
 (0)