Skip to content

Commit

Permalink
feat: support minimum / maximum for numbers (#140)
Browse files Browse the repository at this point in the history
- includes `minimum` / `maximum` on the numeric ir model
- uses these to add validations with `zod`
- add `prettier` to fix bad inline-snapshot indentation (see
jestjs/jest#15014)
- memorize openapi validator in unit tests, speeding them up
considerably (`~6s` -> `~1s` for the zod spec file)
- improve test harness for zod to support directly providing `IRModel`
objects, and also execute the resulting schemas to parse a value
- this makes it easier to write a broader range of tests, and should
also prevent misunderstandings of the zod API

Partially addresses #51
  • Loading branch information
mnahkies committed Apr 6, 2024
1 parent 8375c2f commit 21f2a76
Show file tree
Hide file tree
Showing 12 changed files with 313 additions and 183 deletions.
Expand Up @@ -20081,7 +20081,7 @@ export function createRouter(implementation: Implementation): KoaRouter {
before: z.string().optional(),
after: z.string().optional(),
direction: z.enum(["asc", "desc"]).optional(),
per_page: z.coerce.number().optional(),
per_page: z.coerce.number().min(1).max(100).optional(),
sort: z.enum(["updated", "published"]).optional(),
})

Expand Down Expand Up @@ -21830,8 +21830,8 @@ export function createRouter(implementation: Implementation): KoaRouter {
direction: z.enum(["asc", "desc"]).optional(),
before: z.string().optional(),
after: z.string().optional(),
first: z.coerce.number().optional(),
last: z.coerce.number().optional(),
first: z.coerce.number().min(1).max(100).optional(),
last: z.coerce.number().min(1).max(100).optional(),
per_page: z.coerce.number().optional(),
})

Expand Down Expand Up @@ -29346,8 +29346,8 @@ export function createRouter(implementation: Implementation): KoaRouter {
direction: z.enum(["asc", "desc"]).optional(),
before: z.string().optional(),
after: z.string().optional(),
first: z.coerce.number().optional(),
last: z.coerce.number().optional(),
first: z.coerce.number().min(1).max(100).optional(),
last: z.coerce.number().min(1).max(100).optional(),
per_page: z.coerce.number().optional(),
})

Expand Down Expand Up @@ -34758,7 +34758,7 @@ export function createRouter(implementation: Implementation): KoaRouter {
sort: z.enum(["created", "updated", "published"]).optional(),
before: z.string().optional(),
after: z.string().optional(),
per_page: z.coerce.number().optional(),
per_page: z.coerce.number().min(1).max(100).optional(),
state: z.enum(["triage", "draft", "published", "closed"]).optional(),
})

Expand Down Expand Up @@ -50520,8 +50520,8 @@ export function createRouter(implementation: Implementation): KoaRouter {
per_page: z.coerce.number().optional(),
before: z.string().optional(),
after: z.string().optional(),
first: z.coerce.number().optional(),
last: z.coerce.number().optional(),
first: z.coerce.number().min(1).max(100).optional(),
last: z.coerce.number().min(1).max(100).optional(),
})

const dependabotListAlertsForRepoResponseValidator =
Expand Down Expand Up @@ -63477,7 +63477,7 @@ export function createRouter(implementation: Implementation): KoaRouter {
sort: z.enum(["created", "updated", "published"]).optional(),
before: z.string().optional(),
after: z.string().optional(),
per_page: z.coerce.number().optional(),
per_page: z.coerce.number().min(1).max(100).optional(),
state: z.enum(["triage", "draft", "published", "closed"]).optional(),
})

Expand Down
Expand Up @@ -2113,7 +2113,7 @@ export const s_repository_rule_pull_request = z.object({
dismiss_stale_reviews_on_push: z.coerce.boolean(),
require_code_owner_review: z.coerce.boolean(),
require_last_push_approval: z.coerce.boolean(),
required_approving_review_count: z.coerce.number(),
required_approving_review_count: z.coerce.number().max(10),
required_review_thread_resolution: z.coerce.boolean(),
})
.optional(),
Expand Down Expand Up @@ -3137,7 +3137,7 @@ export const s_global_advisory = z.object({
cvss: z
.object({
vector_string: z.string().nullable(),
score: z.coerce.number().nullable(),
score: z.coerce.number().max(10).nullable(),
})
.nullable(),
cwes: z.array(z.object({ cwe_id: z.string(), name: z.string() })).nullable(),
Expand Down Expand Up @@ -5358,7 +5358,7 @@ export const s_dependabot_alert_security_advisory = z.object({
vulnerabilities: z.array(s_dependabot_alert_security_vulnerability),
severity: z.enum(["low", "medium", "high", "critical"]),
cvss: z.object({
score: z.coerce.number(),
score: z.coerce.number().max(10),
vector_string: z.string().nullable(),
}),
cwes: z.array(z.object({ cwe_id: z.string(), name: z.string() })),
Expand Down Expand Up @@ -6106,7 +6106,7 @@ export const s_protected_branch_pull_request_review = z.object({
.optional(),
dismiss_stale_reviews: z.coerce.boolean(),
require_code_owner_reviews: z.coerce.boolean(),
required_approving_review_count: z.coerce.number().optional(),
required_approving_review_count: z.coerce.number().max(6).optional(),
require_last_push_approval: z.coerce.boolean().optional(),
})

Expand Down Expand Up @@ -6626,7 +6626,7 @@ export const s_repository_advisory = z.object({
cvss: z
.object({
vector_string: z.string().nullable(),
score: z.coerce.number().nullable(),
score: z.coerce.number().max(10).nullable(),
})
.nullable(),
cwes: z.array(z.object({ cwe_id: z.string(), name: z.string() })).nullable(),
Expand Down
Expand Up @@ -843,7 +843,7 @@ export function createRouter(implementation: Implementation): KoaRouter {

const listClientsQuerySchema = z.object({
after: z.string().optional(),
limit: z.coerce.number().optional(),
limit: z.coerce.number().min(1).max(200).optional(),
q: z.string().optional(),
})

Expand Down
Expand Up @@ -39,15 +39,15 @@ export const s_BackchannelAuthorizeRequest = z.intersection(
id_token_hint: z.string().optional(),
login_hint: z.string().optional(),
request: z.string().optional(),
request_expiry: z.coerce.number().optional(),
request_expiry: z.coerce.number().min(1).max(300).optional(),
scope: z.string(),
}),
z.record(z.any()),
)

export const s_BackchannelAuthorizeResponse = z.object({
auth_req_id: z.string().optional(),
expires_in: z.coerce.number().optional(),
expires_in: z.coerce.number().min(1).max(300).optional(),
interval: z.coerce.number().optional(),
})

Expand Down
2 changes: 2 additions & 0 deletions jest.base.js
Expand Up @@ -8,6 +8,8 @@ const config = {
},
resetMocks: true,
testMatch: ["**/*.spec.ts"],
// Note: prettier is required for inline snapshot indentation to work correctly
prettierPath: require.resolve("prettier"),
}

module.exports = config
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -51,6 +51,7 @@
"lerna": "^8.1.2",
"lint-staged": "^15.2.2",
"markdown-toc": "^1.2.0",
"prettier": "^3.2.5",
"source-map-support": "^0.5.21",
"typescript": "~5.3.3"
},
Expand Down
2 changes: 2 additions & 0 deletions packages/openapi-code-generator/src/core/input.ts
Expand Up @@ -436,6 +436,8 @@ function normalizeSchemaObject(
// todo: https://github.com/mnahkies/openapi-code-generator/issues/51
format: schemaObject.format,
enum: enumValues.length ? enumValues : undefined,
minimum: schemaObject.minimum,
maximum: schemaObject.maximum,
} satisfies IRModelNumeric
}
case "string": {
Expand Down
Expand Up @@ -14,6 +14,8 @@ export interface IRModelNumeric extends IRModelBase {
type: "number"
format?: IRModelNumericFormat | string | undefined
enum?: number[] | undefined
minimum?: number | undefined
maximum?: number | undefined
}

export type IRModelStringFormat =
Expand Down
9 changes: 8 additions & 1 deletion packages/openapi-code-generator/src/test/input.test-utils.ts
@@ -1,6 +1,7 @@
import path from "path"
import {jest} from "@jest/globals"
import yaml from "js-yaml"
import _ from "lodash"
import {Input} from "../core/input"
import {logger} from "../core/logger"
import {OpenapiLoader} from "../core/openapi-loader"
Expand Down Expand Up @@ -30,13 +31,19 @@ function fileForVersion(version: Version) {
}
}

export async function unitTestInput(version: Version, skipValidation = false) {
const getValidator = _.memoize(async (skipValidation: boolean) => {
const validator = await OpenapiValidator.create()

if (skipValidation) {
jest.spyOn(validator, "validate").mockResolvedValue()
}

return validator
})

export async function unitTestInput(version: Version, skipValidation = false) {
const validator = await getValidator(skipValidation)

const file = fileForVersion(version)
const loader = await OpenapiLoader.create(file, validator)

Expand Down

0 comments on commit 21f2a76

Please sign in to comment.