Skip to content
This repository has been archived by the owner on Aug 12, 2022. It is now read-only.

Commit

Permalink
strip embed stuff out of library
Browse files Browse the repository at this point in the history
this is handled by the external loader function now
  • Loading branch information
letmaik committed Mar 24, 2016
1 parent 1064646 commit 66e7b12
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 124 deletions.
50 changes: 5 additions & 45 deletions src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ const IriTemplate = 'IriTemplate'

const COVAPI_NS = 'http://coverageapi.org/ns#'
const COVAPI_API = COVAPI_NS + 'api'
const CanInclude = COVAPI_NS + 'canInclude'

const COVJSON_NS = 'http://coveragejson.org/def#'
const Domain = COVJSON_NS + 'Domain'
const Range = COVJSON_NS + 'Range'

const OSGEO_NS = 'http://a9.com/-/opensearch/extensions/geo/1.0/'
const OSTIME_NS = 'http://a9.com/-/opensearch/extensions/time/1.0/'
Expand Down Expand Up @@ -146,12 +141,7 @@ export class API {

console.log(ld.api)
}

if (ld[CanInclude]) {
// server supports optional inclusion via Prefer header
this.supportsPreferHeaders = true
}


this._createCapabilities()
}

Expand Down Expand Up @@ -194,8 +184,7 @@ export class API {
_createCapabilities () {
let caps = {
filter: {},
subset: {},
embed: {}
subset: {}
}
let startstop = () => ({
start: true,
Expand Down Expand Up @@ -251,23 +240,15 @@ export class API {
step: true
}
}
if (this.supportsPreferHeaders) {
caps.embed = {
domain: true,
range: true
}
}
this.capabilities = caps
}

/**
* Option keys: time, x, y, vertical, embed
* Option keys: time, x, y, vertical
*
* Each value except for 'embed' is one of (check this.capabilities to see which ones are supported!):
*
* {start, stop} // intersect match
*
* 'embed' is an object {domain: true, range: true} where both members are optional.
*/
_getFilterTemplateVars (options = {}) {
let templateVars = {}
Expand Down Expand Up @@ -375,27 +356,7 @@ export class API {
return templateVars
}

_getIncludeDomainAndRangeHeaders (options = {}) {
if (!this.supportsPreferHeaders) {
return {}
}
let uris = []
if (options.domain) {
uris.push(Domain)
}
if (options.range) {
uris.push(Range)
}
if (uris.length === 0) {
return {}
}
return {
Prefer: 'return=representation; ' +
'include="' + uris.join(' ') + '"'
}
}

getUrlAndHeaders (options) {
getUrl (options) {
// deep-copy as we delete properties after they are applied
options = JSON.parse(JSON.stringify(options))
let subsetTemplateVars = this._getSubsetTemplateVars(options.subset)
Expand All @@ -407,8 +368,7 @@ export class API {
}

let url = urltemplate.parse(this.urlTemplate.template).expand(templateVars)
let headers = this._getIncludeDomainAndRangeHeaders(options.embed)
return {url, headers}
return url
}

}
Expand Down
119 changes: 40 additions & 79 deletions src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,41 @@ import {shallowcopy, mergeInto} from './util.js'
* @param {object} options Options which control the behaviour of the wrapper.
* @param {function} options.loader
* The function to use for loading coverage data from a URL.
* It is called as loader(url, headers) where headers is an optional object
* of HTTP headers to send.
* It is called as loader(url, options) where options corresponds to the
* options parameter of Coverage.subsetBy* and CoverageCollectionQuery.execute.
* It must return a Promise succeeding with a Coverage Data API object.
*
* @returns {object} The wrapped Coverage Data API object.
*/
export function wrap (data, options) {
if (typeof options.loader !== 'function') {
return doWrap(data, options)
}

function doWrap (data, wrapOptions, loaderOptions) {
if (typeof wrapOptions.loader !== 'function') {
throw new Error('options.loader must be a function')
}
if (data.coverages) {
return wrapCollection(data, options)
return wrapCollection(data, wrapOptions, loaderOptions)
} else {
return wrapCoverage(data, options)
return wrapCoverage(data, wrapOptions)
}
}

function wrapCollection (collection, options) {
function wrapCollection (collection, wrapOptions, loaderOptions) {
// TODO wrap each individual coverage as well!
return API.discover(collection).then(api => {
let newcoll = shallowcopy(collection)
newcoll.query = () => {
let query = collection.query()
return new QueryProxy(query, newcoll, api, options)
return new QueryProxy(query, newcoll, api, wrapOptions)
}
if (api.isPaged) {
let load = options.loader
let load = wrapOptions.loader
let wrapPageLink = url => {
if (!url) return
return {
load: () => load(url, {headers: options.headers}).then(coll => wrap(coll, options))
load: () => load(url, loaderOptions).then(coll => wrap(coll, wrapOptions))
}
}
newcoll.paging = {
Expand All @@ -62,15 +66,14 @@ function wrapCollection (collection, options) {
* if possible.
*/
class QueryProxy {
constructor (query, collection, api, options) {
constructor (query, collection, api, wrapOptions) {
this._query = query
this._collection = collection
this._api = api
this._options = options
this._wrapOptions = wrapOptions

this._filter = {}
this._subset = {}
this._embed = {}
}

filter (spec) {
Expand All @@ -84,51 +87,37 @@ class QueryProxy {
mergeInto(spec, this._subset)
return this
}

embed (spec) {
this._query.embed(spec)
mergeInto(spec, this._embed)
return this
}

execute () {

execute (options) {
let domainTemplate = this._collection.domainTemplate
if (domainTemplate) {
return this._doExecute(domainTemplate)
} else {
// inspect domain of first coverage and assume uniform collection
if (this._collection.coverages.length > 0) {
return this._collection.coverages[0].loadDomain().then(domain => this._doExecute(domain))
return this._collection.coverages[0].loadDomain().then(domain => this._doExecute(options, domain))
} else {
return this._query.execute()
return this._query.execute(options)
}
}
}

_doExecute (domainTemplate) {
let load = this._options.loader
_doExecute (options, domainTemplate) {
let load = this._wrapOptions.loader

let filterCaps = this._api.capabilities.filter
let subsetCaps = this._api.capabilities.subset
let embedCaps = this._api.capabilities.embed
let axisMap = getAxisConcepts(domainTemplate)

// split constraints into API and locally applied ones
let apiConstraints = {
filter: {},
subset: {},
embed: {}
subset: {}
}

let localFilterConstraints = {} // axis name -> spec
let localSubsetConstraints = {} // axis name -> spec

// embedding
// makes only sense when using the API, hence there is no local fall-back
if (embedCaps.domain && embedCaps.range) {
apiConstraints.embed = this._embed
}

// filtering
for (let axis of Object.keys(this._filter)) {
let constraint = this._filter[axis]
Expand Down Expand Up @@ -166,16 +155,13 @@ class QueryProxy {
toLocalConstraintsIfDependencyMissing(apiConstraints.filter, localFilterConstraints, filterCaps, axisMap)
toLocalConstraintsIfDependencyMissing(apiConstraints.subset, localSubsetConstraints, subsetCaps, axisMap)

// TODO if only embed is requested, check if this is already applied and return the original collection in that case
if (Object.keys(apiConstraints.filter).length === 0 &&
Object.keys(apiConstraints.subset).length === 0 &&
Object.keys(apiConstraints.embed).length === 0) {
return this._query.execute()
Object.keys(apiConstraints.subset).length === 0) {
return this._query.execute(options)
}

let {url, headers} = this._api.getUrlAndHeaders(apiConstraints)

return load(url, {headers}).then(resultCollection => {
let url = this._api.getUrl(apiConstraints)
return load(url, options).then(resultCollection => {
// apply remaining query parts
if (Object.keys(localSubsetConstraints).length > 0 || Object.keys(localFilterConstraints).length > 0) {
// the locally queried collection is NOT wrapped! see comment for coverage subsetting below
Expand All @@ -185,18 +171,17 @@ class QueryProxy {
.execute()
} else {
// carry-over headers for paging
let options = getOptionsWithHeaders(this._options, headers)
return wrap(resultCollection, options)
return doWrap(resultCollection, this._wrapOptions, options)
}
})
}
}

function wrapCoverage (coverage, options) {
function wrapCoverage (coverage, wrapOptions) {
return API.discover(coverage).then(api => {
let wrappedCoverage = shallowcopy(coverage)
wrappedCoverage.subsetByIndex = wrappedSubsetByIndex(coverage, wrappedCoverage, api, options)
wrappedCoverage.subsetByValue = wrappedSubsetByValue(coverage, wrappedCoverage, api, options)
wrappedCoverage.subsetByIndex = wrappedSubsetByIndex(coverage, wrappedCoverage, api, wrapOptions)
wrappedCoverage.subsetByValue = wrappedSubsetByValue(coverage, wrappedCoverage, api, wrapOptions)
return wrappedCoverage
})
}
Expand All @@ -220,18 +205,10 @@ function wrappedSubsetByIndex (coverage, wrappedCoverage, api, wrapOptions) {

// we split the subsetting constraints into API-compatible and local ones
let apiConstraints = {
subset: {}, // API concept -> spec
embed: {}
subset: {} // API concept -> spec
}
let localSubsetConstraints = {} // axis name -> spec

// embedding
// makes only sense when using the API, hence there is no local fall-back
let embedCaps = api.capabilities.embed
if (embedCaps.domain && embedCaps.range) {
apiConstraints.embed = options.embed
}

if (caps.index) {
apiConstraints.subset.index = constraints
} else {
Expand Down Expand Up @@ -286,15 +263,15 @@ function wrappedSubsetByIndex (coverage, wrappedCoverage, api, wrapOptions) {
// 3.1. Subset Coverage A by time with API -> Coverage C with API info
// 3.2. Subset Coverage C by bounding box without API -> Coverage D with subset relationship to Coverage C
// TODO implement that or think of something simpler
return coverage.subsetByIndex(constraints)
return coverage.subsetByIndex(constraints, options)
}

let {url, headers} = api.getUrlAndHeaders(apiConstraints)
return wrapOptions.loader(url, {headers}).then(subset => {
let url = api.getUrl(apiConstraints)
return wrapOptions.loader(url, options).then(subset => {
// apply remaining subset constraints
if (Object.keys(localSubsetConstraints).length > 0) {
// again, we DON'T wrap the locally subsetted coverage again, see above
return subset.subsetByIndex(localSubsetConstraints)
return subset.subsetByIndex(localSubsetConstraints, options)
} else {
return wrap(subset, wrapOptions)
}
Expand Down Expand Up @@ -330,18 +307,10 @@ function wrappedSubsetByValue (coverage, wrappedCoverage, api, wrapOptions) {

// we split the subsetting constraints into API-compatible and local ones
let apiConstraints = {
subset: {}, // API concept -> spec
embed: {}
subset: {} // API concept -> spec
}
let localSubsetConstraints = {} // axis name -> spec

// embedding
// makes only sense when using the API, hence there is no local fall-back
let embedCaps = api.capabilities.embed
if (embedCaps.domain && embedCaps.range) {
apiConstraints.embed = options.embed
}

for (let axis of Object.keys(constraints)) {
let useApi = false
let constraint = constraints[axis]
Expand Down Expand Up @@ -395,15 +364,15 @@ function wrappedSubsetByValue (coverage, wrappedCoverage, api, wrapOptions) {

if (Object.keys(apiConstraints.subset).length === 0) {
// again, we DON'T wrap the locally subsetted coverage again, see above
return coverage.subsetByValue(constraints)
return coverage.subsetByValue(constraints, options)
}

let {url, headers} = api.getUrlAndHeaders(apiConstraints)
return wrapOptions.loader(url, {headers}).then(subset => {
let url = api.getUrl(apiConstraints)
return wrapOptions.loader(url, options).then(subset => {
// apply remaining subset constraints
if (Object.keys(localSubsetConstraints).length > 0) {
// again, we DON'T wrap the locally subsetted coverage again, see above
return subset.subsetByValue(localSubsetConstraints)
return subset.subsetByValue(localSubsetConstraints, options)
} else {
return wrap(subset, wrapOptions)
}
Expand Down Expand Up @@ -508,11 +477,3 @@ function cleanedConstraints (constraints) {
return cleanConstraints
}

function getOptionsWithHeaders (options, headers) {
options = shallowcopy(options)
if (!options.headers) {
options.headers = {}
}
mergeInto(headers, options.headers)
return options
}

0 comments on commit 66e7b12

Please sign in to comment.