() → Object
Partially populated options object
Returns an object containing default options for enhanceLROStatus
and enhanceLROStatusEntry
.
Note that you must still add a currentTime
property to the result before use.
const defaultOptions = require('@eluvio/elv-lro-status/defaultOptions')
// Output when system is set to US / Pacific timezone
console.log(JSON.stringify(defaultOptions(), null, 2))
`{
"locale": "en-US",
"stallThreshold": 900,
"timezone": "America/Los_Angeles"
}`
(Object, Object) → Object
options
options:currentTime
The value to use for current time (this is used to compute eta_local
)
options:locale
The Javascript Intl.Locale string to use to format eta_local
options:stallThreshold
How many seconds to wait since LRO info last updated before warning that LRO may have terminated
options:timezone
The time zone to use to format eta_local
lroStatus
The data from ElvClient.LROStatus()
call
An object containing an ok
attribute that is true
if enhancement succeeded or false
if
enhancement failed (generally due to bad options
being passed in).
If enhancement was successful, a result
attribute will contain the enhanced status object.
If enhancement failed, an errors
attribute will contain an array of error message strings, and an errorDetails
attribute will contain an array with any available additional information.
Processes a response from an ElvClient.LROStatus() API call, checking for stalled LROs and bad percentages, adding ETA fields if applicable, and adding a summary.
const enhanceLROStatus = require('@eluvio/elv-lro-status/enhanceLROStatus')
const defaultOptions = require('@eluvio/elv-lro-status/defaultOptions')
const options = Object.assign(defaultOptions(), {currentTime: new Date})
// assumes `client` contains a successfully prepared ElvClient instance
const statusResponse = await client.LROStatus({
libraryId: 'ilib_YOUR_LIBRARY_ID',
objectId: 'iq__YOUR_OBJECT_ID'
});
// check to make sure we received data back
if (statusResponse === undefined) {
throw Error("Received no job status information from server - no LROs started, or object already finalized?");
}
// EXAMPLE: SUCCESSFUL CALL
const enhancedStatus = enhanceLROStatus(options, statusResponse)
console.log(JSON.stringify(enhancedStatus, null, 2)
`{
"ok": true,
"result": {
"LROs": {
"tlro1Ejh9gNWAaTmfWJKeuu99V2MZ17XXkSv3L57AV3CPYHbLmk3E9SF1b": {
"duration": 0,
"duration_ms": 0,
"progress": {
"percentage": 0
},
"estimated_time_left_h_m_s": "unknown (not enough progress yet)",
"run_state": "running",
"start": "2022-04-09T05:09:00Z",
"seconds_since_last_update": 84
},
"tlro1Ejh9gNWAaTmfWJKeuu99V2MZ17XXkSvGJyKKJpmqtmJ2fzo5Fb3Be": {
"duration": 12747000000,
"duration_ms": 12747,
"end": "2022-04-09T05:09:13Z",
"progress": {
"percentage": 100
},
"run_state": "finished",
"start": "2022-04-09T05:09:00Z"
}
},
"summary": {
"run_state": "running",
"estimated_time_left_h_m_s": "unknown (not enough progress yet)"
}
}
}`
// EXAMPLE: FAILED CALL
const badOptions = defaultOptions() // failed to set currentTime
const failedEnhancement = enhanceLROStatus(badOptions, statusResponse)
console.log(JSON.stringify(failedEnhancement, null, 2)
`{
ok: false,
errors: [
"options: expecting currentTime to be Date, got undefined"
],
errorDetails: [
{
"path": "currentTime",
"message": "expecting currentTime to be Date, got undefined"
}
]
}`
Pair String Object → Ok Pair String Object | Err Array
options
options.currentTime
The value to use for current time when calculating ETA fields
options.locale
The locale to use when formatting the eta_local
field
options.stallThreshold
How many seconds to allow to pass between LRO info updates before warning that LRO may have terminated
options.timezone
The timezone to use when formatting the eta_local
field
lro
A Crocks Pair with LRO ID as first element and LRO Status Entry as second element
Either Err([errors])
or Ok(Pair(lroId, enhancedStatusObject))
Returns an enhanced copy of the status entry for a single LRO, wrapped in a Crocks Result.
Bad options will cause an Err
to be returned
const Pair = require('@eluvio/elv-js-helpers/Pair')
const defaultOptions = require('@eluvio/elv-lro-status/defaultOptions')
const options = Object.assign(defaultOptions(), {currentTime: new Date})
const singleLRO = Pair(
'tlro1EjdMMAvWb5iJn2isHdgESes1dq12kpjJ2kukiD5NmnEgCP7iFFBjU',
{
"duration": 335613000000,
"duration_ms": 335613,
"end": "2022-04-08T21:10:34Z",
"progress": {
"percentage": 100
},
"run_state": "finished",
"start": "2022-04-08T21:05:00Z"
}
)
// EXAMPLE: Successful call
enhanceLROStatusEntry(options, singleLRO) //=> Ok Pair('tlro1EjdMMAvWb5iJn2isHdgESes1dq12kpjJ2kukiD5NmnEgCP7iFFBjU', {...})
// EXAMPLE: Failed call
enhanceLROStatusEntry(defaultOptions(), singleLRO) //=> Err [error objects]
* → String | THROW
(unnamed)
The input to validate
The validated input, proxied by ObjectModel
An ObjectModel which validates that an input is:
If input passes validation, will return the input (proxied by ObjectModel)
Throws an exception if passed in an invalid value.
const LROIdModel = require('@eluvio/elv-lro-status/LROIdModel')
LROIdModel('tlro1EjdMMAvWb5iJn2isHdgESes1dq12kpjXCExCepbpWfwMpo2haCxnh') //=> 'tlro1EjdMMAvWb5iJn2isHdgESes1dq12kpjXCExCepbpWfwMpo2haCxnh' (proxied by ObjectModel)
LROIdModel(42) //=> EXCEPTION: 'expecting String, got Number 42'
LROIdModel('foo') //=> EXCEPTION: 'Value is not a valid LRO ID string 'tlro...' (got: foo)'
* → Object | THROW
(unnamed)
The input to validate
The validated input, proxied by ObjectModel
An ObjectModel to validate the progress
attribute of an LRO Status entry
Throws an exception if passed in an invalid value.
const LROProgressModel = require('@eluvio/elv-lro-status/LROProgressModel')
LROProgressModel({}) //=> EXCEPTION: 'expecting percentage to be Number, got undefined'
LROProgressModel({percentage: -2}) //=> EXCEPTION: 'percentage must be >= 0 (got: -2)'
LROProgressModel({percentage: 10}) //=> {percentage: 10} // proxied by ObjectModel
LROProgressModel({percentage: 10, files: 7}) //=> {percentage: 10, files: 7} // proxied by ObjectModel
// NOTE: Object.keys and JSON.stringify will drop attributes not declared in model:
JSON.stringify(
LROProgressModel({percentage: 10, files: 7})
) //=> '{"percentage": 10}'
LROProgressModel('foo') //=> EXCEPTION: `expecting Object, got String "foo"`
* → Object | THROW
(unnamed)
The input to validate
The validated input, proxied by ObjectModel
An ObjectModel to validate the an LRO Status entry
Throws an exception if passed in an invalid value.
const LROStatusEntryModel = require('@eluvio/elv-lro-status/LROStatusEntryModel')
LROStatusEntryModel({}) //=> EXCEPTION: 'expecting duration to be Number, got undefined...'
LROStatusEntryModel({
duration: 0,
duration_ms: 0,
progress: {
percentage: 0
},
run_state: 'running',
start: '2022-04-08T21:05:00Z'
}) //=> (copy of input object) // proxied by ObjectModel
LROStatusEntryModel({
duration: -1,
duration_ms: 0,
progress: {
percentage: 0
},
run_state: 'running',
start: '2022-04-08T21:05:00Z'
}) //=> EXCEPTION: 'duration must be >= 0 (got: -1)'
LROStatusEntryModel('foo') //=> EXCEPTION: `expecting Object, got String "foo"`
* → Object | THROW
(unnamed)
The input to validate
The validated input, proxied by ObjectModel
An ObjectModel to validate the response from an LROStatus() API call
Throws an exception if passed in an invalid value or an empty object.
const LROStatusModel = require('@eluvio/elv-lro-status/LROStatusModel')
LROStatusModel({}) //=> Exception: "Value must not be empty (got: {})"
LROStatusModel({
tlro1EjdMMAvWb5iJn2isHdgESes1dq12kpjXCExCepbpWfwMpo2haCxnh: {
duration: 0,
duration_ms: 0,
progress: {
percentage: 0
},
run_state: 'running',
start: '2022-04-08T21:05:00Z'
}
}) //=> (copy of input object) // proxied by ObjectModel
LROStatusModel({
foo: {
duration: 0,
duration_ms: 0,
progress: {
percentage: 0
},
run_state: 'running',
start: '2022-04-08T21:05:00Z'
}
}) //=> EXCEPTION: 'invalid property name 'foo' (is not a valid LROId)'
LROStatusModel({
tlro1EjdMMAvWb5iJn2isHdgESes1dq12kpjXCExCepbpWfwMpo2haCxnh: {}
}) //=> EXCEPTION: 'TypeError: key 'tlro1EjdMMAvWb5iJn2isHdgESes1dq12kpjXCExCepbpWfwMpo2haCxnh' points to a value that is an invalid LROStatusEntry (LROStatusEntry: expecting duration to be Number, got undefined...'
LROStatusModel(3) //=> EXCEPTION: 'expecting Object, got Number 3'
Type:String"cancelled by node shutdown"
run_state
string returned by server for LROs
const {LRO_RS_CANCELLED_SHUTDOWN} = require('@eluvio/elv-lro-status/lroRunState') //=> 'cancelled by node shutdown'
Type:String"cancelled by timeout"
run_state
string returned by server for LROs
const {LRO_RS_CANCELLED_TIMEOUT} = require('@eluvio/elv-lro-status/lroRunState') //=> 'cancelled by timeout'
Type:String"cancelled by user"
run_state
string returned by server for LROs
const {LRO_RS_CANCELLED_USER} = require('@eluvio/elv-lro-status/lroRunState') //=> 'cancelled by user'
Type:String"failed"
run_state
string returned by server for LROs
const {LRO_RS_FAILED} = require('@eluvio/elv-lro-status/lroRunState') //=> 'failed'
Type:String"finished"
run_state
string returned by server for LROs
const {LRO_RS_FINISHED} = require('@eluvio/elv-lro-status/lroRunState') //=> 'finished'
Type:String"not started"
run_state
string returned by server for LROs
const {LRO_RS_NOT_STARTED} = require('@eluvio/elv-lro-status/lroRunState') //=> 'not started'
Type:String"running"
run_state
string returned by server for LROs
const {LRO_RS_RUNNING} = require('@eluvio/elv-lro-status/lroRunState') //=> 'running'
Type:Array.<String>
A list of all the possible values returned by server for LRO run_state
const {LRO_RUN_STATES} = require('@eluvio/elv-lro-status/lroRunState') //=> ['not started', ..., 'cancelled by node shutdown']
* → Result
options
The options to validate
Fills in missing options for enhanceLROStatus()
with default values, validates after merging, then returns a
Crocks Result, either an Ok
wrapping the final options or an Err
wrapping an array of errors.
const mergeDefaultOptions = require('@eluvio/elv-lro-status/mergeDefaultOptions')
mergeDefaultOptions({
currentTime: new Date,
locale: 'en-US',
timezone: 'America/Los_Angeles'
}) //=> Ok() wrapping a copy of input object with {stallThreshold: 900} merged in
OptionsModel({
locale: 'en-US',
stallThreshold: 900,
timezone: 'America/Los_Angeles'
}) //=> Err([error object])
* → Object → Object | THROW
(unnamed)
The options to validate
The validated options object
An ObjectModel which validates that an input is:
options
argument used by enhanceLROStatus
and enhanceLROStatusEntry
.const OptionsModel = require('@eluvio/elv-lro-status/OptionsModel')
OptionsModel({
currentTime: new Date,
locale: 'en-US',
stallThreshold: 900,
timezone: 'America/Los_Angeles'
}) //=> Object (proxied by ObjectModel)
OptionsModel({
locale: 'en-US',
stallThreshold: 900,
timezone: 'America/Los_Angeles'
}) //=> EXCEPTION: 'expecting currentTime to be Date, got undefined'
Type:Array.<String>
An array of all the enhanced run_state
constants, ordered by increasing precedence. When creating a status summary,
states later in list will take precedence, e.g if one LRO is 'finished' and one LRO is 'running', the summary
run_state
will be 'running'. Generally error/cancel states take precedence over STATE_RUNNING
, which takes
precedence over the remaining states.
The order is:
const {STATES} = require('@eluvio/elv-lro-status/enhancedRunState') //=> ['unknown', ..., 'cancelled by user']
Type:String"bad percentage"
An enhanced run_state
(NOT used by Eluvio Content Fabric servers) to indicate that an LRO has reported a bad number
for progress percentage (i.e. either > 100 for any run_state, or < 100 for run_state 'finished')
const {STATE_BAD_PCT} = require('@eluvio/elv-lro-status/enhancedRunState') //=> 'bad percentage'
Type:String"cancelled by node shutdown"
An enhanced run_state
(also used by Eluvio Content Fabric servers) to indicate that an LRO has been cancelled due
to a server shutdown.
Note that in practice this run_state
is almost never encountered. (TODO: check implementation details)
const {STATE_CANCELLED_SHUTDOWN} = require('@eluvio/elv-lro-status/enhancedRunState') //=> 'cancelled by node shutdown'
Type:String"cancelled by timeout"
An enhanced run_state
(also used by Eluvio Content Fabric servers) to indicate that an LRO has been cancelled due
to a timeout.
Note that in practice this run_state
is almost never encountered. (TODO: check implementation details)
const {STATE_CANCELLED_TIMEOUT} = require('@eluvio/elv-lro-status/enhancedRunState') //=> 'cancelled by timeout'
Type:String"cancelled by user"
An enhanced run_state
(also used by Eluvio Content Fabric servers) to indicate that an LRO has been cancelled by
the user.
Note that in practice this run_state
is almost never encountered. (TODO: check implementation details)
const {STATE_CANCELLED_USER} = require('@eluvio/elv-lro-status/enhancedRunState') //=> 'cancelled by user'
Type:String"failed"
An enhanced run_state
(also used by Eluvio Content Fabric servers) to indicate that an LRO has encountered an error
and has terminated.
const {STATE_FAILED} = require('@eluvio/elv-lro-status/enhancedRunState') //=> 'failed'
Type:String"finished"
An enhanced run_state
(also used by Eluvio Content Fabric servers) to indicate that an LRO has finished.
Note that if the LRO reports a progress percentage that is not 100
, the run_state
will be set to STATE_BAD_PCT
instead.
const {STATE_FINISHED} = require('@eluvio/elv-lro-status/enhancedRunState') //=> 'finished'
Type:String"not started"
An enhanced run_state
(also used by Eluvio Content Fabric servers) to indicate that an LRO has not started yet.
Note that in practice this run_state
is almost never encountered.
const {STATE_NOT_STARTED} = require('@eluvio/elv-lro-status/enhancedRunState') //=> 'not started'
Type:String"running"
An enhanced run_state
(also used by Eluvio Content Fabric servers) to indicate that an LRO is running.
Note that this does not guarantee that the LRO is actually running.
const {STATE_RUNNING} = require('@eluvio/elv-lro-status/enhancedRunState') //=> 'running'
Type:String"stalled"
An enhanced run_state
(NOT used by Eluvio Content Fabric servers) to indicate that an LRO has not updated its
information in a while and may have terminated.
const {STATE_STALLED} = require('@eluvio/elv-lro-status/enhancedRunState') //=> 'stalled'
Type:String"unknown"
An enhanced run_state
(NOT used by Eluvio Content Fabric servers) to indicate that run_state
cannot be determined.
This is an internal initializer value used as a starting point for summarizing a list of states, and should never
get returned to caller.
const {STATE_UNKNOWN} = require('@eluvio/elv-lro-status/enhancedRunState') //=> 'unknown'
Object → Object → Result
options
An object containing options for enhanceLROStatus call
lroStatus
Data from an ElvClient.LROStatus()
call - a key/value map with LRO ID key and LRO status value
A Crocks Result wrapping either the enhanced lroStatus object or an array of errors
Enhance lroResult data obtained via ElvClient.LROStatus().
Returns a Crocks Result, either Ok(enhancedLROStatus) or Err(array of errors)
Internal function, does not validate inputs or unwrap return value to supply a plain object to caller. See source code for public counterpart enhanceLROStatus for example usage.
Pair String Object → Pair String Object
lro
A Crocks Pair with LRO ID as first element and LRO Status Entry as second element
A Crocks Pair with LRO ID as first element and the enhanced copy of LRO Status Entry object with additional fields as second element
Returns an enhanced copy of an LRO Status Entry for an LRO that does not have a run_state
of running
.
Checks for bad reported progress percentage.
const Pair = require('@eluvio/elv-js-helpers/Pair')
const _enhanceNonRunningEntry = require('@eluvio/elv-lro-status/internal/_enhanceNonRunningEntry')
const lro = Pair(
'tlro1Ejh9gNWAaTmfWJKeuu99V2MZ17XXkSvGJyKKJpmqtmJ2fzo5Fb3Be',
{
"duration": 12747000000,
"duration_ms": 12747,
"end": "2022-04-09T05:09:13Z",
"progress": {
"percentage": 100
},
"run_state": "finished",
"start": "2022-04-09T05:09:00Z"
}
)
const enhanced = _enhanceNonRunningEntry(lro)
console.log(Pair.fst())
'tlro1Ejh9gNWAaTmfWJKeuu99V2MZ17XXkSvGJyKKJpmqtmJ2fzo5Fb3Be'
console.log(JSON.stringify(enhanced.snd(), null, 2)
`{
"duration": 12747000000,
"duration_ms": 12747,
"end": "2022-04-09T05:09:13Z",
"progress": {
"percentage": 100
},
"run_state": "finished",
"start": "2022-04-09T05:09:00Z"
}`
// change lro.progress.percentage to a bad value
lro.map(x => x.progress.percentage = 120)
const enhancedBadData = _enhanceNonRunningEntry(lro)
console.log(JSON.stringify(enhancedBadData.snd(), null, 2))
`{
"duration": 12747000000,
"duration_ms": 12747,
"end": "2022-04-09T05:09:13Z",
"progress": {
"percentage": 120
},
"run_state": "bad percentage",
"start": "2022-04-09T05:09:00Z",
"reported_run_state": "finished",
"warning": "Job tlro1Ejh9gNWAaTmfWJKeuu99V2MZ17XXkSvGJyKKJpmqtmJ2fzo5Fb3Be has run_state 'finished', but progress pct is 120"
}`
Object → Pair String Object → Pair String Object
options
options.currentTime
The value to use for current time when calculating ETA fields
options.locale
The locale to use when formatting the eta_local
field
options.stallThreshold
How many seconds to allow to pass between LRO info updates before warning that LRO may have terminated
options.timezone
The timezone to use when formatting the eta_local
field
lro
A Crocks Pair with LRO ID as first element and LRO Status Entry as second element
A Crocks Pair with LRO ID as first element and the enhanced copy of LRO Status Entry object with additional fields as second element
Returns an enhanced copy of an LRO Status Entry for a running LRO. Checks for bad reported progress percentage and stalled LROs Adds ETA fields if possible
const Pair = require('@eluvio/elv-js-helpers/Pair')
const defaultOptions = require('@eluvio/elv-lro-status/defaultOptions')
const _enhanceRunningEntry = require('@eluvio/elv-lro-status/internal/_enhanceRunningEntry')
// for this example, current time is 2022-04-08T21:34:10Z
const options = Object.assign(defaultOptions(), {currentTime: new Date})
const lro = Pair(
'tlro1EjdMMAvWb5iJn2isHdgESes1dq12kpjXCExCepbpWfwMpo2haCxnh',
{
"duration": 1390740000000,
"duration_ms": 1390740,
"progress": {
"percentage": 76.66666666666667
},
"run_state": "running",
"start": "2022-04-08T21:05:00Z"
}
)
const enhanced = _enhanceRunningEntry(options, lro)
console.log(Pair.fst())
'tlro1EjdMMAvWb5iJn2isHdgESes1dq12kpjXCExCepbpWfwMpo2haCxnh'
console.log(JSON.stringify(Pair.snd(), null, 2)
`{
"duration": 1390740000000,
"duration_ms": 1390740,
"progress": {
"percentage": 76.66666666666667
},
"run_state": "running",
"start": "2022-04-08T21:05:00Z",
"seconds_since_last_update": 359,
"estimated_time_left_seconds": 533,
"estimated_time_left_h_m_s": "8m 53s",
"eta_local": "2:43:03 PM PDT"
}`
// if current time were instead 1 hour later (2022-04-08T22:34:10Z) then output would be:
`{
"duration": 1390740000000,
"duration_ms": 1390740,
"progress": {
"percentage": 76.66666666666667
},
"run_state": "stalled",
"start": "2022-04-08T21:05:00Z",
"seconds_since_last_update": 3959,
"estimated_time_left_seconds": 1628,
"estimated_time_left_h_m_s": "27m 08s",
"eta_local": "4:01:18 PM PDT",
"reported_run_state": "running",
"warning": "status has not been updated in 3959 seconds, process may have terminated"
}`
[String] → String
list
An array of enhanced run states
The state with the highest precedence, or value or STATE_UNKNOWN
if list
is empty.
Returns the enhanced run state with maximum precedence from a list, or STATE_UNKNOWN
if an empty list passed in
const _maxEnhancedState = require('@eluvio/elv-lro-status/internal/_maxEnhancedState')
_maxEnhancedState(['running', 'stalled']) //=> 'stalled'
_maxEnhancedState([]) //=> 'unknown'
Object → Number | undefined
(unnamed)
An LRO Status Entry object
The value or undefined
if not found
Retrieves progress percentage from LRO Status entry, protecting against missing keys
const _progressPct = require('@eluvio/elv-lro-status/internal/_progressPct')
_progressPct({progress: {percentage: 10}}) //=> 10
_progressPct({}) //=> undefined
Type:Object
A lookup table for enhanced run state precedence {unknown: 0, finished: 1, ...}
Object → Object
enhancedLROStatus
An object where the keys are LRO IDs and the values are enhanced LRO Status Entries
The summary object
Returns a summary for an object containing enhanced LRO Status Entries
Returns an overall run_state
and (if applicable) ETA fields.
const _sumEnhancedEntries = require('@eluvio/elv-lro-status/internal/_sumEnhancedEntries')
const enhancedEntriesNoProgress = {
"tlro1Ejh9gNWAaTmfWJKeuu99V2MZ17XXkSv3L57AV3CPYHbLmk3E9SF1b": {
"duration": 0,
"duration_ms": 0,
"progress": {
"percentage": 0
},
"estimated_time_left_h_m_s": "unknown (not enough progress yet)",
"run_state": "running",
"start": "2022-04-09T05:09:00Z",
"seconds_since_last_update": 84
},
"tlro1Ejh9gNWAaTmfWJKeuu99V2MZ17XXkSvGJyKKJpmqtmJ2fzo5Fb3Be": {
"duration": 12747000000,
"duration_ms": 12747,
"end": "2022-04-09T05:09:13Z",
"progress": {
"percentage": 100
},
"run_state": "finished",
"start": "2022-04-09T05:09:00Z"
}
}
console.log(JSON.stringify(_sumEnhancedEntries(enhancedEntriesNoProgress), null, 2))
`{
"run_state": "running",
"estimated_time_left_h_m_s": "unknown (not enough progress yet)"
}`
const enhancedEntriesWithProgress = {
'tlro1EjdMMAvWb5iJn2isHdgESes1dq12kpjJ2kukiD5NmnEgCP7iFFBjU': {
'duration': 335613000000,
'duration_ms': 335613,
'end': '2022-04-08T21:10:34Z',
'progress': {
'percentage': 100
},
'run_state': 'finished',
'start': '2022-04-08T21:05:00Z'
},
'tlro1EjdMMAvWb5iJn2isHdgESes1dq12kpjXCExCepbpWfwMpo2haCxnh': {
'duration': 1390740000000,
'duration_ms': 1390740,
'progress': {
'percentage': 76.66666666666667
},
'run_state': 'running',
'start': '2022-04-08T21:05:00Z',
'seconds_since_last_update': 359,
'estimated_time_left_seconds': 423,
'estimated_time_left_h_m_s': '7m 03s',
'eta_local': '2:41:13 PM PDT'
}
}
console.log(JSON.stringify(_sumEnhancedEntries(enhancedEntriesWithProgress), null, 2))
`{
"run_state": "running",
"estimated_time_left_seconds": 423,
"estimated_time_left_h_m_s": "7m 03s",
"eta_local": "2:41:13 PM PDT"
}`
`