alchemymvc 1.4.0 → 1.4.2
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/lib/app/behaviour/revision_behaviour.js +1 -1
- package/lib/app/behaviour/sluggable_behaviour.js +2 -2
- package/lib/app/datasource/mongo_datasource.js +19 -3
- package/lib/app/helper/cron.js +2 -2
- package/lib/app/helper_datasource/00-nosql_datasource.js +9 -3
- package/lib/app/helper_datasource/05-fallback_datasource.js +10 -13
- package/lib/app/helper_datasource/idb_datasource.js +7 -5
- package/lib/app/helper_datasource/remote_datasource.js +1 -1
- package/lib/app/helper_field/password_field.js +4 -2
- package/lib/app/helper_field/schema_field.js +3 -2
- package/lib/app/helper_field/time_field.js +1 -1
- package/lib/app/helper_model/00-base_criteria.js +14 -0
- package/lib/app/helper_model/05-criteria_expressions.js +30 -7
- package/lib/app/helper_model/10-model_criteria.js +47 -8
- package/lib/app/helper_model/document.js +11 -2
- package/lib/app/helper_model/model.js +6 -3
- package/lib/app/model/system_task_history_model.js +134 -0
- package/lib/class/conduit.js +5 -2
- package/lib/class/controller.js +1 -0
- package/lib/class/datasource.js +14 -2
- package/lib/class/document.js +40 -12
- package/lib/class/import_stream_parser.js +299 -0
- package/lib/class/inode_file.js +2 -0
- package/lib/class/migration.js +5 -2
- package/lib/class/model.js +12 -142
- package/lib/class/plugin.js +32 -3
- package/lib/class/postponement.js +1 -1
- package/lib/class/router.js +26 -28
- package/lib/class/schema_client.js +39 -8
- package/lib/class/sitemap.js +2 -2
- package/lib/class/task.js +42 -24
- package/lib/core/alchemy.js +110 -162
- package/lib/core/alchemy_load_functions.js +64 -5
- package/lib/core/base.js +2 -2
- package/lib/core/middleware.js +31 -5
- package/lib/core/prefix.js +1 -1
- package/lib/core/setting.js +12 -9
- package/lib/scripts/create_constants.js +5 -1
- package/lib/stages/00-load_core.js +8 -2
- package/lib/testing/browser.js +1164 -0
- package/lib/testing/harness.js +922 -0
- package/package.json +13 -6
- package/testing/browser.js +27 -0
- package/testing.js +37 -0
package/lib/class/model.js
CHANGED
|
@@ -390,7 +390,7 @@ Model.setStatic(async function checkPathValue(value, name, field_name, conduit)
|
|
|
390
390
|
if (result) {
|
|
391
391
|
let found_value = result[field_name];
|
|
392
392
|
|
|
393
|
-
if (found_value != value && !Object.alike(value, found_value)) {
|
|
393
|
+
if (conduit && found_value != value && !Object.alike(value, found_value)) {
|
|
394
394
|
conduit.rewriteRequestRouteParam(name, found_value);
|
|
395
395
|
}
|
|
396
396
|
}
|
|
@@ -534,7 +534,7 @@ Model.setStatic(function getField(name) {
|
|
|
534
534
|
if (name.indexOf('.') > -1) {
|
|
535
535
|
split = name.split('.');
|
|
536
536
|
|
|
537
|
-
alias =
|
|
537
|
+
alias = split[0];
|
|
538
538
|
|
|
539
539
|
if (this.schema.associations[alias] == null) {
|
|
540
540
|
model = this;
|
|
@@ -1590,7 +1590,7 @@ Model.setMethod(function exportToStream(output, options) {
|
|
|
1590
1590
|
*
|
|
1591
1591
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1592
1592
|
* @since 1.0.5
|
|
1593
|
-
* @version 1.
|
|
1593
|
+
* @version 1.4.1
|
|
1594
1594
|
*
|
|
1595
1595
|
* @param {Stream} input
|
|
1596
1596
|
* @param {Object} options
|
|
@@ -1612,150 +1612,20 @@ Model.setMethod(function importFromStream(input, options) {
|
|
|
1612
1612
|
return Pledge.reject(new Error('No source input stream has been given'));
|
|
1613
1613
|
}
|
|
1614
1614
|
|
|
1615
|
-
let that = this
|
|
1616
|
-
|
|
1617
|
-
extra_stream,
|
|
1618
|
-
pledge = new Pledge(),
|
|
1619
|
-
stopped,
|
|
1620
|
-
paused,
|
|
1621
|
-
buffer,
|
|
1622
|
-
value,
|
|
1623
|
-
seen = 0,
|
|
1624
|
-
left,
|
|
1625
|
-
size,
|
|
1626
|
-
doc;
|
|
1627
|
-
|
|
1628
|
-
input.on('data', function onData(data) {
|
|
1629
|
-
|
|
1630
|
-
if (stopped) {
|
|
1631
|
-
return;
|
|
1632
|
-
}
|
|
1615
|
+
let that = this;
|
|
1616
|
+
let parser = new Classes.Alchemy.ImportStreamParser(input, options);
|
|
1633
1617
|
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1618
|
+
// Model resolver for single-model import:
|
|
1619
|
+
// Validates that the model name matches, returns this model instance
|
|
1620
|
+
parser.setModelResolver(function resolveModel(model_name, current_model) {
|
|
1621
|
+
if (model_name == that.model_name) {
|
|
1622
|
+
return that;
|
|
1638
1623
|
}
|
|
1639
1624
|
|
|
1640
|
-
|
|
1625
|
+
throw new Error('Model names do not match: expected "' + that.model_name + '", got "' + model_name + '"');
|
|
1641
1626
|
});
|
|
1642
1627
|
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
if (paused) {
|
|
1646
|
-
return;
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
if (!current_type && buffer.length < 2) {
|
|
1650
|
-
return;
|
|
1651
|
-
}
|
|
1652
|
-
|
|
1653
|
-
if (!current_type) {
|
|
1654
|
-
current_type = buffer.readUInt8(0);
|
|
1655
|
-
|
|
1656
|
-
if (current_type == 0x01) {
|
|
1657
|
-
size = buffer.readUInt8(1);
|
|
1658
|
-
buffer = buffer.slice(2);
|
|
1659
|
-
} else if (current_type == 0x02 && buffer.length >= 5) {
|
|
1660
|
-
size = buffer.readUInt32BE(1);
|
|
1661
|
-
buffer = buffer.slice(5);
|
|
1662
|
-
} else if (current_type == 0xFF) {
|
|
1663
|
-
size = buffer.readUInt32BE(1);
|
|
1664
|
-
buffer = buffer.slice(5);
|
|
1665
|
-
seen = 0;
|
|
1666
|
-
|
|
1667
|
-
if (!doc) {
|
|
1668
|
-
stopped = true;
|
|
1669
|
-
pledge.reject(new Error('Found extra import data, but no active document'));
|
|
1670
|
-
} else {
|
|
1671
|
-
extra_stream = new require('stream').PassThrough();
|
|
1672
|
-
doc.extraImportFromStream(extra_stream);
|
|
1673
|
-
}
|
|
1674
|
-
} else {
|
|
1675
|
-
// Not enough data? Wait
|
|
1676
|
-
current_type = null;
|
|
1677
|
-
return;
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
|
-
handleRest();
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
function handleRest() {
|
|
1685
|
-
|
|
1686
|
-
if (current_type == 0xFF) {
|
|
1687
|
-
left = size - seen;
|
|
1688
|
-
value = buffer.slice(0, left);
|
|
1689
|
-
|
|
1690
|
-
seen += value.length;
|
|
1691
|
-
|
|
1692
|
-
if (value.length == buffer.length) {
|
|
1693
|
-
buffer = null;
|
|
1694
|
-
} else if (value.length < buffer.length) {
|
|
1695
|
-
buffer = buffer.slice(left);
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
extra_stream.write(value);
|
|
1699
|
-
|
|
1700
|
-
if (value.length == left) {
|
|
1701
|
-
extra_stream.end();
|
|
1702
|
-
current_type = null;
|
|
1703
|
-
|
|
1704
|
-
if (buffer) {
|
|
1705
|
-
handleBuffer();
|
|
1706
|
-
}
|
|
1707
|
-
}
|
|
1708
|
-
|
|
1709
|
-
return;
|
|
1710
|
-
}
|
|
1711
|
-
|
|
1712
|
-
if (buffer.length >= size) {
|
|
1713
|
-
value = buffer.slice(0, size);
|
|
1714
|
-
buffer = buffer.slice(size);
|
|
1715
|
-
} else {
|
|
1716
|
-
// Wait for next call
|
|
1717
|
-
return;
|
|
1718
|
-
}
|
|
1719
|
-
|
|
1720
|
-
if (current_type == 0x01) {
|
|
1721
|
-
value = value.toString();
|
|
1722
|
-
|
|
1723
|
-
if (value == that.model_name) {
|
|
1724
|
-
// Found name!
|
|
1725
|
-
current_type = null;
|
|
1726
|
-
size = 0;
|
|
1727
|
-
} else {
|
|
1728
|
-
stopped = true;
|
|
1729
|
-
return pledge.reject(new Error('Model names do not match'));
|
|
1730
|
-
}
|
|
1731
|
-
} else if (current_type == 0x02) {
|
|
1732
|
-
doc = that.createDocument();
|
|
1733
|
-
input.pause();
|
|
1734
|
-
paused = true;
|
|
1735
|
-
|
|
1736
|
-
doc.importFromBuffer(value).done(function done(err, result) {
|
|
1737
|
-
|
|
1738
|
-
if (err) {
|
|
1739
|
-
stopped = true;
|
|
1740
|
-
return pledge.reject(err);
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
current_type = null;
|
|
1744
|
-
paused = false;
|
|
1745
|
-
input.resume();
|
|
1746
|
-
|
|
1747
|
-
handleBuffer();
|
|
1748
|
-
});
|
|
1749
|
-
|
|
1750
|
-
return;
|
|
1751
|
-
}
|
|
1752
|
-
|
|
1753
|
-
if (buffer && buffer.length) {
|
|
1754
|
-
handleBuffer();
|
|
1755
|
-
}
|
|
1756
|
-
}
|
|
1757
|
-
|
|
1758
|
-
return pledge;
|
|
1628
|
+
return parser.parse();
|
|
1759
1629
|
});
|
|
1760
1630
|
|
|
1761
1631
|
/**
|
package/lib/class/plugin.js
CHANGED
|
@@ -41,7 +41,7 @@ const Plugin = Function.inherits('Alchemy.Base', function Plugin(name, path, def
|
|
|
41
41
|
*
|
|
42
42
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
43
43
|
* @since 1.4.0
|
|
44
|
-
* @version 1.4.
|
|
44
|
+
* @version 1.4.1
|
|
45
45
|
*
|
|
46
46
|
* @return {boolean}
|
|
47
47
|
*/
|
|
@@ -52,16 +52,45 @@ Plugin.setMethod(function doPreload() {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
// Create settings from the `config/settings.js` file
|
|
55
|
-
|
|
55
|
+
// This can fail if the config is invalid
|
|
56
|
+
try {
|
|
57
|
+
this.loadSettingDefinitions();
|
|
58
|
+
} catch (err) {
|
|
59
|
+
this._handleLoadError('settings', err);
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
56
62
|
|
|
57
63
|
// Load the bootstrap file
|
|
58
|
-
|
|
64
|
+
// This can fail due to syntax errors or runtime errors
|
|
65
|
+
try {
|
|
66
|
+
this.loadBootstrap();
|
|
67
|
+
} catch (err) {
|
|
68
|
+
this._handleLoadError('bootstrap', err);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this[FLAGS].preloaded = true;
|
|
59
73
|
|
|
60
74
|
PLUGINS_STAGE.addPostTask(() => {
|
|
61
75
|
return this.startPlugin();
|
|
62
76
|
});
|
|
63
77
|
});
|
|
64
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Handle a plugin loading error
|
|
81
|
+
* Plugin errors are fatal - the server cannot start with a broken plugin
|
|
82
|
+
*
|
|
83
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
84
|
+
* @since 1.4.1
|
|
85
|
+
* @version 1.4.1
|
|
86
|
+
*
|
|
87
|
+
* @param {string} phase Which phase failed (settings, bootstrap, start)
|
|
88
|
+
* @param {Error} err The error that occurred
|
|
89
|
+
*/
|
|
90
|
+
Plugin.setMethod(function _handleLoadError(phase, err) {
|
|
91
|
+
alchemy.handlePluginError(this.name, phase, err);
|
|
92
|
+
});
|
|
93
|
+
|
|
65
94
|
/**
|
|
66
95
|
* Do the rest of the plugin loading
|
|
67
96
|
*
|
package/lib/class/router.js
CHANGED
|
@@ -200,7 +200,7 @@ RouterClass.setMethod(function headerBypass(prefix) {
|
|
|
200
200
|
*
|
|
201
201
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
202
202
|
* @since 0.3.0
|
|
203
|
-
* @version
|
|
203
|
+
* @version 1.4.1
|
|
204
204
|
*/
|
|
205
205
|
RouterClass.setMethod(function getFullMount() {
|
|
206
206
|
|
|
@@ -215,6 +215,11 @@ RouterClass.setMethod(function getFullMount() {
|
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
// Remove trailing slash (but keep single "/" for root)
|
|
219
|
+
if (result.length > 1 && result[result.length - 1] == '/') {
|
|
220
|
+
result = result.slice(0, -1);
|
|
221
|
+
}
|
|
222
|
+
|
|
218
223
|
return result;
|
|
219
224
|
});
|
|
220
225
|
|
|
@@ -228,7 +233,8 @@ RouterClass.setMethod(function getFullMount() {
|
|
|
228
233
|
RouterClass.setMethod(function getPrefix(path) {
|
|
229
234
|
|
|
230
235
|
var prefix,
|
|
231
|
-
begin
|
|
236
|
+
begin,
|
|
237
|
+
key;
|
|
232
238
|
|
|
233
239
|
// See if the path starts with any set prefix
|
|
234
240
|
for (key in prefixes) {
|
|
@@ -1053,34 +1059,12 @@ RouterClass.setMethod('delete', function _delete(name, paths, fnc, options) {
|
|
|
1053
1059
|
return this.add(['delete'], name, paths, fnc, options);
|
|
1054
1060
|
});
|
|
1055
1061
|
|
|
1056
|
-
/**
|
|
1057
|
-
* Get an object of all the routes in this router and its children
|
|
1058
|
-
*
|
|
1059
|
-
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1060
|
-
* @since 0.2.0
|
|
1061
|
-
* @version 0.2.0
|
|
1062
|
-
*/
|
|
1063
|
-
RouterClass.setMethod(function getFullMount() {
|
|
1064
|
-
|
|
1065
|
-
var result = this.mount;
|
|
1066
|
-
|
|
1067
|
-
if (this.parent != null && this.parent.mount != '/') {
|
|
1068
|
-
result = this.parent.mount + result;
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
if (result[result.length-1] == '/') {
|
|
1072
|
-
result = result.slice(0, -1);
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
return result;
|
|
1076
|
-
});
|
|
1077
|
-
|
|
1078
1062
|
/**
|
|
1079
1063
|
* Get the full route object, for internal use
|
|
1080
1064
|
*
|
|
1081
1065
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1082
1066
|
* @since 0.5.0
|
|
1083
|
-
* @version
|
|
1067
|
+
* @version 1.4.1
|
|
1084
1068
|
*
|
|
1085
1069
|
* @param {Object} result Optional object to store sectioned results in
|
|
1086
1070
|
*
|
|
@@ -1111,7 +1095,14 @@ RouterClass.setMethod(function getFullRoutes(result) {
|
|
|
1111
1095
|
temp = {};
|
|
1112
1096
|
|
|
1113
1097
|
for (prefix in route.paths) {
|
|
1114
|
-
|
|
1098
|
+
let source = route.paths[prefix].source;
|
|
1099
|
+
|
|
1100
|
+
// Avoid double slashes when mount is '/' and path starts with '/'
|
|
1101
|
+
if (mount == '/' && source[0] == '/') {
|
|
1102
|
+
temp[prefix] = source;
|
|
1103
|
+
} else {
|
|
1104
|
+
temp[prefix] = mount + source;
|
|
1105
|
+
}
|
|
1115
1106
|
}
|
|
1116
1107
|
|
|
1117
1108
|
section[route.name] = route;
|
|
@@ -1175,7 +1166,7 @@ RouterClass.setMethod(function getOptions(result) {
|
|
|
1175
1166
|
*
|
|
1176
1167
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1177
1168
|
* @since 0.2.0
|
|
1178
|
-
* @version 1.
|
|
1169
|
+
* @version 1.4.1
|
|
1179
1170
|
*
|
|
1180
1171
|
* @param {Object} result Optional object to store sectioned results in
|
|
1181
1172
|
*
|
|
@@ -1206,7 +1197,14 @@ RouterClass.setMethod(function getRoutes(result) {
|
|
|
1206
1197
|
temp = {};
|
|
1207
1198
|
|
|
1208
1199
|
for (prefix in route.paths) {
|
|
1209
|
-
|
|
1200
|
+
let source = route.paths[prefix].source;
|
|
1201
|
+
|
|
1202
|
+
// Avoid double slashes when mount is '/' and path starts with '/'
|
|
1203
|
+
if (mount == '/' && source[0] == '/') {
|
|
1204
|
+
temp[prefix] = source;
|
|
1205
|
+
} else {
|
|
1206
|
+
temp[prefix] = mount + source;
|
|
1207
|
+
}
|
|
1210
1208
|
}
|
|
1211
1209
|
|
|
1212
1210
|
section[route.name] = {
|
|
@@ -1079,7 +1079,7 @@ Schema.setMethod(function getFieldNames() {
|
|
|
1079
1079
|
*
|
|
1080
1080
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1081
1081
|
* @since 0.2.0
|
|
1082
|
-
* @version 1.4.
|
|
1082
|
+
* @version 1.4.1
|
|
1083
1083
|
*
|
|
1084
1084
|
* @param {string|FieldType} _field_or_name Field name, or index name when using `fields` option
|
|
1085
1085
|
* @param {Object} options
|
|
@@ -1111,15 +1111,46 @@ Schema.setMethod(function addIndex(_field_or_name, _options) {
|
|
|
1111
1111
|
// When `fields` is provided, the first argument is the index name
|
|
1112
1112
|
options.name = _field_or_name;
|
|
1113
1113
|
|
|
1114
|
+
// Create the index entry
|
|
1115
|
+
if (this.indexes[options.name] == null) {
|
|
1116
|
+
this.indexes[options.name] = {
|
|
1117
|
+
fields: {},
|
|
1118
|
+
options: options
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Add all fields to the index
|
|
1114
1123
|
for (let field_name of options.fields) {
|
|
1115
|
-
this.
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1124
|
+
let field = this.getField(field_name);
|
|
1125
|
+
|
|
1126
|
+
if (!field) {
|
|
1127
|
+
throw new Error('Could not find field "' + field_name + '" for compound index "' + options.name + '"');
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
let path = field.path;
|
|
1131
|
+
this.indexes[options.name].fields[path] = options.order || 1;
|
|
1132
|
+
this.index_fields[path] = options;
|
|
1121
1133
|
}
|
|
1122
1134
|
|
|
1135
|
+
// Now call ensureIndex once with the complete compound index
|
|
1136
|
+
const that = this;
|
|
1137
|
+
|
|
1138
|
+
that.getDatasource().done(function gotDs(err, datasource) {
|
|
1139
|
+
if (err) {
|
|
1140
|
+
throw err;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
if (datasource.supports('ensure_index') === false) {
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
datasource.ensureIndex(that.model_class, that.indexes[options.name], function ensuredIndex(err, result) {
|
|
1148
|
+
if (err) {
|
|
1149
|
+
alchemy.printLog('error', ['Error ensuring compound index', options.name, 'in model', that.model_name], {err: err});
|
|
1150
|
+
}
|
|
1151
|
+
});
|
|
1152
|
+
});
|
|
1153
|
+
|
|
1123
1154
|
return;
|
|
1124
1155
|
}
|
|
1125
1156
|
|
|
@@ -1153,7 +1184,7 @@ Schema.setMethod(function addIndex(_field_or_name, _options) {
|
|
|
1153
1184
|
}
|
|
1154
1185
|
};
|
|
1155
1186
|
|
|
1156
|
-
if (typeof options.order == '
|
|
1187
|
+
if (typeof options.order == 'string') {
|
|
1157
1188
|
if (options.order == 'asc') {
|
|
1158
1189
|
options.order = 1;
|
|
1159
1190
|
} else {
|
package/lib/class/sitemap.js
CHANGED
|
@@ -353,7 +353,7 @@ Sitemap.setMethod(function addRouteWithParameters(route, config, parameters, for
|
|
|
353
353
|
*
|
|
354
354
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
355
355
|
* @since 1.3.4
|
|
356
|
-
* @version 1.
|
|
356
|
+
* @version 1.4.1
|
|
357
357
|
*
|
|
358
358
|
* @param {string} prefix
|
|
359
359
|
*
|
|
@@ -378,7 +378,7 @@ Sitemap.setMethod(function getXmlBuilder(prefix) {
|
|
|
378
378
|
}
|
|
379
379
|
|
|
380
380
|
for (let info of category.pages) {
|
|
381
|
-
let url = urlset.ele(url);
|
|
381
|
+
let url = urlset.ele('url');
|
|
382
382
|
|
|
383
383
|
url.ele('loc').txt(''+info.url);
|
|
384
384
|
|
package/lib/class/task.js
CHANGED
|
@@ -371,33 +371,51 @@ Task.setMethod(async function start(payload) {
|
|
|
371
371
|
let result;
|
|
372
372
|
|
|
373
373
|
try {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
374
|
+
try {
|
|
375
|
+
result = await this.executor();
|
|
376
|
+
} catch (err) {
|
|
377
|
+
if (err == 'stopped') {
|
|
378
|
+
this.report('stopped', 'Stopped');
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Set the error
|
|
383
|
+
this.error = err;
|
|
384
|
+
|
|
385
|
+
// Report failed
|
|
386
|
+
let report = this.report('failed');
|
|
387
|
+
report.error = err;
|
|
388
|
+
|
|
389
|
+
// Surface the failure on the history document - without
|
|
390
|
+
// this, the row is left with `is_running: true` and no
|
|
391
|
+
// error info, looking identical to a still-running task.
|
|
392
|
+
document.had_error = true;
|
|
393
|
+
document.error_message = String(err && err.message || err);
|
|
394
|
+
document.error_stack = err && err.stack || null;
|
|
395
|
+
|
|
396
|
+
throw err;
|
|
379
397
|
}
|
|
380
398
|
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
399
|
+
// If the command hasn't been manually stopped,
|
|
400
|
+
// report it as done
|
|
401
|
+
if (!this.manual_stop_end) {
|
|
402
|
+
this.report('done');
|
|
403
|
+
}
|
|
404
|
+
} finally {
|
|
405
|
+
// Always close out the history document so it doesn't get
|
|
406
|
+
// stuck as a zombie "running forever" row.
|
|
407
|
+
document.ended_at = new Date();
|
|
408
|
+
document.is_running = false;
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
await document.save();
|
|
412
|
+
} catch (save_err) {
|
|
413
|
+
// Don't let a history-save failure swallow the original
|
|
414
|
+
// executor error on its way out of the catch above.
|
|
415
|
+
alchemy.registerError(save_err);
|
|
416
|
+
}
|
|
395
417
|
}
|
|
396
418
|
|
|
397
|
-
document.ended_at = new Date();
|
|
398
|
-
document.is_running = false;
|
|
399
|
-
await document.save();
|
|
400
|
-
|
|
401
419
|
this[RUNNING_PLEDGE].resolve(result);
|
|
402
420
|
|
|
403
421
|
return result;
|
|
@@ -567,7 +585,7 @@ async function doAsyncLoopUntilNotBusy(max_tries) {
|
|
|
567
585
|
}
|
|
568
586
|
|
|
569
587
|
do {
|
|
570
|
-
|
|
588
|
+
log.info('Waiting for system to be less busy, try', tries);
|
|
571
589
|
await Pledge.after(500);
|
|
572
590
|
tries++;
|
|
573
591
|
} while (tries < max_tries && alchemy.isTooBusy());
|