meadow-endpoints 3.0.5 → 3.0.7

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.
@@ -13,6 +13,48 @@ RUN sudo apt install default-mysql-server default-mysql-client -y
13
13
 
14
14
  RUN echo "Building RETOLD development image..."
15
15
 
16
+ RUN echo "...installing vscode extensions..."
17
+
18
+ # Mocha unit testing in the sidebar
19
+ RUN code-server --install-extension hbenl.vscode-mocha-test-adapter
20
+ RUN code-server --install-extension hbenl.test-adapter-converter
21
+ RUN code-server --install-extension hbenl.vscode-test-explorer
22
+
23
+ # Magic indentation rainbow
24
+ RUN code-server --install-extension oderwat.indent-rainbow
25
+ RUN code-server --install-extension dbaeumer.vscode-eslint
26
+
27
+ # Contextual git
28
+ RUN code-server --install-extension eamodio.gitlens
29
+
30
+ # Other extensions (uncomment them to have them automagic, or run this from a terminal to install in the container):
31
+
32
+ # SQL Tools
33
+ RUN code-server --install-extension mtxr.sqltools
34
+ RUN code-server --install-extension mtxr.sqltools-driver-mysql
35
+
36
+ # Microsoft's AI code completion
37
+ # RUN code-server --install-extension VisualStudioExptTeam.vscodeintellicode
38
+
39
+ # Live server -- make sure to open up the port on the docker image
40
+ # RUN code-server --install-extension ritwickdey.LiveServer
41
+
42
+ # Quick link to required modules' documentation
43
+ RUN code-server --install-extension bengreenier.vscode-node-readme
44
+
45
+ # Switch up fonts
46
+ # RUN code-server --install-extension evan-buss.font-switcher
47
+
48
+ # Icons
49
+ # RUN code-server --install-extension vscode-icons-team.vscode-icons
50
+ # RUN code-server --install-extension PKief.material-icon-theme
51
+
52
+ # Hover over CSS colors to see them previewed
53
+ # RUN code-server --install-extension bierner.color-info
54
+
55
+ # An easy on the eyes color theme
56
+ # RUN code-server --install-extension daylerees.rainglow
57
+
16
58
  RUN echo "...configuring mariadb (mysql) server..."
17
59
  RUN sudo sed -i "s|bind-address|#bind-address|g" /etc/mysql/mariadb.conf.d/50-server.cnf
18
60
  ADD ./.config/luxury-extras/MySQL/MySQL-Security.sql /home/coder/MySQL-Configure-Security.sql
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "meadow-endpoints",
3
- "version": "3.0.5",
3
+ "version": "3.0.7",
4
4
  "description": "Automatic API endpoints for Meadow data.",
5
5
  "main": "source/Meadow-Endpoints.js",
6
6
  "scripts": {
7
7
  "start": "node source/Meadow-Endpoints.js",
8
8
  "coverage": "nyc npm run test && nyc report --reporter=lcov",
9
9
  "test": "./node_modules/.bin/mocha --exit -u tdd -R spec",
10
- "docker-dev-build-image": "docker build ./ -t retold/meadow-endpoints:local",
10
+ "build": "./node_modules/.bin/gulp build",
11
+ "docker-dev-build-image": "docker build ./ -f Dockerfile_LUXURYCode -t retold/meadow-endpoints:local",
11
12
  "docker-dev-run": "docker run -it -d --name meadow-endpoints-dev -p 127.0.0.1:12343:8080 -p 12305:3306 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/meadow-endpoints\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" retold/meadow-endpoints:local"
12
13
  },
13
14
  "mocha": {
@@ -64,6 +64,7 @@ var MeadowEndpoints = function()
64
64
  Upserts: require('./crud/Meadow-Endpoint-BulkUpsert.js'),
65
65
 
66
66
  Delete: require('./crud/Meadow-Endpoint-Delete.js'),
67
+ Undelete: require('./crud/Meadow-Endpoint-Undelete.js'),
67
68
 
68
69
  Count: require('./crud/Meadow-Endpoint-Count.js'),
69
70
  CountBy: require('./crud/Meadow-Endpoint-CountBy.js'),
@@ -184,6 +185,7 @@ var MeadowEndpoints = function()
184
185
  Delete: true,
185
186
  // DEL [/1.0/SomeEndpoint]
186
187
  // DEL [/1.0/SomeEndpoint/:IDRecord]
188
+ // GET [/1.0/SomeEndpoint/Undelete/:IDRecord]
187
189
 
188
190
  Count: true,
189
191
  // GET [/1.0/SomeEndpoints/Count]
@@ -379,6 +381,7 @@ var MeadowEndpoints = function()
379
381
  {
380
382
  pRestServer.del(`${tmpEndpointPrefix}`, _CommonServices.bodyParser(), _EndpointAuthenticators.Delete, wireState, _Endpoints.Delete);
381
383
  pRestServer.del(`${tmpEndpointPrefix}/:IDRecord`, _EndpointAuthenticators.Delete, wireState, _Endpoints.Delete);
384
+ pRestServer.get(`${tmpEndpointPrefix}/Undelete/:IDRecord`, _EndpointAuthenticators.Delete, wireState, _Endpoints.Undelete);
382
385
  }
383
386
  if (_EnabledBehaviors.Count)
384
387
  {
@@ -420,6 +423,17 @@ var MeadowEndpoints = function()
420
423
  return fCallback();
421
424
  };
422
425
 
426
+ var _InvokeSetupCallback;
427
+ var getInvokeSetupCallback = function()
428
+ {
429
+ return _InvokeSetupCallback;
430
+ };
431
+
432
+ var setInvokeSetupCallback = function(fCallback)
433
+ {
434
+ _InvokeSetupCallback = fCallback;
435
+ };
436
+
423
437
  /**
424
438
  * Invoke a meadow endpoint programmatically
425
439
  *
@@ -472,6 +486,10 @@ var MeadowEndpoints = function()
472
486
  //internal invoke mark as authenticated (because this is not called via webservice)
473
487
  pRequest.EndpointAuthenticated = true;
474
488
 
489
+ if (_InvokeSetupCallback && typeof(_InvokeSetupCallback) == 'function')
490
+ {
491
+ _InvokeSetupCallback(pRequest, pResponse, typeof(pOptions) === 'object' && pOptions);
492
+ }
475
493
  return fStageComplete();
476
494
  },
477
495
  function(fStageComplete)
@@ -516,6 +534,8 @@ var MeadowEndpoints = function()
516
534
  // Expose the DAL
517
535
  DAL: _Meadow,
518
536
 
537
+ getInvokeSetupCallback: getInvokeSetupCallback,
538
+ setInvokeSetupCallback: setInvokeSetupCallback,
519
539
  invokeEndpoint: invokeEndpoint,
520
540
 
521
541
  // Factory
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Meadow Endpoint - Undelete a Record
3
+ *
4
+ * @license MIT
5
+ *
6
+ * @author Steven Velozo <steven@velozo.com>
7
+ * @module Meadow
8
+ */
9
+ /**
10
+ * Undelete a record using the Meadow DAL object
11
+ */
12
+
13
+ var libAsync = require('async');
14
+
15
+
16
+ var doAPIUndeleteEndpoint = function(pRequest, pResponse, fNext)
17
+ {
18
+ // This state is the requirement for the UserRoleIndex value in the UserSession object... processed by default as >=
19
+ // The default here is that any authenticated user can use this endpoint.
20
+ pRequest.EndpointAuthorizationRequirement = pRequest.EndpointAuthorizationLevels.Undelete;
21
+
22
+ // INJECT: Pre authorization (for instance to change the authorization level)
23
+
24
+ if (pRequest.CommonServices.authorizeEndpoint(pRequest, pResponse, fNext) === false)
25
+ {
26
+ // If this endpoint fails, it's sent an error automatically.
27
+ return;
28
+ }
29
+
30
+ // INJECT: Pre endpoint operation
31
+
32
+ var tmpIDRecord = 0;
33
+ if (typeof(pRequest.params.IDRecord) === 'string')
34
+ {
35
+ tmpIDRecord = pRequest.params.IDRecord;
36
+ }
37
+ else if (typeof(pRequest.body[pRequest.DAL.defaultIdentifier]) === 'number')
38
+ {
39
+ tmpIDRecord = pRequest.body[pRequest.DAL.defaultIdentifier];
40
+ }
41
+ else if (typeof(pRequest.body[pRequest.DAL.defaultIdentifier]) === 'string')
42
+ {
43
+ tmpIDRecord = pRequest.body[pRequest.DAL.defaultIdentifier];
44
+ }
45
+ // Although the undelete request does allow multiple undeletes, we require an identifier.
46
+ // TODO: Decide if we want to keep this pattern similar to Delete, or, if we want to change it to allow bulk undeletes.
47
+ if (tmpIDRecord < 1)
48
+ {
49
+ return pRequest.CommonServices.sendError('Record undelete failure - a valid record ID is required in the passed-in record.', pRequest, pResponse, fNext);
50
+ }
51
+
52
+ var tmpRecordCount = {};
53
+ var tmpQuery;
54
+
55
+ libAsync.waterfall(
56
+ [
57
+ function(fStageComplete)
58
+ {
59
+ tmpQuery = pRequest.DAL.query;
60
+
61
+ // INJECT: Query configuration and population
62
+ var tmpSchema = pRequest.DAL.schema;
63
+ var tmpHasDeletedBit = false;
64
+ for (let i = 0; i < tmpSchema.length; i++)
65
+ {
66
+ if (tmpSchema[i].Type == 'Deleted')
67
+ {
68
+ // There is a deleted bit on the record!
69
+ tmpHasDeletedBit = true;
70
+ }
71
+ }
72
+
73
+ if (!tmpHasDeletedBit)
74
+ {
75
+ return fStageComplete("NO_DELETED_BIT");
76
+ }
77
+
78
+
79
+ return fStageComplete();
80
+ },
81
+ function(fStageComplete)
82
+ {
83
+ // Now see if the record, with this identifier, for this user, has the deleted bit set to 1
84
+ tmpQuery.addFilter(pRequest.DAL.defaultIdentifier, tmpIDRecord);
85
+ tmpQuery.addFilter('Deleted', 1);
86
+ tmpQuery.setIDUser(pRequest.UserSession.UserID);
87
+
88
+ return fStageComplete();
89
+ },
90
+ function(fStageComplete)
91
+ {
92
+ // Load the record so we can do security checks on it
93
+ pRequest.DAL.doRead(tmpQuery,
94
+ function(pError, pQuery, pRecord)
95
+ {
96
+ if (!pRecord)
97
+ {
98
+ tmpRecordCount = {Count:0};
99
+ return fStageComplete("NO_UNDELETABLE_RECORD_FOUND");
100
+ }
101
+
102
+ pRequest.Record = pRecord;
103
+
104
+ return fStageComplete();
105
+ });
106
+ },
107
+ function(fStageComplete)
108
+ {
109
+ pRequest.Authorizers.authorizeRequest('Undelete', pRequest, fStageComplete);
110
+ },
111
+ function(fStageComplete)
112
+ {
113
+ // INJECT: Once we've check the authorizer and are ready to Undelete, invoke an injected behavior before we execute the actuall delete operation
114
+ return pRequest.BehaviorModifications.runBehavior('Undelete-PreOperation', pRequest, fStageComplete);
115
+ },
116
+ function(fStageComplete)
117
+ {
118
+ // INJECT: Record modification before delete
119
+
120
+ if (pRequest.MeadowAuthorization)
121
+ {
122
+ return fStageComplete(false);
123
+ }
124
+
125
+ // It looks like this record was not authorized. Send an error.
126
+ return fStageComplete({Code:405,Message:'UNAUTHORIZED ACCESS IS NOT ALLOWED'});
127
+ },
128
+ function(fStageComplete)
129
+ {
130
+ // Do the delete
131
+ pRequest.DAL.doUndelete(tmpQuery,
132
+ function(pError, pQuery, pCount)
133
+ {
134
+ // It returns the number of rows deleted
135
+ tmpRecordCount = {Count:pCount};
136
+
137
+ return fStageComplete(pError);
138
+ });
139
+ },
140
+ function(fStageComplete)
141
+ {
142
+ // INJECT: After the delete count is grabbed, let the user alter the response content
143
+ return pRequest.BehaviorModifications.runBehavior('Undelete-PostOperation', pRequest, fStageComplete);
144
+ }
145
+ ], function(pError)
146
+ {
147
+ if (pError &&
148
+ pError !== "NO_RECORD_FOUND")
149
+ {
150
+ return pRequest.CommonServices.sendCodedError('Error undeleting a record.', pError, pRequest, pResponse, fNext);
151
+ }
152
+
153
+ pRequest.CommonServices.log.info('Undeleted '+tmpRecordCount.Count+' records with ID '+tmpIDRecord+'.', {SessionID:pRequest.UserSession.SessionID, RequestID:pRequest.RequestUUID, RequestURL:pRequest.url, Action:pRequest.DAL.scope+'-Undelete'}, pRequest);
154
+ pResponse.send(tmpRecordCount);
155
+
156
+ return fNext();
157
+ }
158
+ );
159
+ };
160
+
161
+ module.exports = doAPIUndeleteEndpoint;
@@ -1007,6 +1007,39 @@ suite
1007
1007
  }
1008
1008
  );
1009
1009
  test
1010
+ (
1011
+ 'delete: undelete a record after deleting it',
1012
+ function(fDone)
1013
+ {
1014
+ // Delete animal 4
1015
+ var tmpRecord = {IDAnimal:4};
1016
+ libSuperTest('http://localhost:9080/')
1017
+ .del('1.0/FableTest')
1018
+ .send(tmpRecord)
1019
+ .end(
1020
+ function(pError, pResponse)
1021
+ {
1022
+ // Expect response to be the count of deleted records.
1023
+ var tmpResult = JSON.parse(pResponse.text);
1024
+ Expect(tmpResult.Count).to.equal(1);
1025
+
1026
+ // Now undelete the record
1027
+ libSuperTest('http://localhost:9080/')
1028
+ .get('1.0/FableTest/Undelete/4')
1029
+ .end(
1030
+ function(pError, pResponse)
1031
+ {
1032
+ // Expect response to be the count of deleted records.
1033
+ var tmpResult = JSON.parse(pResponse.text);
1034
+ Expect(tmpResult.Count).to.equal(1);
1035
+ return fDone();
1036
+ }
1037
+ );
1038
+ }
1039
+ );
1040
+ }
1041
+ );
1042
+ test
1010
1043
  (
1011
1044
  'delete: delete a record with a bad parameter',
1012
1045
  function(fDone)
@@ -1540,6 +1573,35 @@ suite
1540
1573
  {
1541
1574
  var tmpCreatedRecordGUID;
1542
1575
 
1576
+ test
1577
+ (
1578
+ 'invoke: setup method is called',
1579
+ function(fDone)
1580
+ {
1581
+ _MockSessionValidUser.UserRoleIndex = 2;
1582
+ let setupCallCount = 0;
1583
+ let passedRequest, passedResponse, passedOriginalRequest;
1584
+ _MeadowEndpoints.setInvokeSetupCallback((req, res, origReq) =>
1585
+ {
1586
+ ++setupCallCount;
1587
+ passedRequest = req;
1588
+ passedResponse = res;
1589
+ passedOriginalRequest = origReq;
1590
+ });
1591
+ const originalRequest = {UserSession: _MockSessionValidUser};
1592
+ _MeadowEndpoints.invokeEndpoint('Read', {IDRecord: 2}, originalRequest,
1593
+ function(pError, pResponse)
1594
+ {
1595
+ Expect(setupCallCount).to.equal(1);
1596
+ Expect(passedOriginalRequest).to.equal(originalRequest);
1597
+ Expect(passedRequest).to.be.an('object');
1598
+ Expect(passedResponse).to.be.an('object');
1599
+
1600
+ fDone();
1601
+ }
1602
+ );
1603
+ }
1604
+ );
1543
1605
  test
1544
1606
  (
1545
1607
  'invoke create: create a record',