UNPKG

@sap-ux/store

Version:

NPM module for storing persistent data

137 lines 5.64 kB
import { getHybridStore } from '../data-access/hybrid.js'; import { BackendSystem, BackendSystemKey } from '../entities/backend-system.js'; import { Entities } from './constants.js'; import { ConnectionType } from '../types.js'; import { getBackendSystemType, getSapToolsDirectory, isMatch } from '../utils/index.js'; import { join } from 'node:path'; import { readFileSync, writeFileSync } from 'node:fs'; export const SystemDataProvider = class { dataAccessor; entityName = Entities.BackendSystem; logger; constructor(logger, options = {}) { this.logger = logger; this.dataAccessor = getHybridStore(this.logger, options); } async read(key) { return this.dataAccessor.read({ entityName: this.entityName, id: key.getId() }); } async write(entity) { let e; if (!(entity instanceof BackendSystem)) { // We need to use the correct class otherwise the annotations are not effective e = new BackendSystem({ ...entity }); } else { e = entity; } return this.dataAccessor.write({ entityName: this.entityName, id: BackendSystemKey.from(entity).getId(), entity: e }); } async delete(entity) { return this.dataAccessor.del({ entityName: this.entityName, id: BackendSystemKey.from(entity).getId() }); } async getAll(providerRetrievalOptions) { const migrationRequired = this.isMigrationRequired(); const { includeSensitiveData = true, backendSystemFilter } = providerRetrievalOptions ?? {}; // Always fetch sensitive data if migration is pending let systems = await this.dataAccessor.readAll({ entityName: this.entityName, includeSensitiveData: migrationRequired ? true : includeSensitiveData }); const migrationComplete = await this.migrateBackendSystems(systems, includeSensitiveData); // If migration occurred, re-read to get the fully updated dataset if (!migrationComplete) { systems = await this.dataAccessor.readAll({ entityName: this.entityName, includeSensitiveData }); } return this.applyFilters(Object.values(systems), backendSystemFilter); } /** * Determines whether a migration is required based on a local marker file. */ isMigrationRequired() { const migrationFilePath = join(getSapToolsDirectory(), '.systemsMigrated'); try { const raw = readFileSync(migrationFilePath, 'utf-8'); const data = JSON.parse(raw); return !data?.backendSystemMigrationV1; } catch { // If anything fails (file missing, parse error), treat as not migrated return true; } } /** * Applies filter objects to a list of backend systems. */ applyFilters(systems, filters = {}) { if (!filters.connectionType) { // unless a filter for connectionType is explicitly provided, default to filtering by the ABAP Catalog type as this is what existing consumers expect filters.connectionType = 'abap_catalog'; } return systems.filter((system) => isMatch(system, filters)); } /** * Ensures all stored backend systems are migrated to the latest structure. */ async migrateBackendSystems(systems, containsSensitiveData) { let allMigrated = true; for (const [id, system] of Object.entries(systems)) { // Skip already-migrated entries const alreadyMigrated = system?.hasSensitiveData !== undefined && system?.systemType !== undefined && system?.connectionType !== undefined; if (alreadyMigrated) { continue; } allMigrated = false; const migratedSystem = await this.buildMigratedSystem(system, containsSensitiveData, id); await this.dataAccessor.partialUpdate({ entityName: this.entityName, id, entity: { hasSensitiveData: migratedSystem.hasSensitiveData, systemType: migratedSystem.systemType, connectionType: migratedSystem.connectionType } }); } if (!allMigrated) { // Write migration marker file const filePath = join(getSapToolsDirectory(), '.systemsMigrated'); const marker = { backendSystemMigrationV1: new Date().toISOString() }; writeFileSync(filePath, JSON.stringify(marker, null, 2)); } return allMigrated; } /** * Builds a fully migrated BackendSystem instance. */ async buildMigratedSystem(system, containsSensitiveData, id) { let fullSystem = system; // Ensure sensitive data available if needed if (!containsSensitiveData) { fullSystem = await this.dataAccessor.read({ entityName: this.entityName, id }); } const inferredSystemType = fullSystem.systemType ?? getBackendSystemType(fullSystem) ?? 'OnPrem'; const connectionType = fullSystem?.connectionType ?? ConnectionType.AbapCatalog; // will need to be removed once adding different connection types is possible return new BackendSystem({ ...fullSystem, systemType: inferredSystemType, connectionType }); } }; //# sourceMappingURL=backend-system.js.map