alchemymvc 1.3.16 → 1.3.18

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.
@@ -1277,6 +1277,7 @@ Document.setMethod(function hasChanged(name) {
1277
1277
 
1278
1278
  for (key in this.$attributes.original_record) {
1279
1279
  if (!Object.alike(this.$attributes.original_record[key], this[key])) {
1280
+ // @TODO: some special fields always end up being different
1280
1281
  result = true;
1281
1282
  break;
1282
1283
  }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * The Alchemy Task History model:
3
+ * keep track of all the tasks that have been run
4
+ *
5
+ * @constructor
6
+ *
7
+ * @author Jelle De Loecker <jelle@elevenways.be>
8
+ * @since 0.5.0
9
+ * @version 0.5.0
10
+ */
11
+ const AlchemyTaskHistory = Function.inherits('Alchemy.Model.App', 'AlchemyTaskHistory');
12
+
13
+ /**
14
+ * Constitute the class wide schema
15
+ *
16
+ * @author Jelle De Loecker <jelle@elevenways.be>
17
+ * @since 0.5.0
18
+ * @version 1.3.17
19
+ */
20
+ AlchemyTaskHistory.constitute(function addTaskFields() {
21
+
22
+ this.belongsTo('AlchemyTask', {
23
+ description : 'The original AlchemyTask document it belonged to',
24
+ });
25
+
26
+ // The timestamp this was scheduled for
27
+ this.addField('scheduled_at', 'Date', {
28
+ description : 'The original date this was scheduled for',
29
+ });
30
+
31
+ this.addField('process_id', 'Integer', {
32
+ description : 'The process ID of the task',
33
+ });
34
+
35
+ // The type of Task that is running
36
+ this.addField('type', 'Enum', {
37
+ values: Classes.Alchemy.Task.Task.getLiveDescendantsMap(),
38
+ });
39
+
40
+ // The payload/settings of the task
41
+ this.addField('settings', 'Schema', {
42
+ description : 'The settings of the task at the time it ran',
43
+ schema: 'type'
44
+ });
45
+
46
+ this.addField('had_error', 'Boolean', {
47
+ description : 'Did this task run into an error?'
48
+ });
49
+
50
+ this.addField('error_message', 'String', {
51
+ description : 'The main error message'
52
+ });
53
+
54
+ this.addField('error_stack', 'Text', {
55
+ description : 'The error stack trace'
56
+ });
57
+
58
+ this.addField('is_running', 'Boolean', {
59
+ description : 'Is this task still running?',
60
+ default : false,
61
+ });
62
+
63
+ this.addField('started_at', 'Datetime', {
64
+ description : 'The datetime this task actually started running',
65
+ });
66
+
67
+ this.addField('ended_at', 'Datetime', {
68
+ description : 'The datetime this task actually ended',
69
+ });
70
+
71
+ this.addIndex('type');
72
+ this.addIndex('is_running');
73
+ });
74
+
75
+ /**
76
+ * Configure the default chimera fieldsets
77
+ *
78
+ * @author Jelle De Loecker <jelle@elevenways.be>
79
+ * @since 1.3.17
80
+ * @version 1.3.17
81
+ */
82
+ AlchemyTaskHistory.constitute(function chimeraConfig() {
83
+
84
+ if (!this.chimera) {
85
+ return;
86
+ }
87
+
88
+ // Get the list group
89
+ let list = this.chimera.getActionFields('list');
90
+
91
+ list.addField('created');
92
+ list.addField('type');
93
+ list.addField('process_id');
94
+ list.addField('scheduled_at');
95
+ list.addField('started_at');
96
+ list.addField('ended_at');
97
+ list.addField('had_error');
98
+
99
+ // Get the edit group
100
+ let edit = this.chimera.getActionFields('edit');
101
+
102
+ edit.addField('type');
103
+ edit.addField('settings');
104
+ edit.addField('had_error');
105
+ edit.addField('error_message');
106
+ edit.addField('error_stack');
107
+ edit.addField('started_at');
108
+ edit.addField('ended_at');
109
+ });
@@ -1,34 +1,157 @@
1
- var all_task_types = alchemy.getClassGroup('task');
2
-
3
1
  /**
4
- * The Alchemy Task Model class
2
+ * The Alchemy Task model:
3
+ *
5
4
  *
6
5
  * @constructor
7
6
  *
8
- * @author Jelle De Loecker <jelle@develry.be>
9
- * @since 0.5.0
10
- * @version 0.5.0
7
+ * @author Jelle De Loecker <jelle@elevenways.be>
8
+ * @since 1.3.17
9
+ * @version 1.3.17
10
+ */
11
+ const AlchemyTask = Function.inherits('Alchemy.Model.App', 'AlchemyTask');
12
+
13
+ /**
14
+ * Constitute the class wide schema
15
+ *
16
+ * @author Jelle De Loecker <jelle@elevenways.be>
17
+ * @since 1.3.17
18
+ * @version 1.3.17
11
19
  */
12
- var AlchemyTask = Function.inherits('Alchemy.Model.App', function AlchemyTask(conduit, options) {
20
+ AlchemyTask.constitute(function addTaskFields() {
21
+
22
+ this.addField('title', 'String', {
23
+ description : 'The title of the task',
24
+ });
25
+
26
+ this.addField('type', 'Enum', {
27
+ values : Classes.Alchemy.Task.Task.getLiveDescendantsMap(),
28
+ description : 'The type of task to run',
29
+ });
30
+
31
+ this.addField('settings', 'Schema', {
32
+ description : 'The settings to use for running the task',
33
+ schema: 'type'
34
+ });
13
35
 
14
- var that = this;
36
+ this.addField('enabled', 'Boolean', {
37
+ description : 'Is this task enabled?',
38
+ default : true,
39
+ });
15
40
 
16
- AlchemyTask.super.call(this, conduit, options);
41
+ this.addField('frequency', 'String', {
42
+ description : 'The frequency this task should run at (CRON syntax)',
43
+ });
44
+
45
+ this.addField('comment', 'Text', {
46
+ description : 'A comment about this task',
47
+ });
48
+
49
+ this.addField('schedule_type', 'Enum', {
50
+ values: {
51
+ user : 'User',
52
+ system_forced : 'System (forced)',
53
+ system_fallback : 'System (fallback)',
54
+ },
55
+ default: 'user',
56
+ });
57
+
58
+ this.addField('forced_schedule_checksum', 'String', {
59
+ description : 'Checksum of the cron syntax and settings, set only for tasks originating from forced schedules',
60
+ });
61
+
62
+ this.addIndex('forced_schedule_checksum', {
63
+ unique : true,
64
+ sparse : true,
65
+ });
17
66
  });
18
67
 
19
68
  /**
20
- * Constitute the class wide schema
69
+ * Configure the default chimera fieldsets
21
70
  *
22
- * @author Jelle De Loecker <jelle@develry.be>
23
- * @since 0.5.0
24
- * @version 0.5.0
71
+ * @author Jelle De Loecker <jelle@elevenways.be>
72
+ * @since 1.3.17
73
+ * @version 1.3.17
25
74
  */
26
- AlchemyTask.constitute(function addTaskFields() {
75
+ AlchemyTask.constitute(function chimeraConfig() {
76
+
77
+ if (!this.chimera) {
78
+ return;
79
+ }
80
+
81
+ // Get the list group
82
+ let list = this.chimera.getActionFields('list');
83
+
84
+ list.addField('title');
85
+ list.addField('type');
86
+ list.addField('enabled');
87
+ list.addField('frequency');
88
+ list.addField('schedule_type');
89
+
90
+ // Get the edit group
91
+ let edit = this.chimera.getActionFields('edit');
92
+
93
+ edit.addField('title');
94
+ edit.addField('frequency')
95
+ edit.addField('enabled');
96
+ edit.addField('type');
97
+ edit.addField('settings');
98
+ edit.addField('comment');
99
+ });
100
+
101
+ /**
102
+ * Do something before saving the record
103
+ *
104
+ * @author Jelle De Loecker <jelle@elevenways.be>
105
+ * @since 1.3.17
106
+ * @version 1.3.17
107
+ *
108
+ * @param {Document.AlchemyTask} doc
109
+ */
110
+ AlchemyTask.setMethod(function beforeSave(doc) {
111
+
112
+ if (!doc.schedule_type) {
113
+ doc.schedule_type = 'user';
114
+ }
115
+
116
+ if (doc.enabled == null) {
117
+ doc.enabled = false;
118
+ }
119
+
120
+ if (!doc.title) {
121
+ let title = '';
122
+
123
+ if (doc.schedule_type == 'system_forced') {
124
+ title += 'System forced: ';
125
+ }
126
+
127
+ if (doc.schedule_type == 'system_fallback') {
128
+ title += 'System fallback: ';
129
+ }
130
+
131
+ title += doc.type;
132
+ doc.title = title;
133
+ }
134
+ });
135
+
136
+ /**
137
+ * Update the schedules after saving
138
+ *
139
+ * @author Jelle De Loecker <jelle@elevenways.be>
140
+ * @since 1.3.17
141
+ * @version 1.3.17
142
+ *
143
+ * @param {Object} main
144
+ * @param {Object} info
145
+ */
146
+ AlchemyTask.setMethod(function afterSave(main, info) {
27
147
 
28
- // The type of Task that is running
29
- this.addField('type', 'Enum', {values: all_task_types});
148
+ if (!main.type) {
149
+ return;
150
+ }
30
151
 
31
- // When the task ended
32
- this.addField('ended', 'Datetime');
152
+ if (!alchemy.task_service.has_loaded) {
153
+ return;
154
+ }
33
155
 
156
+ alchemy.task_service.rescheduleTasksOfType(main.type);
34
157
  });
package/lib/bootstrap.js CHANGED
@@ -312,6 +312,9 @@ Alchemy.setMethod(function start(options, callback) {
312
312
  // Make sure Blast has executed everything that's still waiting
313
313
  Blast.doLoaded();
314
314
 
315
+ // Call the `afterStart` method
316
+ this.ready(() => this.afterStart());
317
+
315
318
  // Schedule the callback
316
319
  return this.ready(callback);
317
320
  });
@@ -125,12 +125,23 @@ Conduit.setProperty(function scene() {
125
125
  return this.getSession().getScene(this.scene_id);
126
126
  });
127
127
 
128
+ /**
129
+ * The session cookie name to use
130
+ *
131
+ * @author Jelle De Loecker <jelle@elevenways.be>
132
+ * @since 1.3.18
133
+ * @version 1.3.18
134
+ */
135
+ Conduit.setProperty(function session_cookie_name() {
136
+ return alchemy.settings.session_key || 'alchemy_sid';
137
+ });
138
+
128
139
  /**
129
140
  * Enforce the scene_id
130
141
  *
131
142
  * @author Jelle De Loecker <jelle@elevenways.be>
132
143
  * @since 1.1.0
133
- * @version 1.3.10
144
+ * @version 1.3.18
134
145
  */
135
146
  Conduit.enforceProperty(function scene_id(new_value, old_value) {
136
147
 
@@ -160,7 +171,10 @@ Conduit.enforceProperty(function scene_id(new_value, old_value) {
160
171
  path: path,
161
172
 
162
173
  // Cookie should not live for more than 15 seconds
163
- maxAge: 1000 * 15
174
+ maxAge: 1000 * 15,
175
+
176
+ // It should be restricted to the same domain
177
+ domain: false,
164
178
  });
165
179
  }
166
180
  }
@@ -259,12 +273,17 @@ Conduit.setProperty(function is_secure() {
259
273
  *
260
274
  * @author Jelle De Loecker <jelle@develry.be>
261
275
  * @since 1.1.0
262
- * @version 1.1.0
276
+ * @version 1.3.18
263
277
  *
264
- * @param {Object}
278
+ * @param {Object|String}
265
279
  */
266
280
  Conduit.setMethod(function setRequestBody(body) {
267
281
 
282
+ if (typeof body == 'string') {
283
+ this.body = body;
284
+ return;
285
+ }
286
+
268
287
  if (!body) {
269
288
  return;
270
289
  }
@@ -490,22 +509,20 @@ Conduit.setMethod(function time() {
490
509
  /**
491
510
  * Parse the request, get information from the url
492
511
  *
493
- * @author Jelle De Loecker <jelle@develry.be>
512
+ * @author Jelle De Loecker <jelle@elevenways.be>
494
513
  * @since 0.2.0
495
- * @version 1.2.5
514
+ * @version 1.3.18
496
515
  *
497
516
  * @param {IncomingMessage} req
498
517
  * @param {ServerResponse} res
499
518
  */
500
519
  Conduit.setMethod(async function parseRequest() {
501
520
 
502
- var protocol,
503
- section;
504
-
505
521
  if (this.method == null && this.request && this.request.method) {
506
522
  this.method = this.request.method.toLowerCase();
507
523
  }
508
524
 
525
+ this.parseUrl();
509
526
  this.parseShortcuts();
510
527
  this.parseLanguages();
511
528
  this.parsePrefix();
@@ -529,11 +546,25 @@ Conduit.setMethod(async function parseRequest() {
529
546
  this.overrideResponseUrl(new_url);
530
547
  }
531
548
 
549
+ this.parseUrl();
550
+ });
551
+
552
+ /**
553
+ * Parse the url if it hasn't been done already
554
+ *
555
+ * @author Jelle De Loecker <jelle@elevenways.be>
556
+ * @since 1.3.18
557
+ * @version 1.3.18
558
+ */
559
+ Conduit.setMethod(function parseUrl() {
560
+
532
561
  // If the url has already been parsed, return early
533
562
  if (this.url) {
534
- return;
563
+ return false;
535
564
  }
536
565
 
566
+ let protocol;
567
+
537
568
  if (alchemy.settings.assume_https) {
538
569
  protocol = 'https://';
539
570
  } else if (this.headers['x-forwarded-proto']) {
@@ -555,7 +586,12 @@ Conduit.setMethod(async function parseRequest() {
555
586
  // Set the host
556
587
  this.url.hostname = this.headers.host;
557
588
 
558
- let path = this.path;
589
+ // If no URL was set, use the first requests URL (without the path)
590
+ if (!alchemy.settings.url && this.headers.host) {
591
+ alchemy.setUrl(this.url);
592
+ }
593
+
594
+ let path = this.original_path;
559
595
 
560
596
  if (this.prefix) {
561
597
  path = '/' + this.prefix + '/' + path;
@@ -569,9 +605,9 @@ Conduit.setMethod(async function parseRequest() {
569
605
  /**
570
606
  * Parse the headers for shortcuts
571
607
  *
572
- * @author Jelle De Loecker <jelle@develry.be>
608
+ * @author Jelle De Loecker <jelle@elevenways.be>
573
609
  * @since 0.2.0
574
- * @version 0.2.0
610
+ * @version 1.3.17
575
611
  */
576
612
  Conduit.setMethod(function parseShortcuts() {
577
613
 
@@ -597,6 +633,10 @@ Conduit.setMethod(function parseShortcuts() {
597
633
  this.ajax = headers['x-requested-with'] === 'XMLHttpRequest';
598
634
  }
599
635
 
636
+ // Is this request coming from a webview?
637
+ if (this.in_webview == null) {
638
+ this.in_webview = headers['x-protoblast-webview'] === 'true';
639
+ }
600
640
  });
601
641
 
602
642
  /**
@@ -1161,6 +1201,8 @@ Conduit.setMethod(async function callMiddleware() {
1161
1201
  middleDebug.mark('Preparing viewrender');
1162
1202
  }
1163
1203
 
1204
+ // An action will be called,
1205
+ // so we can already prepare the viewrender now.
1164
1206
  that.prepareViewRender();
1165
1207
 
1166
1208
  if (middleDebug) {
@@ -1184,10 +1226,14 @@ Conduit.setMethod(async function callMiddleware() {
1184
1226
  *
1185
1227
  * @author Jelle De Loecker <jelle@develry.be>
1186
1228
  * @since 0.2.0
1187
- * @version 1.1.1
1229
+ * @version 1.3.17
1188
1230
  */
1189
1231
  Conduit.setMethod(function prepareViewRender() {
1190
1232
 
1233
+ if (this.renderer.conduit) {
1234
+ return;
1235
+ }
1236
+
1191
1237
  // Add a link to this conduit
1192
1238
  this.renderer.conduit = this;
1193
1239
  this.renderer.server_var('conduit', this);
@@ -1410,7 +1456,7 @@ Conduit.setMethod(function setResponseUrl(new_url) {
1410
1456
  *
1411
1457
  * @author Jelle De Loecker <jelle@elevenways.be>
1412
1458
  * @since 0.2.0
1413
- * @version 1.3.6
1459
+ * @version 1.3.17
1414
1460
  *
1415
1461
  * @param {Number} status 3xx redirection codes. 302 (temporary redirect) by default
1416
1462
  * @param {String|Object} options Options or url
@@ -1508,13 +1554,18 @@ Conduit.setMethod(function redirect(status, options) {
1508
1554
  }
1509
1555
  }
1510
1556
 
1511
- if (hard_refresh && this.headers['x-hawkejs-request']) {
1512
- this.setHeader('x-hawkejs-navigate', url);
1557
+ if (options?.popup) {
1558
+ this.setHeader('x-hawkejs-popup', options.popup);
1513
1559
 
1514
- if (options && options.popup) {
1515
- this.setHeader('x-hawkejs-popup', options.popup);
1560
+ if (options.popup_url) {
1561
+ this.setHeader('x-hawkejs-popup-url', options.popup_url);
1516
1562
  }
1517
1563
 
1564
+ hard_refresh = true;
1565
+ }
1566
+
1567
+ if (hard_refresh && this.headers['x-hawkejs-request']) {
1568
+ this.setHeader('x-hawkejs-navigate', url);
1518
1569
  } else {
1519
1570
  this.setHeader('Location', url);
1520
1571
  }
@@ -1921,11 +1972,11 @@ Conduit.setMethod(function createScene() {
1921
1972
  *
1922
1973
  * @author Jelle De Loecker <jelle@develry.be>
1923
1974
  * @since 0.2.0
1924
- * @version 1.1.0
1975
+ * @version 1.3.17
1925
1976
  */
1926
1977
  Conduit.setMethod(function render(template_name, options, callback) {
1927
1978
 
1928
- var that = this,
1979
+ let that = this,
1929
1980
  templates;
1930
1981
 
1931
1982
  if (typeof options == 'function') {
@@ -1935,6 +1986,10 @@ Conduit.setMethod(function render(template_name, options, callback) {
1935
1986
  options = {};
1936
1987
  }
1937
1988
 
1989
+ // Make sure the viewrender is prepared
1990
+ // (Fallback)
1991
+ this.prepareViewRender();
1992
+
1938
1993
  if (template_name) {
1939
1994
  templates = [template_name];
1940
1995
  }
@@ -2266,7 +2321,7 @@ Conduit.setMethod(function _sendStream(stream, options) {
2266
2321
  *
2267
2322
  * @author Jelle De Loecker <jelle@elevenways.be>
2268
2323
  * @since 0.2.0
2269
- * @version 1.3.6
2324
+ * @version 1.3.18
2270
2325
  *
2271
2326
  * @param {Boolean} create Create a session if none exist
2272
2327
  *
@@ -2279,13 +2334,10 @@ Conduit.setMethod(function getSession(allow_create = true) {
2279
2334
  return this.sessionData;
2280
2335
  }
2281
2336
 
2282
- let cookie_name,
2337
+ let cookie_name = this.session_cookie_name,
2283
2338
  session_id,
2284
2339
  session;
2285
2340
 
2286
- // Set the name of the cookie (could change in the future)
2287
- cookie_name = alchemy.settings.session_key || 'alchemy_sid';
2288
-
2289
2341
  // Get the ID of the session
2290
2342
  session_id = this.cookie(cookie_name);
2291
2343
 
@@ -2449,9 +2501,9 @@ Conduit.setMethod(function setRouteParameters(data) {
2449
2501
  /**
2450
2502
  * Get/set a cookie
2451
2503
  *
2452
- * @author Jelle De Loecker <jelle@develry.be>
2504
+ * @author Jelle De Loecker <jelle@elevenways.be>
2453
2505
  * @since 0.2.0
2454
- * @version 0.4.2
2506
+ * @version 1.3.18
2455
2507
  *
2456
2508
  * @param {String} name
2457
2509
  * @param {Mixed} value
@@ -2459,10 +2511,6 @@ Conduit.setMethod(function setRouteParameters(data) {
2459
2511
  */
2460
2512
  Conduit.setMethod(function cookie(name, value, options) {
2461
2513
 
2462
- var header,
2463
- arr,
2464
- key;
2465
-
2466
2514
  // Return if cookies are disabled
2467
2515
  if (!alchemy.settings.cookies) {
2468
2516
  return;
@@ -2493,12 +2541,34 @@ Conduit.setMethod(function cookie(name, value, options) {
2493
2541
  // Store it in the new_cookies object, for quick access
2494
2542
  this.new_cookies[name] = value;
2495
2543
 
2544
+ // If there is no domain set, see if it is required
2545
+ if ((options.domain === true || options.domain == null) && alchemy.settings.cookie_domain && this.url) {
2546
+ let wanted_domain = alchemy.settings.cookie_domain,
2547
+ actual_domain = this.url.hostname;
2548
+
2549
+ if (wanted_domain != actual_domain) {
2550
+ // You can only set a cookie on the current domain,
2551
+ // or a parent domain. But not on a child domain.
2552
+ if (wanted_domain.length < actual_domain.length && actual_domain.endsWith(wanted_domain)) {
2553
+ // It's allowed!
2554
+ } else {
2555
+ // When on a domain like "11ways.be" you
2556
+ // can not set a cookie for "www.11ways.be"
2557
+ wanted_domain = false;
2558
+ }
2559
+ }
2560
+
2561
+ if (wanted_domain) {
2562
+ options.domain = wanted_domain;
2563
+ }
2564
+ }
2565
+
2496
2566
  if (this.websocket) {
2497
2567
  return this.socket.emit('alchemy-set-cookie', {name: name, value: value, options: options});
2498
2568
  }
2499
2569
 
2500
2570
  // Create the basic header string
2501
- header = String.encodeCookie(name, value, options);
2571
+ let header = String.encodeCookie(name, value, options);
2502
2572
 
2503
2573
  // Add this to the cookieheader array
2504
2574
  this.new_cookie_header.push(header);
@@ -1246,6 +1246,30 @@ Model.setMethod(function nukeCache(associated, parent) {
1246
1246
  }
1247
1247
  });
1248
1248
 
1249
+ /**
1250
+ * Perform a MongoDB pipeline
1251
+ *
1252
+ * @author Jelle De Loecker <jelle@elevenways.be>
1253
+ * @since 1.3.17
1254
+ * @version 1.3.17
1255
+ *
1256
+ * @param {Array} pipeline
1257
+ *
1258
+ * @return {Promise}
1259
+ */
1260
+ Model.setMethod(async function executeMongoPipeline(pipeline) {
1261
+
1262
+ if (typeof this.datasource.collection != 'function') {
1263
+ throw new Error('The `' + this.model_name + '` model does not seem to use MongoDB, unable to perform pipeline');
1264
+ }
1265
+
1266
+ let collection = await this.datasource.collection(this.table);
1267
+ let cursor = await collection.aggregate(pipeline);
1268
+ let result = await cursor.toArray();
1269
+
1270
+ return result;
1271
+ });
1272
+
1249
1273
  /**
1250
1274
  * Delete the given record id
1251
1275
  *
@@ -892,7 +892,7 @@ Schema.setMethod(function getFieldNames() {
892
892
  *
893
893
  * @author Jelle De Loecker <jelle@develry.be>
894
894
  * @since 0.2.0
895
- * @version 1.3.0
895
+ * @version 1.3.17
896
896
  *
897
897
  * @param {String|FieldType} _field
898
898
  * @param {Object} options
@@ -901,24 +901,19 @@ Schema.setMethod(function getFieldNames() {
901
901
  */
902
902
  Schema.setMethod(function addIndex(_field, _options) {
903
903
 
904
- var that = this,
905
- datasource,
906
- options,
907
- order,
908
- field,
909
- path;
910
-
911
904
  // `Schema` is the `Client.Schema` class in this scope
912
905
  if (Blast.isNode && this.constructor == Schema) {
913
906
  return;
914
907
  }
915
908
 
916
- field = this.getField(_field);
909
+ let field = this.getField(_field);
917
910
 
918
911
  if (!field) {
919
912
  throw new Error('Could not find field "' + _field + '"');
920
913
  }
921
914
 
915
+ let options;
916
+
922
917
  if (typeof _options === 'string') {
923
918
  options = {};
924
919
  options[_options] = true;
@@ -961,6 +956,8 @@ Schema.setMethod(function addIndex(_field, _options) {
961
956
  this.has_alternates++;
962
957
  }
963
958
 
959
+ const that = this;
960
+
964
961
  that.getDatasource().done(function gotDs(err, datasource) {
965
962
 
966
963
  if (err) {
@@ -977,7 +974,7 @@ Schema.setMethod(function addIndex(_field, _options) {
977
974
  return alchemy.printLog('error', ['Unable to ensure index on this datasource', options.name], {err: new Error()});
978
975
  }
979
976
 
980
- path = field.path;
977
+ let path = field.path;
981
978
 
982
979
  if (options.db_property) {
983
980
  path += '.' + options.db_property;
@@ -990,7 +987,7 @@ Schema.setMethod(function addIndex(_field, _options) {
990
987
  datasource.ensureIndex(that.model_class, that.indexes[options.name], function ensuredIndex(err, result) {
991
988
 
992
989
  if (err) {
993
- alchemy.printLog('error', ['Error ensuring index', options.name], {err: err});
990
+ alchemy.printLog('error', ['Error ensuring index', options.name, 'in model', that.model_name], {err: err});
994
991
  }
995
992
  });
996
993
  });