@aaroncadillac/crudify-mongo 1.2.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,9 @@
1
+ name : Publish
2
+
1
3
  on:
2
4
  push:
3
- branches: master
5
+ tags:
6
+ - 'v*'
4
7
 
5
8
  jobs:
6
9
  publish:
@@ -10,9 +10,9 @@ jobs:
10
10
  runs-on: ubuntu-latest
11
11
  steps:
12
12
  - name: Checkout repository
13
- uses: actions/checkout@v3
13
+ uses: actions/checkout@v4
14
14
  - name: Set up Node.js
15
- uses: actions/setup-node@v3
15
+ uses: actions/setup-node@v4
16
16
  with:
17
17
  node-version: '20'
18
18
  - name: Install dependencies
package/README.md CHANGED
@@ -55,6 +55,116 @@ const routes = ( fastify, opts, done ) => {
55
55
  })
56
56
  ```
57
57
 
58
+ ### Swagger auto-documentation
59
+
60
+ You can add Swagger auto-documentation to your API, using the [`@fastify/swagger`](https://github.com/fastify/fastify-swagger) and [`@fastify/swagger-ui`](https://github.com/fastify/fastify-swagger-ui) plugins.
61
+
62
+ Once you have those plugins installed, you can use the following code to add Swagger auto-documentation to your API:
63
+
64
+ ```js
65
+ // server.js
66
+
67
+ import 'dotenv/config';
68
+ import Fastify from 'fastify'
69
+ import { dbConnection } from './db-connection.js';
70
+ import { routes } from './routes.js';
71
+ import * as Swagger from '@fastify/swagger'
72
+ import * as SwaggerUI from '@fastify/swagger-ui'
73
+
74
+ const fastify = Fastify({
75
+ logger: true
76
+ })
77
+
78
+ fastify.register(await dbConnection)
79
+ process.env.NODE_ENV == 'development' && fastify.register(Swagger) && await fastify.register(SwaggerUI, {
80
+ routePrefix: '/docs',
81
+ uiConfig: {
82
+ docExpansion: 'list',
83
+ deepLinking: true
84
+ },
85
+ uiHooks: {
86
+ onRequest: function (request, reply, next) { next() },
87
+ preHandler: function (request, reply, next) { next() }
88
+ },
89
+ staticCSP: true,
90
+ transformStaticCSP: (header) => header,
91
+ transformSpecification: (swaggerObject, request, reply) => { return swaggerObject },
92
+ transformSpecificationClone: true
93
+ })
94
+
95
+ fastify.register(routes)
96
+
97
+ fastify.listen({ port: process.env.PORT || 3000, host: process.env.HOST || 'localhost' }, function (err, address) {
98
+ if (err) {
99
+ fastify.log.error(err)
100
+ process.exit(1)
101
+ }
102
+ })
103
+ ```
104
+
105
+ As you can see in the code above, you only need to import the `@fastify/swagger` and `@fastify/swagger-ui` plugins, and then use the `fastify.register` method to register the plugins., you even can customize your swagger documentation path and enable documentation only in development environment (recommended).
106
+
107
+ ### Body and Response schema auto generation
108
+
109
+ To help you to use Swagger `crudify-mongo 1.5.0+` supports `body` and `response` schema auto generation, this feature is enable by default and you only need to install [`mongoose-schema-jsonschema`](https://github.com/DScheglov/mongoose-schema-jsonschema) to use it, this dependency helps you to use your mongoose model schema as a json schema compatible with fastify specification based on [AJV schema validation](https://ajv.js.org).
110
+
111
+ Here an example
112
+
113
+ ```js
114
+ // models/user.js
115
+
116
+ import mongoose from 'mongoose';
117
+ import paginate from 'mongoose-paginate-v2'; // Required for pagination
118
+ import extendMongoose from 'mongoose-schema-jsonschema'; // Required for body and response schema auto generation
119
+
120
+ extendMongoose(mongoose);
121
+
122
+ const Schema = mongoose.Schema;
123
+
124
+ const userSchema = new Schema({
125
+ name: {
126
+ type: String,
127
+ required: true,
128
+ },
129
+ age: {
130
+ type: Number,
131
+ required: true,
132
+ },
133
+ email: {
134
+ type: String,
135
+ required: true,
136
+ },
137
+ }, {
138
+ timestamps: true,
139
+ });
140
+
141
+ userSchema.plugin(paginate);
142
+
143
+ const UserSchema = userSchema.jsonSchema();
144
+ const UserModel = mongoose.model('Users', userSchema);
145
+
146
+ export {
147
+ UserModel,
148
+ UserSchema
149
+ }
150
+ ```
151
+
152
+ Then you need to add on the `JSONSchema` property of your fastify routes
153
+
154
+ ```js
155
+ // routes.js
156
+ import { Crudify } from '@aaroncadillac/crudify-mongo';
157
+ import { UserModel, UserSchema } from './models/user.js';
158
+
159
+ const routes = ( fastify, opts, done ) => {
160
+ fastify.register(Crudify, {
161
+ url: '/users',
162
+ Model: UserModel
163
+ JSONSchema: UserSchema
164
+ })
165
+ }
166
+ ```
167
+
58
168
  ## Getting started
59
169
 
60
170
  ### Installation
package/crudify.js CHANGED
@@ -1,21 +1,96 @@
1
- function Crudify (fastify, opts, next) {
2
- fastify.get(`${opts.url}`, opts, async (req, reply) => {
3
- try {
4
- opts = { ...opts,
5
- schema: {
6
- querystring: {
7
- type: 'object',
8
- properties: {
9
- filters: {
10
- type: 'string'
11
- },
12
- pagination: {
13
- type: 'string'
14
- }
1
+ function removeKeys(obj, keysToRemove) {
2
+ if (typeof obj !== 'object' || obj === null) return;
3
+
4
+ for (const key in obj) {
5
+ if (keysToRemove.includes(key)) {
6
+ delete obj[key];
7
+ } else if (typeof obj[key] === 'object' && obj[key] !== null) {
8
+ removeKeys(obj[key], keysToRemove);
9
+ }
10
+ }
11
+ }
12
+
13
+
14
+ function addPostBody(opts) {
15
+ const schema = opts.JSONSchema
16
+ if ( schema ) {
17
+ const properties = {...schema.properties}
18
+ removeKeys(properties, ['x-ref'])
19
+ const newOpts = {
20
+ ...opts,
21
+ schema: {
22
+ ...opts.schema,
23
+ body: {
24
+ type: schema.type,
25
+ properties,
26
+ required: schema.required
27
+ }
28
+ }
29
+ }
30
+ delete newOpts.schema.body.properties._id
31
+ delete newOpts.schema.body.properties.createdAt
32
+ delete newOpts.schema.body.properties.updatedAt
33
+ return newOpts
34
+ } else {
35
+ return opts
36
+ }
37
+ }
38
+
39
+ function addGetResponseUnitary(opts) {
40
+ const schema = opts.JSONSchema
41
+ if ( schema ) {
42
+ const newOpts = {
43
+ ...opts,
44
+ schema: {
45
+ response: {
46
+ 200: schema
47
+ }
48
+ }
49
+ }
50
+ return newOpts
51
+ } else {
52
+ return opts
53
+ }
54
+ }
55
+
56
+ function addGetResponseArray(opts) {
57
+ const schema = opts.JSONSchema
58
+ if ( schema ) {
59
+ const newOpts = {
60
+ ...opts,
61
+ schema: {
62
+ response: {
63
+ 200: {
64
+ type: 'array',
65
+ items: schema
66
+ }
67
+ },
68
+ querystring: {
69
+ type: 'object',
70
+ properties: {
71
+ filters: {
72
+ type: 'string'
73
+ },
74
+ pagination: {
75
+ type: 'string'
15
76
  }
16
77
  }
17
78
  }
18
79
  }
80
+ }
81
+ return newOpts
82
+ } else {
83
+ return opts
84
+ }
85
+ }
86
+
87
+ function Crudify (fastify, opts, next) {
88
+ const optsGet = addGetResponseUnitary(opts)
89
+ const optsGetAll = addGetResponseArray(opts)
90
+ const optsPost = addPostBody(optsGet)
91
+
92
+ fastify.get(`${opts.url}`, optsGetAll, async (req, reply) => {
93
+ try {
19
94
  const filterObject = JSON.parse(req.query.filters ||= '{}')
20
95
  for (const key in filterObject) {
21
96
  if (typeof filterObject[key] === 'string') {
@@ -31,7 +106,7 @@ function Crudify (fastify, opts, next) {
31
106
  }
32
107
  })
33
108
 
34
- fastify.get(`${opts.url}/:id`, opts, async (req, reply) => {
109
+ fastify.get(`${opts.url}/:id`, optsGet, async (req, reply) => {
35
110
  try {
36
111
  const response = await opts.Model.findById(req.params.id)
37
112
  if (!response) {
@@ -48,7 +123,7 @@ function Crudify (fastify, opts, next) {
48
123
  }
49
124
  })
50
125
 
51
- fastify.post(`${opts.url}`, opts, async (req, reply) => {
126
+ fastify.post(`${opts.url}`, optsPost, async (req, reply) => {
52
127
  try {
53
128
  await opts.Model.validate(req.body)
54
129
  } catch (err) {
@@ -60,8 +135,8 @@ function Crudify (fastify, opts, next) {
60
135
  throw new Error(err)
61
136
  }
62
137
  })
63
-
64
- fastify.put(`${opts.url}/:id`, opts, async (req, reply) => {
138
+
139
+ fastify.put(`${opts.url}/:id`, optsGet, async (req, reply) => {
65
140
  try {
66
141
  const response = await opts.Model.findOneAndUpdate({ _id: req.params.id }, req.body, {
67
142
  new: true,
@@ -112,10 +187,10 @@ function Crudify (fastify, opts, next) {
112
187
  }
113
188
  })
114
189
 
115
- opts.additionalRoutes?.map(route => {
116
- fastify.route(route)
117
- })
118
-
190
+ if (opts.additionalRoutes) {
191
+ opts.additionalRoutes(fastify, opts)
192
+ }
193
+
119
194
  next()
120
195
  }
121
196
 
@@ -6,6 +6,7 @@ export default {
6
6
  },
7
7
  autoStart: false,
8
8
  instance: {},
9
+ replSet: false
9
10
  },
10
- useSharedDBForAllJestWorkers: false
11
+ useSharedDBForAllJestWorkers: false,
11
12
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aaroncadillac/crudify-mongo",
3
- "version": "1.2.0",
3
+ "version": "1.6.0",
4
4
  "description": "An automatic CRUD generator for fastify",
5
5
  "main": "crudify.js",
6
6
  "repository": "git@github.com:aaroncadillac/crudify-mongo.git",
@@ -10,10 +10,7 @@ let connection;
10
10
 
11
11
  beforeAll(async () => {
12
12
  app = fastify();
13
- await connect(global.__MONGO_URI__, {
14
- useNewUrlParser: true,
15
- useUnifiedTopology: true,
16
- });
13
+ await connect(global.__MONGO_URI__);
17
14
  connection = mongoose.connection
18
15
  const userSchema = new Schema({ name: String, age: Number, email: String });
19
16
  userSchema.plugin(paginate);