From f85927196fb458aa6183b4d36a56a5a6690413db Mon Sep 17 00:00:00 2001 From: Pawel Date: Fri, 19 Jul 2024 16:35:40 +0200 Subject: [PATCH] validate values, default them to appropriate properties and test them --- src/lib/blueprint.ts | 47 ++++++++-------- test/data-validation.test.ts | 61 +++++++++++++++++---- test/fixtures/types/openapi.ts | 12 ++-- test/snapshots/blueprint.test.ts.md | 24 +++++--- test/snapshots/blueprint.test.ts.snap | Bin 1369 -> 1447 bytes test/snapshots/seam-blueprint.test.ts.md | 32 +++++------ test/snapshots/seam-blueprint.test.ts.snap | Bin 2673 -> 2671 bytes 7 files changed, 112 insertions(+), 64 deletions(-) diff --git a/src/lib/blueprint.ts b/src/lib/blueprint.ts index e48eb0b..6feaef3 100644 --- a/src/lib/blueprint.ts +++ b/src/lib/blueprint.ts @@ -405,29 +405,28 @@ const createResponse = (responses: OpenapiOperation['responses']): Response => { } } -const PropertySchema = z.object({ - name: z.string(), - description: z.string().optional().default(''), - // TODO: Remove the transforms. - // This should be a boolean, but the OpenAPI spec allows for a string value - isDeprecated: z - .union([z.boolean(), z.string()]) - .transform((value) => { - if (typeof value === 'boolean') return value - return value === 'true' - }) - .default(false), - deprecationMessage: z.string().optional().default(''), - isUndocumented: z - .union([z.boolean(), z.string()]) - .transform((value) => { - if (typeof value === 'boolean') return value - return value === 'true' - }) - .default(false), - 'x-undocumented': z.string().optional(), - 'x-deprecated': z.string().default('false'), -}) +const PropertySchema = z + .object({ + name: z.string(), + description: z.string().optional().default(''), + // TODO: Remove the transform + isDeprecated: z + .union([z.boolean(), z.string()]) + .transform((value) => { + if (typeof value === 'boolean') return value + return value === 'true' + }) + .default(false), + deprecationMessage: z.string().optional().default(''), + isUndocumented: z.boolean().default(true), + 'x-undocumented': z.string().optional(), + 'x-deprecated': z.string().default('false'), + }) + .transform((data) => ({ + ...data, + isUndocumented: + data['x-undocumented'] !== undefined ? true : data.isUndocumented, + })) const createProperties = ( properties: Record, @@ -438,7 +437,7 @@ const createProperties = ( description: prop.description, isDeprecated: prop.deprecated, deprecationMessage: prop['x-deprecated'], - isUndocumented: prop['x-undocumented'] !== undefined, + isUndocumented: true, // Default value, will be overwritten if x-undocumented is present }) const baseProperty = { diff --git a/test/data-validation.test.ts b/test/data-validation.test.ts index 273d95a..37c0198 100644 --- a/test/data-validation.test.ts +++ b/test/data-validation.test.ts @@ -63,26 +63,63 @@ test('Deprecated and undocumented properties are correctly handled', (t) => { const blueprint = createBlueprint(types) const fooResource = blueprint.resources['foo'] - const systemTypeProp = fooResource?.properties.find( - (p) => p.name === 'system_type', + const deprecatedProp = fooResource?.properties.find( + (p) => p.name === 'deprecated_foo', ) - t.truthy(systemTypeProp, 'system_type property should exist') t.true( - systemTypeProp?.isDeprecated, - 'system_type should be marked as deprecated', + deprecatedProp?.isDeprecated, + 'deprecated property should be marked as deprecated', ) t.is( - systemTypeProp?.deprecationMessage, - 'Use `external_type` instead.', + deprecatedProp?.deprecationMessage, + 'Use `other_foo_type` instead.', 'Deprecation message should be correct', ) - const warningsProp = fooResource?.properties.find( - (p) => p.name === 'warnings', + const undocumentedProp = fooResource?.properties.find( + (p) => p.name === 'undocumented_foo', ) - t.truthy(warningsProp, 'warnings property should exist') + t.truthy(undocumentedProp, 'undocumented property should exist') t.true( - warningsProp?.isUndocumented, - 'warnings should be marked as undocumented', + undocumentedProp?.isUndocumented, + 'undocumented property should be marked as undocumented', ) + + const missingXProp = fooResource?.properties.find( + (p) => p.name === 'missing_x_properties', + ) + t.truthy(missingXProp, 'Property missing x- values should exist') + t.false( + missingXProp?.isDeprecated, + 'Property without x-deprecated should not be marked as deprecated', + ) + t.true( + missingXProp?.isUndocumented, + 'Property without x-undocumented should be marked as undocumented', + ) +}) + +test('Properties missing x- values get correct default values', (t) => { + // @ts-expect-error Remove once the fixture is properly typed + const blueprint = createBlueprint(types) + const fooResource = blueprint.resources['foo'] + + const missingXProp = fooResource?.properties.find( + (p) => p.name === 'missing_x_properties', + ) + + t.truthy(missingXProp, 'Property missing x- values should exist') + t.is(missingXProp?.type, 'string', 'Type should be correctly set') + t.is( + missingXProp?.description, + 'This property is missing `x-` properties.', + 'Description should be set correctly', + ) + t.false(missingXProp?.isDeprecated, 'isDeprecated should default to false') + t.is( + missingXProp?.deprecationMessage, + '', + 'deprecationMessage should default to an empty string', + ) + t.true(missingXProp?.isUndocumented, 'isUndocumented should default to true') }) diff --git a/test/fixtures/types/openapi.ts b/test/fixtures/types/openapi.ts index c3b321f..872af41 100644 --- a/test/fixtures/types/openapi.ts +++ b/test/fixtures/types/openapi.ts @@ -17,18 +17,18 @@ export default { description: 'Foo name', type: 'string', }, - system_type: { + deprecated_foo: { type: 'string', enum: ['type1', 'type2'], deprecated: true, - 'x-deprecated': 'Use `external_type` instead.', + 'x-deprecated': 'Use `other_foo_type` instead.', }, external_type: { type: 'string', enum: ['type1', 'type2'], - description: 'Brand-specific terminology for the foo type.', + description: 'Foo-specific terminology for the foo type.', }, - warnings: { + undocumented_foo: { type: 'array', items: { type: 'object', @@ -37,6 +37,10 @@ export default { }, 'x-undocumented': 'Currently, no warnings defined for `foo`s.', }, + missing_x_properties: { + type: 'string', + description: 'This property is missing `x-` properties.', + }, }, required: ['foo_id', 'name'], }, diff --git a/test/snapshots/blueprint.test.ts.md b/test/snapshots/blueprint.test.ts.md index bd32217..8671935 100644 --- a/test/snapshots/blueprint.test.ts.md +++ b/test/snapshots/blueprint.test.ts.md @@ -16,7 +16,7 @@ Generated by [AVA](https://avajs.dev). deprecationMessage: '', description: 'Foo id', isDeprecated: false, - isUndocumented: false, + isUndocumented: true, name: 'foo_id', type: 'string', }, @@ -24,16 +24,16 @@ Generated by [AVA](https://avajs.dev). deprecationMessage: '', description: 'Foo name', isDeprecated: false, - isUndocumented: false, + isUndocumented: true, name: 'name', type: 'string', }, { - deprecationMessage: 'Use `external_type` instead.', + deprecationMessage: 'Use `other_foo_type` instead.', description: '', isDeprecated: true, - isUndocumented: false, - name: 'system_type', + isUndocumented: true, + name: 'deprecated_foo', type: 'enum', values: [ { @@ -46,9 +46,9 @@ Generated by [AVA](https://avajs.dev). }, { deprecationMessage: '', - description: 'Brand-specific terminology for the foo type.', + description: 'Foo-specific terminology for the foo type.', isDeprecated: false, - isUndocumented: false, + isUndocumented: true, name: 'external_type', type: 'enum', values: [ @@ -65,9 +65,17 @@ Generated by [AVA](https://avajs.dev). description: '', isDeprecated: false, isUndocumented: true, - name: 'warnings', + name: 'undocumented_foo', type: 'list', }, + { + deprecationMessage: '', + description: 'This property is missing `x-` properties.', + isDeprecated: false, + isUndocumented: true, + name: 'missing_x_properties', + type: 'string', + }, ], resourceType: 'foo', }, diff --git a/test/snapshots/blueprint.test.ts.snap b/test/snapshots/blueprint.test.ts.snap index b6005379fe33ced50ee52d77d1e1dd170a49d643..299da96f31b488f89b53794be1d9dea51877fa3c 100644 GIT binary patch literal 1447 zcmV;Y1z7q)RzVFJKu-yxh=NG)hn$Sp7!7z4VlJKpL%;+P@dwjA z+mq?)o!trfAw90l_tmSaS9Pp0-D0)<;i2P{ySznNEuapkTUs6J2d>B6&~yB7 zhVk&w@p|3i&fe-N87iV5(MWCsfQ)Ez1Hafyf{ugd7Gi z8nu-&(E}Q?PEey^-1TK&S3Y1J9(^}aB~tQLiTzCe$=eu<9Fcwk@U!TEly*QP(jNf+ zNnpz6AaG_$Icb@u8GA`$S5$J<6}b@&Q;55YgsdlX@ynC|`w8$W5r&q`ptMc~9+d?xZ9)DN#l0zt0!Tid)je`Nt3^VN$VjI8Ow^R( zrjbLO!9_y0N6lQ^d?jisF-;eCDwoh`N|&aaQj0685p`Svb}PWk3hA7l4bANfFpu6ihAAw9qU9?ILir2%Ibe=Zk=304oe&lL72CfY1VC4pvXW~Jwa{HWBaAJCe^UFPkeVdylIH4On-tkbX- zxPFA}?VQ^fv)#J5L%8AAIaXSaKT4JW8F9niUY*rCZR+(vrkK0Y_Rt%u!PsPGs2uOx zgz_N|T(5Z(;?$xzBX4d+q`7$p8W^WKbev@Q?vUD3jL*YQoU&anyaI9uvF%%68!o#;%sCUJ&If`_&-wl?%>&26|f zo714}daT8ovvz|8HlLw(gE2euP!#i~q)CY$iz3H9dyp>F7E86r=vKYFXv zhhH>PT5ia1!nW|>#Ecu-bCuGp?S^*S4MW#!+EWMDPT5yWs9TZ{qjQ5#9-JIlybat( z^xH85tT<`!8|g1goxo{RPJ>Kj14_mx`mEJ}9_Y|06-Ed-(AS{_H1wGlipwZ{2Pj0O zW0a3ke!FY-{&i{g^&rhIU$bV#In8#mh(aUsY7wyUM@|W#Cv@Fj<*4>D>svDV*pRjzkGr+`kX?Tk-E*b! z?ZaP4(`-q?Zl|*gz>EaePp2hg^y&8TAl=Shmu|V8Z`LEwI@v5g!OXr~;n|1j`p7 zSTWCg(BR`;iH`0^hQzJE@qFD!DI>xmy>jR4iBprdoN3x}r;|s%{{Z`;)Y%0T008pz Bs|f%A literal 1369 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 ZuCTdKhh(V3GNo&+{{xWSCffHZ008VMTn7LE delta 2253 zcmV;;2r~EY6!8>)K~_N^Q*L2!b7*gLAa*kf0{|$wn4#?%DVQ-5RfGS{DI;Rvlvk$y0Q^}9|8G)Jk!TdVY^?m1_d(OG%RNZ^OzL0S|>t=rUBUCWqP|pe| z5%dx#PyK+of^nDlJnIR5_anAV1o_nT6FTZ4|FpPPb0L6>0bB>b1JJJ3?b7PzGB;66 zXj)lJCX>*AnxW~?S~TrT06L^xyGv^bDEIP#MS1w>sftH+ftMHZ_g1uPvC^-#p`E4w znhsNvGcNU9ms&zv6nh20pH)j@)t1DySiJ@`YeiV?z91kg+GI&;ZEX$B)k2%PwN;xM ziN(`=g$tU4))*oZns$j+^oO3;fZH|Tgr-=as}|IMKCJ=IX~3%*5Q_nOVhUv{;=WjD z&*iZSd;GxjX&_j{PfaDqUbEuUHO+5^Rud|GtU_S}b#32ct|-Lgx0R1vlB3)w7FC9- z@rlTm$DJV-pDBz{(D6p44xZ1H!W!4w+I65;2TUE=0ZZ#E~`N)K|UkJSSbETih3x%F2$NqJg*keYD3YHVqGXcD#iLxd|8U| zP#mcPw1!YTD#gZ7JR!wIC_XR6rci9IM;PdTgr*&oVoNCAC&kuK{J9j96}kMMaB&y2 z|LwzEBf~5`S3esE4#WWw2fiN%o{s}>#DS3p#hbP25~!bT01h+&q5-%d0bHK|?nnSn zB@}91g!;z>@LB@sXaYXn41BE_INc2Vvst0mN2miW!0r~{U<+`j6{t%Bdy;^iRH*TP z2=)FX@VO-LWD+>u2ApgIo@oQx+7)U;gnD5+aB(}Z&<-?o0Oxc7S9bvSbSTuu2=$vC zz_&Yq7bL9{nCk>YC-CJ?g_?*^f7%J0?gakY30%|#+}H&i?*dMCDbyyF8h^SAc&-b0 zy$hJ@27+$jL^trWZs6r^h29()`K@j`06oBT4{)jnc(w=lTMsbNt5CZm)C0Z1p$nJ*yZG+UKlZS;-?f>X7B`l&RAgxoafZS3zV8Ks8}r1rnY(D@ zISsfWJcC#k<=j{dyu8nEoXiU0^NVI?Y|r9*IVHKhmN(-_Xk3N`H8%BBG$)B(|L*Zi-kuM1%mh3Ny9WI83QQ9PSzO`wF&Q zAs?hNgj3rn{&cD5iokP>!-bLGq1}1crU4hkm98n7V-JhHSfBxQEy~SGg9~=8 zP2y8ql7-_Ianr=M(=y_r=hMOq|0!6NE_i>v^sLW#oAFgIn>CF*#-)H}mC&&xO$K~_L* z>Iz02o>~aJTt!A!N7M8J>XU$`M3$!ftmjgmTJVAtDdaK>wTKm&)Lf82qylPDw)8eb z<$0KEZfbSLDUugc#(#nrUB6qBM%`f=6`W4ft7dIe-UOSfYvK2+ENt^zSswqfTzFhXyp~Ev_$KR8PSHHd2)g5{_|gt44uR@5!VGAXtpYHx{)8jFLa$+|82 zJ^S*|CNB#DFW@^PVd`jT%*HaL{$2IM1(yWGwNu=u7F%GJA!v|euIG4*D{H>Z8Pce& z`97t$3L3b?Nq@^B*Yyo`^SN-_wq!CyKj-8?VE2@3;Vot{}|arh3{`wr3OD@C`dL zXOiLCe}8ayO6mi8OPAp!Dy5!q>DP_|M@NC< zD)iiHXk+P6{;|>0vH$a<(nO8bTT(U?5X&G_ZZDFH5yOv!` zj5E)5m`f|Ku&v7(B%G5p$vEChr2(7