UI styling improvements: dashboard headers and navigation
- Restore blue PageHeader on Dashboard (/app-components) - Update homepage (/) with subtle header design without blue bar - Add uniform PageHeader styling to application edit page - Fix Rapporten link on homepage to point to /reports overview - Improve header descriptions spacing for better readability
This commit is contained in:
@@ -1,17 +1,16 @@
|
||||
/**
|
||||
* DataService - Main entry point for application data access
|
||||
*
|
||||
* Routes requests to either:
|
||||
* - CMDBService (using local cache) for real Jira data
|
||||
* - MockDataService for development without Jira
|
||||
* ALWAYS uses Jira Assets API via CMDBService (local cache layer).
|
||||
* Mock data has been removed - all data must come from Jira Assets.
|
||||
*/
|
||||
|
||||
import { config } from '../config/env.js';
|
||||
import { cmdbService, type UpdateResult } from './cmdbService.js';
|
||||
import { cacheStore, type CacheStats } from './cacheStore.js';
|
||||
import { normalizedCacheStore as cacheStore, type CacheStats } from './normalizedCacheStore.js';
|
||||
import { normalizedCacheStore } from './normalizedCacheStore.js';
|
||||
import { jiraAssetsClient } from './jiraAssetsClient.js';
|
||||
import { jiraAssetsService } from './jiraAssets.js';
|
||||
import { mockDataService } from './mockData.js';
|
||||
import { logger } from './logger.js';
|
||||
import type {
|
||||
ApplicationComponent,
|
||||
@@ -47,16 +46,8 @@ import type {
|
||||
import { calculateRequiredEffortWithMinMax } from './effortCalculation.js';
|
||||
import { calculateApplicationCompleteness } from './dataCompletenessConfig.js';
|
||||
|
||||
// Determine if we should use real Jira Assets or mock data
|
||||
// Jira PAT is now configured per-user, so we check if schema is configured
|
||||
// The actual PAT is provided per-request via middleware
|
||||
const useJiraAssets = !!config.jiraSchemaId;
|
||||
|
||||
if (useJiraAssets) {
|
||||
logger.info('DataService: Using CMDB cache layer with Jira Assets API');
|
||||
} else {
|
||||
logger.info('DataService: Using mock data (Jira credentials not configured)');
|
||||
}
|
||||
// NOTE: All data comes from Jira Assets API - no mock data fallback
|
||||
// If schemas aren't configured yet, operations will fail gracefully with appropriate errors
|
||||
|
||||
// =============================================================================
|
||||
// Reference Cache (for enriching IDs to ObjectReferences)
|
||||
@@ -121,42 +112,111 @@ async function lookupReferences<T extends CMDBObject>(
|
||||
// Helper Functions
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Load description for an object from database
|
||||
* Looks for a description attribute (field_name like 'description' or attr_name like 'Description')
|
||||
*/
|
||||
async function getDescriptionFromDatabase(objectId: string): Promise<string | null> {
|
||||
try {
|
||||
const { normalizedCacheStore } = await import('./normalizedCacheStore.js');
|
||||
const db = (normalizedCacheStore as any).db;
|
||||
if (!db) return null;
|
||||
|
||||
// Try to find description attribute by common field names
|
||||
const descriptionFieldNames = ['description', 'Description', 'DESCRIPTION'];
|
||||
|
||||
// First, get the object to find its type
|
||||
const objRow = await db.queryOne<{ object_type_name: string }>(`
|
||||
SELECT object_type_name FROM objects WHERE id = ?
|
||||
`, [objectId]);
|
||||
|
||||
if (!objRow) return null;
|
||||
|
||||
// Try each possible description field name
|
||||
for (const fieldName of descriptionFieldNames) {
|
||||
const descRow = await db.queryOne<{ text_value: string }>(`
|
||||
SELECT av.text_value
|
||||
FROM attribute_values av
|
||||
JOIN attributes a ON av.attribute_id = a.id
|
||||
WHERE av.object_id = ?
|
||||
AND (a.field_name = ? OR a.attr_name = ?)
|
||||
AND av.text_value IS NOT NULL
|
||||
AND av.text_value != ''
|
||||
LIMIT 1
|
||||
`, [objectId, fieldName, fieldName]);
|
||||
|
||||
if (descRow?.text_value) {
|
||||
return descRow.text_value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
logger.debug(`Failed to get description from database for object ${objectId}`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert ObjectReference to ReferenceValue format used by frontend
|
||||
* Try to enrich with description from jiraAssetsService cache if available
|
||||
* If not in cache or cache entry has no description, fetch it async
|
||||
* PRIMARY: Load from database cache (no API calls)
|
||||
* FALLBACK: Only use API if object not in database
|
||||
*/
|
||||
async function toReferenceValue(ref: ObjectReference | null | undefined): Promise<ReferenceValue | null> {
|
||||
if (!ref) return null;
|
||||
|
||||
// Try to get enriched ReferenceValue from jiraAssetsService cache (includes description if available)
|
||||
const enriched = useJiraAssets ? jiraAssetsService.getEnrichedReferenceValue(ref.objectKey, ref.objectId) : null;
|
||||
// PRIMARY SOURCE: Try to load from database first (no API calls)
|
||||
try {
|
||||
const { normalizedCacheStore } = await import('./normalizedCacheStore.js');
|
||||
const db = (normalizedCacheStore as any).db;
|
||||
if (db) {
|
||||
await db.ensureInitialized?.();
|
||||
|
||||
// Get basic object info from database
|
||||
const objRow = await db.queryOne<{
|
||||
id: string;
|
||||
object_key: string;
|
||||
label: string;
|
||||
}>(`
|
||||
SELECT id, object_key, label
|
||||
FROM objects
|
||||
WHERE id = ? OR object_key = ?
|
||||
LIMIT 1
|
||||
`, [ref.objectId, ref.objectKey]);
|
||||
|
||||
if (objRow) {
|
||||
// Object exists in database - extract description if available
|
||||
const description = await getDescriptionFromDatabase(objRow.id);
|
||||
|
||||
return {
|
||||
objectId: objRow.id,
|
||||
key: objRow.object_key || ref.objectKey,
|
||||
name: objRow.label || ref.label,
|
||||
...(description && { description }),
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.debug(`Failed to load reference object ${ref.objectId} from database`, error);
|
||||
}
|
||||
|
||||
// FALLBACK: Object not in database - check Jira Assets service cache
|
||||
// Only fetch from API if really needed (object missing from database)
|
||||
const enriched = jiraAssetsService.getEnrichedReferenceValue(ref.objectKey, ref.objectId);
|
||||
|
||||
if (enriched && enriched.description) {
|
||||
// Use enriched value with description
|
||||
// Use enriched value with description from service cache
|
||||
return enriched;
|
||||
}
|
||||
|
||||
// Cache miss or no description - fetch it async if using Jira Assets
|
||||
if (useJiraAssets && enriched && !enriched.description) {
|
||||
// We have a cached value but it lacks description - fetch it
|
||||
const fetched = await jiraAssetsService.fetchEnrichedReferenceValue(ref.objectKey, ref.objectId);
|
||||
if (fetched) {
|
||||
return fetched;
|
||||
}
|
||||
// If fetch failed, return the cached value anyway
|
||||
// Last resort: Object not in database and not in service cache
|
||||
// Only return basic info - don't fetch from API here
|
||||
// API fetching should only happen during sync operations
|
||||
if (enriched) {
|
||||
return enriched;
|
||||
}
|
||||
|
||||
if (useJiraAssets) {
|
||||
// Cache miss - fetch it
|
||||
const fetched = await jiraAssetsService.fetchEnrichedReferenceValue(ref.objectKey, ref.objectId);
|
||||
if (fetched) {
|
||||
return fetched;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to basic conversion without description (if fetch failed or not using Jira Assets)
|
||||
// Basic fallback - return what we have from the ObjectReference
|
||||
return {
|
||||
objectId: ref.objectId,
|
||||
key: ref.objectKey,
|
||||
@@ -172,7 +232,8 @@ function toReferenceValues(refs: ObjectReference[] | null | undefined): Referenc
|
||||
return refs.map(ref => ({
|
||||
objectId: ref.objectId,
|
||||
key: ref.objectKey,
|
||||
name: ref.label,
|
||||
// Use label if available, otherwise fall back to objectKey, then objectId
|
||||
name: ref.label || ref.objectKey || ref.objectId || 'Unknown',
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -225,6 +286,18 @@ async function toApplicationDetails(app: ApplicationComponent): Promise<Applicat
|
||||
logger.info(`[toApplicationDetails] Converting cached object ${app.objectKey || app.id} to ApplicationDetails`);
|
||||
logger.info(`[toApplicationDetails] confluenceSpace from cache: ${app.confluenceSpace} (type: ${typeof app.confluenceSpace})`);
|
||||
|
||||
// Debug logging for reference fields
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
logger.debug(`[toApplicationDetails] businessOwner: ${JSON.stringify(app.businessOwner)}`);
|
||||
logger.debug(`[toApplicationDetails] systemOwner: ${JSON.stringify(app.systemOwner)}`);
|
||||
logger.debug(`[toApplicationDetails] technicalApplicationManagement: ${JSON.stringify(app.technicalApplicationManagement)}`);
|
||||
logger.debug(`[toApplicationDetails] supplierProduct: ${JSON.stringify(app.supplierProduct)}`);
|
||||
logger.debug(`[toApplicationDetails] applicationFunction: ${JSON.stringify(app.applicationFunction)}`);
|
||||
logger.debug(`[toApplicationDetails] applicationManagementDynamicsFactor: ${JSON.stringify(app.applicationManagementDynamicsFactor)}`);
|
||||
logger.debug(`[toApplicationDetails] applicationManagementComplexityFactor: ${JSON.stringify(app.applicationManagementComplexityFactor)}`);
|
||||
logger.debug(`[toApplicationDetails] applicationManagementNumberOfUsers: ${JSON.stringify(app.applicationManagementNumberOfUsers)}`);
|
||||
}
|
||||
|
||||
// Handle confluenceSpace - it can be a string (URL) or number (legacy), convert to string
|
||||
const confluenceSpaceValue = app.confluenceSpace !== null && app.confluenceSpace !== undefined
|
||||
? (typeof app.confluenceSpace === 'string' ? app.confluenceSpace : String(app.confluenceSpace))
|
||||
@@ -302,57 +375,17 @@ async function toApplicationDetails(app: ApplicationComponent): Promise<Applicat
|
||||
|
||||
// Convert array of ObjectReferences to ReferenceValue[]
|
||||
const applicationFunctions = toReferenceValues(app.applicationFunction);
|
||||
|
||||
return {
|
||||
id: app.id,
|
||||
key: app.objectKey,
|
||||
name: app.label,
|
||||
description: app.description || null,
|
||||
status: (app.status || 'In Production') as ApplicationStatus,
|
||||
searchReference: app.searchReference || null,
|
||||
|
||||
// Organization info
|
||||
organisation: organisation?.name || null,
|
||||
businessOwner: extractLabel(app.businessOwner),
|
||||
systemOwner: extractLabel(app.systemOwner),
|
||||
functionalApplicationManagement: app.functionalApplicationManagement || null,
|
||||
technicalApplicationManagement: extractLabel(app.technicalApplicationManagement),
|
||||
technicalApplicationManagementPrimary: extractDisplayValue(app.technicalApplicationManagementPrimary),
|
||||
technicalApplicationManagementSecondary: extractDisplayValue(app.technicalApplicationManagementSecondary),
|
||||
|
||||
// Technical info
|
||||
medischeTechniek: app.medischeTechniek || false,
|
||||
technischeArchitectuur: app.technischeArchitectuurTA || null,
|
||||
supplierProduct: extractLabel(app.supplierProduct),
|
||||
|
||||
// Classification
|
||||
applicationFunctions,
|
||||
businessImportance: businessImportance?.name || null,
|
||||
businessImpactAnalyse,
|
||||
hostingType,
|
||||
|
||||
// Application Management
|
||||
governanceModel,
|
||||
applicationType,
|
||||
applicationSubteam,
|
||||
applicationTeam,
|
||||
dynamicsFactor,
|
||||
complexityFactor,
|
||||
numberOfUsers,
|
||||
applicationManagementHosting,
|
||||
applicationManagementTAM,
|
||||
platform,
|
||||
|
||||
// Override
|
||||
overrideFTE: app.applicationManagementOverrideFTE ?? null,
|
||||
requiredEffortApplicationManagement: null,
|
||||
|
||||
// Enterprise Architect reference
|
||||
reference: app.reference || null,
|
||||
|
||||
// Confluence Space (URL string)
|
||||
confluenceSpace: confluenceSpaceValue,
|
||||
};
|
||||
|
||||
// Convert supplier fields to ReferenceValue format
|
||||
const [
|
||||
supplierTechnical,
|
||||
supplierImplementation,
|
||||
supplierConsultancy,
|
||||
] = await Promise.all([
|
||||
toReferenceValue(app.supplierTechnical),
|
||||
toReferenceValue(app.supplierImplementation),
|
||||
toReferenceValue(app.supplierConsultancy),
|
||||
]);
|
||||
|
||||
// Calculate data completeness percentage
|
||||
// Convert ApplicationDetails-like structure to format expected by completeness calculator
|
||||
@@ -399,6 +432,9 @@ async function toApplicationDetails(app: ApplicationComponent): Promise<Applicat
|
||||
medischeTechniek: app.medischeTechniek || false,
|
||||
technischeArchitectuur: app.technischeArchitectuurTA || null,
|
||||
supplierProduct: extractLabel(app.supplierProduct),
|
||||
supplierTechnical: supplierTechnical,
|
||||
supplierImplementation: supplierImplementation,
|
||||
supplierConsultancy: supplierConsultancy,
|
||||
|
||||
// Classification
|
||||
applicationFunctions,
|
||||
@@ -659,22 +695,31 @@ export const dataService = {
|
||||
page: number = 1,
|
||||
pageSize: number = 25
|
||||
): Promise<SearchResult> {
|
||||
if (!useJiraAssets) {
|
||||
return mockDataService.searchApplications(filters, page, pageSize);
|
||||
}
|
||||
|
||||
// Get all applications from cache
|
||||
// Get all applications from cache (always from Jira Assets)
|
||||
let apps = await cmdbService.getObjects<ApplicationComponent>('ApplicationComponent');
|
||||
|
||||
logger.debug(`DataService: Found ${apps.length} applications in cache for search`);
|
||||
|
||||
// If cache is empty, log a warning
|
||||
if (apps.length === 0) {
|
||||
logger.warn('DataService: Cache is empty - no applications found. A full sync may be needed.');
|
||||
}
|
||||
|
||||
// Apply filters locally
|
||||
if (filters.searchText) {
|
||||
const search = filters.searchText.toLowerCase();
|
||||
apps = apps.filter(app =>
|
||||
app.label.toLowerCase().includes(search) ||
|
||||
app.objectKey.toLowerCase().includes(search) ||
|
||||
app.searchReference?.toLowerCase().includes(search) ||
|
||||
app.description?.toLowerCase().includes(search)
|
||||
);
|
||||
if (filters.searchText && filters.searchText.trim()) {
|
||||
const search = filters.searchText.toLowerCase().trim();
|
||||
const beforeFilter = apps.length;
|
||||
apps = apps.filter(app => {
|
||||
const label = app.label?.toLowerCase() || '';
|
||||
const objectKey = app.objectKey?.toLowerCase() || '';
|
||||
const searchRef = app.searchReference?.toLowerCase() || '';
|
||||
const description = app.description?.toLowerCase() || '';
|
||||
return label.includes(search) ||
|
||||
objectKey.includes(search) ||
|
||||
searchRef.includes(search) ||
|
||||
description.includes(search);
|
||||
});
|
||||
logger.debug(`DataService: Search filter "${filters.searchText}" reduced results from ${beforeFilter} to ${apps.length}`);
|
||||
}
|
||||
|
||||
if (filters.statuses && filters.statuses.length > 0) {
|
||||
@@ -834,11 +879,14 @@ export const dataService = {
|
||||
* Get application by ID (from cache)
|
||||
*/
|
||||
async getApplicationById(id: string): Promise<ApplicationDetails | null> {
|
||||
if (!useJiraAssets) {
|
||||
return mockDataService.getApplicationById(id);
|
||||
// Try to get by ID first (handles both Jira object IDs and object keys)
|
||||
let app = await cmdbService.getObject<ApplicationComponent>('ApplicationComponent', id);
|
||||
|
||||
// If not found by ID, try by object key (e.g., "ICMT-123" or numeric IDs that might be keys)
|
||||
if (!app) {
|
||||
app = await cmdbService.getObjectByKey<ApplicationComponent>('ApplicationComponent', id);
|
||||
}
|
||||
|
||||
const app = await cmdbService.getObject<ApplicationComponent>('ApplicationComponent', id);
|
||||
|
||||
if (!app) return null;
|
||||
|
||||
return toApplicationDetails(app);
|
||||
@@ -848,13 +896,18 @@ export const dataService = {
|
||||
* Get application for editing (force refresh from Jira)
|
||||
*/
|
||||
async getApplicationForEdit(id: string): Promise<ApplicationDetails | null> {
|
||||
if (!useJiraAssets) {
|
||||
return mockDataService.getApplicationById(id);
|
||||
}
|
||||
|
||||
const app = await cmdbService.getObject<ApplicationComponent>('ApplicationComponent', id, {
|
||||
// Try to get by ID first (handles both Jira object IDs and object keys)
|
||||
let app = await cmdbService.getObject<ApplicationComponent>('ApplicationComponent', id, {
|
||||
forceRefresh: true,
|
||||
});
|
||||
|
||||
// If not found by ID, try by object key (e.g., "ICMT-123" or numeric IDs that might be keys)
|
||||
if (!app) {
|
||||
app = await cmdbService.getObjectByKey<ApplicationComponent>('ApplicationComponent', id, {
|
||||
forceRefresh: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!app) return null;
|
||||
|
||||
return toApplicationDetails(app);
|
||||
@@ -884,11 +937,7 @@ export const dataService = {
|
||||
): Promise<UpdateResult> {
|
||||
logger.info(`dataService.updateApplication called for ${id}`);
|
||||
|
||||
if (!useJiraAssets) {
|
||||
const success = await mockDataService.updateApplication(id, updates);
|
||||
return { success };
|
||||
}
|
||||
|
||||
// Always update via Jira Assets API
|
||||
// Convert to CMDBService format
|
||||
// IMPORTANT: For reference fields, we pass ObjectReference objects (with objectKey)
|
||||
// because buildAttributeValues in cmdbService expects to extract objectKey for Jira API
|
||||
@@ -978,7 +1027,7 @@ export const dataService = {
|
||||
// ===========================================================================
|
||||
|
||||
async getDynamicsFactors(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return mockDataService.getDynamicsFactors();
|
||||
// Always get from Jira Assets cache
|
||||
const items = await cmdbService.getObjects<ApplicationManagementDynamicsFactor>('ApplicationManagementDynamicsFactor');
|
||||
return items.map(item => ({
|
||||
objectId: item.id,
|
||||
@@ -991,7 +1040,7 @@ export const dataService = {
|
||||
},
|
||||
|
||||
async getComplexityFactors(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return mockDataService.getComplexityFactors();
|
||||
// Always get from Jira Assets cache
|
||||
const items = await cmdbService.getObjects<ApplicationManagementComplexityFactor>('ApplicationManagementComplexityFactor');
|
||||
return items.map(item => ({
|
||||
objectId: item.id,
|
||||
@@ -1004,7 +1053,7 @@ export const dataService = {
|
||||
},
|
||||
|
||||
async getNumberOfUsers(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return mockDataService.getNumberOfUsers();
|
||||
// Always get from Jira Assets cache
|
||||
const items = await cmdbService.getObjects<ApplicationManagementNumberOfUsers>('ApplicationManagementNumberOfUsers');
|
||||
return items.map(item => ({
|
||||
objectId: item.id,
|
||||
@@ -1017,7 +1066,7 @@ export const dataService = {
|
||||
},
|
||||
|
||||
async getGovernanceModels(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return mockDataService.getGovernanceModels();
|
||||
// Always get from Jira Assets cache
|
||||
const items = await cmdbService.getObjects<IctGovernanceModel>('IctGovernanceModel');
|
||||
return items.map(item => ({
|
||||
objectId: item.id,
|
||||
@@ -1030,24 +1079,26 @@ export const dataService = {
|
||||
},
|
||||
|
||||
async getOrganisations(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return mockDataService.getOrganisations();
|
||||
// Always get from Jira Assets cache
|
||||
const items = await cmdbService.getObjects<Organisation>('Organisation');
|
||||
logger.debug(`DataService: Found ${items.length} organisations in cache`);
|
||||
return items.map(item => ({ objectId: item.id, key: item.objectKey, name: item.label }));
|
||||
},
|
||||
|
||||
async getHostingTypes(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return mockDataService.getHostingTypes();
|
||||
// Always get from Jira Assets cache
|
||||
const items = await cmdbService.getObjects<HostingType>('HostingType');
|
||||
return items.map(item => ({
|
||||
objectId: item.id,
|
||||
key: item.objectKey,
|
||||
logger.debug(`DataService: Found ${items.length} hosting types in cache`);
|
||||
return items.map(item => ({
|
||||
objectId: item.id,
|
||||
key: item.objectKey,
|
||||
name: item.label,
|
||||
summary: item.description || undefined, // Use description as summary for display
|
||||
}));
|
||||
},
|
||||
|
||||
async getBusinessImpactAnalyses(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return mockDataService.getBusinessImpactAnalyses();
|
||||
// Always get from Jira Assets cache
|
||||
const items = await cmdbService.getObjects<BusinessImpactAnalyse>('BusinessImpactAnalyse');
|
||||
return items.map(item => ({
|
||||
objectId: item.id,
|
||||
@@ -1059,7 +1110,7 @@ export const dataService = {
|
||||
},
|
||||
|
||||
async getApplicationManagementHosting(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return mockDataService.getApplicationManagementHosting();
|
||||
// Always get from Jira Assets cache
|
||||
const items = await cmdbService.getObjects<ApplicationManagementHosting>('ApplicationManagementHosting');
|
||||
return items.map(item => ({
|
||||
objectId: item.id,
|
||||
@@ -1070,7 +1121,7 @@ export const dataService = {
|
||||
},
|
||||
|
||||
async getApplicationManagementTAM(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return mockDataService.getApplicationManagementTAM();
|
||||
// Always get from Jira Assets cache
|
||||
const items = await cmdbService.getObjects<ApplicationManagementTam>('ApplicationManagementTam');
|
||||
return items.map(item => ({
|
||||
objectId: item.id,
|
||||
@@ -1081,7 +1132,7 @@ export const dataService = {
|
||||
},
|
||||
|
||||
async getApplicationFunctions(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return mockDataService.getApplicationFunctions();
|
||||
// Always get from Jira Assets cache
|
||||
const items = await cmdbService.getObjects<ApplicationFunction>('ApplicationFunction');
|
||||
return items.map(item => ({
|
||||
objectId: item.id,
|
||||
@@ -1098,7 +1149,7 @@ export const dataService = {
|
||||
},
|
||||
|
||||
async getApplicationFunctionCategories(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return mockDataService.getApplicationFunctionCategories();
|
||||
// Always get from Jira Assets cache
|
||||
const items = await cmdbService.getObjects<ApplicationFunctionCategory>('ApplicationFunctionCategory');
|
||||
return items.map(item => ({
|
||||
objectId: item.id,
|
||||
@@ -1109,19 +1160,17 @@ export const dataService = {
|
||||
},
|
||||
|
||||
async getApplicationSubteams(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return []; // Mock mode: no subteams
|
||||
// Use jiraAssetsService directly as schema doesn't include this object type
|
||||
// Always get from Jira Assets API (schema doesn't include this object type)
|
||||
return jiraAssetsService.getApplicationSubteams();
|
||||
},
|
||||
|
||||
async getApplicationTeams(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return []; // Mock mode: no teams
|
||||
// Use jiraAssetsService directly as schema doesn't include this object type
|
||||
// Always get from Jira Assets API (schema doesn't include this object type)
|
||||
return jiraAssetsService.getApplicationTeams();
|
||||
},
|
||||
|
||||
async getSubteamToTeamMapping(): Promise<Record<string, ReferenceValue | null>> {
|
||||
if (!useJiraAssets) return {}; // Mock mode: no mapping
|
||||
// Always get from Jira Assets API
|
||||
// Convert Map to plain object for JSON serialization
|
||||
const mapping = await jiraAssetsService.getSubteamToTeamMapping();
|
||||
const result: Record<string, ReferenceValue | null> = {};
|
||||
@@ -1132,7 +1181,7 @@ export const dataService = {
|
||||
},
|
||||
|
||||
async getApplicationTypes(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return mockDataService.getApplicationTypes();
|
||||
// Always get from Jira Assets cache
|
||||
const items = await cmdbService.getObjects<ApplicationManagementApplicationType>('ApplicationManagementApplicationType');
|
||||
return items.map(item => ({
|
||||
objectId: item.id,
|
||||
@@ -1143,8 +1192,9 @@ export const dataService = {
|
||||
},
|
||||
|
||||
async getBusinessImportance(): Promise<ReferenceValue[]> {
|
||||
if (!useJiraAssets) return mockDataService.getBusinessImportance();
|
||||
// Always get from Jira Assets cache
|
||||
const items = await cmdbService.getObjects<BusinessImportance>('BusinessImportance');
|
||||
logger.debug(`DataService: Found ${items.length} business importance values in cache`);
|
||||
return items.map(item => ({ objectId: item.id, key: item.objectKey, name: item.label }));
|
||||
},
|
||||
|
||||
@@ -1153,8 +1203,7 @@ export const dataService = {
|
||||
// ===========================================================================
|
||||
|
||||
async getStats(includeDistributions: boolean = true) {
|
||||
if (!useJiraAssets) return mockDataService.getStats();
|
||||
|
||||
// Always get from Jira Assets cache
|
||||
const allApps = await cmdbService.getObjects<ApplicationComponent>('ApplicationComponent');
|
||||
|
||||
// Statuses to exclude for most metrics
|
||||
@@ -1231,9 +1280,7 @@ export const dataService = {
|
||||
},
|
||||
|
||||
async getTeamDashboardData(excludedStatuses: ApplicationStatus[] = []): Promise<TeamDashboardData> {
|
||||
if (!useJiraAssets) return mockDataService.getTeamDashboardData(excludedStatuses);
|
||||
|
||||
// Use jiraAssetsService directly as it has proper Team/Subteam field parsing
|
||||
// Always get from Jira Assets API (has proper Team/Subteam field parsing)
|
||||
return jiraAssetsService.getTeamDashboardData(excludedStatuses);
|
||||
},
|
||||
|
||||
@@ -1253,7 +1300,7 @@ export const dataService = {
|
||||
applicationCount: number;
|
||||
}>;
|
||||
}> {
|
||||
// For mock data, use the same implementation (cmdbService routes to mock data when useJiraAssets is false)
|
||||
// Always get from Jira Assets cache
|
||||
// Get all applications from cache to access all fields including BIA
|
||||
let apps = await cmdbService.getObjects<ApplicationComponent>('ApplicationComponent');
|
||||
|
||||
@@ -1421,13 +1468,13 @@ export const dataService = {
|
||||
// Utility
|
||||
// ===========================================================================
|
||||
|
||||
isUsingJiraAssets(): boolean {
|
||||
return useJiraAssets;
|
||||
async isUsingJiraAssets(): Promise<boolean> {
|
||||
// Always returns true - mock data removed, only Jira Assets is used
|
||||
return true;
|
||||
},
|
||||
|
||||
async testConnection(): Promise<boolean> {
|
||||
if (!useJiraAssets) return true;
|
||||
// Only test connection if token is configured
|
||||
// Always test Jira Assets connection (requires token)
|
||||
if (!jiraAssetsClient.hasToken()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user