fable 3.0.37 → 3.0.39
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/.config/configstore/update-notifier-npm.json +7 -1
- package/.config/vscode-sqltools/runningInfo.json +1 -1
- package/dist/fable.compatible.js +737 -67
- package/dist/fable.compatible.min.js +8 -8
- package/dist/fable.compatible.min.js.map +1 -1
- package/dist/fable.js +719 -49
- package/dist/fable.min.js +8 -8
- package/dist/fable.min.js.map +1 -1
- package/package.json +6 -3
- package/retold-harness/bookstore-serve-meadow-endpoint-apis.js +2 -4
- package/retold-harness/package.json +3 -3
- package/source/Fable.js +14 -17
- package/source/services/Fable-Service-CSVParser.js +199 -0
- package/source/services/Fable-Service-DataFormat.js +0 -3
- package/source/services/Fable-Service-FilePersistence-Web.js +36 -0
- package/source/services/Fable-Service-FilePersistence.js +127 -0
- package/source/services/Fable-Service-Utility.js +3 -2
- package/test/CSVParser_tests.js +66 -0
- package/test/FilePersistence_tests.js +37 -0
- package/test/Manifest_tests.js +79 -0
- package/test/RestClient_test.js +1 -1
- package/test/data/books.csv +10001 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fable",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.39",
|
|
4
4
|
"description": "An entity behavior management and API bundling library.",
|
|
5
5
|
"main": "source/Fable.js",
|
|
6
6
|
"scripts": {
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"build": "./node_modules/.bin/gulp build",
|
|
11
11
|
"build-compatible": "GULP_CUSTOM_BUILD_TARGET=compatible ./node_modules/.bin/gulp build",
|
|
12
12
|
"docker-dev-build-image": "docker build ./ -f Dockerfile_LUXURYCode -t retold/fable:local",
|
|
13
|
-
"docker-dev-run": "docker run -it -d --name retold-fable-dev -p 30001:8080 -p 8086:8086 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/fable\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" retold/fable:local"
|
|
13
|
+
"docker-dev-run": "docker run -it -d --name retold-fable-dev -p 30001:8080 -p 8086:8086 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/fable\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" retold/fable:local",
|
|
14
|
+
"docker-dev-shell": "docker exec -it retold-fable-dev /bin/bash"
|
|
14
15
|
},
|
|
15
16
|
"mocha": {
|
|
16
17
|
"diff": true,
|
|
@@ -31,7 +32,8 @@
|
|
|
31
32
|
]
|
|
32
33
|
},
|
|
33
34
|
"browser": {
|
|
34
|
-
"./source/service/Fable-Service-EnvironmentData.js": "./source/service/Fable-Service-EnvironmentData-Web.js"
|
|
35
|
+
"./source/service/Fable-Service-EnvironmentData.js": "./source/service/Fable-Service-EnvironmentData-Web.js",
|
|
36
|
+
"./source/service/Fable-Service-FilePersistence.js": "./source/service/Fable-Service-FilePersistence-Web.js"
|
|
35
37
|
},
|
|
36
38
|
"repository": {
|
|
37
39
|
"type": "git",
|
|
@@ -71,6 +73,7 @@
|
|
|
71
73
|
"fable-serviceproviderbase": "^3.0.3",
|
|
72
74
|
"fable-settings": "^3.0.5",
|
|
73
75
|
"fable-uuid": "^3.0.5",
|
|
76
|
+
"manyfest": "^1.0.24",
|
|
74
77
|
"precedent": "^1.0.10",
|
|
75
78
|
"simple-get": "^4.0.1"
|
|
76
79
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
+
* @license MIT
|
|
6
7
|
* @author <steven@velozo.com>
|
|
7
8
|
*/
|
|
8
9
|
|
|
@@ -74,16 +75,13 @@ let fStartServiceServer = (fInitializeCallback) =>
|
|
|
74
75
|
_MeadowEndpoints[tmpDALEntityName].connectRoutes(_Orator.webServer);
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
let tmpNames = Object.keys(_MeadowEndpoints.Book);
|
|
78
|
-
console.log(JSON.stringify(tmpNames));
|
|
79
|
-
|
|
80
78
|
// 100. Add a post processing hook to the Book DAL on single reads
|
|
81
79
|
/*
|
|
82
80
|
This post processing step will look for all book author joins then
|
|
83
81
|
load all appropriate authors and stuff them in the book record before
|
|
84
82
|
returning it.
|
|
85
83
|
*/
|
|
86
|
-
_MeadowEndpoints.Book.
|
|
84
|
+
_MeadowEndpoints.Book.controller.BehaviorInjection.setBehavior('Read-PostOperation',
|
|
87
85
|
(pRequest, pRequestState, fComplete) =>
|
|
88
86
|
{
|
|
89
87
|
// Get the join records
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"async": "^3.2.4",
|
|
14
|
-
"fable": "^3.0.
|
|
14
|
+
"fable": "^3.0.37",
|
|
15
15
|
"meadow": "^2.0.4",
|
|
16
|
-
"meadow-endpoints": "^
|
|
17
|
-
"mysql2": "^3.3.
|
|
16
|
+
"meadow-endpoints": "^4.0.2",
|
|
17
|
+
"mysql2": "^3.3.1",
|
|
18
18
|
"orator": "^3.0.11",
|
|
19
19
|
"orator-serviceserver-restify": "^1.0.4",
|
|
20
20
|
"papaparse": "^5.4.1"
|
package/source/Fable.js
CHANGED
|
@@ -9,15 +9,6 @@ const libFableLog = require('fable-log');
|
|
|
9
9
|
|
|
10
10
|
const libFableServiceManager = require('./Fable-ServiceManager.js');
|
|
11
11
|
|
|
12
|
-
// Default Services
|
|
13
|
-
const libFableServiceEnvironmentData = require('./services/Fable-Service-EnvironmentData.js');
|
|
14
|
-
const libFableServiceDataFormat = require('./services/Fable-Service-DataFormat.js');
|
|
15
|
-
const libFableServiceMetaTemplate = require('./services/Fable-Service-MetaTemplate.js');
|
|
16
|
-
const libFableServiceOperation = require('./services/Fable-Service-Operation.js');
|
|
17
|
-
const libFableServiceRestClient = require('./services/Fable-Service-RestClient.js');
|
|
18
|
-
const libFableServiceTemplate = require('./services/Fable-Service-Template.js');
|
|
19
|
-
const libFableServiceUtility = require('./services/Fable-Service-Utility.js');
|
|
20
|
-
|
|
21
12
|
class Fable
|
|
22
13
|
{
|
|
23
14
|
constructor(pSettings)
|
|
@@ -51,14 +42,20 @@ class Fable
|
|
|
51
42
|
this.serviceManager.connectPreinitServiceProviderInstance(this._coreServices.SettingsManager);
|
|
52
43
|
|
|
53
44
|
// Initialize and instantiate the default baked-in Data Arithmatic service
|
|
54
|
-
this.serviceManager.addAndInstantiateServiceType('EnvironmentData',
|
|
55
|
-
this.serviceManager.addServiceType('Template',
|
|
56
|
-
this.serviceManager.addServiceType('MetaTemplate',
|
|
57
|
-
this.serviceManager.addAndInstantiateServiceType('DataFormat',
|
|
58
|
-
this.serviceManager.addAndInstantiateServiceType('Utility',
|
|
59
|
-
this.serviceManager.addServiceType('Operation',
|
|
60
|
-
this.serviceManager.addServiceType('RestClient',
|
|
45
|
+
this.serviceManager.addAndInstantiateServiceType('EnvironmentData', require('./services/Fable-Service-EnvironmentData.js'));
|
|
46
|
+
this.serviceManager.addServiceType('Template', require('./services/Fable-Service-Template.js'));
|
|
47
|
+
this.serviceManager.addServiceType('MetaTemplate', require('./services/Fable-Service-MetaTemplate.js'));
|
|
48
|
+
this.serviceManager.addAndInstantiateServiceType('DataFormat', require('./services/Fable-Service-DataFormat.js'));
|
|
49
|
+
this.serviceManager.addAndInstantiateServiceType('Utility', require('./services/Fable-Service-Utility.js'));
|
|
50
|
+
this.serviceManager.addServiceType('Operation', require('./services/Fable-Service-Operation.js'));
|
|
51
|
+
this.serviceManager.addServiceType('RestClient', require('./services/Fable-Service-RestClient.js'));
|
|
52
|
+
this.serviceManager.addServiceType('CSVParser', require('./services/Fable-Service-CSVParser.js'));
|
|
53
|
+
this.serviceManager.addServiceType('Manifest', require('manyfest'));
|
|
54
|
+
}
|
|
61
55
|
|
|
56
|
+
get isFable()
|
|
57
|
+
{
|
|
58
|
+
return true;
|
|
62
59
|
}
|
|
63
60
|
|
|
64
61
|
get settings()
|
|
@@ -94,7 +91,7 @@ class Fable
|
|
|
94
91
|
get fable()
|
|
95
92
|
{
|
|
96
93
|
return this;
|
|
97
|
-
}
|
|
94
|
+
};
|
|
98
95
|
}
|
|
99
96
|
|
|
100
97
|
// This is for backwards compatibility
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
2
|
+
/**
|
|
3
|
+
* Parsing CSVs. Why? Because it's a thing that needs to be done.
|
|
4
|
+
*
|
|
5
|
+
* 1. And the other node CSV parsers had issues with the really messy files we had.
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
* 2. None of the CSV parsers dealt with and multi-line quoted string columns
|
|
9
|
+
* which are apparently a-ok according to the official spec.
|
|
10
|
+
* Plus a lot of them are asynchronous because apparently that's the best way to
|
|
11
|
+
* do anything; unfortunately some files have a sequence issue with that.
|
|
12
|
+
*
|
|
13
|
+
* @class CSVParser
|
|
14
|
+
*/
|
|
15
|
+
class CSVParser extends libFableServiceProviderBase
|
|
16
|
+
{
|
|
17
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
18
|
+
{
|
|
19
|
+
super(pFable, pOptions, pServiceHash);
|
|
20
|
+
|
|
21
|
+
this.serviceType = 'CSVParser';
|
|
22
|
+
|
|
23
|
+
this.Header = [];
|
|
24
|
+
this.HeaderFieldNames = [];
|
|
25
|
+
|
|
26
|
+
this.Delimiter = ',';
|
|
27
|
+
this.QuoteCharacter = '"';
|
|
28
|
+
|
|
29
|
+
this.CleanCharacters = ['\r'];
|
|
30
|
+
|
|
31
|
+
this.HeaderLineIndex = 0;
|
|
32
|
+
this.HasHeader = true;
|
|
33
|
+
this.HasSetHeader = false;
|
|
34
|
+
this.EmitHeader = false;
|
|
35
|
+
|
|
36
|
+
this.EmitJSON = true;
|
|
37
|
+
|
|
38
|
+
this.EscapedQuoteString = '"';
|
|
39
|
+
|
|
40
|
+
// Current Line Parsing State
|
|
41
|
+
this.CurrentLine = '';
|
|
42
|
+
this.CurrentRecord = [];
|
|
43
|
+
|
|
44
|
+
this.InQuote = false;
|
|
45
|
+
this.InEscapedQuote = false;
|
|
46
|
+
|
|
47
|
+
this.LinesParsed = 0;
|
|
48
|
+
this.RowsEmitted = 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
marshalRowToJSON(pRowArray)
|
|
52
|
+
{
|
|
53
|
+
if (!Array.isArray(pRowArray))
|
|
54
|
+
{
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (let i = this.HeaderFieldNames.length; i < pRowArray.length; i++)
|
|
59
|
+
{
|
|
60
|
+
this.HeaderFieldNames[i] = `${i}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let tmpObject = {};
|
|
64
|
+
|
|
65
|
+
for (let i = 0; i < pRowArray.length; i++)
|
|
66
|
+
{
|
|
67
|
+
tmpObject[this.HeaderFieldNames[i]] = pRowArray[i];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return tmpObject;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Set the header data, for use in marshalling to JSON.
|
|
74
|
+
setHeader (pHeaderArray)
|
|
75
|
+
{
|
|
76
|
+
this.Header = pHeaderArray;
|
|
77
|
+
|
|
78
|
+
for (let i = 0; i < this.Header.length; i++)
|
|
79
|
+
{
|
|
80
|
+
if (typeof(this.Header[i]) == 'undefined')
|
|
81
|
+
{
|
|
82
|
+
this.HeaderFieldNames[i] = `${i}`;
|
|
83
|
+
}
|
|
84
|
+
else
|
|
85
|
+
{
|
|
86
|
+
this.HeaderFieldNames[i] = this.Header[i].toString();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
resetRowState()
|
|
92
|
+
{
|
|
93
|
+
this.CurrentRecord = [];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
pushLine()
|
|
97
|
+
{
|
|
98
|
+
for (let i = 0; i < this.CleanCharacters.length; i++)
|
|
99
|
+
{
|
|
100
|
+
this.CurrentLine = this.CurrentLine.replace(this.CleanCharacters[i],'');
|
|
101
|
+
}
|
|
102
|
+
this.CurrentRecord.push(this.CurrentLine);
|
|
103
|
+
this.CurrentLine = '';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
emitRow(pFormatAsJSON)
|
|
107
|
+
{
|
|
108
|
+
let tmpFormatAsJSON = (typeof(pFormatAsJSON) == 'undefined') ? this.EmitJSON : pFormatAsJSON;
|
|
109
|
+
|
|
110
|
+
this.RowsEmitted++;
|
|
111
|
+
let tmpCompletedRecord = this.CurrentRecord;
|
|
112
|
+
this.CurrentRecord = [];
|
|
113
|
+
|
|
114
|
+
if (tmpFormatAsJSON)
|
|
115
|
+
{
|
|
116
|
+
return this.marshalRowToJSON(tmpCompletedRecord);
|
|
117
|
+
}
|
|
118
|
+
else
|
|
119
|
+
{
|
|
120
|
+
return tmpCompletedRecord;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
parseCSVLine (pLineString)
|
|
125
|
+
{
|
|
126
|
+
this.LinesParsed++;
|
|
127
|
+
|
|
128
|
+
for (let i = 0; i < pLineString.length; i++)
|
|
129
|
+
{
|
|
130
|
+
if ((!this.InQuote) && (pLineString[i] == this.Delimiter))
|
|
131
|
+
{
|
|
132
|
+
this.pushLine();
|
|
133
|
+
}
|
|
134
|
+
else if (pLineString[i] == this.QuoteCharacter)
|
|
135
|
+
{
|
|
136
|
+
// If we are in the second part of an escaped quote, ignore it.
|
|
137
|
+
if (this.InEscapedQuote)
|
|
138
|
+
{
|
|
139
|
+
this.InEscapedQuote = false;
|
|
140
|
+
}
|
|
141
|
+
// If we aren't in a quote, enter quote
|
|
142
|
+
else if (!this.InQuote)
|
|
143
|
+
{
|
|
144
|
+
this.InQuote = true;
|
|
145
|
+
}
|
|
146
|
+
// We are in a quote, so peek forward to see if this is an "escaped" quote pair
|
|
147
|
+
else if ((i < pLineString.length) && (pLineString[i+1] == this.QuoteCharacter))
|
|
148
|
+
{
|
|
149
|
+
this.CurrentLine += this.EscapedQuoteString;
|
|
150
|
+
this.InEscapedQuote = true;
|
|
151
|
+
}
|
|
152
|
+
// We are in a quote, this isn't an "escaped" quote pair, so go out of quote mode
|
|
153
|
+
else
|
|
154
|
+
{
|
|
155
|
+
this.InQuote = false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else
|
|
159
|
+
{
|
|
160
|
+
this.CurrentLine += pLineString[i];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// See if we are in a multiline quoted entry -- if not, emit the row.
|
|
165
|
+
if (!this.InQuote)
|
|
166
|
+
{
|
|
167
|
+
// Push the last remaining column from the buffer to the current line.
|
|
168
|
+
this.pushLine();
|
|
169
|
+
|
|
170
|
+
// Check to see if there is a header -- and if so, if this is the header row
|
|
171
|
+
if (this.HasHeader && !this.HasSetHeader && (this.RowsEmitted == this.HeaderLineIndex))
|
|
172
|
+
{
|
|
173
|
+
this.HasSetHeader = true;
|
|
174
|
+
// Override the format as json bit
|
|
175
|
+
this.setHeader(this.emitRow(false));
|
|
176
|
+
|
|
177
|
+
// No matter what, formatting this as JSON is silly and we don't want to go there anyway.
|
|
178
|
+
if (this.EmitHeader)
|
|
179
|
+
{
|
|
180
|
+
return this.Header;
|
|
181
|
+
}
|
|
182
|
+
else
|
|
183
|
+
{
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else
|
|
188
|
+
{
|
|
189
|
+
return this.emitRow();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else
|
|
193
|
+
{
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
module.exports = CSVParser;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const libFableServiceBase = require('../Fable-ServiceManager.js').ServiceProviderBase;
|
|
2
|
+
|
|
3
|
+
class FableServiceFilePersistence extends libFableServiceBase
|
|
4
|
+
{
|
|
5
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
6
|
+
{
|
|
7
|
+
super(pFable, pOptions, pServiceHash);
|
|
8
|
+
|
|
9
|
+
this.serviceType = 'FilePersistence';
|
|
10
|
+
|
|
11
|
+
if (!this.options.hasOwnProperty('Mode'))
|
|
12
|
+
{
|
|
13
|
+
this.options.Mode = parseInt('0777', 8) & ~process.umask();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
existsSync(pPath)
|
|
18
|
+
{
|
|
19
|
+
//return libFS.existsSync(pPath);
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
exists(pPath, fCallback)
|
|
24
|
+
{
|
|
25
|
+
let tmpFileExists = this.existsSync(pPath);;
|
|
26
|
+
|
|
27
|
+
return fCallback(null, tmpFileExists);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
makeFolderRecursive (pParameters, fCallback)
|
|
31
|
+
{
|
|
32
|
+
return fCallback();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = FableServiceFilePersistence;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
const libFableServiceBase = require('../Fable-ServiceManager.js').ServiceProviderBase;
|
|
2
|
+
|
|
3
|
+
const libFS = require('fs');
|
|
4
|
+
const libPath = require('path');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FableServiceFilePersistence extends libFableServiceBase
|
|
8
|
+
{
|
|
9
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
10
|
+
{
|
|
11
|
+
super(pFable, pOptions, pServiceHash);
|
|
12
|
+
|
|
13
|
+
this.serviceType = 'FilePersistence';
|
|
14
|
+
|
|
15
|
+
if (!this.options.hasOwnProperty('Mode'))
|
|
16
|
+
{
|
|
17
|
+
this.options.Mode = parseInt('0777', 8) & ~process.umask();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
existsSync(pPath)
|
|
22
|
+
{
|
|
23
|
+
return libFS.existsSync(pPath);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
exists(pPath, fCallback)
|
|
27
|
+
{
|
|
28
|
+
let tmpFileExists = this.existsSync(pPath);;
|
|
29
|
+
|
|
30
|
+
return fCallback(null, tmpFileExists);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
makeFolderRecursive (pParameters, fCallback)
|
|
34
|
+
{
|
|
35
|
+
let tmpParameters = pParameters;
|
|
36
|
+
|
|
37
|
+
if (typeof(pParameters) == 'string')
|
|
38
|
+
{
|
|
39
|
+
tmpParameters = { Path: pParameters };
|
|
40
|
+
}
|
|
41
|
+
else if (typeof(pParameters) !== 'object')
|
|
42
|
+
{
|
|
43
|
+
fCallback(new Error('Parameters object or string not properly passed to recursive folder create.'));
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if ((typeof(tmpParameters.Path) !== 'string'))
|
|
48
|
+
{
|
|
49
|
+
fCallback(new Error('Parameters object needs a path to run the folder create operation.'));
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!tmpParameters.hasOwnProperty('Mode'))
|
|
54
|
+
{
|
|
55
|
+
tmpParameters.Mode = this.options.Mode;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check if we are just starting .. if so, build the initial state for our recursive function
|
|
59
|
+
if (typeof(tmpParameters.CurrentPathIndex) === 'undefined')
|
|
60
|
+
{
|
|
61
|
+
// Build the tools to start recursing
|
|
62
|
+
tmpParameters.ActualPath = libPath.normalize(tmpParameters.Path);
|
|
63
|
+
tmpParameters.ActualPathParts = tmpParameters.ActualPath.split(libPath.sep);
|
|
64
|
+
tmpParameters.CurrentPathIndex = 0;
|
|
65
|
+
tmpParameters.CurrentPath = '';
|
|
66
|
+
}
|
|
67
|
+
else
|
|
68
|
+
{
|
|
69
|
+
// This is not our first run, so we will continue the recursion.
|
|
70
|
+
// Build the new base path
|
|
71
|
+
if (tmpParameters.CurrentPath == libPath.sep)
|
|
72
|
+
{
|
|
73
|
+
tmpParameters.CurrentPath = tmpParameters.CurrentPath + tmpParameters.ActualPathParts[tmpParameters.CurrentPathIndex];
|
|
74
|
+
}
|
|
75
|
+
else
|
|
76
|
+
{
|
|
77
|
+
tmpParameters.CurrentPath = tmpParameters.CurrentPath + libPath.sep + tmpParameters.ActualPathParts[tmpParameters.CurrentPathIndex];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Increment the path index
|
|
81
|
+
tmpParameters.CurrentPathIndex++;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check if the path is fully complete
|
|
85
|
+
if (tmpParameters.CurrentPathIndex >= tmpParameters.ActualPathParts.length)
|
|
86
|
+
{
|
|
87
|
+
fCallback(null);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Check if the path exists
|
|
92
|
+
libFS.open(tmpParameters.CurrentPath + libPath.sep + tmpParameters.ActualPathParts[tmpParameters.CurrentPathIndex], 'r',
|
|
93
|
+
function(pError, pFileDescriptor)
|
|
94
|
+
{
|
|
95
|
+
if (pFileDescriptor)
|
|
96
|
+
{
|
|
97
|
+
libFS.closeSync(pFileDescriptor);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (pError && pError.code=='ENOENT')
|
|
101
|
+
{
|
|
102
|
+
/* Path doesn't exist, create it */
|
|
103
|
+
libFS.mkdir(tmpParameters.CurrentPath + libPath.sep + tmpParameters.ActualPathParts[tmpParameters.CurrentPathIndex], tmpParameters.Mode,
|
|
104
|
+
(pCreateError) =>
|
|
105
|
+
{
|
|
106
|
+
if (!pCreateError)
|
|
107
|
+
{
|
|
108
|
+
// We have now created our folder and there was no error -- continue.
|
|
109
|
+
return this.makeFolderRecursive(tmpParameters, fCallback);
|
|
110
|
+
}
|
|
111
|
+
else
|
|
112
|
+
{
|
|
113
|
+
fCallback(pCreateError);
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
else
|
|
119
|
+
{
|
|
120
|
+
return this.makeFolderRecursive(tmpParameters, fCallback);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
module.exports = FableServiceFilePersistence;
|
|
@@ -85,8 +85,9 @@ class FableServiceUtility extends libFableServiceBase
|
|
|
85
85
|
// and: 1986-06-11T09:34:46.012Z+0200
|
|
86
86
|
// ... and converts them into javascript timestamps, following the directions of the timezone stuff.
|
|
87
87
|
//
|
|
88
|
-
// This is not meant to replace the more complex libraries.
|
|
89
|
-
// This *is* meant to be a simple, small, and fast way to convert ISO strings to dates in engines
|
|
88
|
+
// This is not meant to replace the more complex libraries such as moment or luxon.
|
|
89
|
+
// This *is* meant to be a simple, small, and fast way to convert ISO strings to dates in engines
|
|
90
|
+
// with ultra limited JS capabilities where those don't work.
|
|
90
91
|
isoStringToDate (pISOString)
|
|
91
92
|
{
|
|
92
93
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Fable
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
var libFable = require('../source/Fable.js');
|
|
10
|
+
|
|
11
|
+
const libFS = require('fs');
|
|
12
|
+
const libReadline = require('readline');
|
|
13
|
+
|
|
14
|
+
var Chai = require("chai");
|
|
15
|
+
var Expect = Chai.expect;
|
|
16
|
+
|
|
17
|
+
suite
|
|
18
|
+
(
|
|
19
|
+
'CSV Parser',
|
|
20
|
+
function()
|
|
21
|
+
{
|
|
22
|
+
suite
|
|
23
|
+
(
|
|
24
|
+
'Parse CSV',
|
|
25
|
+
function()
|
|
26
|
+
{
|
|
27
|
+
test
|
|
28
|
+
(
|
|
29
|
+
'Pull CSV Data',
|
|
30
|
+
function(fDone)
|
|
31
|
+
{
|
|
32
|
+
let testFable = new libFable();
|
|
33
|
+
let tmpCSVParser = testFable.serviceManager.instantiateServiceProvider('CSVParser', {Name: 'Big Complex Integration Operation'}, 'CSV Parser-123');
|
|
34
|
+
let tmpRecords = [];
|
|
35
|
+
|
|
36
|
+
const tmpReadline = libReadline.createInterface(
|
|
37
|
+
{
|
|
38
|
+
input: libFS.createReadStream(`${__dirname}/data/books.csv`),
|
|
39
|
+
crlfDelay: Infinity
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
tmpReadline.on('line',
|
|
43
|
+
(pLine) =>
|
|
44
|
+
{
|
|
45
|
+
let tmpRecord = tmpCSVParser.parseCSVLine(pLine);
|
|
46
|
+
if (tmpRecord)
|
|
47
|
+
{
|
|
48
|
+
tmpRecords.push(tmpRecord);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
tmpReadline.on('close',
|
|
53
|
+
() =>
|
|
54
|
+
{
|
|
55
|
+
console.log(`Readline closed... testing import!`);
|
|
56
|
+
Expect(tmpRecords.length).to.equal(10000);
|
|
57
|
+
Expect(tmpRecords[0].authors).to.equal('Suzanne Collins');
|
|
58
|
+
return fDone();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Fable
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
var libFable = require('../source/Fable.js');
|
|
10
|
+
|
|
11
|
+
var Chai = require("chai");
|
|
12
|
+
var Expect = Chai.expect;
|
|
13
|
+
|
|
14
|
+
suite
|
|
15
|
+
(
|
|
16
|
+
'File Persistence',
|
|
17
|
+
function()
|
|
18
|
+
{
|
|
19
|
+
suite
|
|
20
|
+
(
|
|
21
|
+
'Work with Files',
|
|
22
|
+
function()
|
|
23
|
+
{
|
|
24
|
+
test
|
|
25
|
+
(
|
|
26
|
+
'Recursively Create a Folder',
|
|
27
|
+
function(fDone)
|
|
28
|
+
{
|
|
29
|
+
let testFable = new libFable();
|
|
30
|
+
|
|
31
|
+
return fDone();
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
);
|