From 7c74a3dca905e7b51c898920f01c943d39102d62 Mon Sep 17 00:00:00 2001 From: Junqiu Lei Date: Thu, 23 Mar 2023 09:42:14 -0700 Subject: [PATCH] Add maps stats api Signed-off-by: Junqiu Lei --- CHANGELOG.md | 1 + common/index.ts | 5 +- server/common/stats/stats_helper.ts | 84 +++++++++++++++++++++++++++++ server/common/stats/type.ts | 25 +++++++++ server/plugin.ts | 6 ++- server/routes/index.ts | 3 +- server/routes/stats_router.ts | 45 ++++++++++++++++ 7 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 server/common/stats/stats_helper.ts create mode 100644 server/common/stats/type.ts create mode 100644 server/routes/stats_router.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 29c58d23..5df4a4ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Infrastructure * Add CHANGELOG ([#342](https://github.com/opensearch-project/dashboards-maps/pull/342)) +* Add maps metrics api from saved object ([#362](https://github.com/opensearch-project/dashboards-maps/pull/362)) ### Documentation diff --git a/common/index.ts b/common/index.ts index 921bf002..85fb491e 100644 --- a/common/index.ts +++ b/common/index.ts @@ -82,8 +82,11 @@ export const APP_PATH = { LANDING_PAGE_PATH: '/', CREATE_MAP: '/create', EDIT_MAP: '/:id', + STATS: '/stats', }; +export const APP_API = '/api/maps-dashboards'; + export enum DASHBOARDS_MAPS_LAYER_NAME { OPENSEARCH_MAP = 'OpenSearch map', DOCUMENTS = 'Documents', @@ -147,7 +150,7 @@ export const LAYER_ICON_TYPE_MAP: { [key: string]: string } = { [DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP]: 'globe', }; -//refer https://github.com/opensearch-project/i18n-plugin/blob/main/DEVELOPER_GUIDE.md#new-locale for OSD supported languages +// refer https://github.com/opensearch-project/i18n-plugin/blob/main/DEVELOPER_GUIDE.md#new-locale for OSD supported languages export const OSD_LANGUAGES = ['en', 'es', 'fr', 'de', 'ja', 'ko', 'zh']; // all these codes are also supported in vector tiles map export const FALLBACK_LANGUAGE = 'en'; diff --git a/server/common/stats/stats_helper.ts b/server/common/stats/stats_helper.ts new file mode 100644 index 00000000..abe463d1 --- /dev/null +++ b/server/common/stats/stats_helper.ts @@ -0,0 +1,84 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SavedObjectsFindResponse } from '../../../../../src/core/server'; +import { MapLayerSpecification } from '../../../public/model/mapLayerType'; +import { MapSavedObjectAttributes } from '../../../common/map_saved_object_attributes'; +import { MAP_STATS, MAPS_STATS } from './type'; +import { DASHBOARDS_MAPS_LAYER_TYPE } from '../../../common'; + +export const getStats = (mapsSavedObjects: SavedObjectsFindResponse) => { + let totalLayersCount = 0; + let totalDocumentsLayersCount = 0; + let totalOpensearchBaseLayersCount = 0; + let totalCustomTmsLayersCount = 0; + let totalCustomWmsLayersCount = 0; + let totalLayersFiltersCount = 0; + const mapsInfo: MAP_STATS[] = []; + mapsSavedObjects.saved_objects.forEach((mapRes) => { + let documentsLayersCount = 0; + let opensearchBaseLayersCount = 0; + let customTmsLayersCount = 0; + let customWmsLayersCount = 0; + let layersFiltersCount = 0; + const layerListJsonString = mapRes.attributes.layerList as string; + const layerList: MapLayerSpecification[] = layerListJsonString + ? JSON.parse(layerListJsonString) + : []; + const layersCount = layerList.length; + totalLayersCount += layerList.length; + + layerList.forEach((layer) => { + switch (layer.type) { + case DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS: + totalDocumentsLayersCount++; + documentsLayersCount++; + const layerFiltersCount = layer.source.filters.length; + layersFiltersCount += layerFiltersCount; + totalLayersFiltersCount += layerFiltersCount; + break; + case DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP: + totalOpensearchBaseLayersCount++; + opensearchBaseLayersCount++; + break; + case DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP: + if (layer.source.customType === 'wms') { + totalCustomWmsLayersCount++; + customWmsLayersCount++; + } else { + totalCustomTmsLayersCount++; + customTmsLayersCount++; + } + break; + } + }); + + mapsInfo.push({ + id: mapRes.id, + layers_filters_total: layersFiltersCount, + layers_count: { + total: layersCount, + documents: documentsLayersCount, + opensearch_base_map: opensearchBaseLayersCount, + custom_tms: customTmsLayersCount, + custom_wms: customWmsLayersCount, + }, + }); + }); + + const metrics: MAPS_STATS = { + maps_total: mapsSavedObjects.total, + layers_filters_total: totalLayersFiltersCount, + layers_count: { + total: totalLayersCount, + documents: totalDocumentsLayersCount, + opensearch_base_map: totalOpensearchBaseLayersCount, + custom_tms: totalCustomTmsLayersCount, + custom_wms: totalCustomWmsLayersCount, + }, + maps: mapsInfo, + }; + return metrics; +}; diff --git a/server/common/stats/type.ts b/server/common/stats/type.ts new file mode 100644 index 00000000..c4894c62 --- /dev/null +++ b/server/common/stats/type.ts @@ -0,0 +1,25 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export interface MAPS_STATS { + maps_total: number; + layers_filters_total: number; + layers_count: LAYER_STATS; + maps: MAP_STATS[]; +} + +export interface MAP_STATS { + id: string; + layers_filters_total: number; + layers_count: LAYER_STATS; +} + +export interface LAYER_STATS { + total: number; + documents: number; + opensearch_base_map: number; + custom_tms: number; + custom_wms: number; +} diff --git a/server/plugin.ts b/server/plugin.ts index cae4e8ce..e51c01fc 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -22,13 +22,14 @@ import { } from './types'; import { createGeospatialCluster } from './clusters'; import { GeospatialService, OpensearchService } from './services'; -import { geospatial, opensearch } from '../server/routes'; +import { geospatial, opensearch, statsRoute } from '../server/routes'; import { mapSavedObjectsType } from './saved_objects'; import { capabilitiesProvider } from './saved_objects/capabilities_provider'; import { ConfigSchema } from '../common/config'; export class CustomImportMapPlugin - implements Plugin { + implements Plugin +{ private readonly logger: Logger; private readonly globalConfig$; private readonly config$; @@ -62,6 +63,7 @@ export class CustomImportMapPlugin // Register server side APIs geospatial(geospatialService, router); opensearch(opensearchService, router); + statsRoute(router); // Register saved object types core.savedObjects.registerType(mapSavedObjectsType); diff --git a/server/routes/index.ts b/server/routes/index.ts index f3ed5073..9a8032ba 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -5,5 +5,6 @@ import geospatial from './geospatial'; import opensearch from './opensearch'; +import { statsRoute } from './stats_router'; -export { geospatial, opensearch }; +export { geospatial, opensearch, statsRoute }; diff --git a/server/routes/stats_router.ts b/server/routes/stats_router.ts new file mode 100644 index 00000000..feea7ae7 --- /dev/null +++ b/server/routes/stats_router.ts @@ -0,0 +1,45 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ResponseError } from '@opensearch-project/opensearch/lib/errors'; +import { + IOpenSearchDashboardsResponse, + IRouter, + SavedObjectsFindResponse, +} from '../../../../src/core/server'; +import { APP_API, APP_PATH, MAP_SAVED_OBJECT_TYPE } from '../../common'; +import { getStats } from '../common/stats/stats_helper'; +import { MapSavedObjectAttributes } from '../../common/map_saved_object_attributes'; + +export function statsRoute(router: IRouter) { + router.get( + { + path: `${APP_API}${APP_PATH.STATS}`, + validate: false, + }, + async ( + context, + request, + response + ): Promise> => { + try { + const savedObjectsClient = context.core.savedObjects.client; + const mapsSavedObjects: SavedObjectsFindResponse = + await savedObjectsClient?.find({ type: MAP_SAVED_OBJECT_TYPE }); + const stats = getStats(mapsSavedObjects); + return response.ok({ + body: stats, + }); + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + return response.custom({ + statusCode: error.statusCode || 500, + body: error.message, + }); + } + } + ); +}