/*
* Copyright 2003-2006, 2009, 2017, United States Government, as represented by the Administrator of the
* National Aeronautics and Space Administration. All rights reserved.
*
* The NASAWorldWind/WebWorldWind platform is licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @exports OpenSearchService
*/
define([
'../../error/ArgumentError',
'../../util/Logger',
'./responseFormats/atomParser/OpenSearchAtomParser',
'./OpenSearchConstants',
'./descriptionDocument/OpenSearchDescriptionDocument',
'./responseFormats/OpenSearchParserRegistry',
'./OpenSearchRequest',
'./OpenSearchUtils',
'../../util/Promise'
],
function (ArgumentError,
Logger,
OpenSearchAtomParser,
OpenSearchConstants,
OpenSearchDescriptionDocument,
OpenSearchParserRegistry,
OpenSearchRequest,
OpenSearchUtils,
Promise) {
'use strict';
/**
* Constructs a service for interacting with OpenSearch servers.
*
* @alias OpenSearchService
* @constructor
* @classdesc Provides a service for interacting with OpenSearch servers.
*
* The service exposes two methods:
* - discover used to get the description document;
* - search used for querying the search engine.
*
* By default, this service can handle Atom for EO and GeoJSON responses.
* Other types of response formats can by added using the registerParser method.
*/
var OpenSearchService = function () {
this._url = '';
this._descriptionDocument = null;
this._rawDescriptionDocument = null;
this._parserRegistry = new OpenSearchParserRegistry();
this.registerDefaultParsers();
};
Object.defineProperties(OpenSearchService.prototype, {
/**
* URL of the OpenSearch description document.
* @memberof OpenSearchService.prototype
* @type {String}
*/
url: {
get: function () {
return this._url;
}
},
/**
* The latest parsed OpenSearch description document.
* @memberof OpenSearchService.prototype
* @type {OpenSearchDescriptionDocument}
*/
descriptionDocument: {
get: function () {
return this._descriptionDocument;
}
},
/**
* The latest OpenSearch description document in the raw xml form.
* @memberof OpenSearchService.prototype
* @type {XmlDocument}
*/
rawDescriptionDocument: {
get: function() {
return this._rawDescriptionDocument;
}
},
/**
* A registry of parsers (Atom, GeoJSON) to be used by this service.
* @memberof OpenSearchService.prototype
* @type {OpenSearchParserRegistry}
*/
parserRegistry: {
get: function () {
return this._parserRegistry;
},
set: function (value) {
this._parserRegistry = value;
}
}
});
/**
* Fetches and parses an OpenSearch description document.
* @param {OpenSearchRequest} options See {@link OpenSearchRequest} for possible options. A url is required.
* @return {Promise} A promise which when resolved returns this service, or an error when rejected
* @example openSearchService
* .discover({url: 'http://example.com/opensearch'})
* .then(result => console.log(result))
* .catch(err => console.error(err));
*/
OpenSearchService.create = function(options){
var service = new OpenSearchService();
var requestOptions = new OpenSearchRequest(options);
requestOptions.method = requestOptions.method || 'GET';
if (!requestOptions.url) {
return Promise.reject(new Error('OpenSearchService discover - no url provided'));
}
this._url = requestOptions.url;
return OpenSearchUtils.fetch(requestOptions)
.then(function (responseText) {
var xmlRoot = OpenSearchUtils.parseXml(responseText);
service._rawDescriptionDocument = xmlRoot;
service._descriptionDocument = new OpenSearchDescriptionDocument(xmlRoot);
return service;
});
};
/**
* Performs a search query.
*
* @param {Array|null} searchParams A list of objects, each object must have a name and value property.
* @param {OpenSearchRequest|null} options See {@link OpenSearchRequest} for possible options.
* @return {Promise} A promise which when resolved returns a GeoJSON collection, or an error when rejected.
* @example openSearchService
* .search([
* {name: 'count', value: 10}, {name: 'lat', value: 50}, {name: 'lon', value: 20}
* ])
* .then(result => console.log(result))
* .catch(err => console.error(err));
*/
OpenSearchService.prototype.search = function (searchParams, options) {
if (!this._descriptionDocument) {
return Promise.reject(new Error('OpenSearchService search - no descriptionDocument, create the service via create first'));
}
var self = this;
var requestOptions = new OpenSearchRequest(options);
var supportedFormats = this.getSupportedFormats();
var openSearchUrl = this._descriptionDocument.findFirstSearchUrl({
searchParams: searchParams,
requestOptions: requestOptions,
supportedFormats: supportedFormats
});
if (!openSearchUrl) {
return Promise.reject(new Error('OpenSearchService - no suitable Url found'));
}
requestOptions.method = openSearchUrl.method;
requestOptions.encType = openSearchUrl.encType;
if (openSearchUrl.method === 'GET') {
requestOptions.url = openSearchUrl.createRequestUrl(searchParams);
} else if (openSearchUrl.encType === 'application/x-www-form-urlencoded' ||
openSearchUrl.encType === 'multipart/form-data') {
requestOptions.url = openSearchUrl._baseUrl;
requestOptions.body = openSearchUrl.createRequestBody(searchParams);
requestOptions.addHeader('Content-Type', openSearchUrl.encType);
} else {
return Promise.reject(new Error('OpenSearchService search - unsupported encoding'));
}
// Get Metadata for the products.
// Get available collections.
// Collection has parameters, metadata and all the information as part of the source.
return OpenSearchUtils.fetch(requestOptions)
.then(function (response) {
var responseParser = self.getParser(openSearchUrl.type, requestOptions.relation);
if (!responseParser) {
throw new Error('OpenSearchService search - no suitable response parser found');
}
return responseParser.parse(response, requestOptions.relation);
// What are the possible results of this?
});
};
/**
* Finds an URL that satisfies the provided predicate function.
*
* @param {Function} predicate Function to execute on each value in the description document URLs array,
* taking three arguments:
* element The current element being processed in the array.
* index The index of the current element being processed in the array.
* array The array find was called upon.
* @param {Object|null} context Object to use as "this" when executing the predicate function.
* @return {OpenSearchUrl|undefined} The first URL in the array that satisfies the provided predicate
* function. Otherwise, undefined is returned.
*/
OpenSearchService.prototype.findUrl = function(predicate, context) {
return OpenSearchUtils.arrayFind(this._descriptionDocument.urls, predicate, context);
};
// Do ParserRegistry belong to the OpenSearchService?
/**
* Registers a parser for the specified mime type and relation.
*
* The parse method of the parser will be called with the response of the server when the mime type and
* relation type matches.
*
* @param {String} type Mime type of the parser to register.
* @param {String} rel Relation type of the parser to register.
* @param {Object} parser An object with a parse method.
*/
OpenSearchService.prototype.registerParser = function (type, rel, parser) {
this.parserRegistry.registerParser(type, rel, parser);
};
/**
* Returns the list of supported mime types.
*
* @return {String[]} The list of supported mime types.
*/
OpenSearchService.prototype.getSupportedFormats = function () {
return this.parserRegistry.getFormats();
};
/**
* Returns the response parser for the specified mime type and relation.
*
* @param {String} type Mime type of the parser.
* @param {String} rel Relation type of the parser.
*
* @return {Object|undefined} The response parser.
*/
OpenSearchService.prototype.getParser = function (type, rel) {
return this.parserRegistry.getParser(type, rel);
};
/**
* Removes the response parser for the specified mime type and relation.
*
* @param {String} type Mime type of the registered parser.
* @param {String} rel Relation type of the registered parser.
*/
OpenSearchService.prototype.removeParser = function (type, rel) {
this.parserRegistry.removeParser(type, rel);
};
/**
* Internal. Applications should not call this function.
* Registers the default parsers for an OpenSearchService.
*/
OpenSearchService.prototype.registerDefaultParsers = function () {
this.registerParser('application/atom+xml', OpenSearchConstants.RESULTS, OpenSearchAtomParser);
this.registerParser('application/atom+xml', OpenSearchConstants.COLLECTION, OpenSearchAtomParser);
/** There are 3 accepted mime types for GeoJSON **/
this.registerParser('application/vnd.geo+json', OpenSearchConstants.RESULTS, window.JSON);
this.registerParser('application/vnd.geo+json', OpenSearchConstants.COLLECTION, window.JSON);
this.registerParser('application/geo+json', OpenSearchConstants.RESULTS, window.JSON);
this.registerParser('application/geo+json', OpenSearchConstants.COLLECTION, window.JSON);
this.registerParser('application/json', OpenSearchConstants.RESULTS, window.JSON);
this.registerParser('application/json', OpenSearchConstants.COLLECTION, window.JSON);
};
return OpenSearchService;
});