/** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { generateExtensionUrlPattern, generatePath, } from '@finos/legend-application/browser'; import { addQueryParametersToUrl, stringifyQueryParams, } from '@finos/legend-shared'; import { generateGAVCoordinates } from '@finos/legend-storage'; import { btoaURLSafe } from '@finos/legend-application'; import { DataProductAccessType } from '@finos/legend-graph'; export enum LEGEND_QUERY_ROUTE_PATTERN_TOKEN { GAV = 'gav', QUERY_ID = 'queryId', MAPPING_PATH = 'mappingPath', RUNTIME_PATH = 'runtimePath', SERVICE_PATH = 'servicePath', } export enum DATA_PRODUCT_QUERY_CREATOR_ROUTE_PATTERN_TOKEN { GAV = 'gav', DATA_PRODUCT_PATH = 'dataProductPath', DATA_PRODUCT_ACCESS_TYPE = 'accessType', DATA_PRODUCT_ACCESS_ID = 'accessId', } export enum DATA_PRODUCT_SAMPLE_QUERY_CREATOR_ROUTE_PATTERN_TOKEN { GAV = 'gav', DATA_PRODUCT_PATH = 'dataProductPath', SAMPLE_QUERY_ID = 'sampleQueryId', } export enum INGEST_QUERY_CREATOR_ROUTE_PATTERN_TOKEN { GAV = 'gav', INGEST_DEFINITION_PATH = 'ingestDefinitionPath', DATA_SET = 'dataSet', } export const LEGEND_QUERY_ROUTE_PATTERN = Object.freeze({ DEFAULT: '/', SETUP: '/setup', EDIT_EXISTING_QUERY_SETUP: '/setup/existing-query', CREATE_MAPPING_QUERY_SETUP: '/setup/manual', CLONE_SERVICE_QUERY_SETUP: '/setup/clone-service-query', QUERY_PRODUCTIONIZER_SETUP: '/setup/productionize-query', UPDATE_EXISTING_SERVICE_QUERY_SETUP: '/setup/update-existing-service-query', LOAD_PROJECT_SERVICE_QUERY_SETUP: '/setup/load-project-service-query', CREATE_FROM_MAPPING_QUERY: `/create/manual/:${LEGEND_QUERY_ROUTE_PATTERN_TOKEN.GAV}/:${LEGEND_QUERY_ROUTE_PATTERN_TOKEN.MAPPING_PATH}/:${LEGEND_QUERY_ROUTE_PATTERN_TOKEN.RUNTIME_PATH}`, CREATE_FROM_SERVICE_QUERY: `/create-from-service/:${LEGEND_QUERY_ROUTE_PATTERN_TOKEN.GAV}/:${LEGEND_QUERY_ROUTE_PATTERN_TOKEN.SERVICE_PATH}`, EDIT_EXISTING_QUERY: `/edit/:${LEGEND_QUERY_ROUTE_PATTERN_TOKEN.QUERY_ID}`, DATA_CUBE_EXISTING_QUERY: `/edit/:${LEGEND_QUERY_ROUTE_PATTERN_TOKEN.QUERY_ID}/cube`, DATA_PRODUCT: `/data-product/:${DATA_PRODUCT_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_PRODUCT_ACCESS_TYPE}/:${DATA_PRODUCT_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.GAV}/:${DATA_PRODUCT_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_PRODUCT_PATH}/:${DATA_PRODUCT_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_PRODUCT_ACCESS_ID}`, DATA_PRODUCT_SAMPLE_QUERY: `/data-product/native/sample-query/:${DATA_PRODUCT_SAMPLE_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.GAV}/:${DATA_PRODUCT_SAMPLE_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_PRODUCT_PATH}/:${DATA_PRODUCT_SAMPLE_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.SAMPLE_QUERY_ID}`, INGEST_QUERY: `/ingest/:${INGEST_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.GAV}/:${INGEST_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.INGEST_DEFINITION_PATH}/:${INGEST_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_SET}`, // Developer-only diagnostic page for inspecting depot DataSpace // analytics artifact sizes. See `DataSpaceArtifactInspector`. DEV_DATA_SPACE_INSPECTOR: '/dev/dataspace-inspector', }); // DataProduct export type DataProductPathParams = { [DATA_PRODUCT_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.GAV]: string; [DATA_PRODUCT_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_PRODUCT_PATH]: string; [DATA_PRODUCT_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_PRODUCT_ACCESS_TYPE]: string; [DATA_PRODUCT_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_PRODUCT_ACCESS_ID]: string; }; export type DataProductSampleQueryPathParams = { [DATA_PRODUCT_SAMPLE_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.GAV]: string; [DATA_PRODUCT_SAMPLE_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_PRODUCT_PATH]: string; [DATA_PRODUCT_SAMPLE_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.SAMPLE_QUERY_ID]: string; }; export type IngestQueryCreatorPathParams = { [INGEST_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.GAV]: string; [INGEST_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.INGEST_DEFINITION_PATH]: string; [INGEST_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_SET]: string; }; /** * Generates a data product route for the given access type. */ export const generateDataProductRoute = ( groupId: string, artifactId: string, versionId: string, dataProductPath: string, executionType: string, accessPointId: string, ): string => generatePath(LEGEND_QUERY_ROUTE_PATTERN.DATA_PRODUCT, { [DATA_PRODUCT_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_PRODUCT_ACCESS_TYPE]: executionType, [DATA_PRODUCT_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.GAV]: generateGAVCoordinates(groupId, artifactId, versionId), [DATA_PRODUCT_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_PRODUCT_PATH]: dataProductPath, [DATA_PRODUCT_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_PRODUCT_ACCESS_ID]: accessPointId, }); export const generateDataProductNativeRoute = ( groupId: string, artifactId: string, versionId: string, dataProductPath: string, executionContextKey: string, ): string => generateDataProductRoute( groupId, artifactId, versionId, dataProductPath, DataProductAccessType.NATIVE, executionContextKey, ); export const generateDataProductModelRoute = ( groupId: string, artifactId: string, versionId: string, dataProductPath: string, accessPointId: string, ): string => generateDataProductRoute( groupId, artifactId, versionId, dataProductPath, DataProductAccessType.MODEL, accessPointId, ); export const generateDataProductLakehouseRoute = ( groupId: string, artifactId: string, versionId: string, dataProductPath: string, accessPointId: string, ): string => generateDataProductRoute( groupId, artifactId, versionId, dataProductPath, DataProductAccessType.LAKEHOUSE, accessPointId, ); export const generateDataProductSampleQueryRoute = ( groupId: string, artifactId: string, versionId: string, dataProductPath: string, sampleQueryId: string, ): string => generatePath(LEGEND_QUERY_ROUTE_PATTERN.DATA_PRODUCT_SAMPLE_QUERY, { [DATA_PRODUCT_SAMPLE_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.GAV]: generateGAVCoordinates(groupId, artifactId, versionId), [DATA_PRODUCT_SAMPLE_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_PRODUCT_PATH]: dataProductPath, [DATA_PRODUCT_SAMPLE_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.SAMPLE_QUERY_ID]: sampleQueryId, }); // TODO: figure out where this URL should be generated from. Candidates: // - a "create ingest query" entry point in the query setup landing page // - a deep-link from the ingest definition page in Legend Studio // Once decided, wire callers here. export const generateIngestQueryCreatorRoute = ( groupId: string, artifactId: string, versionId: string, ingestDefinitionPath: string, dataSet: string, ): string => generatePath(LEGEND_QUERY_ROUTE_PATTERN.INGEST_QUERY, { [INGEST_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.GAV]: generateGAVCoordinates( groupId, artifactId, versionId, ), [INGEST_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.INGEST_DEFINITION_PATH]: ingestDefinitionPath, [INGEST_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_SET]: dataSet, }); // setup export enum LEGEND_QUERY_SETUP_QUERY_PARAM_TOKEN { SHOW_ALL_GROUPS = 'showAllGroups', SHOW_ADVANCED_ACTIONS = 'showAdvancedActions', TAG = 'tag', } export enum LEGEND_QUERY_QUERY_PARAM_TOKEN { SERVICE_EXECUTION_KEY = 'executionKey', } export type QuerySetupQueryParams = { [LEGEND_QUERY_SETUP_QUERY_PARAM_TOKEN.SHOW_ALL_GROUPS]?: string | undefined; [LEGEND_QUERY_SETUP_QUERY_PARAM_TOKEN.SHOW_ADVANCED_ACTIONS]?: | string | undefined; [LEGEND_QUERY_SETUP_QUERY_PARAM_TOKEN.TAG]?: string | undefined; }; export const generateQuerySetupRoute = ( showAllGroups?: boolean | undefined, showAdvancedActions?: boolean | undefined, tag?: string | undefined, ): string => addQueryParametersToUrl( generatePath(LEGEND_QUERY_ROUTE_PATTERN.SETUP, {}), stringifyQueryParams({ [LEGEND_QUERY_SETUP_QUERY_PARAM_TOKEN.SHOW_ALL_GROUPS]: showAllGroups ? encodeURIComponent(showAllGroups) : showAllGroups, [LEGEND_QUERY_SETUP_QUERY_PARAM_TOKEN.SHOW_ADVANCED_ACTIONS]: showAdvancedActions ? encodeURIComponent(showAdvancedActions) : undefined, [LEGEND_QUERY_SETUP_QUERY_PARAM_TOKEN.TAG]: tag ? encodeURIComponent(tag) : undefined, }), ); export const generateEditExistingQuerySetupRoute = (): string => generatePath(LEGEND_QUERY_ROUTE_PATTERN.EDIT_EXISTING_QUERY_SETUP, {}); export const generateCreateMappingQuerySetupRoute = (): string => generatePath(LEGEND_QUERY_ROUTE_PATTERN.CREATE_MAPPING_QUERY_SETUP, {}); export const generateCloneServiceQuerySetupRoute = (): string => generateExtensionUrlPattern( generatePath(LEGEND_QUERY_ROUTE_PATTERN.CLONE_SERVICE_QUERY_SETUP, {}), ); export const generateQueryProductionizerSetupRoute = (): string => generateExtensionUrlPattern( generatePath(LEGEND_QUERY_ROUTE_PATTERN.QUERY_PRODUCTIONIZER_SETUP, {}), ); export const generateUpdateExistingServiceQuerySetup = (): string => generateExtensionUrlPattern( generatePath( LEGEND_QUERY_ROUTE_PATTERN.UPDATE_EXISTING_SERVICE_QUERY_SETUP, {}, ), ); export const generateLoadProjectServiceQuerySetup = (): string => generateExtensionUrlPattern( generatePath( LEGEND_QUERY_ROUTE_PATTERN.LOAD_PROJECT_SERVICE_QUERY_SETUP, {}, ), ); export const generateMappingQueryCreatorRoute = ( groupId: string, artifactId: string, versionId: string, mappingPath: string, runtimePath: string, ): string => generatePath(LEGEND_QUERY_ROUTE_PATTERN.CREATE_FROM_MAPPING_QUERY, { [LEGEND_QUERY_ROUTE_PATTERN_TOKEN.GAV]: generateGAVCoordinates( groupId, artifactId, versionId, ), [LEGEND_QUERY_ROUTE_PATTERN_TOKEN.MAPPING_PATH]: mappingPath, [LEGEND_QUERY_ROUTE_PATTERN_TOKEN.RUNTIME_PATH]: runtimePath, }); export type MappingQueryCreatorPathParams = { [LEGEND_QUERY_ROUTE_PATTERN_TOKEN.GAV]: string; [LEGEND_QUERY_ROUTE_PATTERN_TOKEN.MAPPING_PATH]: string; [LEGEND_QUERY_ROUTE_PATTERN_TOKEN.RUNTIME_PATH]: string; }; export const generateServiceQueryCreatorRoute = ( groupId: string, artifactId: string, versionId: string, servicePath: string, executionKey?: string | undefined, ): string => addQueryParametersToUrl( generatePath(LEGEND_QUERY_ROUTE_PATTERN.CREATE_FROM_SERVICE_QUERY, { [LEGEND_QUERY_ROUTE_PATTERN_TOKEN.GAV]: generateGAVCoordinates( groupId, artifactId, versionId, ), [LEGEND_QUERY_ROUTE_PATTERN_TOKEN.SERVICE_PATH]: servicePath, }), stringifyQueryParams({ [LEGEND_QUERY_QUERY_PARAM_TOKEN.SERVICE_EXECUTION_KEY]: executionKey ? encodeURIComponent(executionKey) : executionKey, }), ); export type ServiceQueryCreatorPathParams = { [LEGEND_QUERY_ROUTE_PATTERN_TOKEN.GAV]: string; [LEGEND_QUERY_ROUTE_PATTERN_TOKEN.SERVICE_PATH]: string; }; export type ServiceQueryCreatorQueryParams = { [LEGEND_QUERY_QUERY_PARAM_TOKEN.SERVICE_EXECUTION_KEY]?: string | undefined; }; export const generateExistingQueryEditorRoute = (queryId: string): string => generatePath(LEGEND_QUERY_ROUTE_PATTERN.EDIT_EXISTING_QUERY, { [LEGEND_QUERY_ROUTE_PATTERN_TOKEN.QUERY_ID]: queryId, }); export type ExistingQueryEditorPathParams = { [LEGEND_QUERY_ROUTE_PATTERN_TOKEN.QUERY_ID]: string; }; /** * @external_application_navigation This depends on Legend Studio routing and is hardcoded so it's potentially brittle */ export const EXTERNAL_APPLICATION_NAVIGATION__generateStudioSDLCProjectViewUrl = ( studioApplicationUrl: string, projectId: string, versionId: string | undefined, entityPath: string | undefined, ): string => `${studioApplicationUrl}/view/${projectId}${ versionId ? `/version/${versionId}` : '' }${entityPath ? `/entity/${entityPath}` : ''}`; /** * @external_application_navigation This depends on Legend Studio routing and is hardcoded so it's potentially brittle */ export const EXTERNAL_APPLICATION_NAVIGATION__generateStudioUpdateExistingServiceQueryUrl = ( studioApplicationUrl: string, groupId: string, artifactId: string, servicePath: string, ): string => `${studioApplicationUrl}/extensions/update-service-query/${servicePath}@${generateGAVCoordinates( groupId, artifactId, undefined, )}`; /** * @external_application_navigation This depends on Legend Studio routing and is hardcoded so it's potentially brittle */ export const EXTERNAL_APPLICATION_NAVIGATION__generateStudioUpdateProjectServiceQueryUrl = (studioApplicationUrl: string, projectId: string): string => `${studioApplicationUrl}/extensions/update-project-service-query/${projectId}`; /** * @external_application_navigation This depends on Legend Studio routing and is hardcoded so it's potentially brittle */ export const EXTERNAL_APPLICATION_NAVIGATION__generateStudioProductionizeQueryUrl = (studioApplicationUrl: string, queryId: string): string => `${studioApplicationUrl}/extensions/productionize-query/${queryId}`; /** * @external_application_navigation This depends on Legend Taxonomy routing and is hardcoded so it's potentially brittle */ export const EXTERNAL_APPLICATION_NAVIGATION__generateTaxonomyDataspaceViewUrl = ( taxonomyApplicationUrl: string, groupId: string, artifactId: string, versionId: string, dataspacePath: string, ): string => `${taxonomyApplicationUrl}/dataspace/${generateGAVCoordinates( groupId, artifactId, versionId, )}/${dataspacePath}`; /** * @external_application_navigation This depends on Legend DataCube routing and is hardcoded so it's potentially brittle */ export const EXTERNAL_APPLICATION_NAVIGATION__generateNewDataCubeUrl = ( dataCubeApplicationUrl: string, sourceData: object, options?: { addUrlSafeBase64Characters?: boolean | undefined; }, ): string => { const sourceDataString = JSON.stringify(sourceData); const encodedSourceData = options?.addUrlSafeBase64Characters ? btoaURLSafe(sourceDataString) : btoa(sourceDataString); return `${dataCubeApplicationUrl}?sourceData=${encodeURIComponent( encodedSourceData, )}`; }; /** * @external_application_navigation This depends on Legend Marketplace routing and is hardcoded so it's potentially brittle */ export const EXTERNAL_APPLICATION_NAVIGATION__generateMarketplaceDataProductUrl = ( marketplaceApplicationUrl: string, dataProductId: string, deploymentId: string, ): string => `${marketplaceApplicationUrl}/dataProduct/deployed/${dataProductId}/${deploymentId}`;