Skip to content

Commit

Permalink
Fix rec validator (#140)
Browse files Browse the repository at this point in the history
* Fix record validator

* version

* fix ci

* extends

* fix ci
  • Loading branch information
lucasavila00 authored Apr 18, 2024
1 parent a38ee87 commit e96685e
Show file tree
Hide file tree
Showing 41 changed files with 183 additions and 3,802 deletions.
7 changes: 7 additions & 0 deletions examples/standalone-parser/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# node-server

## 1.0.10

### Patch Changes

- Updated dependencies
- @beff/cli@0.0.13

## 1.0.9

### Patch Changes
Expand Down
4 changes: 2 additions & 2 deletions examples/standalone-parser/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "standalone-parser",
"version": "1.0.9",
"version": "1.0.10",
"description": "",
"main": "index.js",
"scripts": {
Expand All @@ -11,7 +11,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"@beff/cli": "workspace:^0.0.12",
"@beff/cli": "workspace:^0.0.13",
"vitest": "^0.34.4"
}
}
17 changes: 14 additions & 3 deletions examples/standalone-parser/src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import parse from "./generated/parser";

enum AccessLevel {
export enum AccessLevel {
ADMIN = "ADMIN",
USER = "USER",
}

type AvatarSize = `${number}x${number}`;
type Extra = Record<string, string>;
export type Extra = Record<string, string>;

export type User = {
accessLevel: AccessLevel;
Expand All @@ -18,4 +18,15 @@ export type User = {

export type PublicUser = Omit<User, "friends">;

export const { User, PublicUser } = parse.buildParsers<{ User: User; PublicUser: PublicUser }>();
type WithOptionals = {
optional?: string;
};

type Req = Required<WithOptionals>;

export const { Extra, User, PublicUser } = parse.buildParsers<{
Extra: Extra;
User: User;
PublicUser: PublicUser;
Req: Req;
}>();
117 changes: 40 additions & 77 deletions examples/standalone-parser/tests/parser.test.ts
Original file line number Diff line number Diff line change
@@ -1,95 +1,58 @@
import { it, expect } from "vitest";
import { User } from "../src/parser";
import { AccessLevel, Extra, User } from "../src/parser";

it("checks records", () => {
expect(Extra.safeParse({ key: 123 })).toMatchInlineSnapshot(`
{
"errors": [
{
"message": "expected string",
"path": [
"key",
],
"received": 123,
},
],
"success": false,
}
`);
});

it("works on recursive type", () => {
const valid = {
const valid: User = {
name: "User1",
friends: [
{
name: "User2",
friends: [],
accessLevel: AccessLevel.USER,
avatarSize: "100x100",
extra: {},
},
],
accessLevel: AccessLevel.ADMIN,
avatarSize: "100x100",
extra: {
key: "value",
},
};
expect(User.safeParse(valid)).toMatchInlineSnapshot(`
expect(User.parse(valid)).toMatchInlineSnapshot(`
{
"errors": [
{
"errors": [
{
"message": "expected \\"ADMIN\\"",
"path": [],
"received": undefined,
},
{
"message": "expected \\"USER\\"",
"path": [],
"received": undefined,
},
],
"isUnionError": true,
"message": "expected one of",
"path": [
"accessLevel",
],
"received": undefined,
},
{
"message": "expected string",
"path": [
"avatarSize",
],
"received": undefined,
},
{
"message": "expected object",
"path": [
"extra",
],
"received": undefined,
},
{
"errors": [
{
"message": "expected \\"ADMIN\\"",
"path": [],
"received": undefined,
},
{
"message": "expected \\"USER\\"",
"path": [],
"received": undefined,
},
],
"isUnionError": true,
"message": "expected one of",
"path": [
"friends",
"[0]",
"accessLevel",
],
"received": undefined,
},
{
"message": "expected string",
"path": [
"friends",
"[0]",
"avatarSize",
],
"received": undefined,
},
"accessLevel": "ADMIN",
"avatarSize": "100x100",
"extra": {
"key": "value",
},
"friends": [
{
"message": "expected object",
"path": [
"friends",
"[0]",
"extra",
],
"received": undefined,
"accessLevel": "USER",
"avatarSize": "100x100",
"extra": {},
"friends": [],
"name": "User2",
},
],
"success": false,
"name": "User1",
}
`);
const invalid = {
Expand Down
6 changes: 6 additions & 0 deletions packages/beff-cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @beff/cli

## 0.0.13

### Patch Changes

- fix record validator

## 0.0.12

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/beff-cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@beff/cli",
"version": "0.0.12",
"version": "0.0.13",
"description": "",
"bin": {
"beff": "./bin/index.js"
Expand Down
8 changes: 8 additions & 0 deletions packages/beff-client/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# @beff/client

## 0.0.13

### Patch Changes

- fix record validator
- Updated dependencies
- @beff/cli@0.0.13

## 0.0.12

### Patch Changes
Expand Down
4 changes: 2 additions & 2 deletions packages/beff-client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@beff/client",
"version": "0.0.12",
"version": "0.0.13",
"description": "",
"main": "dist/cjs/index.js",
"scripts": {
Expand All @@ -20,7 +20,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"@beff/cli": "workspace:^0.0.12"
"@beff/cli": "workspace:^0.0.13"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^6.7.2",
Expand Down
8 changes: 8 additions & 0 deletions packages/beff-core/src/diag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use crate::{open_api_ast::HTTPMethod, BffFileName, ParsedModule};

#[derive(Debug, Clone)]
pub enum DiagnosticInfoMessage {
ExtendsShouldBeIdent,
TypeArgsInExtendsUnsupported,
RequiredShouldHaveObjectAsTypeArgument,
MissingArgumentsOnRequired,
OmitShouldHaveStringOrStringArrayAsTypeArgument,
Expand Down Expand Up @@ -244,6 +246,12 @@ impl DiagnosticInfoMessage {
DiagnosticInfoMessage::MissingArgumentsOnRequired => {
"Missing arguments on required".to_string()
}
DiagnosticInfoMessage::TypeArgsInExtendsUnsupported => {
"Type arguments in extends are not supported".to_string()
}
DiagnosticInfoMessage::ExtendsShouldBeIdent => {
"Extends should be an identifier".to_string()
}
}
}
}
Expand Down
56 changes: 41 additions & 15 deletions packages/beff-core/src/type_to_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ use swc_atoms::JsWord;
use swc_common::{Span, Spanned};
use swc_ecma_ast::{
Expr, Ident, Lit, MemberProp, Prop, PropName, PropOrSpread, Str, TsArrayType,
TsConditionalType, TsConstructorType, TsEntityName, TsEnumDecl, TsFnOrConstructorType,
TsFnType, TsImportType, TsIndexedAccessType, TsInferType, TsInterfaceDecl, TsIntersectionType,
TsKeywordType, TsKeywordTypeKind, TsLit, TsLitType, TsMappedType, TsOptionalType,
TsParenthesizedType, TsQualifiedName, TsRestType, TsThisType, TsTplLitType, TsTupleType,
TsType, TsTypeElement, TsTypeLit, TsTypeOperator, TsTypeOperatorOp, TsTypeParam,
TsTypeParamDecl, TsTypeParamInstantiation, TsTypePredicate, TsTypeQuery, TsTypeQueryExpr,
TsTypeRef, TsUnionOrIntersectionType, TsUnionType,
TsConditionalType, TsConstructorType, TsEntityName, TsEnumDecl, TsExprWithTypeArgs,
TsFnOrConstructorType, TsFnType, TsImportType, TsIndexedAccessType, TsInferType,
TsInterfaceDecl, TsIntersectionType, TsKeywordType, TsKeywordTypeKind, TsLit, TsLitType,
TsMappedType, TsOptionalType, TsParenthesizedType, TsQualifiedName, TsRestType, TsThisType,
TsTplLitType, TsTupleType, TsType, TsTypeElement, TsTypeLit, TsTypeOperator, TsTypeOperatorOp,
TsTypeParam, TsTypeParamDecl, TsTypeParamInstantiation, TsTypePredicate, TsTypeQuery,
TsTypeQueryExpr, TsTypeRef, TsUnionOrIntersectionType, TsUnionType,
};

pub struct TypeToSchema<'a, R: FileManager> {
Expand Down Expand Up @@ -339,18 +339,36 @@ impl<'a, R: FileManager> TypeToSchema<'a, R> {

Ok(JsonSchema::any_of(values))
}

fn convert_interface_extends(&mut self, typ: &Vec<TsExprWithTypeArgs>) -> Res<Vec<JsonSchema>> {
let mut vs = vec![];

for it in typ {
match it.type_args {
Some(_) => {
return self.error(
&it.span,
DiagnosticInfoMessage::TypeArgsInExtendsUnsupported,
)
}
None => match it.expr.as_ref() {
Expr::Ident(id) => {
let id_ty = self.get_type_ref(id, &None)?;
vs.push(id_ty);
}
_ => return self.error(&it.span, DiagnosticInfoMessage::ExtendsShouldBeIdent),
},
}
}

Ok(vs)
}

fn convert_ts_interface_decl(
&mut self,
typ: &TsInterfaceDecl,
type_args: &Option<Box<TsTypeParamInstantiation>>,
) -> Res<JsonSchema> {
if !typ.extends.is_empty() {
return self.cannot_serialize_error(
&typ.span,
DiagnosticInfoMessage::TsInterfaceExtendsNotSupported,
);
}

let args = type_args
.as_ref()
.map(|it| it.params.iter().map(|it| it.as_ref()).collect::<Vec<_>>());
Expand All @@ -368,7 +386,15 @@ impl<'a, R: FileManager> TypeToSchema<'a, R> {
.collect::<Res<_>>()?,
));
self.type_param_stack.pop();
r

if typ.extends.is_empty() {
r
} else {
let ext = self.convert_interface_extends(&typ.extends)?;
Ok(JsonSchema::all_of(
ext.into_iter().chain(std::iter::once(r?)).collect(),
))
}
}

fn convert_type_export(
Expand Down
14 changes: 14 additions & 0 deletions packages/beff-core/tests/print_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,18 @@ mod tests {
parse.buildParsers<{ A: Required<MaybeUser> }>();
"#));
}
#[test]
fn ok_interface_extends() {
insta::assert_snapshot!(ok(r#"
interface User {
name: string,
age: number,
}
interface Admin extends User {
role: string,
}
parse.buildParsers<{ Admin: Admin }>();
"#));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
source: packages/beff-core/tests/print_parser.rs
expression: "ok(r#\"\n\n interface User {\n name: string,\n age: number,\n }\n interface Admin extends User {\n role: string,\n }\n parse.buildParsers<{ Admin: Admin }>();\n \"#)"
---
type Admin = { "role": string } & User;
type User = { "age": number; "name": string };
type Admin = Admin;

Loading

0 comments on commit e96685e

Please sign in to comment.