Skip to content

Commit

Permalink
Merge pull request #186 from sosna/develop
Browse files Browse the repository at this point in the history
Merge release 2.20.0
  • Loading branch information
sosna committed Aug 5, 2022
2 parents 7e4e260 + da0580d commit f2807f9
Show file tree
Hide file tree
Showing 25 changed files with 3,604 additions and 2,129 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"node": ">=10"
},
"description": "SDMX REST API client for JavaScript",
"version": "2.19.0",
"version": "2.20.0",
"main": "./lib/index.js",
"scripts": {
"prebuild": "rm -rf lib && mkdir lib",
Expand Down
97 changes: 97 additions & 0 deletions src/avail/availability-query2.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{AvailabilityMode} = require './availability-mode'
{AvailabilityReferences} = require './availability-references'
{ContextRefType, Sdmx3SeriesKeyType, NestedNCNameIDType, FiltersType} =
require '../utils/sdmx-patterns'
{isValidEnum, isValidPattern, isValidDate, createErrorMessage} =
require '../utils/validators'

defaults =
context: '*=*:*(*)'
key: '*'
component: '*'
filters: []
mode: AvailabilityMode.EXACT
references: AvailabilityReferences.NONE

isValidKey = (input, name, errors) ->
valid = true
if input.indexOf(",") > -1
for i in input.split ","
r = isValidPattern(i, Sdmx3SeriesKeyType, name, errors)
valid = false unless r
else
r = isValidPattern(input, Sdmx3SeriesKeyType, name, errors)
valid = false unless r
valid

isValidFilters = (input, name, errors) ->
valid = true
for filter in input
r = isValidPattern(filter, FiltersType, name, errors)
valid = false unless r
valid

isValidComp = (input, name, errors) ->
valid = true
if input isnt '*'
if input.indexOf(",") > -1
for i in input.split ","
r = isValidPattern(i, NestedNCNameIDType, name, errors)
valid = false unless r
else
r = isValidPattern(input, NestedNCNameIDType, name, errors)
valid = false unless r
valid

ValidQuery =
context: (i, e) -> isValidPattern(i, ContextRefType, 'context', e)
key: (i, e) -> isValidKey(i, 'series key', e)
component: (i, e) -> isValidComp(i, 'component', e)
updatedAfter: (i, e) -> not i or isValidDate(i, 'updatedAfter', e)
filters: (i, e) -> isValidFilters(i, 'filters', e)
mode: (i, e) -> isValidEnum(i, AvailabilityMode, 'mode', e)
references: (i, e) -> isValidEnum(i, AvailabilityReferences, 'references', e)

isValidQuery = (q) ->
errors = []
isValid = false
for own k, v of q
isValid = ValidQuery[k](v, errors)
break unless isValid
{isValid: isValid, errors: errors}

expected = [
"context"
"key"
"component"
"updatedAfter"
"filters"
"mode"
"references"
]

query = class AvailabilityQuery

@from: (opts) ->
if opts
for own k, v of opts
throw Error createErrorMessage([], 'availability query') \
unless k in expected
context = opts?.context ? defaults.context
key = opts?.key ? defaults.key
filters = opts?.filters ? defaults.filters
filters = [filters] if not Array.isArray filters
query =
context: context
key: key
component: opts?.component ? defaults.component
updatedAfter: opts?.updatedAfter
filters: filters
mode: opts?.mode ? defaults.mode
references: opts?.references ? defaults.references
input = isValidQuery query
throw Error createErrorMessage(input.errors, 'availability query') \
unless input.isValid
query

exports.AvailabilityQuery2 = query
120 changes: 120 additions & 0 deletions src/data/data-query2.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
{ContextRefType, Sdmx3SeriesKeyType, NCNameIDType, FiltersType} =
require '../utils/sdmx-patterns'
{isValidPattern, isValidDate, createErrorMessage} =
require '../utils/validators'

defaults =
context: '*=*:*(*)'
key: '*'
history: false
attributes: 'dsd'
measures: 'all'
filters: []

isValidHistory = (input, errors) ->
valid = typeof input is 'boolean'
errors.push "#{input} is not a valid value for history. Must be true or \
false" unless valid
valid

isValidNObs = (input, name, errors) ->
valid = typeof input is 'number' and input > 0
errors.push "#{input} is not a valid value for #{name}. Must be a positive \
integer" unless valid
valid

isValidComp = (input, name, errors) ->
valid = true
if input.indexOf(",") > -1
for i in input.split ","
r = isValidPattern(i, NCNameIDType, name, errors)
valid = false unless r
else
r = isValidPattern(input, NCNameIDType, name, errors)
valid = false unless r
valid

isValidKey = (input, name, errors) ->
valid = true
if input.indexOf(",") > -1
for i in input.split ","
r = isValidPattern(i, Sdmx3SeriesKeyType, name, errors)
valid = false unless r
else
r = isValidPattern(input, Sdmx3SeriesKeyType, name, errors)
valid = false unless r
valid

isValidFilters = (input, name, errors) ->
valid = true
for filter in input
r = isValidPattern(filter, FiltersType, name, errors)
valid = false unless r
valid


ValidQuery =
context: (i, e) -> isValidPattern(i, ContextRefType, 'context', e)
key: (i, e) -> isValidKey(i, 'series key', e)
updatedAfter: (i, e) -> not i or isValidDate(i, 'updatedAfter', e)
firstNObs: (i, e) -> not i or isValidNObs(i, 'firstNObs', e)
lastNObs: (i, e) -> not i or isValidNObs(i, 'lastNObs', e)
obsDimension: (i, e) ->
not i or isValidPattern(i, NCNameIDType, 'obs dimension', e)
history: (i, e) -> isValidHistory(i, e)
attributes: (i, e) -> isValidComp(i, 'attributes', e)
measures: (i, e) -> isValidComp(i, 'measures', e)
filters: (i, e) -> isValidFilters(i, 'filters', e)

isValidQuery = (q) ->
errors = []
isValid = false
for own k, v of q
isValid = ValidQuery[k](v, errors)
break unless isValid
{isValid: isValid, errors: errors}

expected = [
"context"
"key"
"updatedAfter"
"firstNObs"
"lastNObs"
"obsDimension"
"history"
"attributes"
"measures"
"filters"
]

# A query for data, as defined by the SDMX RESTful API.
query = class DataQuery

@from: (opts) ->
if opts
for own k, v of opts
throw Error createErrorMessage([], 'data query') \
unless k in expected
context = opts?.context ? defaults.context
key = opts?.key ? defaults.key
attrs = opts?.attributes ? defaults.attributes
measures = opts?.measures ? defaults.measures
filters = opts?.filters ? defaults.filters
filters = [filters] if not Array.isArray filters
query =
context: context
key: key
updatedAfter: opts?.updatedAfter
firstNObs: opts?.firstNObs
lastNObs: opts?.lastNObs
obsDimension: opts?.obsDimension
history: opts?.history ? defaults.history
attributes: attrs
measures: measures
filters: filters
input = isValidQuery query
throw Error createErrorMessage(input.errors, 'data query') \
unless input.isValid
query

exports.DataQuery2 = query
101 changes: 94 additions & 7 deletions src/index.coffee
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{DataQuery} = require './data/data-query'
{DataQuery2} = require './data/data-query2'
{DataFormat} = require './data/data-format'
{DataDetail} = require './data/data-detail'
{MetadataQuery} = require './metadata/metadata-query'
Expand All @@ -7,6 +8,7 @@
{MetadataReferences} = require './metadata/metadata-references'
{MetadataType} = require './metadata/metadata-type'
{AvailabilityQuery} = require './avail/availability-query'
{AvailabilityQuery2} = require './avail/availability-query2'
{AvailabilityMode} = require './avail/availability-mode'
{AvailabilityReferences} = require './avail/availability-references'
{SchemaQuery} = require './schema/schema-query'
Expand Down Expand Up @@ -175,6 +177,54 @@ getService = (input) ->
getDataQuery = (input) ->
return DataQuery.from input

#
# Get an SDMX 3.0 RESTful data query.
#
# The expected properties (and their default values) are:
# - *context* (optional) - the reference to the context (default: *=*:*(*)).
# - *key* (optional) - the key of the data to be returned (default: all).
# - *updatedAfter* (optional) - instructs the service to return what has
# changed since the supplied time stamp.
# - *firstNObs* (optional) - the number of observations to be returned,
# starting from the first observation.
# - *lastNObs* (optional) - the number of observations to be returned,
# starting from the last observation.
# - *obsDimension* (optional) - the ID of the dimension to be attached at the
# observation level (default TIME_PERIOD).
# - *history* (optional) - Whether previous versions of the data should be
# returned (default: false).
# - *attributes* (optional) - The attributes to be returned (default: dsd).
# - *measures* (optional) - The measures to be returned (default: all).
# - *filters* (optional) - The component filters to be applied.
#
# @example Create a query for all data belonging to the CBS dataflow,
# maintained by the BIS
# sdmxrest.getDataQuery({context: 'dataflow=BIS:EXR(*)'})
#
# @example Create a query for EXR data, matching values A for the 1st
# dimension, any value for the 2nd dimension, EUR, SP00 and A for the 3rd, 4th
# and 5th dimensions respectively
# sdmxrest.getDataQuery({context: 'dataflow=*:EXR(*)', key: 'A..EUR.SP00.A'})
#
# @example Create a query for the last observation of the EXR data matching the
# supplied key
# sdmxrest.getDataQuery({context: 'dataflow=*:EXR(*)', key: 'A.*.EUR.SP00.A',
# lastNObs: 1})
#
# @example Create a query to get what has changed for the EXR data since
# the supplied point in time
# sdmxrest.getDataQuery({context: 'dataflow=*:EXR(*)',
# updatedAfter: '2016-03-17T14:38:00Z'})
#
# @param [Object] input an object with the desired filters for the query
#
# @throw an error in case a) the mandatory flow is not supplied or b) a value
# not compliant with the SDMX 2.1 RESTful specification is supplied for one of
# the properties.
#
getDataQuery2 = (input) ->
return DataQuery2.from input

#
# Get an SDMX 2.1 RESTful metadata query.
#
Expand Down Expand Up @@ -237,6 +287,32 @@ getMetadataQuery = (input) ->
getAvailabilityQuery = (input) ->
return AvailabilityQuery.from input

#
# Get an SDMX 3.0 RESTful availability query.
#
# The expected properties (and their default values) are:
# - *context* (optional) - the reference to the context (default: *=*:*(*)).
# - *key* (optional) - the key of the data to be returned (default: all)
# - *component* (optional) - the id of the dimension for which to obtain
# availability information (default: all)
# - *updatedAfter* (optional) - instructs the service to return what has
# changed since the supplied time stamp.
# - *mode* (optional) - the possible processing modes (default: exact)
# - *references* (optional) - the references to be returned (default: none)
# - *filters* (optional) - The component filters to be applied.
#
# @example Create an availability query for the ECB EXR dataflow
# sdmxrest.getAvailabilityQuery({context: 'dataflow=ECB:EXR(*)'})
#
# @param [Object] input an object with the desired characteristics of the query
#
# @throw an error in case a) the mandatory flow is not supplied or b) a value
# not compliant with the SDMX 2.1 RESTful specification is supplied for one of
# the properties.
#
getAvailabilityQuery2 = (input) ->
return AvailabilityQuery2.from input

#
# Get an SDMX 2.1 RESTful schema query.
#
Expand Down Expand Up @@ -285,16 +361,25 @@ getUrl = (query, service) ->
throw ReferenceError 'Not a valid service' unless service
throw ReferenceError 'Not a valid query' unless query
s = getService service
q = if (query.mode? or \
(query.flow? and query.references?) or \
(query.flow? and query.component?))
q = if query.resource?
getMetadataQuery query
else if query.context? and query.agency?
getSchemaQuery query
else if query.flow? and \
(query.references? or query.component? \
or query.mode?)
getAvailabilityQuery query
else if query.references? or query.component? \
or query.mode?
getAvailabilityQuery2 query
else if query.flow?
getDataQuery query
else if query.resource?
getMetadataQuery query
else if query.context?
getSchemaQuery query
else if query.context? or query.key? or query.filters? \
or query.firstNObs? or query.lastNObs? or query.obsDimension? \
or query.history? or query.attributes? or query.measures? \
or query.updatedAfter?
getDataQuery2 query
else
throw Error 'Not a valid query' unless q
return new UrlGenerator().getUrl q, s

Expand Down Expand Up @@ -380,8 +465,10 @@ module.exports =
getService: getService
services: services
getDataQuery: getDataQuery
getDataQuery2: getDataQuery2
getMetadataQuery: getMetadataQuery
getAvailabilityQuery: getAvailabilityQuery
getAvailabilityQuery2: getAvailabilityQuery2
getSchemaQuery: getSchemaQuery
getUrl: getUrl
request: request
Expand Down
16 changes: 16 additions & 0 deletions src/utils/api-version.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,21 @@ resources =
# The set of valid resources for the latest API version.
LATEST: resourcesV6

numbers =
v1_0_0: 0
v1_0_1: 1
v1_0_2: 2
v1_1_0: 3
v1_2_0: 4
v1_3_0: 5
v1_4_0: 6
v1_5_0: 7
v2_0_0: 8

getKeyFromVersion = (v) ->
v.replace /\./g, "_"

exports.ApiVersion = Object.freeze versions
exports.ApiResources = Object.freeze resources
exports.ApiNumber = Object.freeze numbers
exports.getKeyFromVersion = getKeyFromVersion
Loading

0 comments on commit f2807f9

Please sign in to comment.