Skip to content

Commit 1d05f9c

Browse files
[#175017856] Handle exclusiveMaximum and exclusiveMinimum (#207)
1 parent 6fcf9c5 commit 1d05f9c

File tree

3 files changed

+218
-12
lines changed

3 files changed

+218
-12
lines changed

__mocks__/api.yaml

+38
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,44 @@ definitions:
213213
type: number
214214
minimum: 0
215215
maximum: 10
216+
WithinRangeExclusiveMinimumNumberTest:
217+
title: WithinRangeExclusiveMinimumNumberTest
218+
type: number
219+
minimum: 0
220+
exclusiveMinimum: true
221+
maximum: 10
222+
WithinRangeExclusiveMaximumNumberTest:
223+
title: WithinRangeExclusiveMaximumNumberTest
224+
type: number
225+
minimum: 0
226+
maximum: 10
227+
exclusiveMaximum: true
228+
WithinRangeExclusiveMinMaxNumberTest:
229+
title: WithinRangeExclusiveMinMaxNumberTest
230+
type: number
231+
minimum: 0
232+
exclusiveMinimum: true
233+
maximum: 10
234+
exclusiveMaximum: true
235+
WithinRangeExclusiveMinimumIntegerTest:
236+
title: WithinRangeExclusiveMinimumIntegerTest
237+
type: integer
238+
minimum: 0
239+
exclusiveMinimum: true
240+
maximum: 10
241+
WithinRangeExclusiveMaximumIntegerTest:
242+
title: WithinRangeExclusiveMaximumIntegerTest
243+
type: integer
244+
minimum: 0
245+
maximum: 10
246+
exclusiveMaximum: true
247+
WithinRangeExclusiveMinMaxIntegerTest:
248+
title: WithinRangeExclusiveMinMaxIntegerTest
249+
type: number
250+
minimum: 0
251+
exclusiveMinimum: true
252+
maximum: 10
253+
exclusiveMaximum: true
216254
InlinePropertyTest:
217255
type: object
218256
properties:

e2e/src/__tests__/test-api/definitions.test.ts

+127-6
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@ import {
88
IWithinRangeIntegerTag,
99
IWithinRangeNumberTag
1010
} from "italia-ts-commons/lib/numbers";
11+
import { readableReport } from "italia-ts-commons/lib/reporters";
12+
import { WithinRangeString } from "italia-ts-commons/lib/strings";
13+
import { WithinRangeExclusiveMaximumIntegerTest } from "../../generated/testapi/WithinRangeExclusiveMaximumIntegerTest";
14+
import { WithinRangeExclusiveMaximumNumberTest } from "../../generated/testapi/WithinRangeExclusiveMaximumNumberTest";
15+
import { WithinRangeExclusiveMinimumIntegerTest } from "../../generated/testapi/WithinRangeExclusiveMinimumIntegerTest";
16+
import { WithinRangeExclusiveMinimumNumberTest } from "../../generated/testapi/WithinRangeExclusiveMinimumNumberTest";
17+
import { WithinRangeExclusiveMinMaxNumberTest } from "../../generated/testapi/WithinRangeExclusiveMinMaxNumberTest";
18+
1119
import { WithinRangeIntegerTest } from "../../generated/testapi/WithinRangeIntegerTest";
1220
import { WithinRangeNumberTest } from "../../generated/testapi/WithinRangeNumberTest";
1321
import { WithinRangeStringTest } from "../../generated/testapi/WithinRangeStringTest";
14-
import { WithinRangeString } from "italia-ts-commons/lib/strings";
15-
import { readableReport } from "italia-ts-commons/lib/reporters";
1622

1723
const { generatedFilesDir, isSpecEnabled } = config.specs.testapi;
1824

@@ -93,7 +99,7 @@ describe("Profile defintion", () => {
9399
});
94100

95101
describe("WithinRangeIntegerTest defintion", () => {
96-
//WithinRangeIntegerTest is defined min=0 max=10 in the spec
102+
// WithinRangeIntegerTest is defined min=0 max=10 in the spec
97103
it.each`
98104
value | expected
99105
${0} | ${true /* lower bound */}
@@ -115,7 +121,7 @@ describe("WithinRangeIntegerTest defintion", () => {
115121
});
116122

117123
describe("WithinRangeNumberTest defintion", () => {
118-
//WithinRangeNumberTest is defined min=0 max=10 in the spec
124+
// WithinRangeNumberTest is defined min=0 max=10 in the spec
119125
it.each`
120126
value | expected
121127
${0} | ${true /* lower bound */}
@@ -135,7 +141,78 @@ describe("WithinRangeNumberTest defintion", () => {
135141
}
136142
);
137143

138-
/* it("should have correct ts types", () => {
144+
describe("WithinRangeExclusiveMinimumNumberTest definition", () => {
145+
// WithinRangeExclusiveMinimumNumberTest is defined min=0 max=10 exclusiveMinimum: true in the spec
146+
it.each`
147+
value | expected
148+
${-1} | ${false}
149+
${0} | ${false}
150+
${0.1} | ${true}
151+
${0.5} | ${true}
152+
${1} | ${true}
153+
${9.9999999} | ${true}
154+
${10} | ${true /* upper bound */}
155+
${10.000001} | ${false}
156+
${11} | ${false}
157+
${100} | ${false}
158+
${undefined} | ${false}
159+
`(
160+
"should decode $value with WithinRangeExclusiveMinimumNumberTest",
161+
({ value, expected }) => {
162+
const result = WithinRangeExclusiveMinimumNumberTest.decode(value);
163+
expect(result.isRight()).toEqual(expected);
164+
}
165+
);
166+
});
167+
describe("WithinRangeExclusiveMaximumNumberTest definition", () => {
168+
// WithinRangeExclusiveMaximumNumberTest is defined min=0 max=10 exclusiveMaximum: true in the spec
169+
it.each`
170+
value | expected
171+
${-1} | ${false}
172+
${0} | ${true /* lower bound */}
173+
${1.5} | ${true}
174+
${5.5} | ${true}
175+
${9} | ${true}
176+
${9.5} | ${true}
177+
${9.999} | ${true}
178+
${10} | ${false}
179+
${11} | ${false}
180+
${100} | ${false}
181+
${undefined} | ${false}
182+
`(
183+
"should decode $value with WithinRangeExclusiveMaximumNumberTest",
184+
({ value, expected }) => {
185+
const result = WithinRangeExclusiveMaximumNumberTest.decode(value);
186+
expect(result.isRight()).toEqual(expected);
187+
}
188+
);
189+
});
190+
191+
describe("WithinRangeExclusiveMinMaxNumberTest definition", () => {
192+
// WithinRangeExclusiveMinMaxNumberTest is defined min=0 max=10 exclusiveMaximum: true exclusiveMinimum: true in the spec
193+
it.each`
194+
value | expected
195+
${-1} | ${false}
196+
${0} | ${false}
197+
${0.1} | ${true}
198+
${1.5} | ${true}
199+
${5.5} | ${true}
200+
${9} | ${true}
201+
${9.5} | ${true}
202+
${9.999} | ${true}
203+
${10} | ${false}
204+
${11} | ${false}
205+
${100} | ${false}
206+
${undefined} | ${false}
207+
`(
208+
"should decode $value with WithinRangeExclusiveMinMaxNumberTest",
209+
({ value, expected }) => {
210+
const result = WithinRangeExclusiveMinMaxNumberTest.decode(value);
211+
expect(result.isRight()).toEqual(expected);
212+
}
213+
);
214+
});
215+
/* it("should have correct ts types", () => {
139216
// value is actually "any"
140217
const value1: WithinRangeNumberTest = WithinRangeNumberTest.decode(10).getOrElseL(err => {
141218
throw new Error(readableReport(err))
@@ -149,8 +226,52 @@ describe("WithinRangeNumberTest defintion", () => {
149226
}) */
150227
});
151228

229+
describe("WithinRangeExclusiveMinimumIntegerTest definition", () => {
230+
// WithinRangeExclusiveMinimumIntegerTest is defined min=0 max=10 exclusiveMinimum: true in the spec
231+
it.each`
232+
value | expected
233+
${0} | ${false}
234+
${-1} | ${false}
235+
${1} | ${true /* lower bound */}
236+
${5} | ${true}
237+
${9} | ${true}
238+
${10} | ${true /* upper bound */}
239+
${11} | ${false}
240+
${100} | ${false}
241+
${undefined} | ${false}
242+
`(
243+
"should decode $value with WithinRangeExclusiveMinimumIntegerTest",
244+
({ value, expected }) => {
245+
const result = WithinRangeExclusiveMinimumIntegerTest.decode(value);
246+
expect(result.isRight()).toEqual(expected);
247+
}
248+
);
249+
});
250+
251+
describe("WithinRangeExclusiveMaximumIntegerTest definition", () => {
252+
// WithinRangeExclusiveMaximumIntegerTest is defined min=0 max=10 exclusiveMaximum: true in the spec
253+
it.each`
254+
value | expected
255+
${0} | ${true /* lower bound */}
256+
${-1} | ${false}
257+
${1} | ${true}
258+
${5} | ${true}
259+
${9} | ${true}
260+
${10} | ${false /* upper bound */}
261+
${11} | ${false}
262+
${100} | ${false}
263+
${undefined} | ${false}
264+
`(
265+
"should decode $value with WithinRangeExclusiveMaximumIntegerTest",
266+
({ value, expected }) => {
267+
const result = WithinRangeExclusiveMaximumIntegerTest.decode(value);
268+
expect(result.isRight()).toEqual(expected);
269+
}
270+
);
271+
});
272+
152273
describe("WithinRangeStringTest defintion", () => {
153-
//WithinRangeStringTest is defined min=8 max=10 in the spec
274+
// WithinRangeStringTest is defined min=8 max=10 in the spec
154275
it.each`
155276
value | expected
156277
${"a".repeat(7)} | ${false}

templates/macros.njk

+53-6
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,55 @@
7171
{% macro defineNumber(definitionName, definition, inline = false) -%}
7272
{% if definition.minimum != undefined and definition.maximum != undefined %}
7373
{{- 'import { IWithinRangeNumberTag, WithinRangeNumber } from "italia-ts-commons/lib/numbers";' | addImport -}}
74-
{% set typedef %}t.union([
75-
WithinRangeNumber<{{ definition.minimum }}, {{ definition.maximum }}, IWithinRangeNumberTag<{{ definition.minimum }}, {{ definition.maximum }}>>({{ definition.minimum }}, {{ definition.maximum }}),
76-
t.literal({{ definition.maximum }})
77-
]){% endset %}
74+
{% if definition.minimum != undefined and definition.maximum != undefined %}
75+
{## [R,L) #}
76+
{% if definition.exclusiveMaximum and not definition.exclusiveMinimum %}
77+
{% set typedef %}
78+
WithinRangeNumber<{{ definition.minimum }}, {{ definition.maximum }}, IWithinRangeNumberTag<{{ definition.minimum }}, {{ definition.maximum }}>>({{ definition.minimum }}, {{ definition.maximum }})
79+
{% endset %}
80+
{## (R,L] #}
81+
{% elif definition.exclusiveMinimum and not definition.exclusiveMaximum %}
82+
{% set typedef %}const WithoutExclusive = t.union([
83+
WithinRangeNumber<{{ definition.minimum }}, {{ definition.maximum }}, IWithinRangeNumberTag<{{ definition.minimum }}, {{ definition.maximum }}>>({{ definition.minimum }}, {{ definition.maximum }}),
84+
t.literal({{ definition.maximum }})
85+
]){% endset %}
86+
{{ defineConst(undefined, 'WithoutExclusive', typedef, false, true) }}
87+
{% set typedef %}
88+
t.brand(
89+
WithoutExclusive,
90+
(
91+
n
92+
): n is t.Branded<
93+
t.TypeOf<typeof WithoutExclusive>,
94+
{ readonly {{ definitionName }}: symbol }
95+
> => n != {{ definition.minimum }},
96+
"{{definitionName}}"
97+
);
98+
{% endset %}
99+
{## (R,L) #}
100+
{% elif definition.exclusiveMaximum and definition.exclusiveMinimum %}
101+
{% set typedef %}const WithoutExclusive = WithinRangeNumber<{{ definition.minimum }}, {{ definition.maximum }}, IWithinRangeNumberTag<{{ definition.minimum }}, {{ definition.maximum }}>>({{ definition.minimum }}, {{ definition.maximum }}){% endset %}
102+
{{ defineConst(undefined, 'WithoutExclusive', typedef, false, true) }}
103+
{% set typedef %}
104+
t.brand(
105+
WithoutExclusive,
106+
(
107+
n
108+
): n is t.Branded<
109+
t.TypeOf<typeof WithoutExclusive>,
110+
{ readonly {{ definitionName }}: symbol }
111+
> => n != {{ definition.minimum }},
112+
"{{definitionName}}"
113+
);
114+
{% endset %}
115+
{## [R,L] #}
116+
{% else %}
117+
{% set typedef %}t.union([
118+
WithinRangeNumber<{{ definition.minimum }}, {{ definition.maximum }}, IWithinRangeNumberTag<{{ definition.minimum }}, {{ definition.maximum }}>>({{ definition.minimum }}, {{ definition.maximum }}),
119+
t.literal({{ definition.maximum }})
120+
]){% endset %}
121+
{% endif %}
122+
{% endif %}
78123
{% elif definition.minimum == "0" %}
79124
{{- 'import { NonNegativeNumber } from "italia-ts-commons/lib/numbers";' | addImport -}}
80125
{% set typedef %}NonNegativeNumber{% endset %}
@@ -89,10 +134,12 @@
89134
#}
90135
{% macro defineInteger(definitionName, definition, inline = false) -%}
91136
{% if definition.minimum != undefined and definition.maximum != undefined %}
137+
{% set minimum = definition.minimum + 1 if definition.exclusiveMinimum else definition.minimum %}
138+
{% set maximum = definition.maximum - 1 if definition.exclusiveMaximum else definition.maximum %}
92139
{{- 'import { IWithinRangeIntegerTag, WithinRangeInteger } from "italia-ts-commons/lib/numbers";' | addImport -}}
93140
{% set typedef %}t.union([
94-
WithinRangeInteger<{{ definition.minimum }}, {{ definition.maximum }}, IWithinRangeIntegerTag<{{ definition.minimum }}, {{ definition.maximum }}>>({{ definition.minimum }}, {{ definition.maximum }}),
95-
t.literal({{ definition.maximum }})
141+
WithinRangeInteger<{{ minimum }}, {{ maximum }}, IWithinRangeIntegerTag<{{ minimum }}, {{ maximum }}>>({{ minimum }}, {{ maximum }}),
142+
t.literal({{ maximum }})
96143
]){% endset %}
97144
{% elif definition.minimum == "0" %}
98145
{{- 'import { NonNegativeInteger } from "italia-ts-commons/lib/numbers";' | addImport -}}

0 commit comments

Comments
 (0)