├── .dockerignore ├── .github └── workflows │ ├── _test.yml │ ├── pull_request.yml │ └── push.yml ├── .gitignore ├── .jshintignore ├── .jshintrc ├── .npmrc ├── Dockerfile ├── LICENSE ├── README.md ├── app.js ├── bin ├── generate-docs ├── start └── units ├── controller ├── coarse_reverse.js ├── libpostal.js ├── markdownToHtml.js ├── place.js ├── placeholder.js ├── predicates │ ├── hasParsedTextProperties.js │ ├── hasRequestErrors.js │ ├── hasRequestParameter.js │ ├── hasResponseData.js │ ├── hasResultsAtLayers.js │ ├── isAdminOnlyAnalysis.js │ ├── isCoarseReverse.js │ ├── isOnlyNonAdminLayers.js │ ├── isPeliasParse.js │ ├── isRequestLayersAnyAddressRelated.js │ ├── isRequestSourcesIncludesWhosOnFirst.js │ ├── isRequestSourcesOnlyWhosOnFirst.js │ └── isRequestSourcesUndefined.js ├── search.js ├── status.js └── structured_libpostal.js ├── helper ├── TypeMapping.js ├── debug.js ├── decode_gid.js ├── diffPlaces.js ├── fieldValue.js ├── geojsonify.js ├── geojsonify_place_details.js ├── iso3166.js ├── logging.js ├── placeTypes.js ├── stackTraceLine.js ├── type_mapping.js ├── type_mapping_discovery.js └── unicode.js ├── index.js ├── middleware ├── 404.js ├── 500.js ├── access_log.js ├── accuracy.js ├── applyOverrides.js ├── assignLabels.js ├── changeLanguage.js ├── confidenceScore.js ├── confidenceScoreFallback.js ├── confidenceScoreReverse.js ├── cors.js ├── dedupe.js ├── distance.js ├── expandDocument.js ├── geocodeJSON.js ├── headers.js ├── interpolate.js ├── jsonp.js ├── localNamingConventions.js ├── mapFields.js ├── normalizeParentIds.js ├── options.js ├── parseBBox.js ├── requestLanguage.js ├── robots.js ├── sendJSON.js ├── sizeCalculator.js ├── sortResponseData.js ├── trimByGranularity.js └── trimByGranularityStructured.js ├── package.json ├── public ├── apiDoc.md └── attribution.md ├── query ├── address_search_using_ids.js ├── autocomplete.js ├── autocomplete_defaults.js ├── reverse.js ├── reverse_defaults.js ├── search.js ├── search_defaults.js ├── search_pelias_parser.js ├── structured_geocoding.js ├── text_parser.js ├── text_parser_pelias.js └── view │ ├── admin_multi_match_first.js │ ├── admin_multi_match_last.js │ ├── boost_sources_and_layers.js │ ├── focus_point_distance_filter.js │ ├── helper.js │ ├── max_character_count_layer_filter.js │ ├── ngrams_last_token_only.js │ ├── ngrams_last_token_only_multi.js │ ├── ngrams_strict.js │ └── phrase_first_tokens_only.js ├── routes ├── default.js └── v1.js ├── sanitizer ├── PeliasParameterError.js ├── PeliasServiceError.js ├── PeliasTimeoutError.js ├── _address_layer_filter.js ├── _boundary_country.js ├── _boundary_gid.js ├── _categories.js ├── _city_name_standardizer.js ├── _debug.js ├── _default_parameters.js ├── _flag_bool.js ├── _geo_autocomplete.js ├── _geo_common.js ├── _geo_reverse.js ├── _geo_search.js ├── _geonames_deprecation.js ├── _geonames_warnings.js ├── _groups.js ├── _ids.js ├── _iso2_to_iso3.js ├── _request_language.js ├── _single_scalar_parameters.js ├── _size.js ├── _sources_and_layers.js ├── _synthesize_analysis.js ├── _targets.js ├── _text.js ├── _text_pelias_parser.js ├── _tokenizer.js ├── autocomplete.js ├── defer_to_pelias_parser.js ├── nearby.js ├── place.js ├── reverse.js ├── sanitizeAll.js ├── search.js ├── structured_geocoding.js └── wrap.js ├── schema.js ├── service ├── configurations │ ├── Interpolation.js │ ├── Language.js │ ├── Libpostal.js │ ├── PlaceHolder.js │ └── PointInPolygon.js ├── mget.js └── search.js └── test ├── ciao.json ├── ciao ├── 404.coffee ├── CORS │ ├── headers_GET.coffee │ └── headers_OPTIONS.coffee ├── autocomplete │ ├── boundary_gid_invalid.coffee │ ├── boundary_gid_valid.coffee │ ├── boundary_rect_valid.coffee │ ├── focus_point_invalid_lat.coffee │ ├── focus_point_invalid_lon.coffee │ ├── focus_point_missing_lat.coffee │ ├── focus_point_missing_lon.coffee │ ├── focus_point_null_island.coffee │ ├── focus_point_valid_duo.coffee │ ├── language_default.coffee │ ├── language_header_invalid.coffee │ ├── language_header_valid.coffee │ ├── language_querystring_invalid.coffee │ ├── language_querystring_valid.coffee │ ├── layers_alias_address.coffee │ ├── layers_alias_coarse.coffee │ ├── layers_invalid.coffee │ ├── layers_mix_invalid_valid.coffee │ ├── layers_multiple.coffee │ ├── layers_single.coffee │ ├── no_params.coffee │ ├── non_scalar_parameter_array.coffee │ ├── non_scalar_parameter_object.coffee │ ├── size_not_default.coffee │ ├── sources_invalid.coffee │ ├── sources_layers_invalid_combo.coffee │ ├── sources_layers_valid_combo.coffee │ ├── sources_multiple.coffee │ ├── sources_single.coffee │ ├── text_invalid.coffee │ └── text_valid.coffee ├── index.coffee ├── place │ ├── basic_place.coffee │ ├── language_default.coffee │ ├── language_header_invalid.coffee │ ├── language_header_valid.coffee │ ├── language_querystring_invalid.coffee │ ├── language_querystring_valid.coffee │ └── missing_id.coffee ├── reverse │ ├── basic_reverse.coffee │ ├── boundary_circle_invalid_radius.coffee │ ├── boundary_circle_valid_radius.coffee │ ├── boundary_circle_valid_radius_coarse.coffee │ ├── boundary_country_invalid_alpha2.coffee │ ├── boundary_country_invalid_alpha3.coffee │ ├── boundary_country_invalid_iso3166.coffee │ ├── boundary_country_valid_alpha2.coffee │ ├── boundary_country_valid_alpha3.coffee │ ├── boundary_gid_invalid.coffee │ ├── boundary_gid_valid.coffee │ ├── duplicate_parameter_name.coffee │ ├── language_default.coffee │ ├── language_header_invalid.coffee │ ├── language_header_valid.coffee │ ├── language_querystring_invalid.coffee │ ├── language_querystring_valid.coffee │ ├── layers_alias_address.coffee │ ├── layers_alias_coarse.coffee │ ├── layers_invalid.coffee │ ├── layers_mix_invalid_valid.coffee │ ├── layers_multiple.coffee │ ├── layers_single.coffee │ ├── non_scalar_parameter_array.coffee │ ├── non_scalar_parameter_object.coffee │ ├── point_invalid_lat.coffee │ ├── point_invalid_lon.coffee │ ├── point_missing_lat.coffee │ ├── point_missing_lon.coffee │ ├── point_null_island.coffee │ ├── point_valid_duo.coffee │ ├── privacy_false.coffee │ ├── privacy_true.coffee │ ├── size_over_max.coffee │ ├── size_under_min.coffee │ ├── size_valid.coffee │ ├── sources_invalid.coffee │ ├── sources_layers_invalid_combo.coffee │ ├── sources_layers_valid_combo.coffee │ ├── sources_multiple.coffee │ └── sources_single.coffee └── search │ ├── address_parsing.coffee │ ├── boundary_circle_invalid_lat_lon_types.coffee │ ├── boundary_circle_invalid_radius.coffee │ ├── boundary_circle_missing_lat.coffee │ ├── boundary_circle_missing_lon.coffee │ ├── boundary_circle_valid_duo.coffee │ ├── boundary_circle_valid_trio.coffee │ ├── boundary_country_invalid_alpha2.coffee │ ├── boundary_country_invalid_alpha3.coffee │ ├── boundary_country_invalid_iso3166.coffee │ ├── boundary_country_valid_alpha2.coffee │ ├── boundary_country_valid_alpha3.coffee │ ├── boundary_gid_invalid.coffee │ ├── boundary_gid_valid.coffee │ ├── boundary_rect_partially_specified.coffee │ ├── boundary_rect_valid.coffee │ ├── focus_point_invalid_lat.coffee │ ├── focus_point_invalid_lon.coffee │ ├── focus_point_missing_lat.coffee │ ├── focus_point_missing_lon.coffee │ ├── focus_point_null_island.coffee │ ├── focus_point_valid_duo.coffee │ ├── language_default.coffee │ ├── language_header_invalid.coffee │ ├── language_header_valid.coffee │ ├── language_querystring_invalid.coffee │ ├── language_querystring_valid.coffee │ ├── layers_alias_address.coffee │ ├── layers_alias_coarse.coffee │ ├── layers_invalid.coffee │ ├── layers_mix_invalid_valid.coffee │ ├── layers_multiple.coffee │ ├── layers_single.coffee │ ├── no_params.coffee │ ├── non_scalar_parameter_array.coffee │ ├── non_scalar_parameter_object.coffee │ ├── privacy_false.coffee │ ├── privacy_true.coffee │ ├── size_over_max.coffee │ ├── size_under_min.coffee │ ├── size_valid.coffee │ ├── sources_invalid.coffee │ ├── sources_layers_invalid_combo.coffee │ ├── sources_layers_valid_combo.coffee │ ├── sources_multiple.coffee │ ├── sources_single.coffee │ ├── text_invalid.coffee │ └── text_valid.coffee ├── ciao_test_data.js ├── test-pelias-config.json └── unit ├── app.js ├── controller ├── coarse_reverse.js ├── index.js ├── libpostal.js ├── place.js ├── placeholder.js ├── predicates │ ├── hasParsedTextProperties.js │ ├── hasRequestErrors.js │ ├── hasRequestParameter.js │ ├── hasResponseData.js │ ├── hasResultsAtLayers.js │ ├── isAdminOnlyAnalysis.js │ ├── isCoarseReverse.js │ ├── isOnlyNonAdminLayers.js │ ├── isPeliasParse.js │ ├── isRequestLayersAnyAddressRelated.js │ ├── isRequestSourcesIncludesWhosOnFirst.js │ ├── isRequestSourcesOnlyWhosOnFirst.js │ └── isRequestSourcesUndefined.js ├── search.js └── structured_libpostal.js ├── fixture ├── autocomplete_boundary_country.js ├── autocomplete_boundary_gid.js ├── autocomplete_custom_boosts.json ├── autocomplete_linguistic_bbox_san_francisco.js ├── autocomplete_linguistic_circle_san_francisco.js ├── autocomplete_linguistic_final_token.js ├── autocomplete_linguistic_focus.js ├── autocomplete_linguistic_focus_null_island.js ├── autocomplete_linguistic_multiple_tokens.js ├── autocomplete_linguistic_multiple_tokens_complete_numeric.js ├── autocomplete_linguistic_one_char_token.js ├── autocomplete_linguistic_only.js ├── autocomplete_linguistic_three_char_token.js ├── autocomplete_linguistic_two_char_token.js ├── autocomplete_linguistic_with_admin.js ├── autocomplete_single_character_street.js ├── autocomplete_with_category_filtering.js ├── autocomplete_with_layer_filtering.js ├── autocomplete_with_source_filtering.js ├── dedupe_elasticsearch_custom_layer_results.js ├── dedupe_elasticsearch_nonascii_results.js ├── dedupe_elasticsearch_results.js ├── dedupe_only_postalcode_differs.js ├── search_boundary_country.js ├── search_boundary_country_multi.js ├── search_boundary_gid.js ├── search_fallback.js ├── search_fallback_postalcode_only.js ├── search_linguistic_bbox.js ├── search_linguistic_focus.js ├── search_linguistic_focus_bbox.js ├── search_linguistic_focus_null_island.js ├── search_linguistic_only.js ├── search_pelias_parser_boundary_country.js ├── search_pelias_parser_boundary_gid.js ├── search_pelias_parser_full_address.js ├── search_pelias_parser_linguistic_bbox.js ├── search_pelias_parser_linguistic_focus.js ├── search_pelias_parser_linguistic_focus_bbox.js ├── search_pelias_parser_linguistic_focus_null_island.js ├── search_pelias_parser_linguistic_only.js ├── search_pelias_parser_partial_address.js ├── search_pelias_parser_regions_address.js ├── search_pelias_parser_with_category_filtering.js ├── search_pelias_parser_with_source_filtering.js ├── search_with_category_filtering.js ├── search_with_custom_boosts.json └── search_with_source_filtering.js ├── helper ├── TypeMapping.js ├── debug.js ├── decode_gid.js ├── diffPlaces.js ├── fieldValue.js ├── geojsonify.js ├── geojsonify_place_details.js ├── iso3166.js ├── logging.js ├── stackTraceLine.js ├── type_mapping.js └── unicode.js ├── middleware ├── access_log.js ├── accuracy.js ├── applyOverrides.js ├── assignLabels.js ├── changeLanguage.js ├── confidenceScore.js ├── confidenceScoreFallback.js ├── confidenceScoreReverse.js ├── dedupe.js ├── distance.js ├── interpolate.js ├── localNamingConventions.js ├── mapFields.js ├── normalizeParentIds.js ├── parseBBox.js ├── requestLanguage.js ├── sendJSON.js ├── sizeCalculator.js ├── sortResponseData.js ├── trimByGranularity.js └── trimByGranularityStructured.js ├── query ├── MockQuery.js ├── address_search_using_ids.js ├── autocomplete.js ├── autocomplete_defaults.js ├── autocomplete_token_matching_permutations.js ├── autocomplete_with_custom_boosts.js ├── reverse.js ├── reverse_defaults.js ├── search.js ├── search_defaults.js ├── search_pelias_parser.js ├── search_with_custom_boosts.js ├── structured_geocoding.js ├── text_parser.js └── view │ ├── boost_sources_and_layers.js │ └── max_character_count_layer_filter.js ├── run.js ├── sanitizer ├── _address_layer_filter.js ├── _boundary_country.js ├── _boundary_gid.js ├── _categories.js ├── _city_name_standardizer.js ├── _debug.js ├── _default_parameters.js ├── _flag_bool.js ├── _geo_common.js ├── _geo_reverse.js ├── _geonames_deprecation.js ├── _geonames_warnings.js ├── _groups.js ├── _ids.js ├── _iso2_to_iso3.js ├── _layers.js ├── _request_language.js ├── _single_scalar_parameters.js ├── _size.js ├── _sources.js ├── _sources_and_layers.js ├── _synthesize_analysis.js ├── _text.js ├── _text_pelias_parser.js ├── _tokenizer.js ├── _truthy.js ├── autocomplete.js ├── defer_to_pelias_parser.js ├── nearby.js ├── place.js ├── reverse.js ├── sanitizeAll.js ├── search.js ├── structured_geocoding.js └── wrap.js ├── schema.js └── service ├── configurations ├── Interpolation.js ├── Language.js ├── Libpostal.js ├── PlaceHolder.js └── PointInPolygon.js ├── mget.js └── search.js /.dockerignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules/ 31 | 32 | # Typescript v1 declaration files 33 | typings/ 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional eslint cache 39 | .eslintcache 40 | 41 | # Optional REPL history 42 | .node_repl_history 43 | 44 | # Output of 'npm pack' 45 | *.tgz 46 | 47 | # Yarn Integrity file 48 | .yarn-integrity 49 | 50 | # Git 51 | .git 52 | -------------------------------------------------------------------------------- /.github/workflows/_test.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | on: workflow_call 3 | jobs: 4 | unit-tests: 5 | runs-on: '${{ matrix.os }}' 6 | strategy: 7 | matrix: 8 | os: 9 | - ubuntu-22.04 10 | node-version: [ 18.x, 20.x, 22.x ] 11 | steps: 12 | - uses: actions/checkout@v4 13 | - name: 'Install node.js ${{ matrix.node-version }}' 14 | uses: actions/setup-node@v4 15 | with: 16 | node-version: '${{ matrix.node-version }}' 17 | - name: Run unit tests 18 | run: | 19 | [[ -f ./bin/ci-setup ]] && ./bin/ci-setup 20 | npm install 21 | npm run ci 22 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: pull_request 3 | jobs: 4 | unit-tests: 5 | # only run this job for forks 6 | if: github.event.pull_request.head.repo.full_name != github.repository 7 | uses: ./.github/workflows/_test.yml 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .idea 4 | *.log 5 | reports 6 | pids 7 | pelias.json 8 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | reports 4 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "curly": true, 4 | "eqeqeq": true, 5 | "esversion": "2022", 6 | "freeze": true, 7 | "immed": true, 8 | "indent": 2, 9 | "latedef": false, 10 | "newcap": true, 11 | "noarg": true, 12 | "noempty": true, 13 | "nonbsp": true, 14 | "nonew": true, 15 | "plusplus": false, 16 | "quotmark": "single", 17 | "undef": true, 18 | "unused": false, 19 | "maxparams": 4, 20 | "maxdepth": 4, 21 | "maxlen": 140 22 | } 23 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # base image 2 | FROM pelias/baseimage 3 | USER pelias 4 | 5 | # change working dir 6 | ENV WORKDIR /code/pelias/api 7 | RUN mkdir -p ${WORKDIR} 8 | WORKDIR ${WORKDIR} 9 | 10 | # copy package.json first to prevent npm install being rerun when only code changes 11 | COPY ./package.json ${WORKDIR} 12 | RUN npm install 13 | 14 | COPY . ${WORKDIR} 15 | 16 | # only allow containers to succeed if tests pass 17 | RUN npm test 18 | 19 | # start service 20 | CMD [ "./bin/start" ] 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Pelias Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var app = require('express')(); 2 | 3 | var peliasConfig = require( 'pelias-config' ).generate(require('./schema')); 4 | 5 | if( peliasConfig.api.accessLog ){ 6 | app.use( require( './middleware/access_log' ).createAccessLogger( peliasConfig.api.accessLog ) ); 7 | } 8 | 9 | /** ----------------------- pre-processing-middleware ----------------------- **/ 10 | 11 | app.use( require('./middleware/headers') ); 12 | app.use( require('./middleware/cors') ); 13 | app.use( require('./middleware/robots') ); 14 | app.use( require('./middleware/options') ); 15 | app.use( require('./middleware/jsonp') ); 16 | 17 | /** ----------------------- routes ----------------------- **/ 18 | 19 | 20 | var defaultRoutes = require('./routes/default'); 21 | defaultRoutes.addRoutes(app); 22 | 23 | var v1 = require('./routes/v1'); 24 | v1.addRoutes(app, peliasConfig); 25 | 26 | /** ----------------------- error middleware ----------------------- **/ 27 | 28 | app.use( require('./middleware/404') ); 29 | app.use( require('./middleware/500') ); 30 | 31 | module.exports = app; 32 | -------------------------------------------------------------------------------- /bin/generate-docs: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | rm -r docs || true 4 | 5 | curl -s http://localhost:3100/v1 > /dev/null || die "Pelias server does not appear to be running \ 6 | on http://localhost:3100, run npm start in another window before generating docs." 7 | 8 | cd test/ciao 9 | node ../../node_modules/ciao/bin/ciao -c ../ciao.json . -d ../../docs 10 | -------------------------------------------------------------------------------- /bin/start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | exec node index.js 3 | -------------------------------------------------------------------------------- /bin/units: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # run tests with pipefail to avoid false passes 4 | # see https://github.com/pelias/pelias/issues/744 5 | set -euo pipefail 6 | 7 | PELIAS_CONFIG=./test/test-pelias-config.json node --trace-uncaught test/unit/run.js | npx tap-dot 8 | -------------------------------------------------------------------------------- /controller/markdownToHtml.js: -------------------------------------------------------------------------------- 1 | const markdown = require('markdown').markdown; 2 | const fs = require('fs'); 3 | 4 | function setup(peliasConfig, markdownFile) { 5 | 6 | // read markdown 7 | const md = fs.readFileSync(markdownFile, 'utf8').trim(); 8 | 9 | // convert to HTML 10 | const html = ` 11 | 12 | 13 | 14 | 15 | Pelias Geocoder 16 | 17 | 18 | 19 | ${markdown.toHTML(md)} 20 | 21 | `.trim(); 22 | 23 | // send HTML 24 | return function controller(req, res) { 25 | res.send(html); 26 | }; 27 | } 28 | 29 | module.exports = setup; 30 | -------------------------------------------------------------------------------- /controller/predicates/hasParsedTextProperties.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | // "arguments" is only available in long-form function declarations, cannot be shortened to fat arrow syntax 4 | // potential improvement: inject set operator to allow for any/all functionality 5 | module.exports = { 6 | all: function() { 7 | // save off property names for future reference 8 | const properties = _.values(arguments); 9 | 10 | // return true if ALL of the supplied properties are in clean.parsed_text 11 | return request => _.isEmpty( 12 | _.difference( 13 | _.values(properties), 14 | _.keys(_.get(request, ['clean', 'parsed_text'], {})) 15 | ) 16 | ); 17 | 18 | }, 19 | any: function() { 20 | // save off property names for future reference 21 | const properties = _.values(arguments); 22 | 23 | // return true if ANY of the supplied properties are in clean.parsed_text 24 | return request => !_.isEmpty( 25 | _.intersection( 26 | _.values(properties), 27 | _.keys(_.get(request, ['clean', 'parsed_text'], {})) 28 | ) 29 | ); 30 | 31 | } 32 | 33 | }; 34 | -------------------------------------------------------------------------------- /controller/predicates/hasRequestErrors.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Debug = require('../../helper/debug'); 3 | const debugLog = new Debug('controller:predicates:has_request_errors'); 4 | const stackTraceLine = require('../../helper/stackTraceLine'); 5 | 6 | module.exports = (req, res) => { 7 | const has_request_errors = _.get(req, 'errors', []).length > 0; 8 | debugLog.push(req, () => ({ 9 | reply: has_request_errors, 10 | stack_trace: stackTraceLine() 11 | })); 12 | return has_request_errors; 13 | }; 14 | -------------------------------------------------------------------------------- /controller/predicates/hasRequestParameter.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Debug = require('../../helper/debug'); 3 | const debugLog = new Debug('controller:predicates:has_request_parameter'); 4 | const stackTraceLine = require('../../helper/stackTraceLine'); 5 | 6 | // returns true IFF req.clean has a key with the supplied name AND a non-empty value 7 | module.exports = (parameter) => (req, res) => { 8 | const value = _.get(req, ['clean', parameter]); 9 | const has_request_parameter = _.isNumber(value) || !_.isEmpty(value); 10 | 11 | debugLog.push(req, () => ({ 12 | reply: {[parameter]: has_request_parameter}, 13 | stack_trace: stackTraceLine() 14 | })); 15 | 16 | return has_request_parameter; 17 | }; 18 | -------------------------------------------------------------------------------- /controller/predicates/hasResponseData.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Debug = require('../../helper/debug'); 3 | const debugLog = new Debug('controller:predicates:has_response_data'); 4 | const stackTraceLine = require('../../helper/stackTraceLine'); 5 | 6 | module.exports = (request, response) => { 7 | const has_response_data = _.get(response, 'data', []).length > 0; 8 | debugLog.push(request, () => ({ 9 | reply: has_response_data, 10 | stack_trace: stackTraceLine() 11 | })); 12 | return has_response_data; 13 | }; 14 | -------------------------------------------------------------------------------- /controller/predicates/hasResultsAtLayers.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Debug = require('../../helper/debug'); 3 | const debugLog = new Debug('controller:predicates:has_results_at_layers'); 4 | const stackTraceLine = require('../../helper/stackTraceLine'); 5 | // returns a function that returns true if any result.layer is in any of the 6 | // supplied layers using array intersection 7 | 8 | // example usage: determining if the response contains only admin results 9 | 10 | module.exports = (layers) => { 11 | return (request, response) => { 12 | const has_results_at_layers = !_.isEmpty( 13 | _.intersection( 14 | // convert layers to an array if it isn't already one 15 | _.castArray(layers), 16 | // pull all the layer properties into an array 17 | _.map(response.data, _.property('layer')) 18 | )); 19 | 20 | debugLog.push(request, () => ({ 21 | reply: {[layers]: has_results_at_layers}, 22 | stack_trace: stackTraceLine() 23 | })); 24 | return has_results_at_layers; 25 | }; 26 | 27 | }; 28 | -------------------------------------------------------------------------------- /controller/predicates/isAdminOnlyAnalysis.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Debug = require('../../helper/debug'); 3 | const debugLog = new Debug('controller:predicates:is_admin_only_analysis'); 4 | const stackTraceLine = require('../../helper/stackTraceLine'); 5 | 6 | module.exports = (request, response) => { 7 | if (!request.clean.hasOwnProperty('parsed_text')) { 8 | debugLog.push(request, false + ' (no parsed_text)'); 9 | return false; 10 | } 11 | 12 | // return true only if all non-admin properties of parsed_text are empty 13 | const is_admin_only_analysis = ['unit', 'housenumber', 'street', 'query', 'category', 'postalcode'].every((prop) => { 14 | return _.isEmpty(request.clean.parsed_text[prop]); 15 | }); 16 | 17 | debugLog.push(request, () => ({ 18 | reply: is_admin_only_analysis, 19 | stack_trace: stackTraceLine() 20 | })); 21 | return is_admin_only_analysis; 22 | }; 23 | -------------------------------------------------------------------------------- /controller/predicates/isCoarseReverse.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Debug = require('../../helper/debug'); 3 | const debugLog = new Debug('controller:predicates:is_coarse_reverse'); 4 | const non_coarse_layers = ['address', 'street', 'venue']; 5 | const stackTraceLine = require('../../helper/stackTraceLine'); 6 | 7 | module.exports = (req, res) => { 8 | // returns true if layers is undefined, empty, or contains 'address', 'street', or 'venue' 9 | const is_coarse_reverse = !_.isEmpty(req.clean.layers) && 10 | _.isEmpty(_.intersection(req.clean.layers, non_coarse_layers)); 11 | debugLog.push(req, () => ({ 12 | reply: is_coarse_reverse, 13 | stack_trace: stackTraceLine() 14 | })); 15 | return is_coarse_reverse; 16 | }; 17 | -------------------------------------------------------------------------------- /controller/predicates/isOnlyNonAdminLayers.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Debug = require('../../helper/debug'); 3 | const debugLog = new Debug('controller:predicates:is_only_non_admin_layers'); 4 | const stackTraceLine = require('../../helper/stackTraceLine'); 5 | 6 | // return true IFF req.clean.layers is empty OR there are non-venue/address/street layers 7 | module.exports = (req, res) => { 8 | const is_only_non_admin_layers = !_.isEmpty(_.get(req, 'clean.layers', [])) && 9 | _.isEmpty(_.difference(req.clean.layers, ['venue', 'address', 'street'])); 10 | 11 | debugLog.push(req, () => ({ 12 | reply: is_only_non_admin_layers, 13 | stack_trace: stackTraceLine() 14 | })); 15 | return is_only_non_admin_layers; 16 | }; 17 | -------------------------------------------------------------------------------- /controller/predicates/isPeliasParse.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Debug = require('../../helper/debug'); 3 | const debugLog = new Debug('controller:predicates:is_pelias_parse'); 4 | const stackTraceLine = require('../../helper/stackTraceLine'); 5 | 6 | // returns true IFF req.clean.parser is pelias 7 | module.exports = (req, res) => { 8 | const is_pelias_parse = _.get(req, 'clean.parser') === 'pelias'; 9 | debugLog.push(req, () => ({ 10 | reply: is_pelias_parse, 11 | stack_trace: stackTraceLine() 12 | })); 13 | return is_pelias_parse; 14 | }; 15 | -------------------------------------------------------------------------------- /controller/predicates/isRequestLayersAnyAddressRelated.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Debug = require('../../helper/debug'); 3 | const debugLog = new Debug('controller:predicates:is_request_layers_any_address_related'); 4 | const stackTraceLine = require('../../helper/stackTraceLine'); 5 | 6 | // return true if any layers allowed by the query are related to an address query 7 | // this includes address and street but NOT venue, postalcode, admin, and custom layers 8 | module.exports = (req, res) => { 9 | const address_related_layers = ['address', 'street']; 10 | 11 | const request_layers = _.get(req, 'clean.layers', []); 12 | let result; 13 | 14 | // handle case where no layers are specified 15 | if (request_layers.length === 0) { 16 | result = true; 17 | } else { 18 | const request_address_related_layers = _.intersection(request_layers, address_related_layers); 19 | result = request_address_related_layers.length > 0; 20 | } 21 | 22 | debugLog.push(req, () => ({ 23 | reply: result, 24 | stack_trace: stackTraceLine() 25 | })); 26 | 27 | return result; 28 | }; 29 | -------------------------------------------------------------------------------- /controller/predicates/isRequestSourcesIncludesWhosOnFirst.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Debug = require('../../helper/debug'); 3 | const debugLog = new Debug('controller:predicates:is_request_sources_includes_whosonfirst'); 4 | const stackTraceLine = require('../../helper/stackTraceLine'); 5 | 6 | // returns true IFF 'whosonfirst' is included in the requested sources 7 | module.exports = (req, res) => { 8 | const is_request_sources_includes_whosonfirst = _.get(req, 'clean.sources', []).includes( 9 | 'whosonfirst' 10 | ); 11 | debugLog.push(req, () => ({ 12 | reply: is_request_sources_includes_whosonfirst, 13 | stack_trace: stackTraceLine() 14 | })); 15 | return is_request_sources_includes_whosonfirst; 16 | }; 17 | -------------------------------------------------------------------------------- /controller/predicates/isRequestSourcesOnlyWhosOnFirst.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Debug = require('../../helper/debug'); 3 | const debugLog = new Debug('controller:predicates:is_request_sources_only_whosonfirst'); 4 | const stackTraceLine = require('../../helper/stackTraceLine'); 5 | 6 | // returns true IFF 'whosonfirst' is the only requested source 7 | module.exports = (req, res) => { 8 | const is_request_sources_only_whosonfirst = _.isEqual( 9 | _.get(req, 'clean.sources', []), 10 | ['whosonfirst'] 11 | ); 12 | debugLog.push(req, () => ({ 13 | reply: is_request_sources_only_whosonfirst, 14 | stack_trace: stackTraceLine() 15 | })); 16 | return is_request_sources_only_whosonfirst; 17 | }; 18 | -------------------------------------------------------------------------------- /controller/predicates/isRequestSourcesUndefined.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Debug = require('../../helper/debug'); 3 | const debugLog = new Debug('controller:predicates:is_request_sources_undefined'); 4 | const stackTraceLine = require('../../helper/stackTraceLine'); 5 | 6 | // returns true IFF there are no requested sources 7 | module.exports = (req, res) => { 8 | const is_request_sources_undefined = _.isEmpty( 9 | _.get(req, 'clean.sources') 10 | ); 11 | debugLog.push(req, () => ({ 12 | reply: is_request_sources_undefined, 13 | stack_trace: stackTraceLine() 14 | })); 15 | return is_request_sources_undefined; 16 | }; 17 | -------------------------------------------------------------------------------- /controller/status.js: -------------------------------------------------------------------------------- 1 | module.exports = function controller( req, res, next) { 2 | res.send('status: ok'); 3 | }; 4 | -------------------------------------------------------------------------------- /helper/debug.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | class Debug { 4 | constructor(moduleName) { 5 | this.name = moduleName || 'unnamed module'; 6 | } 7 | 8 | static isEnabled(req) { 9 | return _.get(req, 'clean.enableDebug') === true; 10 | } 11 | 12 | static validMessage(msg) { 13 | return _.isString(msg) && !_.isEmpty(msg); 14 | } 15 | 16 | push(req, value) { 17 | if (!Debug.isEnabled(req)) { return; } 18 | 19 | if (!_.isArray(req.debug)) { req.debug = []; } 20 | 21 | if (_.isFunction(value)) { 22 | req.debug.push({ [this.name]: value() }); 23 | } else { 24 | req.debug.push({ [this.name]: value }); 25 | } 26 | } 27 | 28 | beginTimer(req, message) { 29 | if (!Debug.isEnabled(req)) { return; } 30 | 31 | if (Debug.validMessage(message)) { 32 | this.push(req, `Timer Began. ${message}`); 33 | } else { 34 | this.push(req, `Timer Began.`); 35 | } 36 | 37 | return Date.now(); 38 | } 39 | 40 | stopTimer(req, timer, message) { 41 | if (!Debug.isEnabled(req)) { return; } 42 | 43 | // measure elapsed duration 44 | const elapsed = _.isFinite(timer) ? (Date.now() - timer) : -1; 45 | 46 | if (Debug.validMessage(message)) { 47 | this.push(req, `Timer Stopped. ${elapsed} ms. ${message}`); 48 | } else { 49 | this.push(req, `Timer Stopped. ${elapsed} ms`); 50 | } 51 | } 52 | } 53 | 54 | module.exports = Debug; 55 | -------------------------------------------------------------------------------- /helper/decode_gid.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | // helper method to decode a GID from a string 4 | // for additional validation see sanitizer/_ids.js 5 | 6 | function decodeGID(gid) { 7 | const parts = gid.split(':'); 8 | 9 | if ( parts.length < 3 ) { 10 | return; 11 | } 12 | 13 | const source = parts[0].toLowerCase(); 14 | const layer = parts[1].toLowerCase(); 15 | 16 | // empty strings and other invalid values are expected to be handled by the caller 17 | return { 18 | source: source, 19 | layer: layer, 20 | id: parts.slice(2).join(':'), 21 | }; 22 | } 23 | 24 | module.exports = decodeGID; 25 | -------------------------------------------------------------------------------- /helper/fieldValue.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | function getStringValue(property) { 4 | // numeric value, cast to string 5 | if (_.isNumber(property)) { 6 | return _.toString(property); 7 | } 8 | 9 | // isEmpty check works for all types of values: strings, arrays, objects 10 | if (_.isEmpty(property)) { 11 | return ''; 12 | } 13 | 14 | if (_.isString(property)) { 15 | return property; 16 | } 17 | 18 | // array value, take first item in array (at this time only used for admin & name values) 19 | if (_.isArray(property)) { 20 | return property[0]; 21 | } 22 | 23 | return _.toString(property); 24 | } 25 | 26 | function getArrayValue(property) { 27 | // numeric value, cast to array 28 | if (_.isNumber(property)) { 29 | return [property]; 30 | } 31 | 32 | // isEmpty check works for all types of values: strings, arrays, objects 33 | if (_.isEmpty(property)) { 34 | return []; 35 | } 36 | 37 | if (_.isArray(property)) { 38 | return property; 39 | } 40 | 41 | return [property]; 42 | } 43 | 44 | module.exports.getStringValue = getStringValue; 45 | module.exports.getArrayValue = getArrayValue; 46 | -------------------------------------------------------------------------------- /helper/logging.js: -------------------------------------------------------------------------------- 1 | var fieldsToRemove = ['text', 'focus.point.lat', 'focus.point.lon', 2 | 'boundary.circle.lat', 'boundary.circle.lon', 'point.lat', 'point.lon']; 3 | 4 | function isDNT(req) { 5 | if (!req.headers) { 6 | return false; 7 | } 8 | return req.headers.DNT || req.headers.dnt || req.headers.do_not_track; 9 | } 10 | 11 | function removeFields(query) { 12 | fieldsToRemove.forEach(function(field) { 13 | if (query[field]) { 14 | query[field] = '[removed]'; 15 | } 16 | }); 17 | 18 | return query; 19 | } 20 | 21 | module.exports = { 22 | isDNT: isDNT, 23 | removeFields: removeFields 24 | }; 25 | -------------------------------------------------------------------------------- /helper/placeTypes.js: -------------------------------------------------------------------------------- 1 | // a list of administrative placetypes 2 | // TODO: turn this into configuration (https://github.com/pelias/api/issues/1161) 3 | module.exports = [ 4 | 'ocean', 5 | 'marinearea', 6 | 'continent', 7 | 'empire', 8 | 'country', 9 | 'dependency', 10 | 'macroregion', 11 | 'region', 12 | 'macrocounty', 13 | 'county', 14 | 'localadmin', 15 | 'locality', 16 | 'borough', 17 | 'neighbourhood', 18 | 'postalcode' 19 | ]; 20 | -------------------------------------------------------------------------------- /helper/stackTraceLine.js: -------------------------------------------------------------------------------- 1 | module.exports = () => { 2 | const stack = new Error().stack.split('\n'); 3 | let targetLine; 4 | 5 | stack.forEach((line) => { 6 | if(line.indexOf('at controller') !== -1) { 7 | targetLine = line.trim(); 8 | } 9 | }); 10 | return targetLine; 11 | }; 12 | -------------------------------------------------------------------------------- /helper/type_mapping.js: -------------------------------------------------------------------------------- 1 | const TypeMapping = require('./TypeMapping'); 2 | 3 | // instantiate a singleton type mapping 4 | // loading normal defaults from pelias-config happens now 5 | // updating that config from Elasticsearch happens later 6 | // before the webserver is started 7 | var tm = new TypeMapping(); 8 | tm.loadFromConfig(); 9 | 10 | // export singleton 11 | module.exports = tm; 12 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const logger = require('pelias-logger').get('api'); 2 | const type_mapping = require('./helper/type_mapping'); 3 | 4 | const app = require('./app'), 5 | port = ( process.env.PORT || 3100 ), 6 | host = ( process.env.HOST || undefined ); 7 | 8 | let server; 9 | 10 | // load Elasticsearch type mappings before starting web server 11 | type_mapping.load(() => { 12 | server = app.listen( port, host, () => { 13 | // ask server for the actual address and port its listening on 14 | const listenAddress = server.address(); 15 | logger.info( `pelias is now running on http://${listenAddress.address}:${listenAddress.port}` ); 16 | }); 17 | }); 18 | 19 | function exitHandler() { 20 | logger.info('Pelias API shutting down'); 21 | 22 | server.close(); 23 | } 24 | 25 | process.on('SIGINT', exitHandler); 26 | process.on('SIGTERM', exitHandler); 27 | -------------------------------------------------------------------------------- /middleware/404.js: -------------------------------------------------------------------------------- 1 | // handle not found errors 2 | function middleware(req, res) { 3 | res.header('Cache-Control','public'); 4 | res.status(404).json({ error: 'not found: invalid path' }); 5 | } 6 | 7 | module.exports = middleware; 8 | -------------------------------------------------------------------------------- /middleware/500.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const logger = require('pelias-logger').get('api'); 3 | 4 | // handle application errors 5 | function middleware(err, req, res) { 6 | 7 | logger.error( 'Error: `%s`. Stack trace: `%s`.', err, err.stack ); 8 | 9 | if( res.statusCode < 400 ){ 10 | logger.info( 'status code changed from', res.statusCode, 'to 500' ); 11 | res.status(500); 12 | } 13 | 14 | // set error message 15 | const error = (err && err.message) ? err.message : err; 16 | let msg = 'internal server error'; 17 | if (_.isString(error) && !_.isEmpty(error)) { 18 | msg = error; 19 | } 20 | 21 | // send response 22 | res.header('Cache-Control','public'); 23 | res.json({ error: msg }); 24 | } 25 | 26 | module.exports = middleware; 27 | -------------------------------------------------------------------------------- /middleware/applyOverrides.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const logger = require('pelias-logger').get('api'); 3 | const codec = require('pelias-model').codec; 4 | 5 | function setup() { 6 | return applyOverrides; 7 | } 8 | 9 | function applyOverrides(req, res, next) { 10 | // do nothing if no result data set 11 | if (!res || !res.data) { 12 | return next(); 13 | } 14 | 15 | res.data = res.data.map(overrideOneRecord); 16 | 17 | next(); 18 | } 19 | 20 | /* 21 | * Rename the fields in one record 22 | */ 23 | function overrideOneRecord(place) { 24 | if (_.has(place, 'addendum.override')) { 25 | try { 26 | const overrideData = codec.decode(place.addendum.override); 27 | place = _.merge(place, overrideData); 28 | } catch (err) { 29 | logger.error('Invalid addendum override json string:', place.addendum); 30 | } 31 | 32 | delete place.addendum.override; 33 | 34 | if (_.isEmpty(place.addendum)) { 35 | delete place.addendum; 36 | } 37 | } 38 | return place; 39 | } 40 | 41 | module.exports = setup; 42 | -------------------------------------------------------------------------------- /middleware/assignLabels.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | const defaultLabelGenerator = require('pelias-labels'); 4 | 5 | function setup(labelGenerator) { 6 | function middleware(req, res, next) { 7 | return assignLabel(req, res, next, labelGenerator || defaultLabelGenerator); 8 | } 9 | 10 | return middleware; 11 | } 12 | 13 | function assignLabel(req, res, next, labelGenerator) { 14 | 15 | // do nothing if there's nothing to process 16 | if (!res || !res.data) { 17 | return next(); 18 | } 19 | 20 | res.data.forEach(function (result) { 21 | result.label = labelGenerator(result, _.get(req, 'clean.lang.iso6393')); 22 | }); 23 | 24 | next(); 25 | } 26 | 27 | module.exports = setup; 28 | -------------------------------------------------------------------------------- /middleware/cors.js: -------------------------------------------------------------------------------- 1 | function middleware(req, res, next){ 2 | res.header('Access-Control-Allow-Origin', '*'); 3 | res.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); 4 | res.header('Access-Control-Allow-Headers', 'X-Requested-With,content-type'); 5 | next(); 6 | } 7 | 8 | module.exports = middleware; 9 | -------------------------------------------------------------------------------- /middleware/distance.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const geolib = require('geolib'); 3 | 4 | function setup(prefix) { 5 | return function (req, res, next) { 6 | const opts = { 7 | prefix: prefix || 'point.' 8 | }; 9 | return computeDistances(req, res, next, opts); 10 | }; 11 | } 12 | 13 | function computeDistances(req, res, next, opts) { 14 | 15 | // do nothing if no result data set 16 | if (!res || !res.data) { 17 | return next(); 18 | } 19 | 20 | if (!(_.isFinite(req.clean[opts.prefix + 'lat']) && 21 | _.isFinite(req.clean[opts.prefix + 'lon']))) { 22 | return next(); 23 | } 24 | 25 | var point = { 26 | latitude: req.clean[opts.prefix + 'lat'], 27 | longitude: req.clean[opts.prefix + 'lon'] 28 | }; 29 | 30 | res.data.forEach(function (place) { 31 | // the result of getDistance is in meters, so convert to kilometers 32 | place.distance = geolib.getDistance( 33 | point, 34 | { latitude: place.center_point.lat, longitude: place.center_point.lon } 35 | ) / 1000; 36 | }); 37 | 38 | next(); 39 | } 40 | 41 | 42 | module.exports = setup; 43 | -------------------------------------------------------------------------------- /middleware/headers.js: -------------------------------------------------------------------------------- 1 | var pkg = require('../package'); 2 | 3 | function middleware(req, res, next){ 4 | res.header('Charset','utf8'); 5 | res.header('Cache-Control','public'); 6 | res.header('Server', 'Pelias/'+pkg.version); 7 | res.header('X-Powered-By', 'pelias'); 8 | next(); 9 | } 10 | 11 | module.exports = middleware; 12 | -------------------------------------------------------------------------------- /middleware/jsonp.js: -------------------------------------------------------------------------------- 1 | function middleware(req, res, next){ 2 | 3 | // store old json function 4 | var json = res.json.bind(res); 5 | 6 | // replace with jsonp aware function 7 | res.json = function( data ){ 8 | 9 | // jsonp 10 | if( req.query && req.query.callback ){ 11 | res.header('Content-type','application/javascript'); 12 | return res.send( req.query.callback + '('+ JSON.stringify( data ) + ');' ); 13 | } 14 | 15 | // regular json 16 | res.header('Content-type','application/json'); 17 | return json( data ); 18 | }; 19 | 20 | next(); 21 | } 22 | 23 | module.exports = middleware; -------------------------------------------------------------------------------- /middleware/options.js: -------------------------------------------------------------------------------- 1 | /** 2 | this functionality is required by CORS as the browser will send an 3 | HTTP OPTIONS request before performing the CORS request. 4 | 5 | if the OPTIONS request returns a non-200 status code then the 6 | transaction will fail. 7 | **/ 8 | 9 | function middleware(req, res, next){ 10 | if( req.method === 'OPTIONS' ){ 11 | res.sendStatus(200); 12 | } else { 13 | next(); 14 | } 15 | } 16 | 17 | module.exports = middleware; 18 | -------------------------------------------------------------------------------- /middleware/parseBBox.js: -------------------------------------------------------------------------------- 1 | var logger = require('pelias-logger').get('api'); 2 | 3 | /** 4 | * Parses the bounding box property in docs, if one is found 5 | */ 6 | 7 | function setup() { 8 | return function (req, res, next) { 9 | // do nothing if no result data set 10 | if (!res || !res.data) { 11 | return next(); 12 | } 13 | 14 | res.data = res.data.map(parseBBox); 15 | 16 | next(); 17 | }; 18 | } 19 | 20 | /* 21 | * Parse the bbox property and form an object 22 | */ 23 | function parseBBox(place) { 24 | 25 | if (place && place.bounding_box) { 26 | try { 27 | place.bounding_box = JSON.parse(place.bounding_box); 28 | } 29 | catch (err) { 30 | logger.error('Invalid bounding_box json string:', place); 31 | delete place.bounding_box; 32 | } 33 | } 34 | 35 | return place; 36 | } 37 | 38 | module.exports = setup; 39 | -------------------------------------------------------------------------------- /middleware/robots.js: -------------------------------------------------------------------------------- 1 | // Prevent search engines from attempting to index the API 2 | // https://developers.google.com/search/reference/robots_meta_tag#xrobotstag 3 | 4 | function middleware(req, res, next) { 5 | res.header('X-Robots-Tag', 'none'); 6 | next(); 7 | } 8 | 9 | module.exports = middleware; -------------------------------------------------------------------------------- /middleware/sizeCalculator.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | 3 | var SIZE_PADDING = 2; 4 | 5 | var DEFAULT_MIN_QUERY_SIZE = 20; 6 | 7 | /** 8 | * Utility for calculating query result size 9 | * incorporating padding for dedupe process 10 | */ 11 | function setup(min_size) { 12 | if (min_size === undefined) { 13 | min_size = DEFAULT_MIN_QUERY_SIZE; 14 | } 15 | 16 | return function setQuerySize(req, res, next) { 17 | if (_.isUndefined(req.clean) || _.isUndefined(req.clean.size)) { 18 | return next(); 19 | } 20 | 21 | req.clean.querySize = calculateSize(req.clean.size, min_size); 22 | next(); 23 | }; 24 | } 25 | 26 | /** 27 | * Add padding or set to 1 28 | * 29 | * @param {number} cleanSize 30 | * @returns {number} 31 | */ 32 | function calculateSize(cleanSize, min_size) { 33 | return Math.max(min_size, cleanSize * SIZE_PADDING); 34 | } 35 | 36 | module.exports = setup; 37 | -------------------------------------------------------------------------------- /middleware/sortResponseData.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const logger = require('pelias-logger').get('api'); 3 | 4 | function setup(comparator, should_execute) { 5 | function middleware(req, res, next) { 6 | // bail early if req/res don't pass conditions for execution or there's no data to sort 7 | if (!should_execute(req, res) || _.isEmpty(res.data)) { 8 | return next(); 9 | } 10 | 11 | // capture the pre-sort order 12 | const presort_order = res.data.map(_.property('_id')); 13 | 14 | // stable operates on array in place 15 | res.data.sort(comparator(req.clean)); 16 | 17 | // capture the post-sort order 18 | const postsort_order = res.data.map(_.property('_id')); 19 | 20 | // log it for debugging purposes 21 | logger.debug([ 22 | `req.clean: ${JSON.stringify(req.clean)}`, 23 | `pre-sort: [${presort_order}]`, 24 | `post-sort: [${postsort_order}]` 25 | ].join(', ')); 26 | 27 | next(); 28 | } 29 | 30 | return middleware; 31 | 32 | } 33 | 34 | module.exports = setup; 35 | -------------------------------------------------------------------------------- /public/apiDoc.md: -------------------------------------------------------------------------------- 1 | # Pelias API 2 | ### Version: [1.0](https://github.com/pelias/api/releases) 3 | 4 | ## [View our documentation on GitHub](https://github.com/pelias/documentation/) 5 | -------------------------------------------------------------------------------- /public/attribution.md: -------------------------------------------------------------------------------- 1 | # Pelias API 2 | ### Version: [1.0](https://github.com/pelias/api/releases) 3 | 4 | ## Attribution 5 | * Geocoding by [Pelias](https://pelias.io). 6 | * Data from 7 | * [OpenStreetMap](http://www.openstreetmap.org/copyright) © OpenStreetMap contributors under [ODbL](http://opendatacommons.org/licenses/odbl/). Also see the [OSM Geocoding Guidelines](https://wiki.osmfoundation.org/wiki/Licence/Community_Guidelines/Geocoding_-_Guideline) for acceptable use. 8 | * [OpenAddresses](http://openaddresses.io) under [various public-domain and share-alike licenses](http://results.openaddresses.io/) 9 | * [GeoNames](http://www.geonames.org/) under [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/) 10 | * [WhosOnFirst](https://www.whosonfirst.org/) under [various CC-BY or CC-0 equivalent licenses](https://whosonfirst.org/docs/licenses/) 11 | -------------------------------------------------------------------------------- /query/view/focus_point_distance_filter.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const type_mapping = require('../../helper/type_mapping'); 3 | 4 | module.exports = function( vs ) { 5 | 6 | if ( !vs.isset('input:name') || 7 | !vs.isset('centroid:field') || 8 | !vs.isset('focus:point:lat') || 9 | !vs.isset('focus:point:lon') ) { 10 | return null; 11 | } 12 | 13 | const text_length = vs.var('input:name').get().length; 14 | 15 | if (text_length > 8) { 16 | return null; 17 | } 18 | 19 | const all_layers_except_address_and_street = _.without(type_mapping.layers, 'address', 'street'); 20 | 21 | const length_to_distance_mapping = { 22 | 1: '100km', 23 | 2: '200km', 24 | 3: '400km', 25 | 4: '600km', 26 | 5: '900km', 27 | 6: '1200km', 28 | 7: '1600km', 29 | 8: '2000km' 30 | }; 31 | 32 | const query = { 33 | bool: { 34 | minimum_should_match: 1, 35 | should: [{ 36 | terms: { 37 | layer: all_layers_except_address_and_street 38 | } 39 | },{ 40 | geo_distance: { 41 | distance: length_to_distance_mapping[text_length], 42 | distance_type: 'plane', 43 | [vs.var('centroid:field')]: { 44 | lat: vs.var('focus:point:lat'), 45 | lon: vs.var('focus:point:lon') 46 | } 47 | } 48 | }] 49 | } 50 | }; 51 | 52 | return query; 53 | }; 54 | -------------------------------------------------------------------------------- /query/view/helper.js: -------------------------------------------------------------------------------- 1 | function toMultiFields(baseField, suffix) { 2 | return [baseField, toSingleField(baseField, suffix)]; 3 | } 4 | 5 | function toSingleField(baseField, suffix) { 6 | // baseField looks like phrase.default or name.default; suffix looks like en, fr.... 7 | const parts = baseField.split('.'); 8 | parts[parts.length - 1] = suffix; 9 | return parts.join('.'); 10 | } 11 | 12 | module.exports = { toMultiFields, toSingleField }; -------------------------------------------------------------------------------- /query/view/ngrams_last_token_only.js: -------------------------------------------------------------------------------- 1 | var peliasQuery = require('pelias-query'), 2 | ngrams_strict = require('./ngrams_strict'); 3 | /** 4 | Ngrams view which trims the 'input:name' and only uses the LAST TOKEN. 5 | 6 | eg. if the input was "100 foo str", then 'input:name' would only be 'str' 7 | note: it is assumed that the rest of the input is matched using another view. 8 | 9 | code notes: this view makes a copy of the $vs object in order to change their 10 | values without mutating the original values, which may be expected in their 11 | unaltered form by other views. 12 | **/ 13 | 14 | module.exports = function( vs ){ 15 | 16 | // get a copy of the *tokens_incomplete* tokens produced from the input:name 17 | var tokens = vs.var('input:name:tokens_incomplete').get(); 18 | 19 | // no valid tokens to use, fail now, don't render this view. 20 | if( !tokens || tokens.length < 1 ){ return null; } 21 | 22 | // make a copy Vars so we don't mutate the original 23 | var vsCopy = new peliasQuery.Vars( vs.export() ); 24 | 25 | // set the 'name' variable in the copy to only the last token 26 | vsCopy.var('input:name').set( tokens.join(' ') ); 27 | 28 | // return the view rendered using the copy 29 | return { 30 | 'constant_score': { 31 | 'filter': ngrams_strict( vsCopy ) 32 | } 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /query/view/ngrams_strict.js: -------------------------------------------------------------------------------- 1 | const peliasQuery = require('pelias-query'); 2 | const toMultiFields = require('./helper').toMultiFields; 3 | 4 | /** 5 | Ngrams view with the additional properties to enable: 6 | type:phrase -> tokens MUST appear in the same order in BOTH query and index 7 | operator:and -> ALL tokens are mandatory, missing any single token will cause 8 | a query failure. 9 | **/ 10 | 11 | module.exports = function( vs ){ 12 | 13 | // validate required params 14 | if( !vs.isset('phrase:slop') ){ 15 | return null; 16 | } 17 | 18 | vs.var('multi_match:ngrams_strict:input', vs.var('input:name').get()); 19 | vs.var('multi_match:ngrams_strict:fields', toMultiFields(vs.var('ngram:field').get(), vs.var('lang').get())); 20 | 21 | vs.var('multi_match:ngrams_strict:analyzer', vs.var('ngram:analyzer').get()); 22 | vs.var('multi_match:ngrams_strict:slop', vs.var('phrase:slop').get()); 23 | vs.var('multi_match:ngrams_strict:boost', vs.var('ngram:boost').get()); 24 | 25 | return peliasQuery.view.leaf.multi_match('ngrams_strict')(vs); 26 | }; 27 | -------------------------------------------------------------------------------- /query/view/phrase_first_tokens_only.js: -------------------------------------------------------------------------------- 1 | const peliasQuery = require('pelias-query'); 2 | const toMultiFields = require('./helper').toMultiFields; 3 | 4 | /** 5 | Phrase view which trims the 'input:name' and uses ALL BUT the last token. 6 | 7 | eg. if the input was "100 foo str", then 'input:name' would only be '100 foo' 8 | note: it is assumed that the rest of the input is matched using another view. 9 | **/ 10 | 11 | module.exports = function( vs ){ 12 | const view_name = 'first_tokens_only'; 13 | // get a copy of the *complete* tokens produced from the input:name 14 | const tokens = vs.var('input:name:tokens_complete').get(); 15 | 16 | // no valid tokens to use, fail now, don't render this view. 17 | if( !tokens || tokens.length < 1 ){ return null; } 18 | 19 | // set the 'input' variable to all but the last token 20 | vs.var(`multi_match:${view_name}:input`).set( tokens.join(' ') ); 21 | vs.var(`multi_match:${view_name}:fields`).set(toMultiFields(vs.var('phrase:field').get(), vs.var('lang').get())); 22 | 23 | vs.var(`multi_match:${view_name}:analyzer`).set(vs.var('phrase:analyzer').get()); 24 | vs.var(`multi_match:${view_name}:boost`).set(vs.var('phrase:boost').get()); 25 | vs.var(`multi_match:${view_name}:slop`).set(vs.var('phrase:slop').get()); 26 | 27 | return peliasQuery.view.leaf.multi_match(view_name)( vs ); 28 | }; 29 | -------------------------------------------------------------------------------- /routes/default.js: -------------------------------------------------------------------------------- 1 | // set up routes that are outside any particular API version 2 | function addRoutes(app) { 3 | function redirectToV1(req, res, next) { 4 | res.redirect(301, '/v1'); 5 | } 6 | 7 | // default root URL traffic to V1 root 8 | // which has a link to the readme and other helpful info 9 | app.get('/', redirectToV1); 10 | } 11 | 12 | module.exports.addRoutes = addRoutes; 13 | -------------------------------------------------------------------------------- /sanitizer/PeliasParameterError.js: -------------------------------------------------------------------------------- 1 | class PeliasParameterError extends Error { 2 | constructor(message = '') { 3 | super(message); 4 | } 5 | 6 | toString() { 7 | return this.message; 8 | } 9 | 10 | toJSON() { 11 | return this.message; 12 | } 13 | } 14 | 15 | module.exports = PeliasParameterError; 16 | -------------------------------------------------------------------------------- /sanitizer/PeliasServiceError.js: -------------------------------------------------------------------------------- 1 | class PeliasServiceError extends Error { 2 | constructor(message = '') { 3 | super(message); 4 | } 5 | 6 | toString() { 7 | return this.message; 8 | } 9 | 10 | toJSON() { 11 | return this.message; 12 | } 13 | } 14 | 15 | module.exports = PeliasServiceError; 16 | -------------------------------------------------------------------------------- /sanitizer/PeliasTimeoutError.js: -------------------------------------------------------------------------------- 1 | // Error subclass for timeouts contacting Pelias services 2 | class PeliasTimeoutError extends Error { 3 | constructor(message = '') { 4 | super(message); 5 | } 6 | 7 | toString() { 8 | return this.message; 9 | } 10 | 11 | toJSON() { 12 | return this.message; 13 | } 14 | } 15 | 16 | module.exports = PeliasTimeoutError; 17 | -------------------------------------------------------------------------------- /sanitizer/_boundary_gid.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | function _sanitize(raw, clean) { 4 | // error & warning messages 5 | var messages = { errors: [], warnings: [] }; 6 | 7 | // target input param 8 | var boundary_gid = raw['boundary.gid']; 9 | 10 | // param 'boundary.gid' is optional and should not 11 | // error when simply not set by the user 12 | // must be valid string 13 | if (!_.isNil(boundary_gid)) { 14 | if (!_.isString(boundary_gid) || _.isEmpty(boundary_gid)) { 15 | messages.errors.push('boundary.gid is not a string'); 16 | } 17 | else { 18 | // boundary gid should take the form of source:layer:id, 19 | // or source:layer:type:id for OpenStreetMap ids 20 | var fields = boundary_gid.split(':').filter(function(x) { 21 | // keep only non-empty values 22 | return x !== ''; 23 | }); 24 | if ( _.inRange(fields.length, 3, 5) ) { 25 | clean['boundary.gid'] = fields.slice(2).join(':'); 26 | } 27 | else { 28 | messages.errors.push(boundary_gid + ' does not follow source:layer:id format'); 29 | } 30 | } 31 | } 32 | 33 | return messages; 34 | } 35 | 36 | function _expected(){ 37 | return [{ name: 'boundary.gid' }]; 38 | } 39 | 40 | module.exports = () => ({ 41 | sanitize: _sanitize, 42 | expected: _expected 43 | }); 44 | -------------------------------------------------------------------------------- /sanitizer/_geo_autocomplete.js: -------------------------------------------------------------------------------- 1 | var geo_common = require ('./_geo_common'); 2 | var LAT_LON_IS_REQUIRED = false; 3 | var RECT_IS_REQUIRED = false; 4 | var CIRCLE_IS_REQUIRED = false; 5 | 6 | // validate inputs, convert types and apply defaults 7 | function _sanitize( raw, clean ){ 8 | 9 | // error & warning messages 10 | var messages = { errors: [], warnings: [] }; 11 | 12 | try { 13 | geo_common.sanitize_point( 'focus.point', clean, raw, LAT_LON_IS_REQUIRED ); 14 | geo_common.sanitize_rect( 'boundary.rect', clean, raw, RECT_IS_REQUIRED ); 15 | geo_common.sanitize_circle( 'boundary.circle', clean, raw, CIRCLE_IS_REQUIRED ); 16 | } 17 | catch (err) { 18 | messages.errors.push( err.message ); 19 | } 20 | 21 | return messages; 22 | } 23 | 24 | function _expected(){ 25 | return [ 26 | { name: 'focus.point.lat' }, 27 | { name: 'focus.point.lon' }, 28 | { name: 'boundary.circle.lon'}, 29 | { name: 'boundary.circle.lat'}, 30 | { name: 'boundary.circle.radius'}, 31 | { name: 'boundary.rect.min_lat' }, 32 | { name: 'boundary.rect.max_lat' }, 33 | { name: 'boundary.rect.min_lon' }, 34 | { name: 'boundary.rect.max_lon' }]; 35 | } 36 | 37 | module.exports = () => ({ 38 | sanitize: _sanitize, 39 | expected: _expected 40 | }); 41 | -------------------------------------------------------------------------------- /sanitizer/_geo_search.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const geo_common = require ('./_geo_common'); 3 | 4 | const LAT_LON_IS_REQUIRED = false; 5 | const RECT_IS_REQUIRED = false; 6 | const CIRCLE_IS_REQUIRED = false; 7 | 8 | // validate inputs, convert types and apply defaults 9 | function _sanitize( raw, clean ){ 10 | 11 | // error & warning messages 12 | var messages = { errors: [], warnings: [] }; 13 | 14 | try { 15 | geo_common.sanitize_point( 'focus.point', clean, raw, LAT_LON_IS_REQUIRED ); 16 | geo_common.sanitize_rect( 'boundary.rect', clean, raw, RECT_IS_REQUIRED ); 17 | geo_common.sanitize_circle( 'boundary.circle', clean, raw, CIRCLE_IS_REQUIRED ); 18 | } 19 | catch (err) { 20 | messages.errors.push( err.message ); 21 | } 22 | 23 | return messages; 24 | } 25 | 26 | function _expected(){ 27 | return [ 28 | { name: 'focus.point.lat' }, 29 | { name: 'focus.point.lon' }, 30 | { name: 'boundary.circle.lon'}, 31 | { name: 'boundary.circle.lat'}, 32 | { name: 'boundary.circle.radius'}, 33 | { name: 'boundary.rect.min_lat' }, 34 | { name: 'boundary.rect.max_lat' }, 35 | { name: 'boundary.rect.min_lon' }, 36 | { name: 'boundary.rect.max_lon' }]; 37 | } 38 | 39 | module.exports = () => ({ 40 | sanitize: _sanitize, 41 | expected: _expected 42 | }); 43 | -------------------------------------------------------------------------------- /sanitizer/_geonames_deprecation.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | /** 4 | * Now that we have the pip-service, we have stopped supporting returning Geonames for coarse reverse. 5 | * 6 | * However, until the `/nearby` endpoint is finalized, we still want to support Geonames for 7 | * _non-coarse_ reverse. 8 | **/ 9 | 10 | const coarse_reverse_message ='coarse /reverse does not support geonames. See https://github.com/pelias/pelias/issues/675 for more info'; 11 | 12 | function _sanitize( raw, clean ) { 13 | // error & warning messages 14 | const messages = { errors: [], warnings: [] }; 15 | 16 | // return taking no action unless this is a coarse-only reverse request 17 | const non_coarse_layers = ['address', 'street', 'venue']; 18 | const is_coarse_reverse = !_.isEmpty(clean.layers) && 19 | _.isEmpty(_.intersection(clean.layers, non_coarse_layers)); 20 | if (!is_coarse_reverse) { 21 | return messages; 22 | } 23 | 24 | if (_.isEqual(clean.sources, ['geonames']) || _.isEqual(clean.sources, ['gn'])) { 25 | messages.errors.push(coarse_reverse_message); 26 | 27 | } else if (_.includes(clean.sources, 'geonames') || _.includes(clean.sources, 'gn')) { 28 | clean.sources = _.without(clean.sources, 'geonames', 'gn'); 29 | messages.warnings.push(coarse_reverse_message); 30 | } 31 | 32 | return messages; 33 | 34 | } 35 | 36 | module.exports = () => ({ 37 | sanitize: _sanitize 38 | }); 39 | -------------------------------------------------------------------------------- /sanitizer/_geonames_warnings.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | const non_admin_fields = ['housenumber', 'street', 'query', 'category']; 4 | 5 | function hasAnyNonAdminFields(parsed_text) { 6 | return !_.isEmpty( 7 | _.intersection( 8 | _.keys(parsed_text), 9 | non_admin_fields)); 10 | } 11 | 12 | function _sanitize( raw, clean ){ 13 | // error & warning messages 14 | const messages = { errors: [], warnings: [] }; 15 | 16 | // bail early if analysis isn't admin-only 17 | if (_.isUndefined(clean.parsed_text) || hasAnyNonAdminFields(clean.parsed_text)) { 18 | return messages; 19 | } 20 | 21 | // the analysis is admin-only, so add errors or warnings if geonames was requested 22 | if (_.isEqual(clean.sources, ['geonames'])) { 23 | // if requested sources is only geonames, return an error 24 | messages.errors.push('input contains only administrative area data, ' + 25 | 'no results will be returned when sources=geonames'); 26 | 27 | } else if (_.includes(clean.sources, 'geonames')) { 28 | // if there are other sources besides geonames, return an warning 29 | messages.warnings.push('input contains only administrative area data, ' + 30 | 'geonames results will not be returned'); 31 | 32 | } 33 | 34 | return messages; 35 | } 36 | 37 | module.exports = () => ({ 38 | sanitize: _sanitize 39 | }); 40 | -------------------------------------------------------------------------------- /sanitizer/_iso2_to_iso3.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const iso3166 = require('../helper/iso3166'); 3 | 4 | // this sanitizer exists solely to convert an ISO2 country value to ISO3 5 | // eg - 'TH' -> 'THA' 6 | // this can go away once altnames imports ISO2 country values from WOF 7 | function _sanitize( raw, clean ){ 8 | // error & warning messages 9 | const messages = { errors: [], warnings: [] }; 10 | 11 | if (clean.hasOwnProperty('parsed_text') && iso3166.isISO2Code(clean.parsed_text.country)) { 12 | clean.parsed_text.country = iso3166.convertISO2ToISO3(clean.parsed_text.country); 13 | } 14 | 15 | return messages; 16 | } 17 | 18 | // export function 19 | module.exports = () => ({ 20 | sanitize: _sanitize 21 | }); 22 | -------------------------------------------------------------------------------- /sanitizer/_request_language.js: -------------------------------------------------------------------------------- 1 | // this sanitizer exists solely to allow `lang` as a request parameter 2 | function _sanitize( raw, clean ){ 3 | // error & warning messages 4 | return { errors: [], warnings: [] }; 5 | } 6 | 7 | function _expected(){ 8 | return [{ 'name': 'lang' }]; 9 | } 10 | 11 | // export function 12 | module.exports = () => ({ 13 | sanitize: _sanitize, 14 | expected: _expected 15 | }); 16 | -------------------------------------------------------------------------------- /sanitizer/_single_scalar_parameters.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | // validate inputs 4 | function _sanitize( raw, clean ){ 5 | // error & warning messages 6 | var messages = { errors: [], warnings: [] }; 7 | 8 | Object.keys(raw).forEach(key => { 9 | if (_.isArray(raw[key])) { 10 | messages.errors.push('\'' + key + '\' parameter can only have one value'); 11 | delete raw[key]; 12 | } else if (_.isObject(raw[key])) { 13 | messages.errors.push('\'' + key + '\' parameter must be a scalar'); 14 | delete raw[key]; 15 | } 16 | }); 17 | 18 | return messages; 19 | } 20 | 21 | // export function 22 | module.exports = () => ({ 23 | sanitize: _sanitize 24 | }); 25 | -------------------------------------------------------------------------------- /sanitizer/_text.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const unicode = require('../helper/unicode'); 3 | const MAX_TEXT_LENGTH = 140; 4 | 5 | // ref: https://en.wikipedia.org/wiki/Quotation_mark 6 | const QUOTES = `"'«»‘’‚‛“”„‟‹›⹂「」『』〝〞〟﹁﹂﹃﹄"'「」`; 7 | 8 | // validate texts, convert types and apply defaults 9 | function _sanitize( raw, clean ){ 10 | 11 | // error & warning messages 12 | const messages = { errors: [], warnings: [] }; 13 | 14 | // normalize unicode marks 15 | let text = unicode.normalize(raw.text); 16 | 17 | // remove superfluous whitespace and quotes 18 | text = _.trim(_.trim(text), QUOTES); 19 | 20 | // validate input 'text' 21 | if( !_.isString(text) || _.isEmpty(text) ){ 22 | messages.errors.push(`invalid param 'text': text length, must be >0`); 23 | } else { 24 | if( text.length > MAX_TEXT_LENGTH ){ 25 | messages.warnings.push(`param 'text' truncated to ${MAX_TEXT_LENGTH} characters`); 26 | text = text.substring(0, MAX_TEXT_LENGTH); 27 | } 28 | clean.text = text; 29 | } 30 | 31 | return messages; 32 | } 33 | 34 | function _expected(){ 35 | return [{ name: 'text' }]; 36 | } 37 | // export function 38 | module.exports = () => ({ 39 | sanitize: _sanitize, 40 | expected: _expected 41 | }); 42 | -------------------------------------------------------------------------------- /sanitizer/place.js: -------------------------------------------------------------------------------- 1 | var sanitizeAll = require('../sanitizer/sanitizeAll'); 2 | 3 | /** 4 | * a list of query-string parameters groups for this endpoint 5 | * which will be replaced with default values if unset. 6 | * 7 | * note: all parameters in each group MUST be unset in the req 8 | * and a replacement available in the defaults for them to be replaced. 9 | * 10 | * the idea being that it makes sense for some parameters to be 11 | * treated as a cohesive group (such as lat/lon pairs), if one or 12 | * the other is missing then we don't make modifications. 13 | */ 14 | const paramGroups = [ 15 | ['gid'], 16 | ['categories'] 17 | ]; 18 | 19 | module.exports.middleware = (_api_pelias_config) => { 20 | var sanitizers = { 21 | defaultParameters: require('../sanitizer/_default_parameters')(_api_pelias_config.defaultParameters, paramGroups), 22 | singleScalarParameters: require('../sanitizer/_single_scalar_parameters')(), 23 | debug: require('../sanitizer/_debug')(_api_pelias_config.exposeInternalDebugTools), 24 | ids: require('../sanitizer/_ids')(), 25 | private: require('../sanitizer/_flag_bool')('private', false), 26 | categories: require('../sanitizer/_categories')(true), 27 | request_language: require('../sanitizer/_request_language')() 28 | }; 29 | 30 | return function(req, res, next){ 31 | sanitizeAll.runAllChecks(req, sanitizers); 32 | next(); 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /sanitizer/wrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | normalize co-ordinates that lie outside of the normal ranges. 3 | 4 | longitude wrapping simply requires adding +- 360 to the value until it comes 5 | in to range. 6 | 7 | for the latitude values we need to flip the longitude whenever the latitude 8 | crosses a pole. 9 | **/ 10 | 11 | 12 | function wrap( lat, lon ){ 13 | 14 | var point = { lat: lat, lon: lon }; 15 | var quadrant = Math.floor( Math.abs(lat) / 90) % 4; 16 | var pole = ( lat > 0 ) ? 90 : -90; 17 | var offset = lat % 90; 18 | 19 | switch( quadrant ){ 20 | case 0: 21 | point.lat = offset; 22 | break; 23 | case 1: 24 | point.lat = pole - offset; 25 | point.lon += 180; 26 | break; 27 | case 2: 28 | point.lat = -offset; 29 | point.lon += 180; 30 | break; 31 | case 3: 32 | point.lat = -pole + offset; 33 | break; 34 | } 35 | 36 | if( point.lon > 180 || point.lon <= -180 ){ 37 | point.lon -= Math.floor(( point.lon + 180 ) / 360) * 360; 38 | } 39 | 40 | return point; 41 | } 42 | 43 | module.exports = wrap; 44 | -------------------------------------------------------------------------------- /service/configurations/Language.js: -------------------------------------------------------------------------------- 1 | const url = require('url'); 2 | 3 | const _ = require('lodash'); 4 | 5 | const ServiceConfiguration = require('pelias-microservice-wrapper').ServiceConfiguration; 6 | 7 | class Language extends ServiceConfiguration { 8 | constructor(o) { 9 | super('language', o); 10 | } 11 | 12 | getParameters(req, res) { 13 | // find all the values for all keys with names that end with '_id' 14 | const ids = _.get(res, 'data', []).reduce((acc, doc) => { 15 | Array.prototype.push.apply(acc, _.values(_.pickBy(doc.parent, (v, k) => _.endsWith(k, '_id') ) ) ); 16 | return acc; 17 | }, []); 18 | const lang = _.get(req, 'clean.lang.iso6393'); 19 | const parameters = { 20 | // arrays will be nested, so flatten first, then uniqify, and finally join elements with comma 21 | ids: _.uniq(_.flattenDeep(ids)).join(',') 22 | }; 23 | 24 | if (lang) { 25 | parameters.lang = lang; 26 | } 27 | 28 | return parameters; 29 | 30 | } 31 | 32 | getUrl(req) { 33 | return url.resolve(this.baseUrl, 'parser/findbyid'); 34 | } 35 | 36 | } 37 | 38 | module.exports = Language; 39 | -------------------------------------------------------------------------------- /service/configurations/Libpostal.js: -------------------------------------------------------------------------------- 1 | const url = require('url'); 2 | 3 | const ServiceConfiguration = require('pelias-microservice-wrapper').ServiceConfiguration; 4 | 5 | class Libpostal extends ServiceConfiguration { 6 | constructor(o, propertyExtractor) { 7 | super('libpostal', o); 8 | 9 | // save off the propertyExtractor function 10 | // this is used to extract a single property from req. eg: 11 | // * _.property('clean.text') 12 | // * _.property('clean.parsed_text.address') 13 | // will return those properties from req 14 | this.propertyExtractor = propertyExtractor; 15 | 16 | } 17 | 18 | getParameters(req) { 19 | return { 20 | address: this.propertyExtractor(req) 21 | }; 22 | 23 | } 24 | 25 | getUrl(req) { 26 | return url.resolve(this.baseUrl, 'parse'); 27 | } 28 | 29 | } 30 | 31 | module.exports = Libpostal; 32 | -------------------------------------------------------------------------------- /service/configurations/PlaceHolder.js: -------------------------------------------------------------------------------- 1 | const url = require('url'); 2 | 3 | const _ = require('lodash'); 4 | 5 | const ServiceConfiguration = require('pelias-microservice-wrapper').ServiceConfiguration; 6 | 7 | class PlaceHolder extends ServiceConfiguration { 8 | constructor(o) { 9 | super('placeholder', o); 10 | } 11 | 12 | getParameters(req) { 13 | const parameters = {}; 14 | 15 | if (_.has(req.clean.parsed_text, 'street')) { 16 | // assemble all these fields into a space-delimited string 17 | parameters.text = _.values(_.pick(req.clean.parsed_text, 18 | ['neighbourhood', 'borough', 'city', 'county', 'state', 'country'])).join(' '); 19 | 20 | } else { 21 | parameters.text = req.clean.text; 22 | 23 | } 24 | 25 | if (_.has(req.clean, 'lang.iso6393')) { 26 | parameters.lang = req.clean.lang.iso6393; 27 | } 28 | 29 | return parameters; 30 | } 31 | 32 | getUrl(req) { 33 | return url.resolve(this.baseUrl, 'parser/search'); 34 | } 35 | 36 | } 37 | 38 | module.exports = PlaceHolder; 39 | -------------------------------------------------------------------------------- /service/configurations/PointInPolygon.js: -------------------------------------------------------------------------------- 1 | const url = require('url'); 2 | 3 | const _ = require('lodash'); 4 | 5 | const ServiceConfiguration = require('pelias-microservice-wrapper').ServiceConfiguration; 6 | 7 | class PointInPolygon extends ServiceConfiguration { 8 | constructor(o) { 9 | super('pip', o); 10 | } 11 | 12 | getParameters(req) { 13 | if (_.has(req, 'clean.layers')) { 14 | return { 15 | layers: _.join(req.clean.layers, ',') 16 | }; 17 | } 18 | 19 | return {}; 20 | } 21 | 22 | getUrl(req) { 23 | // use resolve to eliminate possibility of duplicate /'s in URL 24 | return url.resolve(this.baseUrl, `${req.clean['point.lon']}/${req.clean['point.lat']}`); 25 | } 26 | 27 | } 28 | 29 | module.exports = PointInPolygon; 30 | -------------------------------------------------------------------------------- /service/mget.js: -------------------------------------------------------------------------------- 1 | var logger = require( 'pelias-logger' ).get( 'api' ); 2 | 3 | function service( esclient, query, cb ){ 4 | 5 | // elasticsearch command 6 | var cmd = { 7 | body: { 8 | docs: query 9 | } 10 | }; 11 | 12 | // query elasticsearch 13 | const startTime = new Date(); 14 | esclient.mget( cmd, function( err, data ){ 15 | if (data) { 16 | data.response_time = new Date() - startTime; 17 | } 18 | 19 | // handle elasticsearch errors 20 | if( err ){ 21 | logger.error( `elasticsearch error ${err}`); 22 | return cb( err ); 23 | } 24 | 25 | // map returned documents 26 | var docs = []; 27 | if( data && Array.isArray(data.docs) ){ 28 | 29 | docs = data.docs.filter( function( doc ){ 30 | 31 | // remove docs not actually found 32 | return doc.found; 33 | 34 | }).map( function( doc ){ 35 | 36 | // map metadata in to _source so we 37 | // can serve it up to the consumer 38 | doc._source._id = doc._id; 39 | 40 | return doc._source; 41 | }); 42 | } 43 | 44 | // fire callback 45 | return cb( null, docs, data ); 46 | }); 47 | 48 | } 49 | 50 | module.exports = service; 51 | -------------------------------------------------------------------------------- /test/ciao.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaults": { 3 | "protocol": "http", 4 | "host": "localhost", 5 | "port": 3100 6 | }, 7 | "config": {} 8 | } -------------------------------------------------------------------------------- /test/ciao/404.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> invalid path 3 | path: '/notexist' 4 | 5 | #? not found 6 | response.statusCode.should.be.equal 404 7 | 8 | #? content-type header correctly set 9 | response.should.have.header 'Content-Type','application/json; charset=utf-8' 10 | 11 | #? cache-control header correctly set 12 | response.should.have.header 'Cache-Control','public' 13 | 14 | #? should respond in json with server info 15 | should.exist json 16 | should.exist json.error 17 | json.error.should.be.equal 'not found: invalid path' -------------------------------------------------------------------------------- /test/ciao/CORS/headers_GET.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> cross-origin resource sharing 3 | path: '/' 4 | 5 | #? access control headers correctly set 6 | response.should.have.header 'Access-Control-Allow-Origin','*' 7 | response.should.have.header 'Access-Control-Allow-Methods','GET, OPTIONS' 8 | response.should.have.header 'Access-Control-Allow-Headers','X-Requested-With,content-type' 9 | -------------------------------------------------------------------------------- /test/ciao/CORS/headers_OPTIONS.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> cross-origin resource sharing 3 | path: '/' 4 | method: 'OPTIONS' 5 | 6 | #? access control headers correctly set 7 | response.should.have.header 'Access-Control-Allow-Origin','*' 8 | response.should.have.header 'Access-Control-Allow-Methods','GET, OPTIONS' 9 | response.should.have.header 'Access-Control-Allow-Headers','X-Requested-With,content-type' 10 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/boundary_gid_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding gid 3 | path: '/v1/autocomplete?text=a&boundary.gid=abc::' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | json.geocoding.errors.should.eql [ 27 | 'abc:: does not follow source:layer:id format' 28 | ] 29 | 30 | #? expected warnings 31 | should.not.exist json.geocoding.warnings 32 | 33 | #? inputs 34 | json.geocoding.query['text'].should.eql 'a' 35 | json.geocoding.query['size'].should.eql 10 36 | should.not.exist json.geocoding.query['boundary.gid'] -------------------------------------------------------------------------------- /test/ciao/autocomplete/boundary_gid_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding gid 3 | path: '/v1/autocomplete?text=a&boundary.gid=whosonfirst:neighbourhood:85869245' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query['boundary.gid'].should.eql '85869245' -------------------------------------------------------------------------------- /test/ciao/autocomplete/boundary_rect_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> focus point 3 | path: '/v1/autocomplete?text=cairo&boundary.rect.min_lat=30&boundary.rect.max_lat=32&boundary.rect.min_lon=29&boundary.rect.max_lon=31' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'cairo' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query['boundary.rect.min_lat'].should.eql 30 35 | json.geocoding.query['boundary.rect.max_lat'].should.eql 32 36 | json.geocoding.query['boundary.rect.min_lon'].should.eql 29 37 | json.geocoding.query['boundary.rect.max_lon'].should.eql 31 38 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/focus_point_invalid_lat.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> focus point 3 | path: '/v1/autocomplete?text=a&focus.point.lat=foo&focus.point.lon=-73.990342' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'missing param \'focus.point.lat\'' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | should.not.exist json.geocoding.query['focus.point.lat'] 36 | should.not.exist json.geocoding.query['focus.point.lon'] 37 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/focus_point_invalid_lon.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> focus point 3 | path: '/v1/autocomplete?text=a&focus.point.lat=40.744243&focus.point.lon=' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'missing param \'focus.point.lon\'' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | json.geocoding.query['focus.point.lat'].should.eql 40.744243 36 | should.not.exist json.geocoding.query['focus.point.lon'] 37 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/focus_point_missing_lat.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> focus point 3 | path: '/v1/autocomplete?text=a&focus.point.lon=-73.990342' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'parameters focus.point.lat and focus.point.lon must both be specified' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | should.not.exist json.geocoding.query['focus.point.lat'] 36 | should.not.exist json.geocoding.query['focus.point.lon'] 37 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/focus_point_missing_lon.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> focus point 3 | path: '/v1/autocomplete?text=a&focus.point.lat=40.744243' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'parameters focus.point.lat and focus.point.lon must both be specified' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | should.not.exist json.geocoding.query['focus.point.lat'] 36 | should.not.exist json.geocoding.query['focus.point.lon'] 37 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/focus_point_null_island.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> focus point null island 3 | path: '/v1/autocomplete?text=a&focus.point.lat=0&focus.point.lon=0' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query['focus.point.lat'].should.eql 0 35 | json.geocoding.query['focus.point.lon'].should.eql 0 -------------------------------------------------------------------------------- /test/ciao/autocomplete/focus_point_valid_duo.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> focus point 3 | path: '/v1/autocomplete?text=a&focus.point.lat=40.744243&focus.point.lon=-73.990342' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query['focus.point.lat'].should.eql 40.744243 35 | json.geocoding.query['focus.point.lon'].should.eql -73.990342 -------------------------------------------------------------------------------- /test/ciao/autocomplete/language_default.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/autocomplete?text=example' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? language 32 | json.geocoding.query['lang'].should.eql { 33 | name: 'English', 34 | iso6391: 'en', 35 | iso6393: 'eng', 36 | defaulted: true 37 | } 38 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/language_header_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/autocomplete?text=example' 4 | headers: 'Accept-Language': 'example' 5 | 6 | #? 200 ok 7 | response.statusCode.should.be.equal 200 8 | response.should.have.header 'charset', 'utf8' 9 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 10 | 11 | #? valid geocoding block 12 | should.exist json.geocoding 13 | should.exist json.geocoding.version 14 | should.exist json.geocoding.attribution 15 | should.exist json.geocoding.query 16 | should.exist json.geocoding.engine 17 | should.exist json.geocoding.engine.name 18 | should.exist json.geocoding.engine.author 19 | should.exist json.geocoding.engine.version 20 | should.exist json.geocoding.timestamp 21 | 22 | #? valid geojson 23 | json.type.should.be.equal 'FeatureCollection' 24 | json.features.should.be.instanceof Array 25 | 26 | #? expected errors 27 | should.not.exist json.geocoding.errors 28 | 29 | #? expected warnings 30 | json.geocoding.warnings.should.eql [ 'invalid language provided via header' ] 31 | 32 | #? language 33 | json.geocoding.query['lang'].should.eql { 34 | name: 'English', 35 | iso6391: 'en', 36 | iso6393: 'eng', 37 | defaulted: true 38 | } 39 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/language_header_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/autocomplete?text=example' 4 | headers: 'Accept-Language': 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5' 5 | 6 | #? 200 ok 7 | response.statusCode.should.be.equal 200 8 | response.should.have.header 'charset', 'utf8' 9 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 10 | 11 | #? valid geocoding block 12 | should.exist json.geocoding 13 | should.exist json.geocoding.version 14 | should.exist json.geocoding.attribution 15 | should.exist json.geocoding.query 16 | should.exist json.geocoding.engine 17 | should.exist json.geocoding.engine.name 18 | should.exist json.geocoding.engine.author 19 | should.exist json.geocoding.engine.version 20 | should.exist json.geocoding.timestamp 21 | 22 | #? valid geojson 23 | json.type.should.be.equal 'FeatureCollection' 24 | json.features.should.be.instanceof Array 25 | 26 | #? expected errors 27 | should.not.exist json.geocoding.errors 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? language 33 | json.geocoding.query['lang'].should.eql { 34 | defaulted: false, 35 | iso6391: 'fr', 36 | iso6393: 'fra', 37 | name: 'French' 38 | } 39 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/language_querystring_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/autocomplete?lang=example&text=example' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | json.geocoding.warnings.should.eql [ 'invalid language provided via querystring' ] 30 | 31 | #? language 32 | json.geocoding.query['lang'].should.eql { 33 | name: 'English', 34 | iso6391: 'en', 35 | iso6393: 'eng', 36 | defaulted: true 37 | } 38 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/language_querystring_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/autocomplete?lang=fr&text=example' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? language 32 | json.geocoding.query['lang'].should.eql { 33 | defaulted: false, 34 | iso6391: 'fr', 35 | iso6393: 'fra', 36 | name: 'French' 37 | } 38 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/layers_alias_address.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> layer alias 3 | path: '/v1/autocomplete?text=a&layers=address' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query.layers.should.eql ["address"] 35 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/layers_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> layer alias 3 | path: '/v1/autocomplete?text=a&layers=notlayer' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,street,country,macroregion,region,county,localadmin,locality,borough,neighbourhood,continent,empire,dependency,macrocounty,macrohood,microhood,disputed,postalcode,ocean,marinearea' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/layers_multiple.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> layer alias 3 | path: '/v1/autocomplete?text=a&layers=country,region' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query.layers.should.eql ["country","region"] 35 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/layers_single.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> layer alias 3 | path: '/v1/autocomplete?text=a&layers=country' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query.layers.should.eql ["country"] 35 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/no_params.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> no params specified 3 | path: '/v1/autocomplete' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'invalid param \'text\': text length, must be >0' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['size'].should.eql 10 34 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/non_scalar_parameter_array.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> define two sources 3 | path: '/v1/autocomplete?text=A&sources=A&sources=B' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected warnings 26 | should.not.exist json.geocoding.warnings 27 | 28 | #? expected errors 29 | should.exist json.geocoding.errors 30 | json.geocoding.errors.should.eql [ '\'sources\' parameter can only have one value' ] 31 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/non_scalar_parameter_object.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> define parameter as object 3 | path: '/v1/autocomplete?text=A¶meter[idx]=value' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected warnings 26 | should.not.exist json.geocoding.warnings 27 | 28 | #? expected errors 29 | should.exist json.geocoding.errors 30 | json.geocoding.errors.should.eql [ '\'parameter\' parameter must be a scalar' ] 31 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/size_not_default.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> set size (autocomplete does not allow size to be changed) 3 | path: '/v1/autocomplete?text=a&size=3' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.exist json.geocoding.warnings 30 | json.geocoding.warnings.should.eql [ 'out-of-range integer \'size\', using MIN_SIZE' ] 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 # should remain the default size 35 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/sources_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> sources filter 3 | path: '/v1/autocomplete?text=a&sources=openstreetmap,notasource' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ '\'notasource\' is an invalid sources parameter. Valid options: osm,oa,gn,wof,openstreetmap,openaddresses,geonames,whosonfirst' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/sources_layers_valid_combo.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> sources and layers specified 3 | path: '/v1/autocomplete?text=a&sources=openaddresses&layers=address' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query.layers.should.eql ["address"] 35 | json.geocoding.query.sources.should.eql ["openaddresses"] 36 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/sources_multiple.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> sources filter 3 | path: "/v1/autocomplete?text=a&sources=openstreetmap,geonames" 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header "charset", 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query.sources.should.eql ["openstreetmap", "geonames"] 35 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/sources_single.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> sources filter 3 | path: '/v1/autocomplete?text=a&sources=openstreetmap' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query.sources.should.eql ["openstreetmap"] 35 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/text_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> basic autocomplete 3 | path: '/v1/autocomplete?text=' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'invalid param \'text\': text length, must be >0' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | should.not.exist json.geocoding.query['text'] 34 | json.geocoding.query['size'].should.eql 10 35 | -------------------------------------------------------------------------------- /test/ciao/autocomplete/text_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> basic autocomplete 3 | path: '/v1/autocomplete?text=a' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 -------------------------------------------------------------------------------- /test/ciao/index.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> api root 3 | path: '/v1/' 4 | 5 | #? endpoint available 6 | response.statusCode.should.be.equal 200 7 | 8 | #? content-type header correctly set 9 | response.should.have.header 'Content-Type','text/html; charset=utf-8' 10 | 11 | #? charset header correctly set 12 | response.should.have.header 'Charset','utf8' 13 | 14 | #? cache-control header correctly set 15 | response.should.have.header 'Cache-Control','public' 16 | 17 | #? server header correctly set 18 | response.should.have.header 'Server' 19 | response.headers.server.should.match /Pelias\/\d{1,2}\.\d{1,2}\.\d{1,2}/ 20 | 21 | #? vanity header correctly set 22 | response.should.have.header 'X-Powered-By','pelias' 23 | -------------------------------------------------------------------------------- /test/ciao/place/basic_place.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> basic place 3 | path: '/v1/place?ids=geonames:venue:1' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['ids'].should.eql [{ id: '1', layer: 'venue', source: 'geonames' }] 33 | should.not.exist json.geocoding.query['size'] 34 | -------------------------------------------------------------------------------- /test/ciao/place/language_default.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/place?ids=geonames:venue:1' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? language 32 | json.geocoding.query['lang'].should.eql { 33 | name: 'English', 34 | iso6391: 'en', 35 | iso6393: 'eng', 36 | defaulted: true 37 | } 38 | -------------------------------------------------------------------------------- /test/ciao/place/language_header_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/place?ids=geonames:venue:1' 4 | headers: 'Accept-Language': 'example' 5 | 6 | #? 200 ok 7 | response.statusCode.should.be.equal 200 8 | response.should.have.header 'charset', 'utf8' 9 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 10 | 11 | #? valid geocoding block 12 | should.exist json.geocoding 13 | should.exist json.geocoding.version 14 | should.exist json.geocoding.attribution 15 | should.exist json.geocoding.query 16 | should.exist json.geocoding.engine 17 | should.exist json.geocoding.engine.name 18 | should.exist json.geocoding.engine.author 19 | should.exist json.geocoding.engine.version 20 | should.exist json.geocoding.timestamp 21 | 22 | #? valid geojson 23 | json.type.should.be.equal 'FeatureCollection' 24 | json.features.should.be.instanceof Array 25 | 26 | #? expected errors 27 | should.not.exist json.geocoding.errors 28 | 29 | #? expected warnings 30 | json.geocoding.warnings.should.eql [ 'invalid language provided via header' ] 31 | 32 | #? language 33 | json.geocoding.query['lang'].should.eql { 34 | name: 'English', 35 | iso6391: 'en', 36 | iso6393: 'eng', 37 | defaulted: true 38 | } 39 | -------------------------------------------------------------------------------- /test/ciao/place/language_header_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/place?ids=geonames:venue:1' 4 | headers: 'Accept-Language': 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5' 5 | 6 | #? 200 ok 7 | response.statusCode.should.be.equal 200 8 | response.should.have.header 'charset', 'utf8' 9 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 10 | 11 | #? valid geocoding block 12 | should.exist json.geocoding 13 | should.exist json.geocoding.version 14 | should.exist json.geocoding.attribution 15 | should.exist json.geocoding.query 16 | should.exist json.geocoding.engine 17 | should.exist json.geocoding.engine.name 18 | should.exist json.geocoding.engine.author 19 | should.exist json.geocoding.engine.version 20 | should.exist json.geocoding.timestamp 21 | 22 | #? valid geojson 23 | json.type.should.be.equal 'FeatureCollection' 24 | json.features.should.be.instanceof Array 25 | 26 | #? expected errors 27 | should.not.exist json.geocoding.errors 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? language 33 | json.geocoding.query['lang'].should.eql { 34 | defaulted: false, 35 | iso6391: 'fr', 36 | iso6393: 'fra', 37 | name: 'French' 38 | } 39 | -------------------------------------------------------------------------------- /test/ciao/place/language_querystring_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/place?lang=example&ids=geonames:venue:1' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | json.geocoding.warnings.should.eql [ 'invalid language provided via querystring' ] 30 | 31 | #? language 32 | json.geocoding.query['lang'].should.eql { 33 | name: 'English', 34 | iso6391: 'en', 35 | iso6393: 'eng', 36 | defaulted: true 37 | } 38 | -------------------------------------------------------------------------------- /test/ciao/place/language_querystring_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/place?lang=fr&ids=geonames:venue:1' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? language 32 | json.geocoding.query['lang'].should.eql { 33 | defaulted: false, 34 | iso6391: 'fr', 35 | iso6393: 'fra', 36 | name: 'French' 37 | } 38 | -------------------------------------------------------------------------------- /test/ciao/place/missing_id.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> missing id 3 | path: '/v1/place' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'invalid param \'ids\': length must be >0' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | should.not.exist json.geocoding.query['ids'] 34 | should.not.exist json.geocoding.query['size'] 35 | -------------------------------------------------------------------------------- /test/ciao/reverse/basic_reverse.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> basic reverse 3 | path: '/v1/reverse?point.lat=1&point.lon=2' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['point.lat'].should.eql 1 33 | json.geocoding.query['point.lon'].should.eql 2 34 | json.geocoding.query['size'].should.eql 10 -------------------------------------------------------------------------------- /test/ciao/reverse/boundary_circle_invalid_radius.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding circle 3 | path: '/v1/reverse?point.lat=40.744243&point.lon=-73.990342&boundary.circle.radius=foo' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'missing param \'boundary.circle.radius\'' ] 28 | 29 | #? inputs 30 | # json.geocoding.query['size'].should.eql 10 31 | json.geocoding.query['point.lat'].should.eql 40.744243 32 | json.geocoding.query['point.lon'].should.eql -73.990342 33 | should.not.exist json.geocoding.query['boundary.circle.radius'] 34 | should.not.exist json.geocoding.query['boundary.circle.lat'] 35 | should.not.exist json.geocoding.query['boundary.circle.lon'] 36 | -------------------------------------------------------------------------------- /test/ciao/reverse/boundary_circle_valid_radius.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding circle 3 | path: '/v1/reverse?point.lat=40.744243&point.lon=-73.990342&boundary.circle.radius=999.9' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | # expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query['point.lat'].should.eql 40.744243 34 | json.geocoding.query['point.lon'].should.eql -73.990342 35 | json.geocoding.query['boundary.circle.lat'].should.eql 40.744243 36 | json.geocoding.query['boundary.circle.lon'].should.eql -73.990342 37 | json.geocoding.query['boundary.circle.radius'].should.eql 999.9 38 | -------------------------------------------------------------------------------- /test/ciao/reverse/boundary_country_invalid_alpha2.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding country 3 | path: '/v1/reverse?point.lat=1&point.lon=1&boundary.country=ZZ' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'ZZ is not a valid ISO2/ISO3 country code' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['size'].should.eql 10 34 | should.not.exist json.geocoding.query['boundary.country'] 35 | -------------------------------------------------------------------------------- /test/ciao/reverse/boundary_country_invalid_alpha3.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding country 3 | path: '/v1/reverse?point.lat=1&point.lon=1&boundary.country=ZZZ' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'ZZZ is not a valid ISO2/ISO3 country code' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['size'].should.eql 10 34 | should.not.exist json.geocoding.query['boundary.country'] 35 | -------------------------------------------------------------------------------- /test/ciao/reverse/boundary_country_invalid_iso3166.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding country 3 | path: '/v1/reverse?point.lat=1&point.lon=1&boundary.country=FOOBAR' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'FOOBAR is not a valid ISO2/ISO3 country code' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['size'].should.eql 10 34 | should.not.exist json.geocoding.query['boundary.country'] 35 | -------------------------------------------------------------------------------- /test/ciao/reverse/boundary_country_valid_alpha2.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding country 3 | path: '/v1/reverse?point.lat=1&point.lon=1&boundary.country=US' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query['boundary.country'].should.eql 'USA' -------------------------------------------------------------------------------- /test/ciao/reverse/boundary_country_valid_alpha3.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding country 3 | path: '/v1/reverse?point.lat=1&point.lon=1&boundary.country=USA' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query['boundary.country'].should.eql 'USA' -------------------------------------------------------------------------------- /test/ciao/reverse/boundary_gid_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding gid 3 | path: '/v1/reverse?point.lat=1&point.lon=1&boundary.gid=abc::' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | json.geocoding.errors.should.eql [ 27 | 'abc:: does not follow source:layer:id format' 28 | ] 29 | 30 | #? expected warnings 31 | should.not.exist json.geocoding.warnings 32 | 33 | #? inputs 34 | json.geocoding.query['size'].should.eql 10 35 | should.not.exist json.geocoding.query['boundary.gid'] -------------------------------------------------------------------------------- /test/ciao/reverse/boundary_gid_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding gid 3 | path: '/v1/reverse?point.lat=1&point.lon=1&boundary.gid=whosonfirst:neighbourhood:85869245' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query['boundary.gid'].should.eql '85869245' -------------------------------------------------------------------------------- /test/ciao/reverse/duplicate_parameter_name.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> set size 3 | path: '/v1/reverse?point.lat=1&point.lon=1¶m=value1¶m=value2' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected warnings 26 | should.not.exist json.geocoding.warnings 27 | 28 | #? expected errors 29 | should.exist json.geocoding.errors 30 | json.geocoding.errors.should.eql [ '\'param\' parameter can only have one value' ] 31 | -------------------------------------------------------------------------------- /test/ciao/reverse/language_default.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/reverse?point.lat=1&point.lon=2' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? language 32 | json.geocoding.query['lang'].should.eql { 33 | name: 'English', 34 | iso6391: 'en', 35 | iso6393: 'eng', 36 | defaulted: true 37 | } 38 | -------------------------------------------------------------------------------- /test/ciao/reverse/language_header_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/reverse?point.lat=1&point.lon=2' 4 | headers: 'Accept-Language': 'example' 5 | 6 | #? 200 ok 7 | response.statusCode.should.be.equal 200 8 | response.should.have.header 'charset', 'utf8' 9 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 10 | 11 | #? valid geocoding block 12 | should.exist json.geocoding 13 | should.exist json.geocoding.version 14 | should.exist json.geocoding.attribution 15 | should.exist json.geocoding.query 16 | should.exist json.geocoding.engine 17 | should.exist json.geocoding.engine.name 18 | should.exist json.geocoding.engine.author 19 | should.exist json.geocoding.engine.version 20 | should.exist json.geocoding.timestamp 21 | 22 | #? valid geojson 23 | json.type.should.be.equal 'FeatureCollection' 24 | json.features.should.be.instanceof Array 25 | 26 | #? expected errors 27 | should.not.exist json.geocoding.errors 28 | 29 | #? expected warnings 30 | json.geocoding.warnings.should.eql [ 'invalid language provided via header' ] 31 | 32 | #? language 33 | json.geocoding.query['lang'].should.eql { 34 | name: 'English', 35 | iso6391: 'en', 36 | iso6393: 'eng', 37 | defaulted: true 38 | } 39 | -------------------------------------------------------------------------------- /test/ciao/reverse/language_header_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/reverse?point.lat=1&point.lon=2' 4 | headers: 'Accept-Language': 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5' 5 | 6 | #? 200 ok 7 | response.statusCode.should.be.equal 200 8 | response.should.have.header 'charset', 'utf8' 9 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 10 | 11 | #? valid geocoding block 12 | should.exist json.geocoding 13 | should.exist json.geocoding.version 14 | should.exist json.geocoding.attribution 15 | should.exist json.geocoding.query 16 | should.exist json.geocoding.engine 17 | should.exist json.geocoding.engine.name 18 | should.exist json.geocoding.engine.author 19 | should.exist json.geocoding.engine.version 20 | should.exist json.geocoding.timestamp 21 | 22 | #? valid geojson 23 | json.type.should.be.equal 'FeatureCollection' 24 | json.features.should.be.instanceof Array 25 | 26 | #? expected errors 27 | should.not.exist json.geocoding.errors 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? language 33 | json.geocoding.query['lang'].should.eql { 34 | defaulted: false, 35 | iso6391: 'fr', 36 | iso6393: 'fra', 37 | name: 'French' 38 | } 39 | -------------------------------------------------------------------------------- /test/ciao/reverse/language_querystring_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/reverse?lang=example&point.lat=1&point.lon=2' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | json.geocoding.warnings.should.eql [ 'invalid language provided via querystring' ] 30 | 31 | #? language 32 | json.geocoding.query['lang'].should.eql { 33 | name: 'English', 34 | iso6391: 'en', 35 | iso6393: 'eng', 36 | defaulted: true 37 | } 38 | -------------------------------------------------------------------------------- /test/ciao/reverse/language_querystring_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/reverse?lang=fr&point.lat=1&point.lon=2' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? language 32 | json.geocoding.query['lang'].should.eql { 33 | defaulted: false, 34 | iso6391: 'fr', 35 | iso6393: 'fra', 36 | name: 'French' 37 | } 38 | -------------------------------------------------------------------------------- /test/ciao/reverse/layers_alias_address.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> layer alias 3 | path: '/v1/reverse?point.lat=1&point.lon=2&layers=address' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query.layers.should.eql ["address"] 34 | -------------------------------------------------------------------------------- /test/ciao/reverse/layers_alias_coarse.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> layer alias 3 | path: '/v1/reverse?point.lat=1&point.lon=2&layers=coarse' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query.layers.should.eql [ "continent", 34 | "empire", 35 | "country", 36 | "dependency", 37 | "macroregion", 38 | "region", 39 | "locality", 40 | "localadmin", 41 | "macrocounty", 42 | "county", 43 | "macrohood", 44 | "borough", 45 | "neighbourhood", 46 | "microhood", 47 | "disputed", 48 | "postalcode", 49 | "ocean", 50 | "marinearea" 51 | ] 52 | -------------------------------------------------------------------------------- /test/ciao/reverse/layers_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> layer alias 3 | path: '/v1/reverse?point.lat=1&point.lon=2&layers=notlayer' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,street,country,macroregion,region,county,localadmin,locality,borough,neighbourhood,continent,empire,dependency,macrocounty,macrohood,microhood,disputed,postalcode,ocean,marinearea' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['size'].should.eql 10 34 | should.not.exist json.geocoding.query['types'] 35 | should.not.exist json.geocoding.query['type'] 36 | -------------------------------------------------------------------------------- /test/ciao/reverse/layers_multiple.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> layer alias 3 | path: '/v1/reverse?point.lat=1&point.lon=2&layers=country,region' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query.layers.should.eql ["country","region"] 34 | -------------------------------------------------------------------------------- /test/ciao/reverse/layers_single.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> layer alias 3 | path: '/v1/reverse?point.lat=1&point.lon=2&layers=country' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query.layers.should.eql ["country"] 34 | -------------------------------------------------------------------------------- /test/ciao/reverse/non_scalar_parameter_array.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> define two sources 3 | path: '/v1/reverse?point.lat=1&point.lon=1&sources=A&sources=B' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected warnings 26 | should.not.exist json.geocoding.warnings 27 | 28 | #? expected errors 29 | should.exist json.geocoding.errors 30 | json.geocoding.errors.should.eql [ '\'sources\' parameter can only have one value' ] 31 | -------------------------------------------------------------------------------- /test/ciao/reverse/non_scalar_parameter_object.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> define parameter as object 3 | path: '/v1/reverse?point.lat=1&point.lon=1¶meter[idx]=value' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected warnings 26 | should.not.exist json.geocoding.warnings 27 | 28 | #? expected errors 29 | should.exist json.geocoding.errors 30 | json.geocoding.errors.should.eql [ '\'parameter\' parameter must be a scalar' ] 31 | -------------------------------------------------------------------------------- /test/ciao/reverse/point_invalid_lat.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> point 3 | path: '/v1/reverse?point.lat=foo&point.lon=-73.990342' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'missing param \'point.lat\'' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['size'].should.eql 10 34 | should.not.exist json.geocoding.query['point.lat'] 35 | should.not.exist json.geocoding.query['point.lon'] 36 | -------------------------------------------------------------------------------- /test/ciao/reverse/point_invalid_lon.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> point 3 | path: '/v1/reverse?point.lat=40.744243&point.lon=' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'missing param \'point.lon\'' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query['point.lat'].should.eql 40.744243 35 | should.not.exist json.geocoding.query['point.lon'] 36 | -------------------------------------------------------------------------------- /test/ciao/reverse/point_missing_lat.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> point 3 | path: '/v1/reverse?point.lon=-73.990342' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'parameters point.lat and point.lon must both be specified' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['size'].should.eql 10 34 | should.not.exist json.geocoding.query['point.lat'] 35 | should.not.exist json.geocoding.query['point.lon'] 36 | -------------------------------------------------------------------------------- /test/ciao/reverse/point_missing_lon.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> point 3 | path: '/v1/reverse?point.lat=40.744243' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'parameters point.lat and point.lon must both be specified' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['size'].should.eql 10 34 | should.not.exist json.geocoding.query['point.lat'] 35 | should.not.exist json.geocoding.query['point.lon'] 36 | -------------------------------------------------------------------------------- /test/ciao/reverse/point_null_island.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> point null island 3 | path: '/v1/reverse?point.lat=0&point.lon=0' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query['point.lat'].should.eql 0 34 | json.geocoding.query['point.lon'].should.eql 0 -------------------------------------------------------------------------------- /test/ciao/reverse/point_valid_duo.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> point 3 | path: '/v1/reverse?point.lat=40.744243&point.lon=-73.990342' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query['point.lat'].should.eql 40.744243 34 | json.geocoding.query['point.lon'].should.eql -73.990342 -------------------------------------------------------------------------------- /test/ciao/reverse/privacy_false.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> accept privacy var 3 | path: '/v1/reverse?point.lat=1&point.lon=2&private=false' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query['private'].should.eql false -------------------------------------------------------------------------------- /test/ciao/reverse/privacy_true.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> accept privacy var 3 | path: '/v1/reverse?point.lat=1&point.lon=2&private=true' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query['private'].should.eql true -------------------------------------------------------------------------------- /test/ciao/reverse/size_over_max.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> set size 3 | path: '/v1/reverse?point.lat=1&point.lon=1&size=999' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.exist json.geocoding.warnings 30 | json.geocoding.warnings.should.eql [ 'out-of-range integer \'size\', using MAX_SIZE' ] 31 | 32 | #? inputs 33 | json.geocoding.query['size'].should.eql 40 -------------------------------------------------------------------------------- /test/ciao/reverse/size_under_min.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> set size 3 | path: '/v1/reverse?point.lat=1&point.lon=1&size=0' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.exist json.geocoding.warnings 30 | json.geocoding.warnings.should.eql [ 'out-of-range integer \'size\', using MIN_SIZE' ] 31 | 32 | #? inputs 33 | json.geocoding.query['size'].should.eql 1 -------------------------------------------------------------------------------- /test/ciao/reverse/size_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> set size 3 | path: '/v1/reverse?point.lat=1&point.lon=1&size=3' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 3 -------------------------------------------------------------------------------- /test/ciao/reverse/sources_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> sources filter 3 | path: '/v1/reverse?point.lat=1&point.lon=2&sources=openstreetmap,notasource' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ '\'notasource\' is an invalid sources parameter. Valid options: osm,oa,gn,wof,openstreetmap,openaddresses,geonames,whosonfirst' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['size'].should.eql 10 34 | should.not.exist json.geocoding.query['types'] 35 | should.not.exist json.geocoding.query['type'] 36 | -------------------------------------------------------------------------------- /test/ciao/reverse/sources_layers_invalid_combo.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> sources and layers specified (invalid combo) 3 | path: '/v1/reverse?point.lat=1&point.lon=2&sources=whosonfirst&layers=address' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'You have specified both the `sources` and `layers` parameters in a combination that will return no results: the whosonfirst source has nothing in the address layer' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query.layers.should.eql ["address"] 35 | json.geocoding.query.sources.should.eql ["whosonfirst"] 36 | -------------------------------------------------------------------------------- /test/ciao/reverse/sources_layers_valid_combo.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> sources and layers specified 3 | path: '/v1/reverse?point.lat=1&point.lon=2&sources=openaddresses&layers=address' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query.layers.should.eql ["address"] 34 | json.geocoding.query.sources.should.eql ["openaddresses"] 35 | -------------------------------------------------------------------------------- /test/ciao/reverse/sources_multiple.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> sources filter 3 | path: '/v1/reverse?point.lat=1&point.lon=2&sources=openstreetmap,openaddresses' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query.sources.should.eql ["openstreetmap", "openaddresses"] 34 | -------------------------------------------------------------------------------- /test/ciao/reverse/sources_single.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> sources filter 3 | path: '/v1/reverse?point.lat=1&point.lon=2&sources=openstreetmap' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['size'].should.eql 10 33 | json.geocoding.query.sources.should.eql ["openstreetmap"] 34 | -------------------------------------------------------------------------------- /test/ciao/search/boundary_circle_invalid_lat_lon_types.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding circle 3 | path: '/v1/search?text=a&boundary.circle.lat=foo&boundary.circle.lon=bar' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'missing param \'boundary.circle.lat\'' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | should.not.exist json.geocoding.query['boundary.circle.lat'] 36 | should.not.exist json.geocoding.query['boundary.circle.lon'] 37 | should.not.exist json.geocoding.query['boundary.circle.radius'] 38 | -------------------------------------------------------------------------------- /test/ciao/search/boundary_circle_invalid_radius.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding circle 3 | path: '/v1/search?text=a&boundary.circle.lat=40.744243&boundary.circle.lon=-73.990342&boundary.circle.radius=foo' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'missing param \'boundary.circle.radius\'' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | should.not.exist json.geocoding.query['boundary.circle.lat'] 36 | should.not.exist json.geocoding.query['boundary.circle.lon'] 37 | should.not.exist json.geocoding.query['boundary.circle.radius'] 38 | -------------------------------------------------------------------------------- /test/ciao/search/boundary_circle_valid_duo.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding circle 3 | path: '/v1/search?text=a&boundary.circle.lat=40.744243&boundary.circle.lon=-73.990342' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query['boundary.circle.lat'].should.eql 40.744243 35 | json.geocoding.query['boundary.circle.lon'].should.eql -73.990342 36 | should.not.exist json.geocoding.query['boundary.circle.radius'] -------------------------------------------------------------------------------- /test/ciao/search/boundary_circle_valid_trio.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding circle 3 | path: '/v1/search?text=a&boundary.circle.lat=40.744243&boundary.circle.lon=-73.990342&boundary.circle.radius=100' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query['boundary.circle.lat'].should.eql 40.744243 35 | json.geocoding.query['boundary.circle.lon'].should.eql -73.990342 36 | json.geocoding.query['boundary.circle.radius'].should.eql 100 -------------------------------------------------------------------------------- /test/ciao/search/boundary_country_invalid_alpha2.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding country 3 | path: '/v1/search?text=a&boundary.country=ZZ' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'ZZ is not a valid ISO2/ISO3 country code' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | should.not.exist json.geocoding.query['boundary.country'] 36 | -------------------------------------------------------------------------------- /test/ciao/search/boundary_country_invalid_alpha3.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding country 3 | path: '/v1/search?text=a&boundary.country=ZZZ' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'ZZZ is not a valid ISO2/ISO3 country code' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | should.not.exist json.geocoding.query['boundary.country'] 36 | -------------------------------------------------------------------------------- /test/ciao/search/boundary_country_invalid_iso3166.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding country 3 | path: '/v1/search?text=a&boundary.country=FOOBAR' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'FOOBAR is not a valid ISO2/ISO3 country code' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | should.not.exist json.geocoding.query['boundary.country'] 36 | -------------------------------------------------------------------------------- /test/ciao/search/boundary_country_valid_alpha2.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding country 3 | path: '/v1/search?text=a&boundary.country=US' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query['boundary.country'].should.eql 'USA' -------------------------------------------------------------------------------- /test/ciao/search/boundary_country_valid_alpha3.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding country 3 | path: '/v1/search?text=a&boundary.country=USA' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query['boundary.country'].should.eql 'USA' -------------------------------------------------------------------------------- /test/ciao/search/boundary_gid_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding gid 3 | path: '/v1/search?text=a&boundary.gid=abc::' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | json.geocoding.errors.should.eql [ 27 | 'abc:: does not follow source:layer:id format' 28 | ] 29 | 30 | #? expected warnings 31 | should.not.exist json.geocoding.warnings 32 | 33 | #? inputs 34 | json.geocoding.query['text'].should.eql 'a' 35 | json.geocoding.query['size'].should.eql 10 36 | should.not.exist json.geocoding.query['boundary.gid'] -------------------------------------------------------------------------------- /test/ciao/search/boundary_gid_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> bounding gid 3 | path: '/v1/search?text=a&boundary.gid=whosonfirst:neighbourhood:85869245' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query['boundary.gid'].should.eql '85869245' -------------------------------------------------------------------------------- /test/ciao/search/focus_point_invalid_lat.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> focus point 3 | path: '/v1/search?text=a&focus.point.lat=foo&focus.point.lon=-73.990342' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'missing param \'focus.point.lat\'' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | should.not.exist json.geocoding.query['focus.point.lat'] 36 | should.not.exist json.geocoding.query['focus.point.lon'] 37 | -------------------------------------------------------------------------------- /test/ciao/search/focus_point_invalid_lon.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> focus point 3 | path: '/v1/search?text=a&focus.point.lat=40.744243&focus.point.lon=' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'missing param \'focus.point.lon\'' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | json.geocoding.query['focus.point.lat'].should.eql 40.744243 36 | should.not.exist json.geocoding.query['focus.point.lon'] 37 | -------------------------------------------------------------------------------- /test/ciao/search/focus_point_missing_lat.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> focus point 3 | path: '/v1/search?text=a&focus.point.lon=-73.990342' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'parameters focus.point.lat and focus.point.lon must both be specified' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | should.not.exist json.geocoding.query['focus.point.lat'] 36 | should.not.exist json.geocoding.query['focus.point.lon'] 37 | -------------------------------------------------------------------------------- /test/ciao/search/focus_point_missing_lon.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> focus point 3 | path: '/v1/search?text=a&focus.point.lat=40.744243' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'parameters focus.point.lat and focus.point.lon must both be specified' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | should.not.exist json.geocoding.query['focus.point.lat'] 36 | should.not.exist json.geocoding.query['focus.point.lon'] 37 | -------------------------------------------------------------------------------- /test/ciao/search/focus_point_null_island.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> focus point null island 3 | path: '/v1/search?text=a&focus.point.lat=0&focus.point.lon=0' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query['focus.point.lat'].should.eql 0 35 | json.geocoding.query['focus.point.lon'].should.eql 0 -------------------------------------------------------------------------------- /test/ciao/search/focus_point_valid_duo.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> focus point 3 | path: '/v1/search?text=a&focus.point.lat=40.744243&focus.point.lon=-73.990342' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query['focus.point.lat'].should.eql 40.744243 35 | json.geocoding.query['focus.point.lon'].should.eql -73.990342 -------------------------------------------------------------------------------- /test/ciao/search/language_default.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/search?text=example' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? language 32 | json.geocoding.query['lang'].should.eql { 33 | name: 'English', 34 | iso6391: 'en', 35 | iso6393: 'eng', 36 | defaulted: true 37 | } 38 | -------------------------------------------------------------------------------- /test/ciao/search/language_header_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/search?text=example' 4 | headers: 'Accept-Language': 'example' 5 | 6 | #? 200 ok 7 | response.statusCode.should.be.equal 200 8 | response.should.have.header 'charset', 'utf8' 9 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 10 | 11 | #? valid geocoding block 12 | should.exist json.geocoding 13 | should.exist json.geocoding.version 14 | should.exist json.geocoding.attribution 15 | should.exist json.geocoding.query 16 | should.exist json.geocoding.engine 17 | should.exist json.geocoding.engine.name 18 | should.exist json.geocoding.engine.author 19 | should.exist json.geocoding.engine.version 20 | should.exist json.geocoding.timestamp 21 | 22 | #? valid geojson 23 | json.type.should.be.equal 'FeatureCollection' 24 | json.features.should.be.instanceof Array 25 | 26 | #? expected errors 27 | should.not.exist json.geocoding.errors 28 | 29 | #? expected warnings 30 | json.geocoding.warnings.should.eql [ 'invalid language provided via header' ] 31 | 32 | #? language 33 | json.geocoding.query['lang'].should.eql { 34 | name: 'English', 35 | iso6391: 'en', 36 | iso6393: 'eng', 37 | defaulted: true 38 | } 39 | -------------------------------------------------------------------------------- /test/ciao/search/language_header_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/search?text=example' 4 | headers: 'Accept-Language': 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5' 5 | 6 | #? 200 ok 7 | response.statusCode.should.be.equal 200 8 | response.should.have.header 'charset', 'utf8' 9 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 10 | 11 | #? valid geocoding block 12 | should.exist json.geocoding 13 | should.exist json.geocoding.version 14 | should.exist json.geocoding.attribution 15 | should.exist json.geocoding.query 16 | should.exist json.geocoding.engine 17 | should.exist json.geocoding.engine.name 18 | should.exist json.geocoding.engine.author 19 | should.exist json.geocoding.engine.version 20 | should.exist json.geocoding.timestamp 21 | 22 | #? valid geojson 23 | json.type.should.be.equal 'FeatureCollection' 24 | json.features.should.be.instanceof Array 25 | 26 | #? expected errors 27 | should.not.exist json.geocoding.errors 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? language 33 | json.geocoding.query['lang'].should.eql { 34 | defaulted: false, 35 | iso6391: 'fr', 36 | iso6393: 'fra', 37 | name: 'French' 38 | } 39 | -------------------------------------------------------------------------------- /test/ciao/search/language_querystring_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/search?lang=example&text=example' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | json.geocoding.warnings.should.eql [ 'invalid language provided via querystring' ] 30 | 31 | #? language 32 | json.geocoding.query['lang'].should.eql { 33 | name: 'English', 34 | iso6391: 'en', 35 | iso6393: 'eng', 36 | defaulted: true 37 | } 38 | -------------------------------------------------------------------------------- /test/ciao/search/language_querystring_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> language 3 | path: '/v1/search?lang=fr&text=example' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? language 32 | json.geocoding.query['lang'].should.eql { 33 | defaulted: false, 34 | iso6391: 'fr', 35 | iso6393: 'fra', 36 | name: 'French' 37 | } 38 | -------------------------------------------------------------------------------- /test/ciao/search/layers_alias_address.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> layer alias 3 | path: '/v1/search?text=a&layers=address' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query.layers.should.eql ["address"] 35 | -------------------------------------------------------------------------------- /test/ciao/search/layers_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> layer alias 3 | path: '/v1/search?text=a&layers=notlayer' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,street,country,macroregion,region,county,localadmin,locality,borough,neighbourhood,continent,empire,dependency,macrocounty,macrohood,microhood,disputed,postalcode,ocean,marinearea' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | -------------------------------------------------------------------------------- /test/ciao/search/layers_multiple.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> layer alias 3 | path: '/v1/search?text=a&layers=country,region' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query.layers.should.eql ["country","region"] 35 | -------------------------------------------------------------------------------- /test/ciao/search/layers_single.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> layer alias 3 | path: '/v1/search?text=a&layers=country' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query.layers.should.eql ["country"] 35 | -------------------------------------------------------------------------------- /test/ciao/search/no_params.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> no params specified 3 | path: '/v1/search' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'invalid param \'text\': text length, must be >0' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['size'].should.eql 10 34 | -------------------------------------------------------------------------------- /test/ciao/search/non_scalar_parameter_array.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> define two sources 3 | path: '/v1/search?text=A&sources=A&sources=B' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected warnings 26 | should.not.exist json.geocoding.warnings 27 | 28 | #? expected errors 29 | should.exist json.geocoding.errors 30 | json.geocoding.errors.should.eql [ '\'sources\' parameter can only have one value' ] 31 | -------------------------------------------------------------------------------- /test/ciao/search/non_scalar_parameter_object.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> define parameter as object 3 | path: '/v1/search?text=A¶meter[idx]=value' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected warnings 26 | should.not.exist json.geocoding.warnings 27 | 28 | #? expected errors 29 | should.exist json.geocoding.errors 30 | json.geocoding.errors.should.eql [ '\'parameter\' parameter must be a scalar' ] 31 | -------------------------------------------------------------------------------- /test/ciao/search/privacy_false.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> accept privacy var 3 | path: '/v1/search?text=a&private=false' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query['private'].should.eql false -------------------------------------------------------------------------------- /test/ciao/search/privacy_true.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> accept privacy var 3 | path: '/v1/search?text=a&private=true' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query['private'].should.eql true -------------------------------------------------------------------------------- /test/ciao/search/size_over_max.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> set size 3 | path: '/v1/search?text=a&size=999' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.exist json.geocoding.warnings 30 | json.geocoding.warnings.should.eql [ 'out-of-range integer \'size\', using MAX_SIZE' ] 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 40 -------------------------------------------------------------------------------- /test/ciao/search/size_under_min.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> set size 3 | path: '/v1/search?text=a&size=0' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.exist json.geocoding.warnings 30 | json.geocoding.warnings.should.eql [ 'out-of-range integer \'size\', using MIN_SIZE' ] 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 1 -------------------------------------------------------------------------------- /test/ciao/search/size_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> set size 3 | path: '/v1/search?text=a&size=3' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 3 -------------------------------------------------------------------------------- /test/ciao/search/sources_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> sources filter 3 | path: '/v1/search?text=a&sources=openstreetmap,notasource' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ '\'notasource\' is an invalid sources parameter. Valid options: osm,oa,gn,wof,openstreetmap,openaddresses,geonames,whosonfirst' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | json.geocoding.query['text'].should.eql 'a' 34 | json.geocoding.query['size'].should.eql 10 35 | -------------------------------------------------------------------------------- /test/ciao/search/sources_layers_valid_combo.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> sources and layers specified 3 | path: '/v1/search?text=a&sources=openaddresses&layers=address' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query.layers.should.eql ["address"] 35 | json.geocoding.query.sources.should.eql ["openaddresses"] 36 | -------------------------------------------------------------------------------- /test/ciao/search/sources_multiple.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> sources filter 3 | path: "/v1/search?text=a&sources=openstreetmap,geonames" 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header "charset", 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query.sources.should.eql ["openstreetmap", "geonames"] 35 | -------------------------------------------------------------------------------- /test/ciao/search/sources_single.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> sources filter 3 | path: '/v1/search?text=a&sources=openstreetmap' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | json.geocoding.query.sources.should.eql ["openstreetmap"] 35 | -------------------------------------------------------------------------------- /test/ciao/search/text_invalid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> basic search 3 | path: '/v1/search?text=' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 400 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.exist json.geocoding.errors 27 | json.geocoding.errors.should.eql [ 'invalid param \'text\': text length, must be >0' ] 28 | 29 | #? expected warnings 30 | should.not.exist json.geocoding.warnings 31 | 32 | #? inputs 33 | should.not.exist json.geocoding.query['text'] 34 | json.geocoding.query['size'].should.eql 10 35 | -------------------------------------------------------------------------------- /test/ciao/search/text_valid.coffee: -------------------------------------------------------------------------------- 1 | 2 | #> basic search 3 | path: '/v1/search?text=a' 4 | 5 | #? 200 ok 6 | response.statusCode.should.be.equal 200 7 | response.should.have.header 'charset', 'utf8' 8 | response.should.have.header 'content-type', 'application/json; charset=utf-8' 9 | 10 | #? valid geocoding block 11 | should.exist json.geocoding 12 | should.exist json.geocoding.version 13 | should.exist json.geocoding.attribution 14 | should.exist json.geocoding.query 15 | should.exist json.geocoding.engine 16 | should.exist json.geocoding.engine.name 17 | should.exist json.geocoding.engine.author 18 | should.exist json.geocoding.engine.version 19 | should.exist json.geocoding.timestamp 20 | 21 | #? valid geojson 22 | json.type.should.be.equal 'FeatureCollection' 23 | json.features.should.be.instanceof Array 24 | 25 | #? expected errors 26 | should.not.exist json.geocoding.errors 27 | 28 | #? expected warnings 29 | should.not.exist json.geocoding.warnings 30 | 31 | #? inputs 32 | json.geocoding.query['text'].should.eql 'a' 33 | json.geocoding.query['size'].should.eql 10 34 | -------------------------------------------------------------------------------- /test/test-pelias-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "api": { 3 | "targets": { 4 | "auto_discover": false 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/unit/app.js: -------------------------------------------------------------------------------- 1 | const proxyquire = require('proxyquire').noCallThru(); 2 | 3 | module.exports.tests = {}; 4 | 5 | module.exports.tests.invalid_configuration = (test, common) => { 6 | test('configuration validation throwing error should rethrow', (t) => { 7 | t.throws(() => { 8 | proxyquire('../../app', { 9 | './schema': 'this is the schema', 10 | 'pelias-config': { 11 | generate: (schema) => { 12 | // the schema passed to generate should be the require'd schema 13 | t.equals(schema, 'this is the schema'); 14 | 15 | throw Error('config is not valid'); 16 | } 17 | } 18 | }); 19 | 20 | }, /config is not valid/); 21 | 22 | t.end(); 23 | 24 | }); 25 | 26 | }; 27 | 28 | module.exports.all = (tape, common) => { 29 | 30 | function test(name, testFunction) { 31 | return tape('app: ' + name, testFunction); 32 | } 33 | 34 | for( var testCase in module.exports.tests ){ 35 | module.exports.tests[testCase](test, common); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /test/unit/fixture/dedupe_elasticsearch_custom_layer_results.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | '_id': '101914069', 4 | 'layer': 'venue', 5 | 'source': 'openstreetmap', 6 | 'name': { 7 | 'default': 'Nike World Headquarters' 8 | }, 9 | 'parent': { 10 | 'country_a': ['USA'], 11 | 'country': ['United States'], 12 | 'region': ['Oregon'], 13 | 'region_id': ['85688513'] 14 | }, 15 | 'confidence': 0.98 16 | }, 17 | { 18 | '_id': '2456::trimet::major_employer', 19 | 'layer': 'major_employer', 20 | 'source': 'transit', 21 | 'name': { 22 | 'default': 'Nike World Headquarters' 23 | }, 24 | 'parent': { 25 | 'country_a': ['USA'], 26 | 'country': ['United States'], 27 | 'region': ['Oregon'], 28 | 'region_id': ['85688513'] 29 | }, 30 | 'confidence': 0.50 31 | } 32 | ]; -------------------------------------------------------------------------------- /test/unit/fixture/dedupe_only_postalcode_differs.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | '_id': '101914069', 4 | 'layer': 'venue', 5 | 'source': 'openstreetmap', 6 | 'name': { 7 | 'default': 'A place' 8 | }, 9 | 'parent': { 10 | 'country_a': ['USA'] 11 | } 12 | }, 13 | { 14 | '_id': '323', 15 | 'layer': 'venue', 16 | 'source': 'openstreetmap', 17 | 'name': { 18 | 'default': 'A place' 19 | }, 20 | 'address_parts': { 21 | 'zip': '97005' 22 | }, 23 | 'parent': { 24 | 'country_a': ['USA'] 25 | } 26 | } 27 | ]; 28 | -------------------------------------------------------------------------------- /test/unit/helper/stackTraceLine.js: -------------------------------------------------------------------------------- 1 | const stackTraceLine = require('../../../helper/stackTraceLine'); 2 | 3 | module.exports.tests = {}; 4 | module.exports.tests.stackTrace = (test, common) => { 5 | test('No exceptions thrown when function is called', (t) => { 6 | t.doesNotThrow(stackTraceLine, 'No exceptions thrown'); 7 | t.end(); 8 | }); 9 | }; 10 | 11 | module.exports.all = function (tape, common) { 12 | 13 | function test(name, testFunction) { 14 | return tape('[helper] stackTraceLine: ' + name, testFunction); 15 | } 16 | 17 | for( var testCase in module.exports.tests ){ 18 | module.exports.tests[testCase](test, common); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /test/unit/middleware/distance.js: -------------------------------------------------------------------------------- 1 | var distance = require('../../../middleware/distance')(); 2 | 3 | module.exports.tests = {}; 4 | 5 | module.exports.tests.computeDistance = function(test, common) { 6 | test('valid lat/lon and results', function(t) { 7 | var req = { 8 | clean: { 9 | 'point.lat': 45, 10 | 'point.lon': -77 11 | } 12 | }; 13 | var res = { 14 | data: [ 15 | { 16 | center_point: { 17 | lat: 40, 18 | lon: -71 19 | } 20 | } 21 | ] 22 | }; 23 | 24 | var expected = 742.735; 25 | distance(req, res, function () { 26 | t.equal(res.data[0].distance, expected, 'correct distance computed'); 27 | t.end(); 28 | }); 29 | }); 30 | }; 31 | 32 | module.exports.all = function (tape, common) { 33 | 34 | function test(name, testFunction) { 35 | return tape('[middleware] distance: ' + name, testFunction); 36 | } 37 | 38 | for( var testCase in module.exports.tests ){ 39 | module.exports.tests[testCase](test, common); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /test/unit/query/MockQuery.js: -------------------------------------------------------------------------------- 1 | module.exports = class MockQuery { 2 | constructor() { 3 | this._score_functions = []; 4 | this._sort_functions = []; 5 | this._filter_functions = []; 6 | } 7 | 8 | render(vs) { 9 | return { 10 | vs: vs, 11 | score_functions: this._score_functions, 12 | sort_functions: this._sort_functions, 13 | filter_functions: this._filter_functions 14 | }; 15 | } 16 | 17 | score(view) { 18 | this._score_functions.push(view); 19 | return this; 20 | } 21 | 22 | sort(view) { 23 | this._sort_functions.push(view); 24 | return this; 25 | } 26 | 27 | filter(view) { 28 | this._filter_functions.push(view); 29 | return this; 30 | } 31 | 32 | }; 33 | -------------------------------------------------------------------------------- /test/unit/query/autocomplete_defaults.js: -------------------------------------------------------------------------------- 1 | var defaults = require('../../../query/autocomplete_defaults'); 2 | 3 | module.exports.tests = {}; 4 | 5 | module.exports.tests.interface = function(test, common) { 6 | test('valid interface', function(t) { 7 | t.equal(typeof defaults, 'object', 'defaults defined'); 8 | t.end(); 9 | }); 10 | }; 11 | 12 | module.exports.all = function (tape, common) { 13 | 14 | function test(name, testFunction) { 15 | return tape('autocomplete defaults ' + name, testFunction); 16 | } 17 | 18 | for( var testCase in module.exports.tests ){ 19 | module.exports.tests[testCase](test, common); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /test/unit/query/reverse_defaults.js: -------------------------------------------------------------------------------- 1 | var defaults = require('../../../query/reverse_defaults'); 2 | 3 | module.exports.tests = {}; 4 | 5 | module.exports.tests.interface = function(test, common) { 6 | test('valid interface', function(t) { 7 | t.equal(typeof defaults, 'object', 'defaults defined'); 8 | t.end(); 9 | }); 10 | }; 11 | 12 | module.exports.all = function (tape, common) { 13 | 14 | function test(name, testFunction) { 15 | return tape('reverse defaults ' + name, testFunction); 16 | } 17 | 18 | for( var testCase in module.exports.tests ){ 19 | module.exports.tests[testCase](test, common); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /test/unit/query/search_defaults.js: -------------------------------------------------------------------------------- 1 | var defaults = require('../../../query/search_defaults'); 2 | 3 | module.exports.tests = {}; 4 | 5 | module.exports.tests.interface = function(test, common) { 6 | test('valid interface', function(t) { 7 | t.equal(typeof defaults, 'object', 'defaults defined'); 8 | t.end(); 9 | }); 10 | }; 11 | 12 | module.exports.all = function (tape, common) { 13 | 14 | function test(name, testFunction) { 15 | return tape('search defaults ' + name, testFunction); 16 | } 17 | 18 | for( var testCase in module.exports.tests ){ 19 | module.exports.tests[testCase](test, common); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /test/unit/sanitizer/_request_language.js: -------------------------------------------------------------------------------- 1 | const sanitizer = require('../../../sanitizer/_request_language')(); 2 | 3 | module.exports.tests = {}; 4 | 5 | module.exports.tests.do_nothing = (test, common) => { 6 | test('sanitize should return empty warnings/erros', t => { 7 | const messages = sanitizer.sanitize(); 8 | 9 | t.deepEquals(messages.errors, [], 'no errors'); 10 | t.deepEquals(messages.warnings, [], 'no warnings'); 11 | t.end(); 12 | 13 | }); 14 | 15 | }; 16 | 17 | module.exports.tests.expected = (test, common) => { 18 | test('expected should contain only \'lang\'', t => { 19 | const expected = sanitizer.expected(); 20 | 21 | t.deepEquals(expected, [{'name': 'lang'}]); 22 | t.end(); 23 | 24 | }); 25 | }; 26 | 27 | module.exports.all = (tape, common) => { 28 | function test(name, testFunction) { 29 | return tape(`SANITIZE _request_language: ${name}`, testFunction); 30 | } 31 | 32 | for( const testCase in module.exports.tests ){ 33 | module.exports.tests[testCase](test, common); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /test/unit/sanitizer/_truthy.js: -------------------------------------------------------------------------------- 1 | var isTruthy = require('../../../sanitizer/_truthy'); 2 | 3 | module.exports.tests = {}; 4 | 5 | module.exports.tests.sanitize_truthy = function(test, common) { 6 | var valid_values = ['true', true, 1, '1', 'yes', 'y']; 7 | valid_values.forEach(function(value) { 8 | test('truthy value ' + value, function(t) { 9 | t.equal(isTruthy(value), true, 'returns true'); 10 | t.end(); 11 | }); 12 | }); 13 | 14 | var valid_false_values = ['false', false, 0, '0', 'no', 'n', null, -1, 123, NaN, 'abc']; 15 | valid_false_values.forEach(function(value) { 16 | test('falsey value ' + value, function(t) { 17 | t.equal(isTruthy(value), false, 'returns false'); 18 | t.end(); 19 | }); 20 | }); 21 | }; 22 | 23 | module.exports.all = function (tape, common) { 24 | function test(name, testFunction) { 25 | return tape('SANTIZE _truthy ' + name, testFunction); 26 | } 27 | 28 | for( var testCase in module.exports.tests ){ 29 | module.exports.tests[testCase](test, common); 30 | } 31 | }; 32 | --------------------------------------------------------------------------------