Skip to content

Commit e0dbc06

Browse files
authored
Refactor Schema utility functions such as omit, pick, and partial to preserve the recursive capability of ObjectType, while adding a required utility function. (#198)
* refactor(farrow-schema): enhance Utilities with pick, omit, partial, and required functionalities with preserve the recursive capability of ObjectType * test(farrow-schema): Based on the test results, some problematic code has been modified, and relevant tests for the required utility function have been added.
1 parent 894de34 commit e0dbc06

File tree

4 files changed

+287
-58
lines changed

4 files changed

+287
-58
lines changed

packages/farrow-schema/__tests__/formatter.test.ts

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ import {
1919
ReadOnly,
2020
ReadOnlyDeep,
2121
Tuple,
22+
Optional,
2223
} from '../src/schema'
2324

2425
import { formatSchema } from '../src/formatter'
25-
import { partial } from '../src/helper'
26+
import { partial, required } from '../src/helper'
2627

2728
describe('Formatter', () => {
2829
it('supports format Number', () => {
@@ -729,12 +730,11 @@ describe('Formatter', () => {
729730

730731
const result0 = formatSchema(PartialUser)
731732
const result1 = formatSchema(PartialPerson)
732-
733733
expect(result0).toEqual({
734734
typeId: 0,
735735
types: {
736736
'0': {
737-
type: 'Struct',
737+
type: 'Object',
738738
fields: {
739739
name: {
740740
typeId: 2,
@@ -782,7 +782,6 @@ describe('Formatter', () => {
782782
},
783783
},
784784
})
785-
786785
expect(result1).toEqual({
787786
typeId: 0,
788787
types: {
@@ -822,4 +821,92 @@ describe('Formatter', () => {
822821
},
823822
})
824823
})
824+
it('support format required struct/object', () => {
825+
class User extends ObjectType {
826+
name = String
827+
friends = Optional(List(User))
828+
}
829+
const Person = Struct({
830+
name: Optional(String),
831+
age: Int,
832+
})
833+
834+
const result0 = formatSchema(required(User))
835+
const result1 = formatSchema(required(Person))
836+
expect(result0).toEqual({
837+
typeId: 0,
838+
types: {
839+
'0': {
840+
type: 'Object',
841+
fields: {
842+
name: {
843+
typeId: 1,
844+
$ref: '#/types/1',
845+
},
846+
friends: {
847+
typeId: 3,
848+
$ref: '#/types/3',
849+
},
850+
},
851+
},
852+
'1': {
853+
type: 'Scalar',
854+
valueType: 'string',
855+
valueName: 'String',
856+
},
857+
'2': {
858+
type: 'Object',
859+
name: 'User',
860+
fields: {
861+
name: {
862+
typeId: 1,
863+
$ref: '#/types/1',
864+
},
865+
friends: {
866+
typeId: 4,
867+
$ref: '#/types/4',
868+
},
869+
},
870+
},
871+
'3': {
872+
type: 'List',
873+
itemTypeId: 2,
874+
$ref: '#/types/2',
875+
},
876+
'4': {
877+
type: 'Optional',
878+
itemTypeId: 3,
879+
$ref: '#/types/3',
880+
},
881+
},
882+
})
883+
expect(result1).toEqual({
884+
typeId: 0,
885+
types: {
886+
'0': {
887+
type: 'Struct',
888+
fields: {
889+
name: {
890+
typeId: 1,
891+
$ref: '#/types/1',
892+
},
893+
age: {
894+
typeId: 2,
895+
$ref: '#/types/2',
896+
},
897+
},
898+
},
899+
'1': {
900+
type: 'Scalar',
901+
valueType: 'string',
902+
valueName: 'String',
903+
},
904+
'2': {
905+
type: 'Scalar',
906+
valueType: 'number',
907+
valueName: 'Int',
908+
},
909+
},
910+
})
911+
})
825912
})

packages/farrow-schema/__tests__/validator.test.ts

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
import * as Schema from '../src/schema'
22
import { ReadOnly, TypeOf, ReadOnlyDeep, Optional } from '../src/schema'
3-
import { createSchemaValidator, RegExp, ValidationError, ValidationResult, Validator, ValidatorType } from '../src/validator'
4-
import { pick, omit, keyof, partial } from '../src/helper'
3+
import {
4+
createSchemaValidator,
5+
RegExp,
6+
ValidationError,
7+
ValidationResult,
8+
Validator,
9+
ValidatorType,
10+
} from '../src/validator'
11+
import { pick, omit, keyof, partial, required } from '../src/helper'
12+
import { formatSchema } from '../src/formatter'
513

614
const {
715
Type,
@@ -271,28 +279,28 @@ describe('Validator', () => {
271279
b: '1',
272280
c: false,
273281
d: [1, 2, 3],
274-
e: 'abc'
282+
e: 'abc',
275283
}),
276284
),
277285
).toEqual({
278286
a: 1,
279287
b: '1',
280288
c: false,
281289
d: [1, 2, 3],
282-
e: 'abc'
290+
e: 'abc',
283291
})
284292

285-
// field e is not optional
286-
expect(
287-
assertErr(
288-
validate({
289-
a: 1,
290-
b: '1',
291-
c: false,
292-
d: [1, 2, 3],
293-
}),
294-
),
295-
).toEqual(true)
293+
// field e is not optional
294+
expect(
295+
assertErr(
296+
validate({
297+
a: 1,
298+
b: '1',
299+
c: false,
300+
d: [1, 2, 3],
301+
}),
302+
),
303+
).toEqual(true)
296304

297305
expect(
298306
assertOk(
@@ -1223,4 +1231,38 @@ describe('Validator', () => {
12231231
expect(assertOk(Validator.validate(PartialPerson, { name: 'only-name' }))).toEqual({ name: 'only-name' })
12241232
expect(assertOk(Validator.validate(PartialPerson, { name: 'name', age: 1 }))).toEqual({ name: 'name', age: 1 })
12251233
})
1234+
it('support required operator', () => {
1235+
class User extends ObjectType {
1236+
name = String
1237+
friends = Optional(List(User))
1238+
}
1239+
const Person = Struct({
1240+
name: String,
1241+
age: Optional(Int),
1242+
})
1243+
const PersonRequired = required(Person)
1244+
const UserRequired = required(User)
1245+
expect(assertOk(Validator.validate(User, { name: 'optional' }))).toEqual({
1246+
name: 'optional',
1247+
})
1248+
expect(
1249+
assertOk(
1250+
Validator.validate(Person, {
1251+
name: 'optional',
1252+
}),
1253+
),
1254+
).toEqual({
1255+
name: 'optional',
1256+
})
1257+
expect(assertErr(Validator.validate(PersonRequired, { name: 'optional' }))).toBe(true)
1258+
expect(assertErr(Validator.validate(UserRequired, { name: 'optional' }))).toBe(true)
1259+
expect(assertOk(Validator.validate(PersonRequired, { name: 'required', age: 1 }))).toEqual({
1260+
name: 'required',
1261+
age: 1,
1262+
})
1263+
expect(assertOk(Validator.validate(UserRequired, { name: 'required', friends: [{ name: 'required' }] }))).toEqual({
1264+
name: 'required',
1265+
friends: [{ name: 'required' }],
1266+
})
1267+
})
12261268
})

packages/farrow-schema/src/formatter.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,9 @@ Formatter.impl(S.ObjectType, (schema) => {
441441

442442
return ctx.addType({
443443
type: 'Object',
444-
name: Constructor.displayName ?? Constructor.name,
444+
// if keep 'Constructor.displayName ?? Constructor.name', it will be 'PartialObject' or 'RequiredObject' with partial function, although we can name it with displayName like 'PartialUser' using Ctor.name in the function, However, in scenarios where tool functions are nested, the default naming might be confusing or troublesome.
445+
// @ts-ignore
446+
name: Constructor.displayName ?? (Constructor.name === 'PartialObject' || Constructor.name === 'RequiredObject' ? undefined : Constructor.name),
445447
namespace: Constructor.namespace,
446448
get fields() {
447449
return getFields()

0 commit comments

Comments
 (0)