Fix TypeScript compilation errors
- Add searchReference to ApplicationListItem type - Fix result variable in toApplicationDetails function - Add query helper functions for req.query parameter handling - Fix req.query.* type errors in routes (applications, cache, classifications, objects) - Fix CompletenessCategoryConfig missing id property - Fix number | null type errors in dataService - Add utils/queryHelpers.ts for reusable query parameter helpers
This commit is contained in:
@@ -6,6 +6,7 @@ import { logger } from '../services/logger.js';
|
|||||||
import { calculateRequiredEffortApplicationManagementWithBreakdown } from '../services/effortCalculation.js';
|
import { calculateRequiredEffortApplicationManagementWithBreakdown } from '../services/effortCalculation.js';
|
||||||
import { findBIAMatch, loadBIAData, clearBIACache, calculateSimilarity } from '../services/biaMatchingService.js';
|
import { findBIAMatch, loadBIAData, clearBIACache, calculateSimilarity } from '../services/biaMatchingService.js';
|
||||||
import { calculateApplicationCompleteness } from '../services/dataCompletenessConfig.js';
|
import { calculateApplicationCompleteness } from '../services/dataCompletenessConfig.js';
|
||||||
|
import { getQueryString } from '../utils/queryHelpers.js';
|
||||||
import type { SearchFilters, ReferenceValue, ClassificationResult, ApplicationDetails, ApplicationStatus } from '../types/index.js';
|
import type { SearchFilters, ReferenceValue, ClassificationResult, ApplicationDetails, ApplicationStatus } from '../types/index.js';
|
||||||
import type { Server, Flows, Certificate, Domain, AzureSubscription, CMDBObjectTypeName } from '../generated/jira-types.js';
|
import type { Server, Flows, Certificate, Domain, AzureSubscription, CMDBObjectTypeName } from '../generated/jira-types.js';
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ router.post('/search', async (req: Request, res: Response) => {
|
|||||||
// Get team dashboard data
|
// Get team dashboard data
|
||||||
router.get('/team-dashboard', async (req: Request, res: Response) => {
|
router.get('/team-dashboard', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const excludedStatusesParam = req.query.excludedStatuses as string | undefined;
|
const excludedStatusesParam = getQueryString(req, 'excludedStatuses');
|
||||||
let excludedStatuses: ApplicationStatus[] = [];
|
let excludedStatuses: ApplicationStatus[] = [];
|
||||||
|
|
||||||
if (excludedStatusesParam && excludedStatusesParam.trim().length > 0) {
|
if (excludedStatusesParam && excludedStatusesParam.trim().length > 0) {
|
||||||
@@ -56,7 +57,7 @@ router.get('/team-dashboard', async (req: Request, res: Response) => {
|
|||||||
// Get team portfolio health metrics
|
// Get team portfolio health metrics
|
||||||
router.get('/team-portfolio-health', async (req: Request, res: Response) => {
|
router.get('/team-portfolio-health', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const excludedStatusesParam = req.query.excludedStatuses as string | undefined;
|
const excludedStatusesParam = getQueryString(req, 'excludedStatuses');
|
||||||
let excludedStatuses: ApplicationStatus[] = [];
|
let excludedStatuses: ApplicationStatus[] = [];
|
||||||
|
|
||||||
if (excludedStatusesParam && excludedStatusesParam.trim().length > 0) {
|
if (excludedStatusesParam && excludedStatusesParam.trim().length > 0) {
|
||||||
@@ -95,7 +96,7 @@ router.get('/business-importance-comparison', async (req: Request, res: Response
|
|||||||
// Test BIA data loading (for debugging)
|
// Test BIA data loading (for debugging)
|
||||||
router.get('/bia-test', async (req: Request, res: Response) => {
|
router.get('/bia-test', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
if (req.query.clear === 'true') {
|
if (getQueryString(req, 'clear') === 'true') {
|
||||||
clearBIACache();
|
clearBIACache();
|
||||||
}
|
}
|
||||||
const biaData = loadBIAData();
|
const biaData = loadBIAData();
|
||||||
@@ -133,7 +134,7 @@ router.get('/bia-debug', async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
// Test each sample app
|
// Test each sample app
|
||||||
for (const app of [...sampleApps, ...testApps]) {
|
for (const app of [...sampleApps, ...testApps]) {
|
||||||
const matchResult = findBIAMatch(app.name, app.searchReference);
|
const matchResult = findBIAMatch(app.name, app.searchReference ?? null);
|
||||||
|
|
||||||
// Find all potential matches in Excel data for detailed analysis
|
// Find all potential matches in Excel data for detailed analysis
|
||||||
const normalizedAppName = app.name.toLowerCase().trim();
|
const normalizedAppName = app.name.toLowerCase().trim();
|
||||||
@@ -246,7 +247,7 @@ router.get('/bia-comparison', async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
for (const app of applications) {
|
for (const app of applications) {
|
||||||
// Find BIA match in Excel
|
// Find BIA match in Excel
|
||||||
const matchResult = findBIAMatch(app.name, app.searchReference);
|
const matchResult = findBIAMatch(app.name, app.searchReference ?? null);
|
||||||
|
|
||||||
// Log first few matches for debugging
|
// Log first few matches for debugging
|
||||||
if (comparisonItems.length < 5) {
|
if (comparisonItems.length < 5) {
|
||||||
@@ -323,7 +324,7 @@ router.get('/bia-comparison', async (req: Request, res: Response) => {
|
|||||||
router.get('/:id', async (req: Request, res: Response) => {
|
router.get('/:id', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const mode = req.query.mode as string | undefined;
|
const mode = getQueryString(req, 'mode');
|
||||||
|
|
||||||
// Don't treat special routes as application IDs
|
// Don't treat special routes as application IDs
|
||||||
if (id === 'team-dashboard' || id === 'team-portfolio-health' || id === 'business-importance-comparison' || id === 'bia-comparison' || id === 'bia-test' || id === 'calculate-effort' || id === 'search') {
|
if (id === 'team-dashboard' || id === 'team-portfolio-health' || id === 'business-importance-comparison' || id === 'bia-comparison' || id === 'bia-test' || id === 'calculate-effort' || id === 'search') {
|
||||||
@@ -617,8 +618,9 @@ router.get('/:id/related/:objectType', async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get requested attributes from query string
|
// Get requested attributes from query string
|
||||||
const requestedAttrs = req.query.attributes
|
const attributesParam = getQueryString(req, 'attributes');
|
||||||
? String(req.query.attributes).split(',').map(a => a.trim())
|
const requestedAttrs = attributesParam
|
||||||
|
? attributesParam.split(',').map(a => a.trim())
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
// Format response - must match RelatedObjectsResponse type expected by frontend
|
// Format response - must match RelatedObjectsResponse type expected by frontend
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Router, Request, Response } from 'express';
|
|||||||
import { cacheStore } from '../services/cacheStore.js';
|
import { cacheStore } from '../services/cacheStore.js';
|
||||||
import { syncEngine } from '../services/syncEngine.js';
|
import { syncEngine } from '../services/syncEngine.js';
|
||||||
import { logger } from '../services/logger.js';
|
import { logger } from '../services/logger.js';
|
||||||
|
import { getQueryString } from '../utils/queryHelpers.js';
|
||||||
import { OBJECT_TYPES } from '../generated/jira-schema.js';
|
import { OBJECT_TYPES } from '../generated/jira-schema.js';
|
||||||
import type { CMDBObjectTypeName } from '../generated/jira-types.js';
|
import type { CMDBObjectTypeName } from '../generated/jira-types.js';
|
||||||
|
|
||||||
@@ -78,10 +79,11 @@ router.post('/sync/:objectType', async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
const { objectType } = req.params;
|
const { objectType } = req.params;
|
||||||
|
|
||||||
// Validate object type
|
// Validate object type - convert to string if array
|
||||||
if (!OBJECT_TYPES[objectType]) {
|
const objectTypeStr = Array.isArray(objectType) ? objectType[0] : objectType;
|
||||||
|
if (!OBJECT_TYPES[objectTypeStr]) {
|
||||||
res.status(400).json({
|
res.status(400).json({
|
||||||
error: `Unknown object type: ${objectType}`,
|
error: `Unknown object type: ${objectTypeStr}`,
|
||||||
supportedTypes: Object.keys(OBJECT_TYPES),
|
supportedTypes: Object.keys(OBJECT_TYPES),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -89,11 +91,11 @@ router.post('/sync/:objectType', async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
logger.info(`Manual sync triggered for ${objectType}`);
|
logger.info(`Manual sync triggered for ${objectType}`);
|
||||||
|
|
||||||
const result = await syncEngine.syncType(objectType as CMDBObjectTypeName);
|
const result = await syncEngine.syncType(objectTypeStr as CMDBObjectTypeName);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
status: 'completed',
|
status: 'completed',
|
||||||
objectType,
|
objectType: objectTypeStr,
|
||||||
stats: result,
|
stats: result,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -113,10 +115,11 @@ router.post('/sync/:objectType', async (req: Request, res: Response) => {
|
|||||||
router.delete('/clear/:objectType', async (req: Request, res: Response) => {
|
router.delete('/clear/:objectType', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { objectType } = req.params;
|
const { objectType } = req.params;
|
||||||
|
const objectTypeStr = Array.isArray(objectType) ? objectType[0] : objectType;
|
||||||
|
|
||||||
if (!OBJECT_TYPES[objectType]) {
|
if (!OBJECT_TYPES[objectTypeStr]) {
|
||||||
res.status(400).json({
|
res.status(400).json({
|
||||||
error: `Unknown object type: ${objectType}`,
|
error: `Unknown object type: ${objectTypeStr}`,
|
||||||
supportedTypes: Object.keys(OBJECT_TYPES),
|
supportedTypes: Object.keys(OBJECT_TYPES),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { dataService } from '../services/dataService.js';
|
|||||||
import { databaseService } from '../services/database.js';
|
import { databaseService } from '../services/database.js';
|
||||||
import { logger } from '../services/logger.js';
|
import { logger } from '../services/logger.js';
|
||||||
import { config } from '../config/env.js';
|
import { config } from '../config/env.js';
|
||||||
|
import { getQueryString, getQueryNumber } from '../utils/queryHelpers.js';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@ router.post('/suggest/:id', async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
// Get provider from query parameter or request body, default to config
|
// Get provider from query parameter or request body, default to config
|
||||||
const provider = (req.query.provider as AIProvider) || (req.body.provider as AIProvider) || config.defaultAIProvider;
|
const provider = (getQueryString(req, 'provider') as AIProvider) || (req.body.provider as AIProvider) || config.defaultAIProvider;
|
||||||
|
|
||||||
if (!aiService.isConfigured(provider)) {
|
if (!aiService.isConfigured(provider)) {
|
||||||
res.status(503).json({
|
res.status(503).json({
|
||||||
@@ -70,7 +71,7 @@ router.get('/function/:code', (req: Request, res: Response) => {
|
|||||||
// Get classification history
|
// Get classification history
|
||||||
router.get('/history', async (req: Request, res: Response) => {
|
router.get('/history', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const limit = parseInt(req.query.limit as string) || 50;
|
const limit = getQueryNumber(req, 'limit', 50);
|
||||||
const history = await databaseService.getClassificationHistory(limit);
|
const history = await databaseService.getClassificationHistory(limit);
|
||||||
res.json(history);
|
res.json(history);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -142,37 +142,39 @@ router.get('/data-completeness', async (req: Request, res: Response) => {
|
|||||||
description: 'Configuration for Data Completeness Score fields',
|
description: 'Configuration for Data Completeness Score fields',
|
||||||
lastUpdated: new Date().toISOString(),
|
lastUpdated: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
categories: {
|
categories: [
|
||||||
general: {
|
{
|
||||||
|
id: 'general',
|
||||||
name: 'General',
|
name: 'General',
|
||||||
description: 'General application information fields',
|
description: 'General application information fields',
|
||||||
fields: [
|
fields: [
|
||||||
{ name: 'Organisation', fieldPath: 'organisation', enabled: true },
|
{ id: 'organisation', name: 'Organisation', fieldPath: 'organisation', enabled: true },
|
||||||
{ name: 'ApplicationFunction', fieldPath: 'applicationFunctions', enabled: true },
|
{ id: 'applicationFunctions', name: 'ApplicationFunction', fieldPath: 'applicationFunctions', enabled: true },
|
||||||
{ name: 'Status', fieldPath: 'status', enabled: true },
|
{ id: 'status', name: 'Status', fieldPath: 'status', enabled: true },
|
||||||
{ name: 'Business Impact Analyse', fieldPath: 'businessImpactAnalyse', enabled: true },
|
{ id: 'businessImpactAnalyse', name: 'Business Impact Analyse', fieldPath: 'businessImpactAnalyse', enabled: true },
|
||||||
{ name: 'Application Component Hosting Type', fieldPath: 'hostingType', enabled: true },
|
{ id: 'hostingType', name: 'Application Component Hosting Type', fieldPath: 'hostingType', enabled: true },
|
||||||
{ name: 'Supplier Product', fieldPath: 'supplierProduct', enabled: true },
|
{ id: 'supplierProduct', name: 'Supplier Product', fieldPath: 'supplierProduct', enabled: true },
|
||||||
{ name: 'Business Owner', fieldPath: 'businessOwner', enabled: true },
|
{ id: 'businessOwner', name: 'Business Owner', fieldPath: 'businessOwner', enabled: true },
|
||||||
{ name: 'System Owner', fieldPath: 'systemOwner', enabled: true },
|
{ id: 'systemOwner', name: 'System Owner', fieldPath: 'systemOwner', enabled: true },
|
||||||
{ name: 'Functional Application Management', fieldPath: 'functionalApplicationManagement', enabled: true },
|
{ id: 'functionalApplicationManagement', name: 'Functional Application Management', fieldPath: 'functionalApplicationManagement', enabled: true },
|
||||||
{ name: 'Technical Application Management', fieldPath: 'technicalApplicationManagement', enabled: true },
|
{ id: 'technicalApplicationManagement', name: 'Technical Application Management', fieldPath: 'technicalApplicationManagement', enabled: true },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
applicationManagement: {
|
{
|
||||||
|
id: 'applicationManagement',
|
||||||
name: 'Application Management',
|
name: 'Application Management',
|
||||||
description: 'Application management classification fields',
|
description: 'Application management classification fields',
|
||||||
fields: [
|
fields: [
|
||||||
{ name: 'ICT Governance Model', fieldPath: 'governanceModel', enabled: true },
|
{ id: 'governanceModel', name: 'ICT Governance Model', fieldPath: 'governanceModel', enabled: true },
|
||||||
{ name: 'Application Management - Application Type', fieldPath: 'applicationType', enabled: true },
|
{ id: 'applicationType', name: 'Application Management - Application Type', fieldPath: 'applicationType', enabled: true },
|
||||||
{ name: 'Application Management - Hosting', fieldPath: 'applicationManagementHosting', enabled: true },
|
{ id: 'applicationManagementHosting', name: 'Application Management - Hosting', fieldPath: 'applicationManagementHosting', enabled: true },
|
||||||
{ name: 'Application Management - TAM', fieldPath: 'applicationManagementTAM', enabled: true },
|
{ id: 'applicationManagementTAM', name: 'Application Management - TAM', fieldPath: 'applicationManagementTAM', enabled: true },
|
||||||
{ name: 'Application Management - Dynamics Factor', fieldPath: 'dynamicsFactor', enabled: true },
|
{ id: 'dynamicsFactor', name: 'Application Management - Dynamics Factor', fieldPath: 'dynamicsFactor', enabled: true },
|
||||||
{ name: 'Application Management - Complexity Factor', fieldPath: 'complexityFactor', enabled: true },
|
{ id: 'complexityFactor', name: 'Application Management - Complexity Factor', fieldPath: 'complexityFactor', enabled: true },
|
||||||
{ name: 'Application Management - Number of Users', fieldPath: 'numberOfUsers', enabled: true },
|
{ id: 'numberOfUsers', name: 'Application Management - Number of Users', fieldPath: 'numberOfUsers', enabled: true },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
};
|
};
|
||||||
res.json(defaultConfig);
|
res.json(defaultConfig);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import { Router, Request, Response } from 'express';
|
import { Router, Request, Response } from 'express';
|
||||||
import { cmdbService } from '../services/cmdbService.js';
|
import { cmdbService } from '../services/cmdbService.js';
|
||||||
import { logger } from '../services/logger.js';
|
import { logger } from '../services/logger.js';
|
||||||
|
import { getQueryString, getQueryNumber } from '../utils/queryHelpers.js';
|
||||||
import { OBJECT_TYPES } from '../generated/jira-schema.js';
|
import { OBJECT_TYPES } from '../generated/jira-schema.js';
|
||||||
import type { CMDBObject, CMDBObjectTypeName } from '../generated/jira-types.js';
|
import type { CMDBObject, CMDBObjectTypeName } from '../generated/jira-types.js';
|
||||||
|
|
||||||
@@ -32,9 +33,9 @@ router.get('/', (req: Request, res: Response) => {
|
|||||||
router.get('/:type', async (req: Request, res: Response) => {
|
router.get('/:type', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { type } = req.params;
|
const { type } = req.params;
|
||||||
const limit = parseInt(req.query.limit as string) || 1000;
|
const limit = getQueryNumber(req, 'limit', 1000);
|
||||||
const offset = parseInt(req.query.offset as string) || 0;
|
const offset = getQueryNumber(req, 'offset', 0);
|
||||||
const search = req.query.search as string | undefined;
|
const search = getQueryString(req, 'search');
|
||||||
|
|
||||||
// Validate type
|
// Validate type
|
||||||
if (!OBJECT_TYPES[type]) {
|
if (!OBJECT_TYPES[type]) {
|
||||||
@@ -71,7 +72,7 @@ router.get('/:type', async (req: Request, res: Response) => {
|
|||||||
router.get('/:type/:id', async (req: Request, res: Response) => {
|
router.get('/:type/:id', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { type, id } = req.params;
|
const { type, id } = req.params;
|
||||||
const forceRefresh = req.query.refresh === 'true';
|
const forceRefresh = getQueryString(req, 'refresh') === 'true';
|
||||||
|
|
||||||
// Validate type
|
// Validate type
|
||||||
if (!OBJECT_TYPES[type]) {
|
if (!OBJECT_TYPES[type]) {
|
||||||
@@ -102,7 +103,7 @@ router.get('/:type/:id', async (req: Request, res: Response) => {
|
|||||||
router.get('/:type/:id/related/:relationType', async (req: Request, res: Response) => {
|
router.get('/:type/:id/related/:relationType', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { type, id, relationType } = req.params;
|
const { type, id, relationType } = req.params;
|
||||||
const attribute = req.query.attribute as string | undefined;
|
const attribute = getQueryString(req, 'attribute');
|
||||||
|
|
||||||
// Validate types
|
// Validate types
|
||||||
if (!OBJECT_TYPES[type]) {
|
if (!OBJECT_TYPES[type]) {
|
||||||
@@ -139,7 +140,7 @@ router.get('/:type/:id/related/:relationType', async (req: Request, res: Respons
|
|||||||
router.get('/:type/:id/referenced-by/:sourceType', async (req: Request, res: Response) => {
|
router.get('/:type/:id/referenced-by/:sourceType', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { type, id, sourceType } = req.params;
|
const { type, id, sourceType } = req.params;
|
||||||
const attribute = req.query.attribute as string | undefined;
|
const attribute = getQueryString(req, 'attribute');
|
||||||
|
|
||||||
// Validate types
|
// Validate types
|
||||||
if (!OBJECT_TYPES[type]) {
|
if (!OBJECT_TYPES[type]) {
|
||||||
|
|||||||
@@ -289,19 +289,18 @@ async function toApplicationDetails(app: ApplicationComponent): Promise<Applicat
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Calculate data completeness percentage
|
// Calculate data completeness percentage
|
||||||
// Convert ApplicationListItem-like structure to format expected by completeness calculator
|
// Convert ApplicationDetails-like structure to format expected by completeness calculator
|
||||||
const appForCompleteness = {
|
const appForCompleteness = {
|
||||||
...result,
|
|
||||||
organisation: organisation?.name || null,
|
organisation: organisation?.name || null,
|
||||||
applicationFunctions: result.applicationFunctions,
|
applicationFunctions: applicationFunctions,
|
||||||
status: result.status,
|
status: (app.status || 'In Production') as ApplicationStatus,
|
||||||
businessImpactAnalyse: businessImpactAnalyse,
|
businessImpactAnalyse: businessImpactAnalyse,
|
||||||
hostingType: hostingType,
|
hostingType: hostingType,
|
||||||
supplierProduct: result.supplierProduct,
|
supplierProduct: extractLabel(app.supplierProduct),
|
||||||
businessOwner: result.businessOwner,
|
businessOwner: extractLabel(app.businessOwner),
|
||||||
systemOwner: result.systemOwner,
|
systemOwner: extractLabel(app.systemOwner),
|
||||||
functionalApplicationManagement: result.functionalApplicationManagement,
|
functionalApplicationManagement: app.functionalApplicationManagement || null,
|
||||||
technicalApplicationManagement: result.technicalApplicationManagement,
|
technicalApplicationManagement: extractLabel(app.technicalApplicationManagement),
|
||||||
governanceModel: governanceModel,
|
governanceModel: governanceModel,
|
||||||
applicationType: applicationType,
|
applicationType: applicationType,
|
||||||
applicationManagementHosting: applicationManagementHosting,
|
applicationManagementHosting: applicationManagementHosting,
|
||||||
@@ -314,7 +313,48 @@ async function toApplicationDetails(app: ApplicationComponent): Promise<Applicat
|
|||||||
const completenessPercentage = calculateApplicationCompleteness(appForCompleteness);
|
const completenessPercentage = calculateApplicationCompleteness(appForCompleteness);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...result,
|
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,
|
||||||
dataCompletenessPercentage: Math.round(completenessPercentage * 10) / 10, // Round to 1 decimal
|
dataCompletenessPercentage: Math.round(completenessPercentage * 10) / 10, // Round to 1 decimal
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -457,6 +497,7 @@ function toApplicationListItem(app: ApplicationComponent): ApplicationListItem {
|
|||||||
id: app.id,
|
id: app.id,
|
||||||
key: app.objectKey,
|
key: app.objectKey,
|
||||||
name: app.label,
|
name: app.label,
|
||||||
|
searchReference: app.searchReference || null,
|
||||||
status: app.status as ApplicationStatus | null,
|
status: app.status as ApplicationStatus | null,
|
||||||
applicationFunctions,
|
applicationFunctions,
|
||||||
governanceModel,
|
governanceModel,
|
||||||
@@ -1209,7 +1250,7 @@ export const dataService = {
|
|||||||
// Get complexity factor value
|
// Get complexity factor value
|
||||||
if (app.applicationManagementComplexityFactor && typeof app.applicationManagementComplexityFactor === 'object') {
|
if (app.applicationManagementComplexityFactor && typeof app.applicationManagementComplexityFactor === 'object') {
|
||||||
const factorObj = complexityFactorCache?.get(app.applicationManagementComplexityFactor.objectId);
|
const factorObj = complexityFactorCache?.get(app.applicationManagementComplexityFactor.objectId);
|
||||||
if (factorObj?.factor !== undefined) {
|
if (factorObj?.factor !== undefined && factorObj.factor !== null) {
|
||||||
metrics.complexityValues.push(factorObj.factor);
|
metrics.complexityValues.push(factorObj.factor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1217,7 +1258,7 @@ export const dataService = {
|
|||||||
// Get dynamics factor value
|
// Get dynamics factor value
|
||||||
if (app.applicationManagementDynamicsFactor && typeof app.applicationManagementDynamicsFactor === 'object') {
|
if (app.applicationManagementDynamicsFactor && typeof app.applicationManagementDynamicsFactor === 'object') {
|
||||||
const factorObj = dynamicsFactorCache?.get(app.applicationManagementDynamicsFactor.objectId);
|
const factorObj = dynamicsFactorCache?.get(app.applicationManagementDynamicsFactor.objectId);
|
||||||
if (factorObj?.factor !== undefined) {
|
if (factorObj?.factor !== undefined && factorObj.factor !== null) {
|
||||||
metrics.dynamicsValues.push(factorObj.factor);
|
metrics.dynamicsValues.push(factorObj.factor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export interface ApplicationListItem {
|
|||||||
id: string;
|
id: string;
|
||||||
key: string;
|
key: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
searchReference?: string | null; // Search reference for matching
|
||||||
status: ApplicationStatus | null;
|
status: ApplicationStatus | null;
|
||||||
applicationFunctions: ReferenceValue[]; // Multiple functions supported
|
applicationFunctions: ReferenceValue[]; // Multiple functions supported
|
||||||
governanceModel: ReferenceValue | null;
|
governanceModel: ReferenceValue | null;
|
||||||
|
|||||||
34
backend/src/utils/queryHelpers.ts
Normal file
34
backend/src/utils/queryHelpers.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Helper functions for Express request query parameters
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Request } from 'express';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a query parameter as a string, handling both string and string[] types
|
||||||
|
*/
|
||||||
|
export function getQueryString(req: Request, key: string): string | undefined {
|
||||||
|
const value = req.query[key];
|
||||||
|
if (value === undefined) return undefined;
|
||||||
|
if (Array.isArray(value)) return value[0] as string;
|
||||||
|
return value as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a query parameter as a number, handling both string and string[] types
|
||||||
|
*/
|
||||||
|
export function getQueryNumber(req: Request, key: string, defaultValue?: number): number {
|
||||||
|
const value = getQueryString(req, key);
|
||||||
|
if (value === undefined) return defaultValue ?? 0;
|
||||||
|
const parsed = parseInt(value, 10);
|
||||||
|
return isNaN(parsed) ? (defaultValue ?? 0) : parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a query parameter as a boolean
|
||||||
|
*/
|
||||||
|
export function getQueryBoolean(req: Request, key: string, defaultValue = false): boolean {
|
||||||
|
const value = getQueryString(req, key);
|
||||||
|
if (value === undefined) return defaultValue;
|
||||||
|
return value === 'true' || value === '1';
|
||||||
|
}
|
||||||
270
docs/AZURE-CLI-QUICKSTART.md
Normal file
270
docs/AZURE-CLI-QUICKSTART.md
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
# Azure CLI - Quick Start Guide
|
||||||
|
|
||||||
|
## 📍 Waar voer je deze commando's uit?
|
||||||
|
|
||||||
|
Je voert deze commando's uit in de **Terminal** (command line) op je computer.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🖥️ Terminal Openen
|
||||||
|
|
||||||
|
### Op macOS (jouw situatie):
|
||||||
|
1. **Open Terminal:**
|
||||||
|
- Druk op `Cmd + Space` (Spotlight)
|
||||||
|
- Typ "Terminal"
|
||||||
|
- Druk Enter
|
||||||
|
- Of: Applications → Utilities → Terminal
|
||||||
|
|
||||||
|
2. **Of gebruik iTerm2** (als je die hebt geïnstalleerd)
|
||||||
|
|
||||||
|
### Op Windows:
|
||||||
|
- **PowerShell** of **Command Prompt**
|
||||||
|
- Druk `Win + R`, typ `powershell`, Enter
|
||||||
|
|
||||||
|
### Op Linux:
|
||||||
|
- Open je terminal emulator (bijv. GNOME Terminal, Konsole)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Stap 1: Check of Azure CLI Geïnstalleerd is
|
||||||
|
|
||||||
|
**Voer dit commando uit in de terminal:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
az --version
|
||||||
|
```
|
||||||
|
|
||||||
|
**Als je een versie ziet** (bijv. `azure-cli 2.50.0`): ✅ Azure CLI is geïnstalleerd, ga door naar Stap 2.
|
||||||
|
|
||||||
|
**Als je een foutmelding krijgt** (bijv. `command not found`): ❌ Azure CLI is niet geïnstalleerd, zie installatie hieronder.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📥 Stap 2: Azure CLI Installeren (als nodig)
|
||||||
|
|
||||||
|
### Op macOS:
|
||||||
|
|
||||||
|
**Optie A: Met Homebrew (Aanbevolen)**
|
||||||
|
```bash
|
||||||
|
# Installeer Homebrew (als je die nog niet hebt)
|
||||||
|
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||||
|
|
||||||
|
# Installeer Azure CLI
|
||||||
|
brew install azure-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
**Optie B: Met Installer**
|
||||||
|
1. Download: https://aka.ms/installazureclimac
|
||||||
|
2. Open het `.pkg` bestand
|
||||||
|
3. Volg de installatie wizard
|
||||||
|
|
||||||
|
**Optie C: Met pip (Python)**
|
||||||
|
```bash
|
||||||
|
pip3 install azure-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
### Op Windows:
|
||||||
|
1. Download: https://aka.ms/installazurecliwindows
|
||||||
|
2. Run de `.msi` installer
|
||||||
|
3. Volg de installatie wizard
|
||||||
|
|
||||||
|
### Op Linux:
|
||||||
|
```bash
|
||||||
|
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
|
||||||
|
```
|
||||||
|
|
||||||
|
**Na installatie, check opnieuw:**
|
||||||
|
```bash
|
||||||
|
az --version
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Stap 3: Login bij Azure
|
||||||
|
|
||||||
|
**Voer dit commando uit:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
az login
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wat gebeurt er:**
|
||||||
|
1. Je browser opent automatisch
|
||||||
|
2. Log in met je Azure account (hetzelfde account dat je gebruikt voor Azure Portal)
|
||||||
|
3. Na succesvol inloggen, sluit je de browser
|
||||||
|
4. De terminal toont je subscriptions
|
||||||
|
|
||||||
|
**Als browser niet opent automatisch:**
|
||||||
|
- Je krijgt een code en URL in de terminal
|
||||||
|
- Kopieer de code
|
||||||
|
- Open de URL in je browser
|
||||||
|
- Voer de code in
|
||||||
|
|
||||||
|
**Verwachte output:**
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"cloudName": "AzureCloud",
|
||||||
|
"id": "12345678-1234-1234-1234-123456789012",
|
||||||
|
"name": "Subscription Name",
|
||||||
|
"state": "Enabled",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**✅ Je bent nu ingelogd!**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 Stap 4: Haal ACR Credentials Op
|
||||||
|
|
||||||
|
**Voer dit commando uit:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
az acr credential show --name zdlas
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verwachte output:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"passwords": [
|
||||||
|
{
|
||||||
|
"name": "password",
|
||||||
|
"value": "abc123xyz..."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "password2",
|
||||||
|
"value": "def456uvw..."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"username": "zdlas"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Noteer:**
|
||||||
|
- **Username**: `zdlas` (of wat er staat)
|
||||||
|
- **Password**: Gebruik `passwords[0].value` (de eerste password)
|
||||||
|
|
||||||
|
**⚠️ Belangrijk:** Deze credentials zijn gevoelig! Deel ze niet en gebruik ze alleen voor de service connection.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Complete Stappen in Terminal
|
||||||
|
|
||||||
|
**Hier is de complete reeks commando's:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Check of Azure CLI geïnstalleerd is
|
||||||
|
az --version
|
||||||
|
|
||||||
|
# 2. Login bij Azure (opent browser)
|
||||||
|
az login
|
||||||
|
|
||||||
|
# 3. Haal ACR credentials op
|
||||||
|
az acr credential show --name zdlas
|
||||||
|
```
|
||||||
|
|
||||||
|
**Kopieer de output** en gebruik de `username` en `passwords[0].value` in Azure DevOps.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Alternatief: Via Azure Portal (Zonder Azure CLI)
|
||||||
|
|
||||||
|
**Als je Azure CLI niet wilt installeren, kun je credentials ook via Azure Portal ophalen:**
|
||||||
|
|
||||||
|
1. **Ga naar Azure Portal**: https://portal.azure.com
|
||||||
|
2. **Ga naar je Container Registry**: Zoek naar `zdlas`
|
||||||
|
3. **Klik op "Access keys"** (links in het menu)
|
||||||
|
4. **Je ziet:**
|
||||||
|
- **Login server**: `zdlas.azurecr.io`
|
||||||
|
- **Username**: `zdlas` (of admin username)
|
||||||
|
- **Password**: Klik op "Show" naast password om het te zien
|
||||||
|
- **Password2**: Alternatieve password
|
||||||
|
|
||||||
|
5. **Kopieer de username en password**
|
||||||
|
|
||||||
|
**✅ Dit is hetzelfde als `az acr credential show`!**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Voor Jouw Situatie (Service Connection)
|
||||||
|
|
||||||
|
**Gebruik deze credentials in Azure DevOps:**
|
||||||
|
|
||||||
|
1. **In de service connection wizard:**
|
||||||
|
- Kies "Docker Registry" → "Others"
|
||||||
|
- **Docker Registry**: `zdlas.azurecr.io`
|
||||||
|
- **Docker ID**: `zdlas` (of de username uit de output)
|
||||||
|
- **Docker Password**: `passwords[0].value` (uit de output)
|
||||||
|
- **Service connection name**: `zuyderland-cmdb-acr-connection`
|
||||||
|
|
||||||
|
2. **Save**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚨 Troubleshooting
|
||||||
|
|
||||||
|
### "az: command not found"
|
||||||
|
|
||||||
|
**Oplossing:** Azure CLI is niet geïnstalleerd
|
||||||
|
- Installeer Azure CLI (zie Stap 2 hierboven)
|
||||||
|
- Of gebruik Azure Portal alternatief (zie hierboven)
|
||||||
|
|
||||||
|
### "az login" opent geen browser
|
||||||
|
|
||||||
|
**Oplossing:**
|
||||||
|
- Kopieer de code en URL uit de terminal
|
||||||
|
- Open de URL handmatig in je browser
|
||||||
|
- Voer de code in
|
||||||
|
|
||||||
|
### "Subscription not found" of "Access denied"
|
||||||
|
|
||||||
|
**Oplossing:**
|
||||||
|
- Check of je ingelogd bent met het juiste Azure account
|
||||||
|
- Check of je toegang hebt tot de subscription waar de ACR staat
|
||||||
|
- Probeer: `az account list` om je subscriptions te zien
|
||||||
|
- Selecteer de juiste subscription: `az account set --subscription "Subscription Name"`
|
||||||
|
|
||||||
|
### "ACR not found"
|
||||||
|
|
||||||
|
**Oplossing:**
|
||||||
|
- Check of de ACR naam correct is: `zdlas`
|
||||||
|
- Check of je toegang hebt tot de ACR
|
||||||
|
- Probeer: `az acr list` om alle ACR's te zien
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 Tips
|
||||||
|
|
||||||
|
1. **Azure CLI blijft ingelogd** - Je hoeft niet elke keer `az login` te doen
|
||||||
|
2. **Check je subscription** - Als je meerdere subscriptions hebt: `az account show`
|
||||||
|
3. **Wissel subscription** - `az account set --subscription "Subscription Name"`
|
||||||
|
4. **Logout** - `az logout` (als je klaar bent)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Meer Informatie
|
||||||
|
|
||||||
|
- [Azure CLI Installatie](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli)
|
||||||
|
- [Azure CLI Login](https://learn.microsoft.com/en-us/cli/azure/authenticate-azure-cli)
|
||||||
|
- [ACR Credentials](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Quick Reference
|
||||||
|
|
||||||
|
**Terminal openen:**
|
||||||
|
- macOS: `Cmd + Space` → "Terminal"
|
||||||
|
|
||||||
|
**Azure CLI commando's:**
|
||||||
|
```bash
|
||||||
|
az --version # Check installatie
|
||||||
|
az login # Login bij Azure
|
||||||
|
az acr credential show --name zdlas # Haal credentials op
|
||||||
|
```
|
||||||
|
|
||||||
|
**Azure Portal alternatief:**
|
||||||
|
- Portal → Container Registry → Access keys
|
||||||
|
|
||||||
|
**Klaar!** 🚀
|
||||||
250
docs/AZURE-PIPELINE-REPO-TROUBLESHOOTING.md
Normal file
250
docs/AZURE-PIPELINE-REPO-TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
# Azure DevOps Pipeline - Repository Not Found
|
||||||
|
|
||||||
|
## 🔴 Probleem: "No matching repositories were found"
|
||||||
|
|
||||||
|
Als Azure DevOps je repository niet kan vinden bij het aanmaken van een pipeline, probeer deze oplossingen:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 1: Check Repository Naam
|
||||||
|
|
||||||
|
**Probleem:** De repository naam komt mogelijk niet overeen.
|
||||||
|
|
||||||
|
**Oplossing:**
|
||||||
|
1. **Ga naar Repos** (links in het menu)
|
||||||
|
2. **Check de exacte repository naam**
|
||||||
|
- Kijk naar de repository die je hebt gepusht
|
||||||
|
- Noteer de exacte naam (inclusief hoofdletters/spaties)
|
||||||
|
|
||||||
|
3. **In de pipeline wizard:**
|
||||||
|
- Zoek naar de repository met de exacte naam
|
||||||
|
- Of probeer verschillende variaties:
|
||||||
|
- `Zuyderland CMDB GUI`
|
||||||
|
- `zuyderland-cmdb-gui`
|
||||||
|
- `ZuyderlandCMDBGUI`
|
||||||
|
|
||||||
|
**Jouw repository naam zou moeten zijn:** `Zuyderland CMDB GUI` (met spaties)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 2: Check Repository Type
|
||||||
|
|
||||||
|
**In de pipeline wizard, probeer verschillende repository types:**
|
||||||
|
|
||||||
|
1. **Azure Repos Git** (als je code in Azure DevOps staat)
|
||||||
|
- Dit is waarschijnlijk wat je nodig hebt
|
||||||
|
- Check of je repository hier staat
|
||||||
|
|
||||||
|
2. **GitHub** (als je code in GitHub staat)
|
||||||
|
- Niet van toepassing voor jou
|
||||||
|
|
||||||
|
3. **Other Git** (als je code ergens anders staat)
|
||||||
|
- Niet van toepassing voor jou
|
||||||
|
|
||||||
|
**Voor jouw situatie:** Kies **"Azure Repos Git"**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 3: Check Repository Toegang
|
||||||
|
|
||||||
|
**Probleem:** Je hebt mogelijk geen toegang tot de repository.
|
||||||
|
|
||||||
|
**Oplossing:**
|
||||||
|
1. **Ga naar Repos** (links in het menu)
|
||||||
|
2. **Check of je de repository ziet**
|
||||||
|
- Als je de repository niet ziet, heb je mogelijk geen toegang
|
||||||
|
3. **Check permissions:**
|
||||||
|
- Project Settings → Repositories → Security
|
||||||
|
- Check of je account toegang heeft
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 4: Check Project
|
||||||
|
|
||||||
|
**Probleem:** Je bent mogelijk in het verkeerde project.
|
||||||
|
|
||||||
|
**Oplossing:**
|
||||||
|
1. **Check het project naam** (bovenaan links)
|
||||||
|
- Moet zijn: **"JiraAssetsCMDB"**
|
||||||
|
2. **Als je in een ander project bent:**
|
||||||
|
- Klik op het project dropdown (bovenaan links)
|
||||||
|
- Selecteer **"JiraAssetsCMDB"**
|
||||||
|
3. **Probeer opnieuw** de pipeline aan te maken
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 5: Refresh/Herlaad
|
||||||
|
|
||||||
|
**Soms helpt een simpele refresh:**
|
||||||
|
|
||||||
|
1. **Refresh de browser pagina** (F5 of Cmd+R)
|
||||||
|
2. **Sluit en open opnieuw** de pipeline wizard
|
||||||
|
3. **Probeer opnieuw**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 6: Check of Repository Bestaat
|
||||||
|
|
||||||
|
**Probleem:** De repository bestaat mogelijk niet in Azure DevOps.
|
||||||
|
|
||||||
|
**Oplossing:**
|
||||||
|
1. **Ga naar Repos** (links in het menu)
|
||||||
|
2. **Check of je repository zichtbaar is**
|
||||||
|
- Je zou moeten zien: `Zuyderland CMDB GUI` (of jouw repo naam)
|
||||||
|
3. **Als de repository niet bestaat:**
|
||||||
|
- Je moet eerst de code pushen naar Azure DevOps
|
||||||
|
- Of de repository aanmaken in Azure DevOps
|
||||||
|
|
||||||
|
**Check of je code al in Azure DevOps staat:**
|
||||||
|
- Ga naar Repos → Files
|
||||||
|
- Je zou je code moeten zien (bijv. `azure-pipelines.yml`, `backend/`, `frontend/`, etc.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 7: Maak Repository Aan (Als Die Niet Bestaat)
|
||||||
|
|
||||||
|
**Als de repository nog niet bestaat in Azure DevOps:**
|
||||||
|
|
||||||
|
### Optie A: Push Code naar Bestaande Repository
|
||||||
|
|
||||||
|
**Als de repository al bestaat maar leeg is:**
|
||||||
|
|
||||||
|
1. **Check de repository URL:**
|
||||||
|
```
|
||||||
|
git@ssh.dev.azure.com:v3/ZuyderlandMedischCentrum/JiraAssetsCMDB/Zuyderland%20CMDB%20GUI
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Push je code:**
|
||||||
|
```bash
|
||||||
|
cd /Users/berthausmans/Documents/Development/zuyderland-cmdb-gui
|
||||||
|
git push azure main
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Check in Azure DevOps:**
|
||||||
|
- Ga naar Repos → Files
|
||||||
|
- Je zou je code moeten zien
|
||||||
|
|
||||||
|
### Optie B: Maak Nieuwe Repository Aan
|
||||||
|
|
||||||
|
**Als de repository helemaal niet bestaat:**
|
||||||
|
|
||||||
|
1. **Ga naar Repos** (links in het menu)
|
||||||
|
2. **Klik op "New repository"** of het "+" icoon
|
||||||
|
3. **Vul in:**
|
||||||
|
- **Repository name**: `Zuyderland CMDB GUI`
|
||||||
|
- **Type**: Git
|
||||||
|
4. **Create**
|
||||||
|
5. **Push je code:**
|
||||||
|
```bash
|
||||||
|
cd /Users/berthausmans/Documents/Development/zuyderland-cmdb-gui
|
||||||
|
git remote add azure git@ssh.dev.azure.com:v3/ZuyderlandMedischCentrum/JiraAssetsCMDB/Zuyderland%20CMDB%20GUI
|
||||||
|
git push azure main
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 8: Gebruik "Other Git" Als Workaround
|
||||||
|
|
||||||
|
**Als niets werkt, gebruik "Other Git" als tijdelijke oplossing:**
|
||||||
|
|
||||||
|
1. **In de pipeline wizard:**
|
||||||
|
- Kies **"Other Git"** (in plaats van "Azure Repos Git")
|
||||||
|
2. **Vul in:**
|
||||||
|
- **Repository URL**: `git@ssh.dev.azure.com:v3/ZuyderlandMedischCentrum/JiraAssetsCMDB/Zuyderland%20CMDB%20GUI`
|
||||||
|
- Of HTTPS: `https://ZuyderlandMedischCentrum@dev.azure.com/ZuyderlandMedischCentrum/JiraAssetsCMDB/_git/Zuyderland%20CMDB%20GUI`
|
||||||
|
3. **Branch**: `main`
|
||||||
|
4. **Continue**
|
||||||
|
|
||||||
|
**⚠️ Let op:** Dit werkt, maar "Azure Repos Git" is de voorkeursoptie.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 Diagnose Stappen
|
||||||
|
|
||||||
|
**Om te diagnosticeren wat het probleem is:**
|
||||||
|
|
||||||
|
### 1. Check of Repository Bestaat
|
||||||
|
|
||||||
|
1. Ga naar **Repos** (links in het menu)
|
||||||
|
2. Check of je `Zuyderland CMDB GUI` ziet
|
||||||
|
3. Klik erop en check of je code ziet
|
||||||
|
|
||||||
|
### 2. Check Repository URL
|
||||||
|
|
||||||
|
**In Terminal:**
|
||||||
|
```bash
|
||||||
|
cd /Users/berthausmans/Documents/Development/zuyderland-cmdb-gui
|
||||||
|
git remote -v
|
||||||
|
```
|
||||||
|
|
||||||
|
**Je zou moeten zien:**
|
||||||
|
```
|
||||||
|
azure git@ssh.dev.azure.com:v3/ZuyderlandMedischCentrum/JiraAssetsCMDB/Zuyderland%20CMDB%20GUI (fetch)
|
||||||
|
azure git@ssh.dev.azure.com:v3/ZuyderlandMedischCentrum/JiraAssetsCMDB/Zuyderland%20CMDB%20GUI (push)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Check of Code Gepusht is
|
||||||
|
|
||||||
|
**In Terminal:**
|
||||||
|
```bash
|
||||||
|
git log azure/main --oneline -5
|
||||||
|
```
|
||||||
|
|
||||||
|
**Als je commits ziet:** ✅ Code is gepusht
|
||||||
|
**Als je een fout krijgt:** ❌ Code is niet gepusht
|
||||||
|
|
||||||
|
### 4. Push Code (Als Niet Gepusht)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git push azure main
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 Aanbevolen Aanpak
|
||||||
|
|
||||||
|
**Probeer in deze volgorde:**
|
||||||
|
|
||||||
|
1. ✅ **Check Repos** - Ga naar Repos en check of je repository bestaat
|
||||||
|
2. ✅ **Check project naam** - Zorg dat je in "JiraAssetsCMDB" project bent
|
||||||
|
3. ✅ **Refresh pagina** - Soms helpt een simpele refresh
|
||||||
|
4. ✅ **Push code** - Als repository leeg is, push je code
|
||||||
|
5. ✅ **Gebruik "Other Git"** - Als workaround
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Quick Fix (Meest Waarschijnlijk)
|
||||||
|
|
||||||
|
**Het probleem is waarschijnlijk dat de repository leeg is of niet bestaat:**
|
||||||
|
|
||||||
|
1. **Check in Azure DevOps:**
|
||||||
|
- Ga naar **Repos** → **Files**
|
||||||
|
- Check of je code ziet (bijv. `azure-pipelines.yml`)
|
||||||
|
|
||||||
|
2. **Als repository leeg is:**
|
||||||
|
```bash
|
||||||
|
cd /Users/berthausmans/Documents/Development/zuyderland-cmdb-gui
|
||||||
|
git push azure main
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Probeer opnieuw** de pipeline aan te maken
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Meer Informatie
|
||||||
|
|
||||||
|
- [Azure DevOps Repositories](https://learn.microsoft.com/en-us/azure/devops/repos/)
|
||||||
|
- [Create Pipeline from Repository](https://learn.microsoft.com/en-us/azure/devops/pipelines/create-first-pipeline)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🆘 Nog Steeds Problemen?
|
||||||
|
|
||||||
|
Als niets werkt:
|
||||||
|
|
||||||
|
1. **Check of je in het juiste project bent** (JiraAssetsCMDB)
|
||||||
|
2. **Check of de repository bestaat** (Repos → Files)
|
||||||
|
3. **Push je code** naar Azure DevOps
|
||||||
|
4. **Gebruik "Other Git"** als workaround
|
||||||
|
|
||||||
|
**De "Other Git" optie werkt altijd**, ook als de repository niet wordt gevonden in de dropdown.
|
||||||
231
docs/AZURE-SERVICE-CONNECTION-AUTH.md
Normal file
231
docs/AZURE-SERVICE-CONNECTION-AUTH.md
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
# Azure DevOps Service Connection - Authentication Type
|
||||||
|
|
||||||
|
## 🎯 Aanbeveling voor Jouw Situatie
|
||||||
|
|
||||||
|
**Voor Zuyderland CMDB GUI met Azure Container Registry:**
|
||||||
|
|
||||||
|
### ✅ **Service Principal** (Aanbevolen) ⭐
|
||||||
|
|
||||||
|
**Waarom:**
|
||||||
|
- ✅ Werkt altijd en is betrouwbaar
|
||||||
|
- ✅ Meest ondersteunde optie
|
||||||
|
- ✅ Eenvoudig te configureren
|
||||||
|
- ✅ Werkt perfect met Azure Container Registry
|
||||||
|
- ✅ Geen speciale vereisten
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Opties Vergelijking
|
||||||
|
|
||||||
|
### Optie 1: **Service Principal** ⭐ **AANBEVOLEN**
|
||||||
|
|
||||||
|
**Hoe het werkt:**
|
||||||
|
- Azure DevOps maakt automatisch een Service Principal aan in Azure AD
|
||||||
|
- De Service Principal krijgt toegang tot je Azure Container Registry
|
||||||
|
- Azure DevOps gebruikt deze credentials om in te loggen bij ACR
|
||||||
|
|
||||||
|
**Voordelen:**
|
||||||
|
- ✅ **Eenvoudig** - Azure DevOps doet alles automatisch
|
||||||
|
- ✅ **Betrouwbaar** - Werkt altijd, geen speciale configuratie nodig
|
||||||
|
- ✅ **Veilig** - Credentials worden veilig opgeslagen in Azure DevOps
|
||||||
|
- ✅ **Meest ondersteund** - Standaard optie voor de meeste scenario's
|
||||||
|
- ✅ **Werkt met alle Azure services** - Niet alleen ACR
|
||||||
|
|
||||||
|
**Nadelen:**
|
||||||
|
- ❌ Maakt een Service Principal aan in Azure AD (maar dit is normaal en veilig)
|
||||||
|
|
||||||
|
**Wanneer gebruiken:**
|
||||||
|
- ✅ **Jouw situatie** - Azure DevOps Services (cloud) met Azure Container Registry
|
||||||
|
- ✅ De meeste scenario's
|
||||||
|
- ✅ Als je eenvoudige, betrouwbare authenticatie wilt
|
||||||
|
- ✅ Standaard keuze voor nieuwe service connections
|
||||||
|
|
||||||
|
**Configuratie:**
|
||||||
|
- Azure DevOps doet alles automatisch
|
||||||
|
- Je hoeft alleen je Azure subscription en ACR te selecteren
|
||||||
|
- Azure DevOps maakt de Service Principal aan en geeft deze de juiste permissions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Optie 2: **Managed Service Identity (MSI)**
|
||||||
|
|
||||||
|
**Hoe het werkt:**
|
||||||
|
- Gebruikt een Managed Identity van Azure DevOps zelf
|
||||||
|
- Geen credentials nodig - Azure beheert alles
|
||||||
|
- Werkt alleen als Azure DevOps een Managed Identity heeft
|
||||||
|
|
||||||
|
**Voordelen:**
|
||||||
|
- ✅ Geen credentials te beheren
|
||||||
|
- ✅ Automatisch geroteerd door Azure
|
||||||
|
- ✅ Modernere aanpak
|
||||||
|
|
||||||
|
**Nadelen:**
|
||||||
|
- ❌ **Werkt alleen met Azure DevOps Server (on-premises)** met Managed Identity
|
||||||
|
- ❌ **Werkt NIET met Azure DevOps Services (cloud)** - Dit is belangrijk!
|
||||||
|
- ❌ Vereist speciale configuratie
|
||||||
|
- ❌ Minder flexibel
|
||||||
|
|
||||||
|
**Wanneer gebruiken:**
|
||||||
|
- ✅ Azure DevOps Server (on-premises) met Managed Identity
|
||||||
|
- ✅ Als je geen credentials wilt beheren
|
||||||
|
- ❌ **NIET voor Azure DevOps Services (cloud)** - Dit werkt niet!
|
||||||
|
|
||||||
|
**Voor jouw situatie:** ❌ **Niet geschikt** - Je gebruikt Azure DevOps Services (cloud), niet on-premises
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Optie 3: **Workload Identity Federation**
|
||||||
|
|
||||||
|
**Hoe het werkt:**
|
||||||
|
- Modernere manier zonder secrets
|
||||||
|
- Gebruikt federated identity (OIDC)
|
||||||
|
- Azure DevOps krijgt een token van Azure AD zonder credentials op te slaan
|
||||||
|
|
||||||
|
**Voordelen:**
|
||||||
|
- ✅ Geen secrets opgeslagen
|
||||||
|
- ✅ Modernere, veiligere aanpak
|
||||||
|
- ✅ Automatisch token management
|
||||||
|
|
||||||
|
**Nadelen:**
|
||||||
|
- ❌ **Nog niet volledig ondersteund** voor alle scenario's
|
||||||
|
- ❌ Kan complexer zijn om te configureren
|
||||||
|
- ❌ Vereist specifieke Azure AD configuratie
|
||||||
|
- ❌ Mogelijk niet beschikbaar in alle Azure DevOps organisaties
|
||||||
|
|
||||||
|
**Wanneer gebruiken:**
|
||||||
|
- ✅ Als je de modernste security features wilt
|
||||||
|
- ✅ Als je organisatie Workload Identity Federation ondersteunt
|
||||||
|
- ✅ Voor nieuwe projecten waar je geen legacy support nodig hebt
|
||||||
|
- ❌ **Niet aanbevolen als je eenvoudige setup wilt**
|
||||||
|
|
||||||
|
**Voor jouw situatie:** ⚠️ **Mogelijk beschikbaar, maar Service Principal is eenvoudiger**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 Jouw Situatie Analyse
|
||||||
|
|
||||||
|
**Jouw setup:**
|
||||||
|
- ✅ Azure DevOps Services (cloud) - `dev.azure.com`
|
||||||
|
- ✅ Azure Container Registry - `zdlas.azurecr.io`
|
||||||
|
- ✅ Eenvoudige setup gewenst
|
||||||
|
- ✅ Betrouwbare authenticatie nodig
|
||||||
|
|
||||||
|
**Conclusie:** ✅ **Service Principal is perfect!**
|
||||||
|
|
||||||
|
**Waarom niet de andere opties:**
|
||||||
|
- ❌ **Managed Service Identity**: Werkt niet met Azure DevOps Services (cloud)
|
||||||
|
- ⚠️ **Workload Identity Federation**: Mogelijk beschikbaar, maar complexer dan nodig
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Checklist: Welke Keuze?
|
||||||
|
|
||||||
|
### Kies **Service Principal** als:
|
||||||
|
- [x] Je Azure DevOps Services (cloud) gebruikt ✅
|
||||||
|
- [x] Je eenvoudige setup wilt ✅
|
||||||
|
- [x] Je betrouwbare authenticatie nodig hebt ✅
|
||||||
|
- [x] Je standaard, goed ondersteunde optie wilt ✅
|
||||||
|
- [x] Je Azure Container Registry gebruikt ✅
|
||||||
|
|
||||||
|
**→ Jouw situatie: ✅ Kies Service Principal!**
|
||||||
|
|
||||||
|
### Kies **Managed Service Identity** als:
|
||||||
|
- [ ] Je Azure DevOps Server (on-premises) gebruikt
|
||||||
|
- [ ] Je Managed Identity hebt geconfigureerd
|
||||||
|
- [ ] Je geen credentials wilt beheren
|
||||||
|
|
||||||
|
**→ Jouw situatie: ❌ Niet geschikt**
|
||||||
|
|
||||||
|
### Kies **Workload Identity Federation** als:
|
||||||
|
- [ ] Je de modernste security features wilt
|
||||||
|
- [ ] Je organisatie dit ondersteunt
|
||||||
|
- [ ] Je geen legacy support nodig hebt
|
||||||
|
- [ ] Je bereid bent om extra configuratie te doen
|
||||||
|
|
||||||
|
**→ Jouw situatie: ⚠️ Mogelijk, maar niet nodig**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Configuratie Stappen (Service Principal)
|
||||||
|
|
||||||
|
Wanneer je **Service Principal** kiest:
|
||||||
|
|
||||||
|
1. **Selecteer Azure Subscription**
|
||||||
|
- Kies je Azure subscription uit de dropdown
|
||||||
|
|
||||||
|
2. **Selecteer Azure Container Registry**
|
||||||
|
- Kies je ACR (`zdlas`) uit de dropdown
|
||||||
|
|
||||||
|
3. **Service Connection Name**
|
||||||
|
- Vul in: `zuyderland-cmdb-acr-connection`
|
||||||
|
- ⚠️ **Belangrijk**: Deze naam moet overeenkomen met `dockerRegistryServiceConnection` in `azure-pipelines.yml`!
|
||||||
|
|
||||||
|
4. **Security**
|
||||||
|
- Azure DevOps maakt automatisch een Service Principal aan
|
||||||
|
- De Service Principal krijgt automatisch de juiste permissions (AcrPush role)
|
||||||
|
- Credentials worden veilig opgeslagen in Azure DevOps
|
||||||
|
|
||||||
|
5. **Save**
|
||||||
|
- Klik "Save" of "Verify and save"
|
||||||
|
- Azure DevOps test automatisch de connection
|
||||||
|
|
||||||
|
**✅ Klaar!** Geen extra configuratie nodig.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Kan Ik Later Wisselen?
|
||||||
|
|
||||||
|
**Ja, maar:**
|
||||||
|
- Je kunt altijd een nieuwe service connection aanmaken met een ander authentication type
|
||||||
|
- Je moet dan wel de pipeline variabelen aanpassen
|
||||||
|
- Service Principal is meestal de beste keuze, dus wisselen is meestal niet nodig
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 Mijn Aanbeveling
|
||||||
|
|
||||||
|
**Voor Zuyderland CMDB GUI:**
|
||||||
|
|
||||||
|
### ✅ **Kies Service Principal** ⭐
|
||||||
|
|
||||||
|
**Waarom:**
|
||||||
|
1. ✅ **Werkt perfect** - Standaard optie voor Azure DevOps Services
|
||||||
|
2. ✅ **Eenvoudig** - Azure DevOps doet alles automatisch
|
||||||
|
3. ✅ **Betrouwbaar** - Meest geteste en ondersteunde optie
|
||||||
|
4. ✅ **Veilig** - Credentials worden veilig beheerd door Azure DevOps
|
||||||
|
5. ✅ **Perfect voor jouw situatie** - Cloud Azure DevOps + Azure Container Registry
|
||||||
|
|
||||||
|
**Je hebt niet nodig:**
|
||||||
|
- ❌ Managed Service Identity (werkt niet met cloud Azure DevOps)
|
||||||
|
- ❌ Workload Identity Federation (complexer dan nodig)
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
1. Kies **Service Principal**
|
||||||
|
2. Selecteer je subscription en ACR
|
||||||
|
3. Geef een naam: `zuyderland-cmdb-acr-connection`
|
||||||
|
4. Save
|
||||||
|
|
||||||
|
**Klaar!** ✅
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Meer Informatie
|
||||||
|
|
||||||
|
- [Azure DevOps Service Connections](https://learn.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints)
|
||||||
|
- [Service Principal Authentication](https://learn.microsoft.com/en-us/azure/devops/pipelines/library/connect-to-azure)
|
||||||
|
- [Managed Service Identity](https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/)
|
||||||
|
- [Workload Identity Federation](https://learn.microsoft.com/en-us/azure/devops/pipelines/library/connect-to-azure?view=azure-devops#workload-identity-federation)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Conclusie
|
||||||
|
|
||||||
|
**Kies: Service Principal** ⭐
|
||||||
|
|
||||||
|
Dit is de beste keuze voor:
|
||||||
|
- ✅ Azure DevOps Services (cloud)
|
||||||
|
- ✅ Azure Container Registry
|
||||||
|
- ✅ Eenvoudige, betrouwbare setup
|
||||||
|
- ✅ Standaard, goed ondersteunde optie
|
||||||
|
|
||||||
|
Je kunt altijd later een andere optie proberen als je dat wilt, maar Service Principal is meestal de beste keuze.
|
||||||
237
docs/AZURE-SERVICE-CONNECTION-TROUBLESHOOTING.md
Normal file
237
docs/AZURE-SERVICE-CONNECTION-TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
# Azure DevOps Service Connection - Troubleshooting
|
||||||
|
|
||||||
|
## 🔴 Probleem: "Loading Registries..." blijft hangen
|
||||||
|
|
||||||
|
Als de Azure Container Registry dropdown blijft laden zonder resultaten, probeer deze oplossingen:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 1: Check Subscription Toegang
|
||||||
|
|
||||||
|
**Probleem:** Je hebt mogelijk geen toegang tot de subscription waar de ACR staat.
|
||||||
|
|
||||||
|
**Oplossing:**
|
||||||
|
1. **Check in Azure Portal:**
|
||||||
|
- Ga naar je Container Registry (`zdlas`)
|
||||||
|
- Klik op **"Access control (IAM)"**
|
||||||
|
- Check of je de juiste rol hebt (bijv. Owner, Contributor, of AcrPush)
|
||||||
|
|
||||||
|
2. **Check Subscription:**
|
||||||
|
- Ga naar je Azure Subscription
|
||||||
|
- Klik op **"Access control (IAM)"**
|
||||||
|
- Check of je toegang hebt tot de subscription
|
||||||
|
|
||||||
|
3. **Probeer opnieuw:**
|
||||||
|
- Ga terug naar Azure DevOps
|
||||||
|
- Selecteer de juiste subscription
|
||||||
|
- Wacht even (kan 10-30 seconden duren)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 2: Refresh/Herlaad de Pagina
|
||||||
|
|
||||||
|
**Soms helpt een simpele refresh:**
|
||||||
|
|
||||||
|
1. **Refresh de browser pagina** (F5 of Cmd+R)
|
||||||
|
2. **Of sluit en open opnieuw** de service connection wizard
|
||||||
|
3. **Probeer opnieuw** de registry te selecteren
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 3: Check Resource Group Locatie
|
||||||
|
|
||||||
|
**Probleem:** Soms laadt Azure DevOps alleen registries in bepaalde regio's.
|
||||||
|
|
||||||
|
**Oplossing:**
|
||||||
|
1. **Check waar je ACR staat:**
|
||||||
|
```bash
|
||||||
|
az acr show --name zdlas --query location -o tsv
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Check of de subscription toegang heeft tot die regio**
|
||||||
|
|
||||||
|
3. **Probeer handmatig de registry naam in te vullen** (zie Oplossing 4)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 4: Handmatig Registry Naam Invoeren
|
||||||
|
|
||||||
|
**Als de dropdown niet werkt, kun je handmatig de registry naam invoeren:**
|
||||||
|
|
||||||
|
1. **In de service connection wizard:**
|
||||||
|
- Laat de dropdown leeg (of selecteer "Enter value manually")
|
||||||
|
- Typ handmatig: `zdlas`
|
||||||
|
- Of de volledige naam: `zdlas.azurecr.io`
|
||||||
|
|
||||||
|
2. **Save en test**
|
||||||
|
|
||||||
|
**Let op:** Soms accepteert Azure DevOps alleen de registry naam zonder `.azurecr.io`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 5: Check Service Principal Permissions
|
||||||
|
|
||||||
|
**Probleem:** De Service Principal die Azure DevOps probeert aan te maken heeft mogelijk niet de juiste permissions.
|
||||||
|
|
||||||
|
**Oplossing:**
|
||||||
|
1. **Maak handmatig een Service Principal aan:**
|
||||||
|
```bash
|
||||||
|
# Login bij Azure
|
||||||
|
az login
|
||||||
|
|
||||||
|
# Maak Service Principal aan
|
||||||
|
az ad sp create-for-rbac --name "zuyderland-cmdb-acr-sp" \
|
||||||
|
--role acrpush \
|
||||||
|
--scopes /subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.ContainerRegistry/registries/zdlas
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Gebruik deze credentials in Azure DevOps:**
|
||||||
|
- Kies "Docker Registry" → "Others"
|
||||||
|
- Vul in:
|
||||||
|
- Registry URL: `zdlas.azurecr.io`
|
||||||
|
- Username: (van de Service Principal output)
|
||||||
|
- Password: (van de Service Principal output)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 6: Gebruik "Others" in plaats van "Azure Container Registry"
|
||||||
|
|
||||||
|
**Als de Azure Container Registry optie niet werkt, gebruik dan "Others":**
|
||||||
|
|
||||||
|
1. **In de service connection wizard:**
|
||||||
|
- Kies **"Docker Registry"**
|
||||||
|
- Kies **"Others"** (in plaats van "Azure Container Registry")
|
||||||
|
|
||||||
|
2. **Vul handmatig in:**
|
||||||
|
- **Docker Registry**: `zdlas.azurecr.io`
|
||||||
|
- **Docker ID**: (leeg laten of je ACR admin username)
|
||||||
|
- **Docker Password**: (je ACR admin password)
|
||||||
|
|
||||||
|
3. **Haal ACR credentials op:**
|
||||||
|
```bash
|
||||||
|
# Login bij Azure
|
||||||
|
az login
|
||||||
|
|
||||||
|
# Haal admin credentials op
|
||||||
|
az acr credential show --name zdlas
|
||||||
|
```
|
||||||
|
|
||||||
|
Gebruik de `username` en `passwords[0].value` uit de output.
|
||||||
|
|
||||||
|
4. **Save en test**
|
||||||
|
|
||||||
|
**⚠️ Let op:** Met "Others" moet je handmatig credentials beheren. Service Principal is veiliger, maar dit werkt als tijdelijke oplossing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 7: Check Browser/Network
|
||||||
|
|
||||||
|
**Probleem:** Browser of network issues kunnen de dropdown blokkeren.
|
||||||
|
|
||||||
|
**Oplossingen:**
|
||||||
|
1. **Probeer een andere browser** (Chrome, Firefox, Edge)
|
||||||
|
2. **Disable browser extensions** (ad blockers, etc.)
|
||||||
|
3. **Check network connectivity** naar Azure
|
||||||
|
4. **Probeer incognito/private mode**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Oplossing 8: Wacht Even en Probeer Later
|
||||||
|
|
||||||
|
**Soms is het een tijdelijk Azure issue:**
|
||||||
|
|
||||||
|
1. **Wacht 5-10 minuten**
|
||||||
|
2. **Probeer opnieuw**
|
||||||
|
3. **Check Azure Status**: https://status.azure.com/
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 Diagnose Stappen
|
||||||
|
|
||||||
|
**Om te diagnosticeren wat het probleem is:**
|
||||||
|
|
||||||
|
### 1. Check ACR Bestaat en is Toegankelijk
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Login bij Azure
|
||||||
|
az login
|
||||||
|
|
||||||
|
# Check of ACR bestaat
|
||||||
|
az acr show --name zdlas
|
||||||
|
|
||||||
|
# Check ACR credentials
|
||||||
|
az acr credential show --name zdlas
|
||||||
|
|
||||||
|
# Check ACR permissions
|
||||||
|
az acr show --name zdlas --query "networkRuleSet" -o table
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Check Azure DevOps Subscription Toegang
|
||||||
|
|
||||||
|
1. Ga naar Azure Portal
|
||||||
|
2. Ga naar je Subscription
|
||||||
|
3. Check "Access control (IAM)"
|
||||||
|
4. Check of je account toegang heeft
|
||||||
|
|
||||||
|
### 3. Check Service Principal Permissions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List Service Principals
|
||||||
|
az ad sp list --display-name "zuyderland-cmdb-acr-sp" -o table
|
||||||
|
|
||||||
|
# Check permissions op ACR
|
||||||
|
az role assignment list --scope /subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.ContainerRegistry/registries/zdlas
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 Aanbevolen Aanpak
|
||||||
|
|
||||||
|
**Probeer in deze volgorde:**
|
||||||
|
|
||||||
|
1. ✅ **Refresh de pagina** (Oplossing 2)
|
||||||
|
2. ✅ **Check subscription toegang** (Oplossing 1)
|
||||||
|
3. ✅ **Handmatig registry naam invoeren** (Oplossing 4)
|
||||||
|
4. ✅ **Gebruik "Others" optie** (Oplossing 6) - als tijdelijke oplossing
|
||||||
|
5. ✅ **Maak handmatig Service Principal** (Oplossing 5) - voor permanente oplossing
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Quick Fix (Aanbevolen)
|
||||||
|
|
||||||
|
**Als de dropdown niet werkt, gebruik deze workaround:**
|
||||||
|
|
||||||
|
1. **Kies "Docker Registry" → "Others"**
|
||||||
|
2. **Vul in:**
|
||||||
|
- Registry URL: `zdlas.azurecr.io`
|
||||||
|
- Username: (haal op met `az acr credential show --name zdlas`)
|
||||||
|
- Password: (haal op met `az acr credential show --name zdlas`)
|
||||||
|
3. **Service connection name**: `zuyderland-cmdb-acr-connection`
|
||||||
|
4. **Save**
|
||||||
|
|
||||||
|
**Dit werkt altijd, ook als de Azure Container Registry optie niet werkt.**
|
||||||
|
|
||||||
|
**Later kun je:**
|
||||||
|
- De service connection verwijderen
|
||||||
|
- Opnieuw aanmaken met "Azure Container Registry" optie (als die dan wel werkt)
|
||||||
|
- Of de "Others" optie behouden (werkt ook prima)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Meer Informatie
|
||||||
|
|
||||||
|
- [Azure DevOps Service Connections Troubleshooting](https://learn.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints)
|
||||||
|
- [ACR Access Control](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication)
|
||||||
|
- [Service Principal Permissions](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-roles)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🆘 Nog Steeds Problemen?
|
||||||
|
|
||||||
|
Als niets werkt:
|
||||||
|
|
||||||
|
1. **Check Azure DevOps logs** (als je toegang hebt)
|
||||||
|
2. **Contact Azure Support** (als je een support plan hebt)
|
||||||
|
3. **Gebruik "Others" optie** als workaround (werkt altijd)
|
||||||
|
|
||||||
|
**De "Others" optie is een volledig werkende oplossing**, alleen iets minder geautomatiseerd dan de Azure Container Registry optie.
|
||||||
@@ -83,8 +83,11 @@ Deze connection geeft Azure DevOps toegang tot je ACR.
|
|||||||
- Klik **"Next"**
|
- Klik **"Next"**
|
||||||
|
|
||||||
6. **Configureer connection**
|
6. **Configureer connection**
|
||||||
|
- **Authentication type**: Kies **"Service Principal"** ⭐ (aanbevolen)
|
||||||
|
- Dit is de standaard en meest betrouwbare optie
|
||||||
|
- Azure DevOps maakt automatisch een Service Principal aan
|
||||||
- **Azure subscription**: Selecteer je Azure subscription
|
- **Azure subscription**: Selecteer je Azure subscription
|
||||||
- **Azure container registry**: Selecteer je ACR uit de dropdown (bijv. `zuyderlandcmdbacr`)
|
- **Azure container registry**: Selecteer je ACR uit de dropdown (bijv. `zdlas`)
|
||||||
- **Service connection name**: `zuyderland-cmdb-acr-connection`
|
- **Service connection name**: `zuyderland-cmdb-acr-connection`
|
||||||
- ⚠️ **Belangrijk**: Deze naam moet overeenkomen met `dockerRegistryServiceConnection` in `azure-pipelines.yml`!
|
- ⚠️ **Belangrijk**: Deze naam moet overeenkomen met `dockerRegistryServiceConnection` in `azure-pipelines.yml`!
|
||||||
- **Description**: Optioneel (bijv. "ACR for CMDB GUI production")
|
- **Description**: Optioneel (bijv. "ACR for CMDB GUI production")
|
||||||
@@ -92,13 +95,45 @@ Deze connection geeft Azure DevOps toegang tot je ACR.
|
|||||||
7. **Save**
|
7. **Save**
|
||||||
- Klik **"Save"** (of **"Verify and save"**)
|
- Klik **"Save"** (of **"Verify and save"**)
|
||||||
- Azure DevOps test automatisch de connection
|
- Azure DevOps test automatisch de connection
|
||||||
|
- Azure DevOps maakt automatisch een Service Principal aan met de juiste permissions
|
||||||
|
|
||||||
|
**💡 Waarom Service Principal?**
|
||||||
|
- ✅ Werkt perfect met Azure DevOps Services (cloud)
|
||||||
|
- ✅ Eenvoudig - Azure DevOps doet alles automatisch
|
||||||
|
- ✅ Betrouwbaar - Meest ondersteunde optie
|
||||||
|
- ✅ Veilig - Credentials worden veilig beheerd
|
||||||
|
|
||||||
|
📚 Zie `docs/AZURE-SERVICE-CONNECTION-AUTH.md` voor details over alle authentication types.
|
||||||
|
|
||||||
**✅ Service connection is aangemaakt!**
|
**✅ Service connection is aangemaakt!**
|
||||||
|
|
||||||
**Troubleshooting:**
|
**Troubleshooting:**
|
||||||
- Als je ACR niet ziet in de dropdown, check of je de juiste subscription hebt geselecteerd
|
- **"Loading Registries..." blijft hangen?**
|
||||||
|
- ✅ Refresh de pagina (F5)
|
||||||
|
- ✅ Check of je de juiste subscription hebt geselecteerd
|
||||||
|
- ✅ Wacht 10-30 seconden (kan even duren)
|
||||||
|
- ✅ **Workaround**: Gebruik "Others" optie (zie hieronder)
|
||||||
- Als verificatie faalt, check of je toegang hebt tot de ACR in Azure Portal
|
- Als verificatie faalt, check of je toegang hebt tot de ACR in Azure Portal
|
||||||
|
|
||||||
|
**🔧 Workaround: Als dropdown niet werkt, gebruik "Others" optie:**
|
||||||
|
|
||||||
|
1. Kies **"Docker Registry"** → **"Others"** (in plaats van "Azure Container Registry")
|
||||||
|
2. Vul handmatig in:
|
||||||
|
- **Docker Registry**: `zdlas.azurecr.io`
|
||||||
|
- **Docker ID**: (haal op met `az acr credential show --name zdlas`)
|
||||||
|
- **Docker Password**: (haal op met `az acr credential show --name zdlas`)
|
||||||
|
3. **Service connection name**: `zuyderland-cmdb-acr-connection`
|
||||||
|
4. Save
|
||||||
|
|
||||||
|
**Haal credentials op:**
|
||||||
|
```bash
|
||||||
|
az login
|
||||||
|
az acr credential show --name zdlas
|
||||||
|
# Gebruik "username" en "passwords[0].value"
|
||||||
|
```
|
||||||
|
|
||||||
|
📚 Zie `docs/AZURE-SERVICE-CONNECTION-TROUBLESHOOTING.md` voor uitgebreide troubleshooting.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🎯 Stap 4: Pipeline Aanmaken en Run
|
## 🎯 Stap 4: Pipeline Aanmaken en Run
|
||||||
|
|||||||
Reference in New Issue
Block a user