Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fff1953

Browse files
authored
Merge pull request #60 from mitchknife/fastify-json-schema
Add JsonSchema support to fastify plugin.
2 parents d17ad41 + 580c68b commit fff1953

File tree

4 files changed

+620
-0
lines changed

4 files changed

+620
-0
lines changed

.editorconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ dotnet_diagnostic.IDE0161.severity = suggestion
208208
dotnet_diagnostic.IDE0170.severity = suggestion
209209
dotnet_diagnostic.IDE0180.severity = warning
210210
dotnet_diagnostic.IDE1005.severity = suggestion
211+
dotnet_diagnostic.JSON002.severity = suggestion
211212
dotnet_diagnostic.SA0001.severity = none
212213
dotnet_diagnostic.SA1003.severity = none
213214
dotnet_diagnostic.SA1008.severity = none

conformance/src/fastify/conformanceApiPlugin.ts

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ export type ConformanceApiPluginOptions = RegisterOptions & {
4141
export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOptions> = async (fastify, opts) => {
4242
const { api, caseInsenstiveQueryStringKeys, includeErrorDetails } = opts;
4343

44+
for (const jsonSchema of jsonSchemas) {
45+
fastify.addSchema(jsonSchema);
46+
}
47+
4448
fastify.setErrorHandler((error, req, res) => {
4549
req.log.error(error);
4650
if (includeErrorDetails) {
@@ -76,6 +80,17 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
7680
fastify.route({
7781
url: '/',
7882
method: 'GET',
83+
schema: {
84+
response: {
85+
200: {
86+
type: 'object',
87+
properties: {
88+
service: { type: 'string' },
89+
version: { type: 'string' },
90+
},
91+
},
92+
},
93+
},
7994
handler: async function (req, res) {
8095
const request: IGetApiInfoRequest = {};
8196

@@ -99,6 +114,16 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
99114
fastify.route({
100115
url: '/widgets',
101116
method: 'GET',
117+
schema: {
118+
response: {
119+
200: {
120+
type: 'object',
121+
properties: {
122+
widgets: { type: 'array', items: { $ref: 'Widget' } },
123+
},
124+
},
125+
},
126+
},
102127
handler: async function (req, res) {
103128
const request: IGetWidgetsRequest = {};
104129

@@ -125,6 +150,11 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
125150
fastify.route({
126151
url: '/widgets',
127152
method: 'POST',
153+
schema: {
154+
response: {
155+
201: { $ref: 'Widget' },
156+
},
157+
},
128158
handler: async function (req, res) {
129159
const request: ICreateWidgetRequest = {};
130160

@@ -155,6 +185,12 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
155185
fastify.route({
156186
url: '/widgets/:id',
157187
method: 'GET',
188+
schema: {
189+
response: {
190+
200: { $ref: 'Widget' },
191+
304: { type: 'boolean' },
192+
},
193+
},
158194
handler: async function (req, res) {
159195
const request: IGetWidgetRequest = {};
160196

@@ -193,6 +229,13 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
193229
fastify.route({
194230
url: '/widgets/:id',
195231
method: 'DELETE',
232+
schema: {
233+
response: {
234+
204: { type: 'object', additionalProperties: false },
235+
404: { type: 'boolean' },
236+
409: { type: 'boolean' },
237+
},
238+
},
196239
handler: async function (req, res) {
197240
const request: IDeleteWidgetRequest = {};
198241

@@ -232,6 +275,11 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
232275
fastify.route({
233276
url: '/widgets/get',
234277
method: 'POST',
278+
schema: {
279+
response: {
280+
200: { type: 'array', items: { type: 'object', properties: { value: { $ref: 'Widget' }, error: { $ref: '_error' } } } },
281+
},
282+
},
235283
handler: async function (req, res) {
236284
const request: IGetWidgetBatchRequest = {};
237285

@@ -259,6 +307,17 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
259307
fastify.route({
260308
url: '/mirrorFields',
261309
method: 'POST',
310+
schema: {
311+
response: {
312+
200: {
313+
type: 'object',
314+
properties: {
315+
field: { $ref: 'Any' },
316+
matrix: { type: 'array', items: { type: 'array', items: { type: 'array', items: { type: 'number' } } } },
317+
},
318+
},
319+
},
320+
},
262321
handler: async function (req, res) {
263322
const request: IMirrorFieldsRequest = {};
264323

@@ -286,6 +345,11 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
286345
fastify.route({
287346
url: '/checkQuery',
288347
method: 'GET',
348+
schema: {
349+
response: {
350+
200: { type: 'object', additionalProperties: false },
351+
},
352+
},
289353
handler: async function (req, res) {
290354
const request: ICheckQueryRequest = {};
291355

@@ -319,6 +383,11 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
319383
fastify.route({
320384
url: '/checkPath/:string/:boolean/:double/:int32/:int64/:decimal/:enum/:datetime',
321385
method: 'GET',
386+
schema: {
387+
response: {
388+
200: { type: 'object', additionalProperties: false },
389+
},
390+
},
322391
handler: async function (req, res) {
323392
const request: ICheckPathRequest = {};
324393

@@ -352,6 +421,11 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
352421
fastify.route({
353422
url: '/mirrorHeaders',
354423
method: 'GET',
424+
schema: {
425+
response: {
426+
200: { type: 'object', additionalProperties: false },
427+
},
428+
},
355429
handler: async function (req, res) {
356430
const request: IMirrorHeadersRequest = {};
357431

@@ -394,6 +468,18 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
394468
fastify.route({
395469
url: '/mixed/:path',
396470
method: 'POST',
471+
schema: {
472+
response: {
473+
200: {
474+
type: 'object',
475+
properties: {
476+
normal: { type: 'string' },
477+
},
478+
},
479+
202: { type: 'object', additionalProperties: true },
480+
204: { type: 'boolean' },
481+
},
482+
},
397483
handler: async function (req, res) {
398484
const request: IMixedRequest = {};
399485

@@ -441,6 +527,16 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
441527
fastify.route({
442528
url: '/required',
443529
method: 'POST',
530+
schema: {
531+
response: {
532+
200: {
533+
type: 'object',
534+
properties: {
535+
normal: { type: 'string' },
536+
},
537+
},
538+
},
539+
},
444540
handler: async function (req, res) {
445541
const request: IRequiredRequest = {};
446542

@@ -478,6 +574,11 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
478574
fastify.route({
479575
url: '/mirrorBytes',
480576
method: 'POST',
577+
schema: {
578+
response: {
579+
200: { type: 'string' },
580+
},
581+
},
481582
handler: async function (req, res) {
482583
const request: IMirrorBytesRequest = {};
483584

@@ -510,6 +611,11 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
510611
fastify.route({
511612
url: '/mirrorText',
512613
method: 'POST',
614+
schema: {
615+
response: {
616+
200: { type: 'string' },
617+
},
618+
},
513619
handler: async function (req, res) {
514620
const request: IMirrorTextRequest = {};
515621

@@ -542,6 +648,11 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
542648
fastify.route({
543649
url: '/bodyTypes',
544650
method: 'POST',
651+
schema: {
652+
response: {
653+
200: { type: 'string' },
654+
},
655+
},
545656
handler: async function (req, res) {
546657
const request: IBodyTypesRequest = {};
547658

@@ -567,6 +678,148 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
567678
});
568679
}
569680

681+
const jsonSchemas = [
682+
{
683+
$id: '_error',
684+
type: 'object',
685+
properties: {
686+
code: { type: 'string' },
687+
message: { type: 'string' },
688+
details: { type: 'object', additionalProperties: true },
689+
innerError: { $ref: '_error' },
690+
}
691+
} as const,
692+
{
693+
$id: 'Widget',
694+
type: 'object',
695+
properties: {
696+
id: { type: 'integer' },
697+
name: { type: 'string' },
698+
}
699+
} as const,
700+
{
701+
$id: 'Any',
702+
type: 'object',
703+
properties: {
704+
string: { type: 'string' },
705+
boolean: { type: 'boolean' },
706+
double: { type: 'number' },
707+
int32: { type: 'integer' },
708+
int64: { type: 'integer' },
709+
decimal: { type: 'number' },
710+
datetime: { type: 'string' },
711+
bytes: { type: 'string' },
712+
object: { type: 'object', additionalProperties: true },
713+
error: { $ref: '_error' },
714+
data: { $ref: 'Any' },
715+
enum: { $ref: 'Answer' },
716+
array: { $ref: 'AnyArray' },
717+
map: { $ref: 'AnyMap' },
718+
result: { $ref: 'AnyResult' },
719+
nullable: { $ref: 'AnyNullable' },
720+
}
721+
} as const,
722+
{
723+
$id: 'AnyArray',
724+
type: 'object',
725+
properties: {
726+
string: { type: 'array', items: { type: 'string' } },
727+
boolean: { type: 'array', items: { type: 'boolean' } },
728+
double: { type: 'array', items: { type: 'number' } },
729+
int32: { type: 'array', items: { type: 'integer' } },
730+
int64: { type: 'array', items: { type: 'integer' } },
731+
decimal: { type: 'array', items: { type: 'number' } },
732+
datetime: { type: 'array', items: { type: 'string' } },
733+
bytes: { type: 'array', items: { type: 'string' } },
734+
object: { type: 'array', items: { type: 'object', additionalProperties: true } },
735+
error: { type: 'array', items: { $ref: '_error' } },
736+
data: { type: 'array', items: { $ref: 'Any' } },
737+
enum: { type: 'array', items: { $ref: 'Answer' } },
738+
array: { type: 'array', items: { type: 'array', items: { type: 'integer' } } },
739+
map: { type: 'array', items: { type: 'object', additionalProperties: { type: 'integer' } } },
740+
result: { type: 'array', items: { type: 'object', properties: { value: { type: 'integer' }, error: { $ref: '_error' } } } },
741+
nullable: { type: 'array', items: { oneOf: [ { type: 'integer' }, { type: 'null' } ] } },
742+
}
743+
} as const,
744+
{
745+
$id: 'AnyMap',
746+
type: 'object',
747+
properties: {
748+
string: { type: 'object', additionalProperties: { type: 'string' } },
749+
boolean: { type: 'object', additionalProperties: { type: 'boolean' } },
750+
double: { type: 'object', additionalProperties: { type: 'number' } },
751+
int32: { type: 'object', additionalProperties: { type: 'integer' } },
752+
int64: { type: 'object', additionalProperties: { type: 'integer' } },
753+
decimal: { type: 'object', additionalProperties: { type: 'number' } },
754+
datetime: { type: 'object', additionalProperties: { type: 'string' } },
755+
bytes: { type: 'object', additionalProperties: { type: 'string' } },
756+
object: { type: 'object', additionalProperties: { type: 'object', additionalProperties: true } },
757+
error: { type: 'object', additionalProperties: { $ref: '_error' } },
758+
data: { type: 'object', additionalProperties: { $ref: 'Any' } },
759+
enum: { type: 'object', additionalProperties: { $ref: 'Answer' } },
760+
array: { type: 'object', additionalProperties: { type: 'array', items: { type: 'integer' } } },
761+
map: { type: 'object', additionalProperties: { type: 'object', additionalProperties: { type: 'integer' } } },
762+
result: { type: 'object', additionalProperties: { type: 'object', properties: { value: { type: 'integer' }, error: { $ref: '_error' } } } },
763+
nullable: { type: 'object', additionalProperties: { oneOf: [ { type: 'integer' }, { type: 'null' } ] } },
764+
}
765+
} as const,
766+
{
767+
$id: 'AnyResult',
768+
type: 'object',
769+
properties: {
770+
string: { type: 'object', properties: { value: { type: 'string' }, error: { $ref: '_error' } } },
771+
boolean: { type: 'object', properties: { value: { type: 'boolean' }, error: { $ref: '_error' } } },
772+
double: { type: 'object', properties: { value: { type: 'number' }, error: { $ref: '_error' } } },
773+
int32: { type: 'object', properties: { value: { type: 'integer' }, error: { $ref: '_error' } } },
774+
int64: { type: 'object', properties: { value: { type: 'integer' }, error: { $ref: '_error' } } },
775+
decimal: { type: 'object', properties: { value: { type: 'number' }, error: { $ref: '_error' } } },
776+
datetime: { type: 'object', properties: { value: { type: 'string' }, error: { $ref: '_error' } } },
777+
bytes: { type: 'object', properties: { value: { type: 'string' }, error: { $ref: '_error' } } },
778+
object: { type: 'object', properties: { value: { type: 'object', additionalProperties: true }, error: { $ref: '_error' } } },
779+
error: { type: 'object', properties: { value: { $ref: '_error' }, error: { $ref: '_error' } } },
780+
data: { type: 'object', properties: { value: { $ref: 'Any' }, error: { $ref: '_error' } } },
781+
enum: { type: 'object', properties: { value: { $ref: 'Answer' }, error: { $ref: '_error' } } },
782+
array: { type: 'object', properties: { value: { type: 'array', items: { type: 'integer' } }, error: { $ref: '_error' } } },
783+
map: { type: 'object', properties: { value: { type: 'object', additionalProperties: { type: 'integer' } }, error: { $ref: '_error' } } },
784+
result: { type: 'object', properties: { value: { type: 'object', properties: { value: { type: 'integer' }, error: { $ref: '_error' } } }, error: { $ref: '_error' } } },
785+
nullable: { type: 'object', properties: { value: { oneOf: [ { type: 'integer' }, { type: 'null' } ] }, error: { $ref: '_error' } } },
786+
}
787+
} as const,
788+
{
789+
$id: 'AnyNullable',
790+
type: 'object',
791+
properties: {
792+
string: { oneOf: [ { type: 'string' }, { type: 'null' } ] },
793+
boolean: { oneOf: [ { type: 'boolean' }, { type: 'null' } ] },
794+
double: { oneOf: [ { type: 'number' }, { type: 'null' } ] },
795+
int32: { oneOf: [ { type: 'integer' }, { type: 'null' } ] },
796+
int64: { oneOf: [ { type: 'integer' }, { type: 'null' } ] },
797+
decimal: { oneOf: [ { type: 'number' }, { type: 'null' } ] },
798+
datetime: { oneOf: [ { type: 'string' }, { type: 'null' } ] },
799+
bytes: { oneOf: [ { type: 'string' }, { type: 'null' } ] },
800+
object: { oneOf: [ { type: 'object', additionalProperties: true }, { type: 'null' } ] },
801+
error: { oneOf: [ { $ref: '_error' }, { type: 'null' } ] },
802+
data: { oneOf: [ { $ref: 'Any' }, { type: 'null' } ] },
803+
enum: { oneOf: [ { $ref: 'Answer' }, { type: 'null' } ] },
804+
array: { oneOf: [ { type: 'array', items: { type: 'integer' } }, { type: 'null' } ] },
805+
map: { oneOf: [ { type: 'object', additionalProperties: { type: 'integer' } }, { type: 'null' } ] },
806+
result: { oneOf: [ { type: 'object', properties: { value: { type: 'integer' }, error: { $ref: '_error' } } }, { type: 'null' } ] },
807+
}
808+
} as const,
809+
{
810+
$id: 'HasWidget',
811+
type: 'object',
812+
properties: {
813+
widget: { $ref: 'Widget' },
814+
}
815+
} as const,
816+
{
817+
$id: 'Answer',
818+
type: 'string',
819+
enum: [ 'yes', 'no', 'maybe' ],
820+
} as const,
821+
] as const;
822+
570823
/** API for a Facility test server. */
571824
export interface IConformanceApi {
572825
/** Gets API information. */

0 commit comments

Comments
 (0)