superdesk/superdesk-client-core

View on GitHub
scripts/apps/authoring/metadata/PlacesService.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import {httpRequestJsonLocal} from 'core/helpers/network';
import {omit} from 'lodash';
import {IBaseRestApiResponse, ILocated, IRestApiResponse} from 'superdesk-api';

export interface IGeoName {
    /** name of the place, eg. Prague */
    name: string;

    /** geonames id, eg. "3073494" */
    code: string;

    state: string;
    state_code: string;
    region?: string;
    region_code?: string;

    country: string;
    country_code: string;
    feature_class?: string;

    location?: {
        lat: number;
        lan: number;
    };

    /** timezone identifier, eg. Europe/Prague */
    tz: string;

    scheme: 'geonames';
}

/**
 * Search service for populated places (city, village)
 */
export interface IPlacesService {

    /**
     * Search for dateline
     *
     * it will use geonames if available, cities cv otherwise
     *
     * @param query must be included in place name
     * @param lang ISO-639 2-letter language code (en)
     */
    searchDateline: (query: string, lang: string) => Promise<Array<IGeoName>>;

    /**
     * Search for place using geonames
     *
     * @param query must be included in place name
     * @param lang ISO-639 2-letter language code (en)
     */
    searchGeonames: (query: string, lang: string) => Promise<Array<ILocated>>;
}

PlacesServiceFactory.$inject = ['api', 'features', 'metadata'];
export default function PlacesServiceFactory(api, features, metadata) {
    const geoNameToCity = (data: IGeoName): ILocated => ({
        dateline: 'city',
        country_code: data.country_code,
        tz: data.tz,
        city_code: data.name,
        state_code: data.state_code,
        state: data.state,
        city: data.name,
        country: data.country,
        code: data.code,
        scheme: data.scheme,
        place: data,
    });

    class PlacesService implements IPlacesService {
        searchDateline(query: string, lang: string, abortSignal?: AbortSignal) {
            return this._searchGeonames(query, lang, true, abortSignal)
                .then((geonames) => geonames.map((x) => geoNameToCity(x)))
                .catch(() => this._searchCities(query));
        }

        searchGeonames(query: string, lang: string) {
            return this._searchGeonames(query, lang);
        }

        _searchCities(name: string) {
            return metadata.fetchCities().then((cities) =>
                name != null && name.length
                    ? cities.filter((t) => t.city.toLowerCase().indexOf(name.toLowerCase()) !== -1)
                    : cities,
            );
        }

        _searchGeonames(name: string, lang: string, dateline: boolean = false, abortSignal?: AbortSignal) {
            const params = {name, lang};

            if (name == null || name.length === 0) {
                return Promise.resolve([]);
            }

            if (dateline) {
                params['style'] = 'full';
                params['featureClass'] = 'P';
            }

            return features.places_autocomplete
                ? httpRequestJsonLocal<IRestApiResponse<ILocated>>({
                    method: 'GET',
                    abortSignal: abortSignal,
                    path: '/places_autocomplete',
                    urlParams: params,
                })
                    .then((response) => response._items.map((place) => omit(place, ['_created', '_updated', '_etag'])))
                : Promise.reject();
        }
    }

    return new PlacesService();
}