adminmate-express-mongoose 1.2.5 → 1.3.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.
package/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # Adminmate (Express.js + Mongoose)
2
+
3
+ Adminmate is a powerful & flexible back-office solution build for small to big teams. ✌️
4
+
5
+ It provides an extremely flexible API developed in NodeJS that communicate with a powerful frontend back-office we host.
6
+
7
+ As the security & privacy of your data is our main focus, the Data API is host by yourself and secured by your own credentials.
8
+
9
+ ## Getting started
10
+
11
+ [https://adminmate.io](https://adminmate.io)
12
+
13
+ ## Databases compatibility
14
+
15
+ Adminmate is compatible with the most famous database systems like **MySQL**, **PostgreSQL**, **SQLite** and **MongoDB**. We are working hard on adding more soon!
16
+
17
+ ## Features
18
+
19
+ Adminmate comes with all the features you need for your back-office:
20
+ * **Data**: Data Explorer, CRUD, Filters, Segments, Actions
21
+ * **Dashboards & Charts**: Unlimited Dashboards & Charts
22
+ * **Collaboration**: Powerful collaboration tool
23
+ * **Activity**: Track everything that happening on your database data
24
+ * **Access Control**: Team-based Access Control
25
+
26
+ ### Data explorer
27
+
28
+ ![Alt text](https://adminmate.io/github/list-screen.svg)
29
+
30
+ ### Dashboards & Charts
31
+
32
+ ![Alt text](https://adminmate.io/github/homepage-screen.svg)
33
+
34
+ ### Activity
35
+
36
+ ![Alt text](https://adminmate.io/github/activity-screen.svg)
37
+
38
+ ## Who are the contributors ?
39
+
40
+ Adminmate is a bootstrapped project tailored by **Marc Delalonde** and aims to stay an *independent project, driven by the community*.
package/index.js CHANGED
@@ -7,6 +7,7 @@ const fnHelper = require('./src/helpers/functions');
7
7
  const { getAll } = require('./src/controllers/model-getall');
8
8
  const { getIn } = require('./src/controllers/model-getin');
9
9
  const { getOne } = require('./src/controllers/model-getone');
10
+ const { getRefs } = require('./src/controllers/model-getrefs');
10
11
  const { postOne } = require('./src/controllers/model-postone');
11
12
  const { putOne } = require('./src/controllers/model-putone');
12
13
  const { deleteSome } = require('./src/controllers/model-deletesome');
@@ -15,15 +16,21 @@ const { customQuery } = require('./src/controllers/model-query');
15
16
 
16
17
  const Adminmate = ({ projectId, secretKey, authKey, masterPassword, models, charts, authorizedIps }) => {
17
18
  const api = {
19
+ // App config
20
+ getAppConfig: fnHelper.getAppConfig,
21
+
18
22
  // General
19
23
  getModelProperties: fnHelper.getModelProperties,
20
24
  getModelRealname: fnHelper.getModelRealname,
21
25
  getModelRelationships: fnHelper.getModelAssociations,
26
+ getModelPrimaryKeys: fnHelper.getModelPrimaryKeys,
27
+ getModelWhereClause: fnHelper.getModelWhereClause,
22
28
 
23
29
  // CRUD
24
30
  modelGetAll: getAll,
25
31
  modelGetIn: getIn,
26
32
  modelGetOne: getOne,
33
+ modelGetRefs: getRefs,
27
34
  modelPostOne: postOne,
28
35
  modelPutOne: putOne,
29
36
  modelDeleteSome: deleteSome,
package/jest.config.js CHANGED
@@ -1,5 +1,4 @@
1
1
  module.exports = {
2
- testEnvironment: 'node',
3
2
  projects: [
4
3
  {
5
4
  displayName: 'mongodb',
package/package.json CHANGED
@@ -1,23 +1,32 @@
1
1
  {
2
2
  "name": "adminmate-express-mongoose",
3
- "version": "1.2.5",
3
+ "version": "1.3.0",
4
4
  "description": "Adminmate Express/Mongoose connector",
5
5
  "author": "Marc Delalonde",
6
+ "homepage": "http://adminmate.io",
7
+ "license": "GPL-3.0",
8
+ "keywords": [
9
+ "adminmate",
10
+ "admin",
11
+ "panel",
12
+ "interface",
13
+ "back-office",
14
+ "mongodb",
15
+ "mongoose"
16
+ ],
6
17
  "scripts": {
7
18
  "start": "node ./index",
8
- "test": "jest --runInBand",
9
- "reset-db": "node migration.js"
19
+ "test": "jest --runInBand"
10
20
  },
11
21
  "repository": {
12
22
  "type": "git",
13
23
  "url": "https://github.com/Adminmate/adminmate-express-mongoose.git"
14
24
  },
15
25
  "dependencies": {
16
- "adminmate-express-core": "^1.1.4",
26
+ "adminmate-express-core": "^1.2.0",
17
27
  "lodash": "^4.17.21",
18
28
  "moment": "^2.29.1",
19
- "mongoose": "^5.9.7",
20
- "mongoose-legacy-pluralize": "^1.0.2",
29
+ "mongoose": "~5.9.7",
21
30
  "serialize-error": "^7.0.1"
22
31
  },
23
32
  "devDependencies": {
@@ -1,9 +1,56 @@
1
+ const fnHelper = require('../helpers/functions');
2
+
1
3
  module.exports = async (currentModel, data) => {
4
+ // Get relationship model
5
+ const relationshipModel = fnHelper.getModelObject(data.relationship_model);
6
+ if (!relationshipModel) {
7
+ return res.status(403).json({ message: 'Invalid request' });
8
+ }
9
+
10
+ // Default limit
11
+ let limit = data.limit || 10;
12
+
13
+ let _value = 1;
14
+ if (data.relationship_field && ['sum', 'avg'].includes(data.relationship_operation)) {
15
+ _value = `$${data.relationship_field}`;
16
+ }
17
+
18
+ const repartitionData = await relationshipModel
19
+ .aggregate([
20
+ {
21
+ $group: {
22
+ _id: `$${data.relationship_model_ref_field}`,
23
+ count: data.operation === 'avg' ? { $avg: _value } : { $sum: _value },
24
+ }
25
+ },
26
+ {
27
+ $project: {
28
+ key: '$_id',
29
+ value: '$count',
30
+ _id: false
31
+ }
32
+ }
33
+ ])
34
+ .limit(limit)
35
+ .sort({ value: -1 });
36
+
37
+ const parentIds = repartitionData.map(d => d.key);
38
+ const parentData = await currentModel.find({ _id: parentIds }).select(data.field).lean();
39
+
40
+ repartitionData.forEach(d => {
41
+ d.item_model = data.model;
42
+ d.item_id = d.key;
43
+ const parent = parentData.find(p => p._id.toString() === d.key.toString());
44
+ if (parent) {
45
+ d.key = parent[data.field];
46
+ }
47
+ });
48
+
2
49
  return {
3
- success: false,
50
+ success: true,
4
51
  data: {
5
52
  config: null,
6
- data: {}
53
+ data: repartitionData
7
54
  }
8
55
  };
9
56
  };
@@ -12,12 +12,15 @@ module.exports = async (currentModel, data) => {
12
12
 
13
13
  const toSum = data.field && data.operation === 'sum' ? `$${data.field}` : 1;
14
14
 
15
+ // To set the max date
16
+ const toDate = data.to ? moment(data.to) : moment();
17
+
15
18
  let matchReq = {};
16
19
  let groupFormat = '';
17
20
 
18
21
  // Day timeframe
19
22
  if (data.timeframe === 'day') {
20
- const startOfCurrentDay = moment().startOf('day');
23
+ const startOfCurrentDay = toDate.startOf('day');
21
24
  matchReq = {
22
25
  '$gte': new Date(startOfCurrentDay.clone().subtract(30, 'day').startOf('day').format()),
23
26
  '$lt': new Date(startOfCurrentDay.format())
@@ -26,7 +29,7 @@ module.exports = async (currentModel, data) => {
26
29
  }
27
30
  // Week timeframe
28
31
  else if (data.timeframe === 'week') {
29
- const startOfCurrentWeek = moment().startOf('week');
32
+ const startOfCurrentWeek = toDate.startOf('week');
30
33
  matchReq = {
31
34
  '$gte': new Date(startOfCurrentWeek.clone().subtract(26, 'week').startOf('week').format()),
32
35
  '$lt': new Date(startOfCurrentWeek.format())
@@ -35,7 +38,7 @@ module.exports = async (currentModel, data) => {
35
38
  }
36
39
  // Month timeframe
37
40
  else if (data.timeframe === 'month') {
38
- const startOfCurrentMonth = moment().startOf('month');
41
+ const startOfCurrentMonth = toDate.startOf('month');
39
42
  matchReq = {
40
43
  '$gte': new Date(startOfCurrentMonth.clone().subtract(12, 'month').startOf('month').format()),
41
44
  '$lt': new Date(startOfCurrentMonth.format())
@@ -44,7 +47,7 @@ module.exports = async (currentModel, data) => {
44
47
  }
45
48
  // Year timeframe
46
49
  else if (data.timeframe === 'year') {
47
- const startOfCurrentYear = moment().startOf('year');
50
+ const startOfCurrentYear = toDate.startOf('year');
48
51
  matchReq = {
49
52
  '$gte': new Date(startOfCurrentYear.clone().subtract(8, 'year').startOf('year').format()),
50
53
  '$lt': new Date(startOfCurrentYear.format())
@@ -83,7 +86,7 @@ module.exports = async (currentModel, data) => {
83
86
  // Day timeframe
84
87
  if (data.timeframe === 'day') {
85
88
  for (let i = 1; i <= 30; i++) {
86
- const currentDate = moment().subtract(i, 'day').startOf('day');
89
+ const currentDate = toDate.clone().subtract(i, 'day').startOf('day');
87
90
  const countForTheTimeframe = _.find(repartitionData, { key: currentDate.format('YYYY-MM-DD') });
88
91
  formattedData.push({
89
92
  key: currentDate.format('DD/MM'),
@@ -94,7 +97,7 @@ module.exports = async (currentModel, data) => {
94
97
  // Week timeframe
95
98
  else if (data.timeframe === 'week') {
96
99
  for (let i = 1; i <= 26; i++) {
97
- const currentWeek = moment().subtract(i, 'week').startOf('week');
100
+ const currentWeek = toDate.clone().subtract(i, 'week').startOf('week');
98
101
  const countForTheTimeframe = _.find(repartitionData, { key: currentWeek.format('WW') });
99
102
  formattedData.push({
100
103
  key: currentWeek.startOf('week').format('DD/MM'),
@@ -105,7 +108,7 @@ module.exports = async (currentModel, data) => {
105
108
  // Month timeframe
106
109
  else if (data.timeframe === 'month') {
107
110
  for (let i = 1; i <= 12; i++) {
108
- const currentMonth = moment().subtract(i, 'month').startOf('month');
111
+ const currentMonth = toDate.clone().subtract(i, 'month').startOf('month');
109
112
  const countForTheTimeframe = _.find(repartitionData, { key: currentMonth.format('MM') });
110
113
  formattedData.push({
111
114
  key: currentMonth.startOf('month').format('MMM'),
@@ -116,7 +119,7 @@ module.exports = async (currentModel, data) => {
116
119
  // Year timeframe
117
120
  else if (data.timeframe === 'year') {
118
121
  for (let i = 1; i <= 8; i++) {
119
- const currentYear = moment().subtract(i, 'year').startOf('year');
122
+ const currentYear = toDate.clone().subtract(i, 'year').startOf('year');
120
123
  const countForTheTimeframe = _.find(repartitionData, { key: currentYear.format('YYYY') });
121
124
  formattedData.push({
122
125
  key: currentYear.startOf('year').format('YYYY'),
@@ -3,8 +3,8 @@ const fnHelper = require('../helpers/functions');
3
3
 
4
4
  module.exports.getAutocomplete = async (req, res) => {
5
5
  const modelName = req.params.model;
6
- const search = (req.body.search || '').trim();
7
- const refFields = req.body.refFields;
6
+ const search = (req.query.s || '').trim();
7
+ const refFields = req.headers['am-ref-fields'] || {};
8
8
  const maxItem = 10;
9
9
 
10
10
  const currentModel = fnHelper.getModelObject(modelName);
@@ -3,16 +3,16 @@ const fnHelper = require('../helpers/functions');
3
3
 
4
4
  module.exports.getAll = async (req, res) => {
5
5
  const modelName = req.params.model;
6
- const segment = req.body.segment;
7
- const search = (req.body.search || '').trim();
8
- const filters = req.body.filters;
9
- const fieldsToFetch = req.body.fields || [];
10
- const refFields = req.body.refFields;
11
- const fieldsToSearchIn = req.body.fieldsToSearchIn || [];
12
- const page = parseInt(req.body.page || 1);
6
+ const segment = req.query.segment;
7
+ const search = (req.query.search || '').trim();
8
+ const filters = req.query.filters;
9
+ const fieldsToFetch = req.headers['am-model-fields'] || [];
10
+ const refFields = req.headers['am-ref-fields'] || {};
11
+ const fieldsToSearchIn = req.query.search_in_fields || [];
12
+ const page = parseInt(req.query.page || 1);
13
13
  const nbItemPerPage = 10;
14
14
  const defaultOrdering = [ ['_id', 'DESC'] ];
15
- const order = req.body.order || null;
15
+ const order = req.query.order || null;
16
16
 
17
17
  const currentModel = fnHelper.getModelObject(modelName);
18
18
  if (!currentModel) {
@@ -4,7 +4,8 @@ const fnHelper = require('../helpers/functions');
4
4
  module.exports.getOne = async (req, res) => {
5
5
  const modelName = req.params.model;
6
6
  const modelItemId = req.params.id;
7
- const refFields = req.body.refFields || {};
7
+ const fieldsToFetch = req.headers['am-model-fields'] || [];
8
+ const refFields = req.headers['am-ref-fields'] || {};
8
9
 
9
10
  const currentModel = fnHelper.getModelObject(modelName);
10
11
  if (!currentModel) {
@@ -13,14 +14,14 @@ module.exports.getOne = async (req, res) => {
13
14
 
14
15
  const keys = fnHelper.getModelProperties(currentModel);
15
16
  const defaultFieldsToFetch = keys.map(key => key.path);
16
- const fieldsToFetch = req.body.fields ? req.body.fields : defaultFieldsToFetch;
17
+ const fieldsToFetchSafe = Array.isArray(fieldsToFetch) && fieldsToFetch.length ? fieldsToFetch : defaultFieldsToFetch;
17
18
 
18
19
  // Build ref fields for the model (for mongoose population purpose)
19
- const fieldsToPopulate = fnHelper.getFieldsToPopulate(keys, fieldsToFetch, refFields);
20
+ const fieldsToPopulate = fnHelper.getFieldsToPopulate(keys, fieldsToFetchSafe, refFields);
20
21
 
21
22
  let data = await currentModel
22
23
  .findById(modelItemId)
23
- .select(fieldsToFetch)
24
+ .select(fieldsToFetchSafe)
24
25
  .populate(fieldsToPopulate)
25
26
  .lean()
26
27
  .catch(e => {
@@ -0,0 +1,53 @@
1
+ const fnHelper = require('../helpers/functions');
2
+
3
+ module.exports.getRefs = async (req, res) => {
4
+ const modelName = req.params.model;
5
+ const ids = req.query.ids;
6
+ const refFields = req.headers['am-ref-fields'] || {};
7
+ const nbItemPerPage = 20;
8
+
9
+ if (!ids) {
10
+ return res.status(403).json({ message: 'Missing parameter ids' });
11
+ }
12
+
13
+ const fieldsToFetchSafe = refFields[modelName];
14
+
15
+ // If no ref fields, return default response
16
+ if (!fieldsToFetchSafe) {
17
+ return res.json({
18
+ data: ids.map(id => ({ value: id, label: id }))
19
+ });
20
+ }
21
+
22
+ const currentModel = fnHelper.getModelObject(modelName);
23
+ if (!currentModel) {
24
+ return res.status(403).json({ message: 'Invalid request' });
25
+ }
26
+
27
+ // Find parameters
28
+ const findParams = { _id: ids };
29
+
30
+ const data = await currentModel
31
+ .find(findParams)
32
+ .select(fieldsToFetchSafe)
33
+ .limit(nbItemPerPage)
34
+ .lean()
35
+ .catch(e => {
36
+ res.status(403).json({ message: e.message });
37
+ });
38
+
39
+ if (!data) {
40
+ return res.status(403).json();
41
+ }
42
+
43
+ // Format the response
44
+ const formattedData = ids.map(_id => {
45
+ const match = data.find(d => d._id.toString() === _id.toString());
46
+ const label = match ? fnHelper.fieldsToValues(fieldsToFetchSafe, match) : _id;
47
+ return { value: _id, label };
48
+ });
49
+
50
+ res.json({
51
+ data: formattedData
52
+ });
53
+ };
@@ -3,6 +3,8 @@ const { serializeError } = require('serialize-error');
3
3
  const _ = require('lodash');
4
4
  const moment = require('moment');
5
5
 
6
+ const pjson = require('../../package.json');
7
+
6
8
  const getModelProperties = model => {
7
9
  let modelFields = [];
8
10
  const modelProps = model.schema.paths;
@@ -222,6 +224,12 @@ module.exports.constructQuery = jsonQuery => {
222
224
  return null;
223
225
  };
224
226
 
227
+ module.exports.fieldsToValues = (string, values) => {
228
+ return string.replace(/[a-z._]+/gi, word => {
229
+ return _.get(values, word);
230
+ });
231
+ };
232
+
225
233
  module.exports.refFields = (item, fieldsToPopulate) => {
226
234
  const attributes = Object.keys(item);
227
235
  attributes.forEach(attr => {
@@ -354,6 +362,14 @@ module.exports.constructSearch = (search, fieldsToSearchIn, fieldsToPopulate = [
354
362
  return params;
355
363
  };
356
364
 
365
+ module.exports.getModelWhereClause = (model, idsArray) => {
366
+ return { _id: idsArray };
367
+ };
368
+
369
+ module.exports.getModelPrimaryKeys = model => {
370
+ return ['_id'];
371
+ };
372
+
357
373
  module.exports.getModelAssociations = model => {
358
374
  // Get current model mongoose realname
359
375
  const currentModelRealName = getModelRealname(model);
@@ -462,4 +478,11 @@ module.exports.getCleanOrderStructure = orderConfig => {
462
478
  order[oc[0]] = oc[1];
463
479
  });
464
480
  return order;
481
+ };
482
+
483
+ module.exports.getAppConfig = () => {
484
+ return {
485
+ package: pjson.name,
486
+ version: pjson.version
487
+ };
465
488
  };
package/database.js DELETED
@@ -1,16 +0,0 @@
1
- const mongoose = require('mongoose');
2
-
3
- const User = require('./models/user');
4
- const Car = require('./models/car');
5
- const Blocked = require('./models/blocked');
6
-
7
- const connectDb = () => {
8
- return mongoose.connect('mongodb://localhost:27017/node-express-mongodb-server', {
9
- useNewUrlParser: true,
10
- useUnifiedTopology: true
11
- });
12
- };
13
-
14
- const models = { User, Car, Blocked };
15
-
16
- module.exports = { models, connectDb };
@@ -1,12 +0,0 @@
1
- version: '3'
2
-
3
- services:
4
- mongo:
5
- container_name: adminmate-express-mongoose
6
- environment:
7
- - MONGO_INITDB_DATABASE=demo
8
- - MONGO_DATA_DIR=/data/db
9
- - MONGO_LOG_DIR=/dev/null
10
- image: mongo
11
- ports:
12
- - '27017:27017'