@sap-ux/project-access
Version:
Library to access SAP Fiori tools projects
148 lines • 5.67 kB
JavaScript
import { dirname, join } from 'node:path';
import { readJSON } from '../file/index.js';
/**
* Get the main service name from the manifest.
* LROP: by definition the service name can be read from the UI5 model with "" as name.
* OVP: the main model needs to be read from the sap.ovp config and then the service can be derived.
*
* @param manifest - application manifest
* @returns - main service name
*/
export function getMainService(manifest) {
const model = typeof manifest?.['sap.ovp']?.globalFilterModel === 'string' ? manifest['sap.ovp'].globalFilterModel : '';
return typeof manifest?.['sap.ui5']?.models?.[model]?.dataSource === 'string'
? manifest['sap.ui5'].models[model].dataSource
: undefined;
}
/**
* Return the service annotation specification for a specific app.
*
* @param manifestPath - path to manifest.json
* @param manifest - optionally, parsed content of manifest.json, pass to avoid reading it again.
* @param memFs - optional mem-fs-editor instance
* @returns - service and annotation specification
*/
export async function getServicesAndAnnotations(manifestPath, manifest, memFs) {
const parsedManifest = manifest ?? (await readJSON(manifestPath, memFs));
const manifestFolder = dirname(manifestPath);
const services = {};
const dataSources = parsedManifest?.['sap.app']?.dataSources ?? {};
for (const name in dataSources) {
if (dataSources[name].type !== 'OData') {
continue;
}
services[name] = getServiceSpecification(manifestFolder, name, dataSources);
}
return services;
}
/**
* Get the service specification for a given service.
*
* @param webappFolder - relative path to webapp folder from project root
* @param name - name of the service
* @param dataSources - dataSources from manifest
* @returns - service specification
*/
function getServiceSpecification(webappFolder, name, dataSources) {
const dataSource = dataSources[name];
const uri = dataSource.uri;
const local = typeof dataSource.settings?.localUri === 'string'
? join(webappFolder, dataSource.settings.localUri)
: '';
const odataVersion = dataSource.settings?.odataVersion ?? '2.0';
const annotations = [];
const annotationNames = dataSource.settings?.annotations;
if (Array.isArray(annotationNames)) {
for (const annotationName of annotationNames) {
const annotation = dataSources[annotationName];
if (annotation) {
annotations.push({
uri: annotation.uri,
local: annotation.settings?.localUri ? join(webappFolder, annotation.settings.localUri) : undefined
});
}
}
}
return {
uri,
local,
odataVersion,
annotations
};
}
/**
* Filters data sources by type.
*
* @param {Record<string, ManifestNamespace.DataSource>} dataSources - Data sources from the manifest.
* @param {string} type - Data source type to filter by.
* @returns {Record<string, ManifestNamespace.DataSource>} Data source IDs.
*/
export function filterDataSourcesByType(dataSources, type) {
return Object.fromEntries(Object.entries(dataSources).filter(([, data]) => data.type === type));
}
/**
* Extracts view paths from target settings if present.
*
* @param settings - target settings object
* @returns - array of view path entries, or empty array if not present
*/
function getViewPaths(settings) {
if ('views' in settings &&
settings.views &&
typeof settings.views === 'object' &&
'paths' in settings.views &&
Array.isArray(settings.views.paths)) {
return settings.views.paths;
}
return [];
}
/**
* Find used service entities by analyzing manifest.json
* Currently we do not return entities for Fiori element V2 apps and entities for Fiori Elements V4 apps that use contextPath instead of entitySet
*
* @param manifest - parsed manifest.json
* @returns - array of used entities, each with service URI and entity name
*/
export function getUsedEntitiesFromManifest(manifest) {
const targets = manifest['sap.ui5']?.routing?.targets;
if (!targets || typeof targets !== 'object') {
return [];
}
const mainService = getMainService(manifest) ?? '';
const mainServiceUri = manifest['sap.app']?.dataSources?.[mainService]?.uri ?? '';
const seen = new Set();
const usedEntities = [];
const addEntity = (entitySet) => {
if (typeof entitySet !== 'string') {
return;
}
const key = `${mainServiceUri}${entitySet}`;
if (!seen.has(key)) {
seen.add(key);
usedEntities.push({ service: mainServiceUri, entity: entitySet });
}
};
for (const targetName in targets) {
const target = targets[targetName];
// Resolve settings object with multiple safe checks
const settings = target.options && typeof target.options === 'object' && 'settings' in target.options
? target.options.settings
: undefined;
if (!settings || typeof settings !== 'object') {
continue;
}
// Resolve entitySet from page
if ('entitySet' in settings) {
addEntity(settings.entitySet);
}
// Resolve entitySet from page views
const viewPaths = getViewPaths(settings);
for (const path of viewPaths) {
if (path && typeof path === 'object' && 'entitySet' in path) {
addEntity(path.entitySet);
}
}
}
return usedEntities;
}
//# sourceMappingURL=service.js.map