Refactored fallback jobs
This commit is contained in:
parent
1a0e2b681b
commit
481a82500b
@ -3,17 +3,7 @@
|
||||
var assert = require('assert');
|
||||
var uuid = require('node-uuid');
|
||||
var jobStatus = require('../job_status');
|
||||
var validStatusTransitions = [
|
||||
[jobStatus.PENDING, jobStatus.RUNNING],
|
||||
[jobStatus.PENDING, jobStatus.CANCELLED],
|
||||
[jobStatus.PENDING, jobStatus.UNKNOWN],
|
||||
[jobStatus.PENDING, jobStatus.SKIPPED],
|
||||
[jobStatus.RUNNING, jobStatus.DONE],
|
||||
[jobStatus.RUNNING, jobStatus.FAILED],
|
||||
[jobStatus.RUNNING, jobStatus.CANCELLED],
|
||||
[jobStatus.RUNNING, jobStatus.PENDING],
|
||||
[jobStatus.RUNNING, jobStatus.UNKNOWN]
|
||||
];
|
||||
var validStatusTransitions = require('./job_status_transitions');
|
||||
var mandatoryProperties = [
|
||||
'job_id',
|
||||
'status',
|
||||
@ -22,6 +12,12 @@ var mandatoryProperties = [
|
||||
'updated_at',
|
||||
'user'
|
||||
];
|
||||
var finalStatus = [
|
||||
jobStatus.CANCELLED,
|
||||
jobStatus.DONE,
|
||||
jobStatus.FAILED,
|
||||
jobStatus.UNKNOWN
|
||||
];
|
||||
|
||||
function JobBase(data) {
|
||||
var now = new Date().toISOString();
|
||||
@ -58,6 +54,10 @@ JobBase.prototype.isValidStatusTransition = function (initialStatus, finalStatus
|
||||
return false;
|
||||
};
|
||||
|
||||
JobBase.prototype.isFinalStatus = function (status) {
|
||||
return finalStatus.indexOf(status) !== -1;
|
||||
};
|
||||
|
||||
// should be implemented by childs
|
||||
JobBase.prototype.getNextQuery = function () {
|
||||
throw new Error('Unimplemented method');
|
||||
|
@ -3,28 +3,23 @@
|
||||
var util = require('util');
|
||||
var JobBase = require('./job_base');
|
||||
var jobStatus = require('../job_status');
|
||||
var breakStatus = [
|
||||
jobStatus.CANCELLED,
|
||||
jobStatus.FAILED,
|
||||
jobStatus.UNKNOWN
|
||||
];
|
||||
function isBreakStatus(status) {
|
||||
return breakStatus.indexOf(status) !== -1;
|
||||
}
|
||||
var finalStatus = [
|
||||
jobStatus.CANCELLED,
|
||||
jobStatus.DONE,
|
||||
jobStatus.FAILED,
|
||||
jobStatus.UNKNOWN
|
||||
];
|
||||
function isFinalStatus(status) {
|
||||
return finalStatus.indexOf(status) !== -1;
|
||||
}
|
||||
var QueryFallback = require('./query/query_fallback');
|
||||
var MainFallback = require('./query/main_fallback');
|
||||
var QueryFactory = require('./query/query_factory');
|
||||
|
||||
function JobFallback(jobDefinition) {
|
||||
JobBase.call(this, jobDefinition);
|
||||
|
||||
this.init();
|
||||
|
||||
this.queries = [];
|
||||
for (var i = 0; i < this.data.query.query.length; i++) {
|
||||
this.queries[i] = QueryFactory.create(this.data, i);
|
||||
}
|
||||
|
||||
if (MainFallback.is(this.data)) {
|
||||
this.fallback = new MainFallback();
|
||||
}
|
||||
}
|
||||
util.inherits(JobFallback, JobBase);
|
||||
|
||||
@ -63,11 +58,7 @@ JobFallback.is = function (query) {
|
||||
}
|
||||
|
||||
for (var i = 0; i < query.query.length; i++) {
|
||||
if (!query.query[i].query) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof query.query[i].query !== 'string') {
|
||||
if (!QueryFallback.is(query.query[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -76,7 +67,7 @@ JobFallback.is = function (query) {
|
||||
};
|
||||
|
||||
JobFallback.prototype.init = function () {
|
||||
// jshint maxcomplexity: 8
|
||||
// jshint maxcomplexity: 9
|
||||
for (var i = 0; i < this.data.query.query.length; i++) {
|
||||
if ((this.data.query.query[i].onsuccess || this.data.query.query[i].onerror) &&
|
||||
!this.data.query.query[i].status) {
|
||||
@ -87,87 +78,42 @@ JobFallback.prototype.init = function () {
|
||||
}
|
||||
}
|
||||
|
||||
if ((this.data.query.onsuccess || this.data.query.onerror) && !this.data.status) {
|
||||
if (!this.data.status) {
|
||||
this.data.status = jobStatus.PENDING;
|
||||
this.data.fallback_status = jobStatus.PENDING;
|
||||
|
||||
if (this.data.query.onsuccess || this.data.query.onerror) {
|
||||
this.data.status = jobStatus.PENDING;
|
||||
this.data.fallback_status = jobStatus.PENDING;
|
||||
}
|
||||
} else if (!this.data.status) {
|
||||
this.data.status = jobStatus.PENDING;
|
||||
}
|
||||
};
|
||||
|
||||
JobFallback.prototype.getNextQueryFromQueries = function () {
|
||||
for (var i = 0; i < this.queries.length; i++) {
|
||||
if (this.queries[i].hasNextQuery(this.data)) {
|
||||
return this.queries[i].getNextQuery(this.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JobFallback.prototype.getNextQueryFromFallback = function () {
|
||||
if (this.fallback && this.fallback.hasNextQuery(this.data)) {
|
||||
|
||||
return this.fallback.getNextQuery(this.data);
|
||||
}
|
||||
};
|
||||
|
||||
JobFallback.prototype.getNextQuery = function () {
|
||||
var query = this._getNextQueryFromQuery();
|
||||
var query = this.getNextQueryFromQueries();
|
||||
|
||||
if (!query) {
|
||||
query = this._getNextQueryFromJobFallback();
|
||||
query = this.getNextQueryFromFallback();
|
||||
}
|
||||
|
||||
return query;
|
||||
};
|
||||
|
||||
JobFallback.prototype._hasNextQueryFromQuery = function () {
|
||||
return !!this._getNextQueryFromQuery();
|
||||
};
|
||||
|
||||
JobFallback.prototype._getNextQueryFromQuery = function () {
|
||||
// jshint maxcomplexity: 8
|
||||
for (var i = 0; i < this.data.query.query.length; i++) {
|
||||
|
||||
if (this.data.query.query[i].fallback_status) {
|
||||
if (this._isNextQuery(i)) {
|
||||
return this.data.query.query[i].query;
|
||||
} else if (this._isNextQueryOnSuccess(i)) {
|
||||
return this.data.query.query[i].onsuccess;
|
||||
} else if (this._isNextQueryOnError(i)) {
|
||||
return this.data.query.query[i].onerror;
|
||||
} else if (isBreakStatus(this.data.query.query[i].status)) {
|
||||
return;
|
||||
}
|
||||
} else if (this.data.query.query[i].status === jobStatus.PENDING) {
|
||||
return this.data.query.query[i].query;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JobFallback.prototype._getNextQueryFromJobFallback = function () {
|
||||
if (this.data.fallback_status) {
|
||||
if (this._isNextQueryOnSuccessJob()) {
|
||||
return this.data.query.onsuccess;
|
||||
} else if (this._isNextQueryOnErrorJob()) {
|
||||
return this.data.query.onerror;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JobFallback.prototype._isNextQuery = function (index) {
|
||||
return this.data.query.query[index].status === jobStatus.PENDING;
|
||||
};
|
||||
|
||||
JobFallback.prototype._isNextQueryOnSuccess = function (index) {
|
||||
return this.data.query.query[index].status === jobStatus.DONE &&
|
||||
this.data.query.query[index].onsuccess &&
|
||||
this.data.query.query[index].fallback_status === jobStatus.PENDING;
|
||||
};
|
||||
|
||||
JobFallback.prototype._isNextQueryOnError = function (index) {
|
||||
return this.data.query.query[index].status === jobStatus.FAILED &&
|
||||
this.data.query.query[index].onerror &&
|
||||
this.data.query.query[index].fallback_status === jobStatus.PENDING;
|
||||
};
|
||||
|
||||
JobFallback.prototype._isNextQueryOnSuccessJob = function () {
|
||||
return this.data.status === jobStatus.DONE &&
|
||||
this.data.query.onsuccess &&
|
||||
this.data.fallback_status === jobStatus.PENDING;
|
||||
};
|
||||
|
||||
JobFallback.prototype._isNextQueryOnErrorJob = function () {
|
||||
return this.data.status === jobStatus.FAILED &&
|
||||
this.data.query.onerror &&
|
||||
this.data.fallback_status === jobStatus.PENDING;
|
||||
};
|
||||
|
||||
JobFallback.prototype.setQuery = function (query) {
|
||||
if (!JobFallback.is(query)) {
|
||||
throw new Error('You must indicate a valid SQL');
|
||||
@ -177,99 +123,90 @@ JobFallback.prototype.setQuery = function (query) {
|
||||
};
|
||||
|
||||
JobFallback.prototype.setStatus = function (status, errorMesssage) {
|
||||
var now = new Date().toISOString();
|
||||
var resultFromQuery = this._setQueryStatus(status, errorMesssage);
|
||||
var resultFromJob = this._setJobStatus(status, resultFromQuery.isChangeAppliedToQueryFallback, errorMesssage);
|
||||
// jshint maxcomplexity: 7
|
||||
|
||||
if (!resultFromJob.isValid && !resultFromQuery.isValid) {
|
||||
throw new Error('Cannot set status from ' + this.data.status + ' to ' + status);
|
||||
var now = new Date().toISOString();
|
||||
var hasChanged = {
|
||||
isValid: false,
|
||||
appliedToFallback: false
|
||||
};
|
||||
var result = {};
|
||||
|
||||
for (var i = 0; i < this.queries.length; i++) {
|
||||
result = this.queries[i].setStatus(status, this.data, hasChanged, errorMesssage);
|
||||
|
||||
if (result.isValid) {
|
||||
hasChanged = result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!resultFromQuery.isChangeAppliedToQueryFallback || status === jobStatus.CANCELLED) {
|
||||
this._setSkipped(status);
|
||||
result = this.setJobStatus(status, this.data, hasChanged, errorMesssage);
|
||||
|
||||
if (result.isValid) {
|
||||
hasChanged = result;
|
||||
}
|
||||
|
||||
if (!this.getNextQueryFromQueries() && this.fallback) {
|
||||
result = this.fallback.setStatus(status, this.data, hasChanged);
|
||||
|
||||
if (result.isValid) {
|
||||
hasChanged = result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasChanged.isValid) {
|
||||
throw new Error('Cannot set status to ' + status);
|
||||
}
|
||||
|
||||
this.data.updated_at = now;
|
||||
};
|
||||
|
||||
JobFallback.prototype._setSkipped = function (status) {
|
||||
this._setSkippedQueryStatus();
|
||||
this._setSkippedJobStatus();
|
||||
JobFallback.prototype.setJobStatus = function (status, job, hasChanged, errorMesssage) {
|
||||
var isValid = false;
|
||||
|
||||
if (status === jobStatus.CANCELLED || status === jobStatus.FAILED) {
|
||||
this._setRestPendingToSkipped(status);
|
||||
status = this.shiftStatus(status, hasChanged);
|
||||
|
||||
isValid = this.isValidStatusTransition(job.status, status);
|
||||
|
||||
if (isValid) {
|
||||
job.status = status;
|
||||
}
|
||||
|
||||
if (status === jobStatus.FAILED && errorMesssage && !hasChanged.appliedToFallback) {
|
||||
job.failed_reason = errorMesssage;
|
||||
}
|
||||
|
||||
return { isValid: isValid, appliedToFallback: false };
|
||||
};
|
||||
|
||||
JobFallback.prototype._setSkippedQueryStatus = function () {
|
||||
// jshint maxcomplexity: 8
|
||||
for (var i = 0; i < this.data.query.query.length; i++) {
|
||||
if (this.data.query.query[i].status === jobStatus.FAILED && this.data.query.query[i].onsuccess) {
|
||||
if (this.isValidStatusTransition(this.data.query.query[i].fallback_status, jobStatus.SKIPPED)) {
|
||||
this.data.query.query[i].fallback_status = jobStatus.SKIPPED;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.data.query.query[i].status === jobStatus.DONE && this.data.query.query[i].onerror) {
|
||||
if (this.isValidStatusTransition(this.data.query.query[i].fallback_status, jobStatus.SKIPPED)) {
|
||||
this.data.query.query[i].fallback_status = jobStatus.SKIPPED;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.data.query.query[i].status === jobStatus.CANCELLED && this.data.query.query[i].fallback_status) {
|
||||
if (this.isValidStatusTransition(this.data.query.query[i].fallback_status, jobStatus.SKIPPED)) {
|
||||
this.data.query.query[i].fallback_status = jobStatus.SKIPPED;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JobFallback.prototype._setSkippedJobStatus = function () {
|
||||
JobFallback.prototype.shiftStatus = function (status, hasChanged) {
|
||||
// jshint maxcomplexity: 7
|
||||
|
||||
if (this.data.status === jobStatus.FAILED && this.data.query.onsuccess) {
|
||||
if (this.isValidStatusTransition(this.data.fallback_status, jobStatus.SKIPPED)) {
|
||||
this.data.fallback_status = jobStatus.SKIPPED;
|
||||
if (hasChanged.appliedToFallback) {
|
||||
if (!this.getNextQueryFromQueries() && (status === jobStatus.DONE || status === jobStatus.FAILED)) {
|
||||
status = this._getLastStatusFromFinishedQuery();
|
||||
} else if (status === jobStatus.DONE || status === jobStatus.FAILED){
|
||||
status = jobStatus.PENDING;
|
||||
}
|
||||
} else if (this.getNextQueryFromQueries() && status !== jobStatus.RUNNING) {
|
||||
status = jobStatus.PENDING;
|
||||
}
|
||||
|
||||
if (this.data.status === jobStatus.DONE && this.data.query.onerror) {
|
||||
if (this.isValidStatusTransition(this.data.fallback_status, jobStatus.SKIPPED)) {
|
||||
this.data.fallback_status = jobStatus.SKIPPED;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.data.status === jobStatus.CANCELLED && this.data.fallback_status) {
|
||||
if (this.isValidStatusTransition(this.data.fallback_status, jobStatus.SKIPPED)) {
|
||||
this.data.fallback_status = jobStatus.SKIPPED;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
};
|
||||
|
||||
JobFallback.prototype._setRestPendingToSkipped = function (status) {
|
||||
for (var i = 0; i < this.data.query.query.length; i++) {
|
||||
if (this.data.query.query[i].status === jobStatus.PENDING) {
|
||||
this.data.query.query[i].status = jobStatus.SKIPPED;
|
||||
}
|
||||
if (this.data.query.query[i].status !== status &&
|
||||
this.data.query.query[i].fallback_status === jobStatus.PENDING) {
|
||||
this.data.query.query[i].fallback_status = jobStatus.SKIPPED;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JobFallback.prototype._getLastStatusFromFinishedQuery = function () {
|
||||
var lastStatus = jobStatus.DONE;
|
||||
|
||||
for (var i = 0; i < this.data.query.query.length; i++) {
|
||||
if (this.data.query.query[i].fallback_status) {
|
||||
if (isFinalStatus(this.data.query.query[i].status)) {
|
||||
if (this.isFinalStatus(this.data.query.query[i].status)) {
|
||||
lastStatus = this.data.query.query[i].status;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (isFinalStatus(this.data.query.query[i].status)) {
|
||||
if (this.isFinalStatus(this.data.query.query[i].status)) {
|
||||
lastStatus = this.data.query.query[i].status;
|
||||
} else {
|
||||
break;
|
||||
@ -279,95 +216,3 @@ JobFallback.prototype._getLastStatusFromFinishedQuery = function () {
|
||||
|
||||
return lastStatus;
|
||||
};
|
||||
|
||||
JobFallback.prototype._setJobStatus = function (status, isChangeAppliedToQueryFallback, errorMesssage) {
|
||||
var isValid = false;
|
||||
|
||||
status = this._shiftJobStatus(status, isChangeAppliedToQueryFallback);
|
||||
|
||||
isValid = this.isValidStatusTransition(this.data.status, status);
|
||||
|
||||
if (isValid) {
|
||||
this.data.status = status;
|
||||
} else if (this.data.fallback_status) {
|
||||
|
||||
isValid = this.isValidStatusTransition(this.data.fallback_status, status);
|
||||
|
||||
if (isValid) {
|
||||
this.data.fallback_status = status;
|
||||
}
|
||||
}
|
||||
|
||||
if (status === jobStatus.FAILED && errorMesssage && !isChangeAppliedToQueryFallback) {
|
||||
this.data.failed_reason = errorMesssage;
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: isValid
|
||||
};
|
||||
};
|
||||
|
||||
JobFallback.prototype._shiftJobStatus = function (status, isChangeAppliedToQueryFallback) {
|
||||
// jshint maxcomplexity: 7
|
||||
|
||||
// In some scenarios we have to change the normal flow in order to keep consistency
|
||||
// between query's status and job's status.
|
||||
|
||||
if (isChangeAppliedToQueryFallback) {
|
||||
if (!this._hasNextQueryFromQuery() && (status === jobStatus.DONE || status === jobStatus.FAILED)) {
|
||||
status = this._getLastStatusFromFinishedQuery();
|
||||
} else if (status === jobStatus.DONE || status === jobStatus.FAILED){
|
||||
status = jobStatus.PENDING;
|
||||
}
|
||||
} else if (this._hasNextQueryFromQuery() && status !== jobStatus.RUNNING) {
|
||||
status = jobStatus.PENDING;
|
||||
}
|
||||
|
||||
return status;
|
||||
};
|
||||
|
||||
|
||||
JobFallback.prototype._shouldTryToApplyStatusTransitionToQueryFallback = function (index) {
|
||||
return (this.data.query.query[index].status === jobStatus.DONE && this.data.query.query[index].onsuccess) ||
|
||||
(this.data.query.query[index].status === jobStatus.FAILED && this.data.query.query[index].onerror);
|
||||
};
|
||||
|
||||
JobFallback.prototype._setQueryStatus = function (status, errorMesssage) {
|
||||
// jshint maxcomplexity: 8
|
||||
var isValid = false;
|
||||
var isChangeAppliedToQueryFallback = false;
|
||||
|
||||
for (var i = 0; i < this.data.query.query.length; i++) {
|
||||
isValid = this.isValidStatusTransition(this.data.query.query[i].status, status);
|
||||
|
||||
if (isValid) {
|
||||
this.data.query.query[i].status = status;
|
||||
|
||||
if (status === jobStatus.FAILED && errorMesssage) {
|
||||
this.data.query.query[i].failed_reason = errorMesssage;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (this._shouldTryToApplyStatusTransitionToQueryFallback(i)) {
|
||||
isValid = this.isValidStatusTransition(this.data.query.query[i].fallback_status, status);
|
||||
|
||||
if (isValid) {
|
||||
this.data.query.query[i].fallback_status = status;
|
||||
|
||||
if (status === jobStatus.FAILED && errorMesssage) {
|
||||
this.data.query.query[i].failed_reason = errorMesssage;
|
||||
}
|
||||
|
||||
isChangeAppliedToQueryFallback = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: isValid,
|
||||
isChangeAppliedToQueryFallback: isChangeAppliedToQueryFallback
|
||||
};
|
||||
};
|
||||
|
17
batch/models/job_status_transitions.js
Normal file
17
batch/models/job_status_transitions.js
Normal file
@ -0,0 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
var jobStatus = require('../job_status');
|
||||
|
||||
var validStatusTransitions = [
|
||||
[jobStatus.PENDING, jobStatus.RUNNING],
|
||||
[jobStatus.PENDING, jobStatus.CANCELLED],
|
||||
[jobStatus.PENDING, jobStatus.UNKNOWN],
|
||||
[jobStatus.PENDING, jobStatus.SKIPPED],
|
||||
[jobStatus.RUNNING, jobStatus.DONE],
|
||||
[jobStatus.RUNNING, jobStatus.FAILED],
|
||||
[jobStatus.RUNNING, jobStatus.CANCELLED],
|
||||
[jobStatus.RUNNING, jobStatus.PENDING],
|
||||
[jobStatus.RUNNING, jobStatus.UNKNOWN]
|
||||
];
|
||||
|
||||
module.exports = validStatusTransitions;
|
65
batch/models/query/fallback.js
Normal file
65
batch/models/query/fallback.js
Normal file
@ -0,0 +1,65 @@
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
var QueryBase = require('./query_base');
|
||||
var jobStatus = require('../../job_status');
|
||||
|
||||
function Fallback(index) {
|
||||
QueryBase.call(this, index);
|
||||
}
|
||||
util.inherits(Fallback, QueryBase);
|
||||
|
||||
module.exports = Fallback;
|
||||
|
||||
Fallback.is = function (query) {
|
||||
if (query.onsuccess || query.onerror) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
Fallback.prototype.getNextQuery = function (job) {
|
||||
if (this.hasOnSuccess(job)) {
|
||||
return this.getOnSuccess(job);
|
||||
}
|
||||
if (this.hasOnError(job)) {
|
||||
return this.getOnError(job);
|
||||
}
|
||||
};
|
||||
|
||||
Fallback.prototype.getOnSuccess = function (job) {
|
||||
if (job.query.query[this.index].status === jobStatus.DONE &&
|
||||
job.query.query[this.index].fallback_status === jobStatus.PENDING) {
|
||||
return job.query.query[this.index].onsuccess;
|
||||
}
|
||||
};
|
||||
|
||||
Fallback.prototype.hasOnSuccess = function (job) {
|
||||
return !!this.getOnSuccess(job);
|
||||
};
|
||||
|
||||
Fallback.prototype.getOnError = function (job) {
|
||||
if (job.query.query[this.index].status === jobStatus.FAILED &&
|
||||
job.query.query[this.index].fallback_status === jobStatus.PENDING) {
|
||||
return job.query.query[this.index].onerror;
|
||||
}
|
||||
};
|
||||
|
||||
Fallback.prototype.hasOnError = function (job) {
|
||||
return !!this.getOnError(job);
|
||||
};
|
||||
|
||||
Fallback.prototype.setStatus = function (status, job, errorMesssage) {
|
||||
var isValid = false;
|
||||
|
||||
isValid = this.isValidTransition(job.query.query[this.index].fallback_status, status);
|
||||
|
||||
if (isValid) {
|
||||
job.query.query[this.index].fallback_status = status;
|
||||
if (status === jobStatus.FAILED && errorMesssage) {
|
||||
job.query.query[this.index].failed_reason = errorMesssage;
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
};
|
74
batch/models/query/main_fallback.js
Normal file
74
batch/models/query/main_fallback.js
Normal file
@ -0,0 +1,74 @@
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
var QueryBase = require('./query_base');
|
||||
var jobStatus = require('../../job_status');
|
||||
|
||||
function MainFallback() {
|
||||
QueryBase.call(this);
|
||||
}
|
||||
util.inherits(MainFallback, QueryBase);
|
||||
|
||||
module.exports = MainFallback;
|
||||
|
||||
MainFallback.is = function (job) {
|
||||
if (job.query.onsuccess || job.query.onerror) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
MainFallback.prototype.getNextQuery = function (job) {
|
||||
if (this.hasOnSuccess(job)) {
|
||||
return this.getOnSuccess(job);
|
||||
}
|
||||
|
||||
if (this.hasOnError(job)) {
|
||||
return this.getOnError(job);
|
||||
}
|
||||
};
|
||||
|
||||
MainFallback.prototype.getOnSuccess = function (job) {
|
||||
if (job.status === jobStatus.DONE && job.fallback_status === jobStatus.PENDING) {
|
||||
return job.query.onsuccess;
|
||||
}
|
||||
};
|
||||
|
||||
MainFallback.prototype.hasOnSuccess = function (job) {
|
||||
return !!this.getOnSuccess(job);
|
||||
};
|
||||
|
||||
MainFallback.prototype.getOnError = function (job) {
|
||||
if (job.status === jobStatus.FAILED && job.fallback_status === jobStatus.PENDING) {
|
||||
return job.query.onerror;
|
||||
}
|
||||
};
|
||||
|
||||
MainFallback.prototype.hasOnError = function (job) {
|
||||
return !!this.getOnError(job);
|
||||
};
|
||||
|
||||
MainFallback.prototype.setStatus = function (status, job, previous) {
|
||||
var isValid = false;
|
||||
var appliedToFallback = false;
|
||||
|
||||
if (previous.isValid && !previous.appliedToFallback) {
|
||||
if (this.isFinalStatus(status) && !this.hasNextQuery(job)) {
|
||||
isValid = this.isValidTransition(job.fallback_status, jobStatus.SKIPPED);
|
||||
|
||||
if (isValid) {
|
||||
job.fallback_status = jobStatus.SKIPPED;
|
||||
appliedToFallback = true;
|
||||
}
|
||||
}
|
||||
} else if (!previous.isValid) {
|
||||
isValid = this.isValidTransition(job.fallback_status, status);
|
||||
|
||||
if (isValid) {
|
||||
job.fallback_status = status;
|
||||
appliedToFallback = true;
|
||||
}
|
||||
}
|
||||
|
||||
return { isValid: isValid, appliedToFallback: appliedToFallback };
|
||||
};
|
41
batch/models/query/query.js
Normal file
41
batch/models/query/query.js
Normal file
@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
var QueryBase = require('./query_base');
|
||||
var jobStatus = require('../../job_status');
|
||||
|
||||
function Query(index) {
|
||||
QueryBase.call(this, index);
|
||||
}
|
||||
util.inherits(Query, QueryBase);
|
||||
|
||||
module.exports = Query;
|
||||
|
||||
Query.is = function (query) {
|
||||
if (query.query && typeof query.query === 'string') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
Query.prototype.getNextQuery = function (job) {
|
||||
if (job.query.query[this.index].status === jobStatus.PENDING) {
|
||||
return job.query.query[this.index].query;
|
||||
}
|
||||
};
|
||||
|
||||
Query.prototype.setStatus = function (status, job, errorMesssage) {
|
||||
var isValid = false;
|
||||
|
||||
isValid = this.isValidTransition(job.query.query[this.index].status, status);
|
||||
|
||||
if (isValid) {
|
||||
job.query.query[this.index].status = status;
|
||||
if (status === jobStatus.FAILED && errorMesssage) {
|
||||
job.query.query[this.index].failed_reason = errorMesssage;
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
};
|
51
batch/models/query/query_base.js
Normal file
51
batch/models/query/query_base.js
Normal file
@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
|
||||
var jobStatus = require('../../job_status');
|
||||
var assert = require('assert');
|
||||
var validStatusTransitions = require('../job_status_transitions');
|
||||
var finalStatus = [
|
||||
jobStatus.CANCELLED,
|
||||
jobStatus.DONE,
|
||||
jobStatus.FAILED,
|
||||
jobStatus.UNKNOWN
|
||||
];
|
||||
|
||||
function QueryBase(index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
module.exports = QueryBase;
|
||||
|
||||
QueryBase.prototype.isFinalStatus = function (status) {
|
||||
return finalStatus.indexOf(status) !== -1;
|
||||
};
|
||||
|
||||
// should be implemented
|
||||
QueryBase.prototype.setStatus = function () {
|
||||
throw new Error('Unimplemented method');
|
||||
};
|
||||
|
||||
// should be implemented
|
||||
QueryBase.prototype.getNextQuery = function () {
|
||||
throw new Error('Unimplemented method');
|
||||
};
|
||||
|
||||
QueryBase.prototype.hasNextQuery = function (job) {
|
||||
return !!this.getNextQuery(job);
|
||||
};
|
||||
|
||||
|
||||
QueryBase.prototype.isValidTransition = function (initialStatus, finalStatus) {
|
||||
var transition = [ initialStatus, finalStatus ];
|
||||
|
||||
for (var i = 0; i < validStatusTransitions.length; i++) {
|
||||
try {
|
||||
assert.deepEqual(transition, validStatusTransitions[i]);
|
||||
return true;
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
16
batch/models/query/query_factory.js
Normal file
16
batch/models/query/query_factory.js
Normal file
@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
var QueryFallback = require('./query_fallback');
|
||||
|
||||
function QueryFactory() {
|
||||
}
|
||||
|
||||
module.exports = QueryFactory;
|
||||
|
||||
QueryFactory.create = function (job, index) {
|
||||
if (QueryFallback.is(job.query.query[index])) {
|
||||
return new QueryFallback(job, index);
|
||||
}
|
||||
|
||||
throw new Error('there is no query class for the provided query');
|
||||
};
|
71
batch/models/query/query_fallback.js
Normal file
71
batch/models/query/query_fallback.js
Normal file
@ -0,0 +1,71 @@
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
var QueryBase = require('./query_base');
|
||||
var Query = require('./query');
|
||||
var Fallback = require('./fallback');
|
||||
var jobStatus = require('../../job_status');
|
||||
|
||||
function QueryFallback(job, index) {
|
||||
QueryBase.call(this, index);
|
||||
|
||||
this.init(job, index);
|
||||
}
|
||||
|
||||
util.inherits(QueryFallback, QueryBase);
|
||||
|
||||
QueryFallback.is = function (query) {
|
||||
if (Query.is(query)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
QueryFallback.prototype.init = function (job, index) {
|
||||
this.query = new Query(index);
|
||||
|
||||
if (Fallback.is(job.query.query[index])) {
|
||||
this.fallback = new Fallback(index);
|
||||
}
|
||||
};
|
||||
|
||||
QueryFallback.prototype.getNextQuery = function (job) {
|
||||
if (this.query.hasNextQuery(job)) {
|
||||
return this.query.getNextQuery(job);
|
||||
}
|
||||
|
||||
if (this.fallback && this.fallback.hasNextQuery(job)) {
|
||||
return this.fallback.getNextQuery(job);
|
||||
}
|
||||
};
|
||||
|
||||
QueryFallback.prototype.setStatus = function (status, job, previous, errorMesssage) {
|
||||
// jshint maxcomplexity: 9
|
||||
var isValid = false;
|
||||
var appliedToFallback = false;
|
||||
|
||||
if (previous.isValid && !previous.appliedToFallback) {
|
||||
if (status === jobStatus.FAILED || status === jobStatus.CANCELLED) {
|
||||
this.query.setStatus(jobStatus.SKIPPED, job, errorMesssage);
|
||||
|
||||
if (this.fallback) {
|
||||
this.fallback.setStatus(jobStatus.SKIPPED, job);
|
||||
}
|
||||
}
|
||||
} else if (!previous.isValid) {
|
||||
isValid = this.query.setStatus(status, job, errorMesssage);
|
||||
|
||||
if (this.fallback) {
|
||||
if (!isValid) {
|
||||
isValid = this.fallback.setStatus(status, job, errorMesssage);
|
||||
appliedToFallback = true;
|
||||
} else if (isValid && this.isFinalStatus(status) && !this.fallback.hasNextQuery(job)) {
|
||||
this.fallback.setStatus(jobStatus.SKIPPED, job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { isValid: isValid, appliedToFallback: appliedToFallback };
|
||||
};
|
||||
|
||||
module.exports = QueryFallback;
|
@ -31,7 +31,6 @@ describe('Batch API fallback job', function () {
|
||||
describe('"onsuccess" on first query should be triggered', function () {
|
||||
var fallbackJob = {};
|
||||
|
||||
|
||||
it('should create a job', function (done) {
|
||||
assert.response(app, {
|
||||
url: '/api/v2/sql/job?api_key=1234',
|
||||
@ -951,7 +950,7 @@ describe('Batch API fallback job', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('"onsuccess" should not be triggered for any query and "skipped"', function () {
|
||||
describe('"onsuccess" should be "skipped"', function () {
|
||||
var fallbackJob = {};
|
||||
|
||||
it('should create a job', function (done) {
|
||||
|
Loading…
Reference in New Issue
Block a user