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.
|
|
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
|
-
"
|
|
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',
|