feat(status): ajouter statut NO_YAML pour les entrées sans fichier YAML
- shared: ajoute 'NO_YAML' à ApiStatus - back/apis: status initial dérivé de yamlContent (PENDING si YAML, NO_YAML sinon) - back/uploads: supprime la contrainte "fichier requis", gère le cas sans fichier - front/api-detail: badgeClass/statusLabel gèrent NO_YAML (badge warning "Sans fichier") - front/catalog: badgeClass ajoute le case NO_YAML → badge warning - front/upload: fichier optionnel (canProceedFromStep1 retourne true si 0 fichier) Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
@@ -41,7 +41,7 @@ export class ApisService {
|
||||
description: dto.description ?? null,
|
||||
type: dto.type,
|
||||
yamlContent: dto.yamlContent,
|
||||
status: 'PENDING',
|
||||
status: dto.yamlContent ? 'PENDING' : 'NO_YAML',
|
||||
categoryId: dto.categoryId ?? null,
|
||||
isCurrent,
|
||||
contactFunctionalName: dto.contactFunctionalName,
|
||||
|
||||
@@ -205,7 +205,6 @@ export class UploadsController {
|
||||
contactTechnicalEmail: string;
|
||||
},
|
||||
) {
|
||||
if (!files || files.length === 0) throw new BadRequestException('YAML file is required');
|
||||
if (!body.name) throw new BadRequestException('name is required');
|
||||
if (!body.convention) throw new BadRequestException('convention is required');
|
||||
|
||||
@@ -218,7 +217,10 @@ export class UploadsController {
|
||||
let yamlContent: string;
|
||||
let detectedType: ApiType | null = null;
|
||||
|
||||
if (files.length === 1) {
|
||||
if (!files || files.length === 0) {
|
||||
/* Pas de fichier : entrée créée sans YAML (YAML pourra être ajouté plus tard) */
|
||||
yamlContent = '';
|
||||
} else if (files.length === 1) {
|
||||
/* Cas simple : un seul fichier */
|
||||
yamlContent = fs.readFileSync(files[0].path, 'utf-8');
|
||||
|
||||
@@ -260,8 +262,10 @@ export class UploadsController {
|
||||
contactTechnicalEmail: body.contactTechnicalEmail,
|
||||
});
|
||||
|
||||
/* Fire-and-forget : génération asynchrone */
|
||||
this.docsService.generate(entry.id).catch(console.error);
|
||||
/* Fire-and-forget : génération asynchrone (seulement si YAML fourni) */
|
||||
if (yamlContent) {
|
||||
this.docsService.generate(entry.id).catch(console.error);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
@@ -596,8 +596,17 @@ export class ApiDetailComponent implements OnInit, OnDestroy {
|
||||
onDelete() {
|
||||
if (!confirm('Supprimer cette API ?')) return;
|
||||
this.deleting.set(true);
|
||||
/* Calculer la cible de redirection avant la suppression */
|
||||
const otherVersions = this.versions().filter((v) => v.id !== this.id);
|
||||
const redirectTarget = otherVersions.find((v) => v.isCurrent) ?? otherVersions[0];
|
||||
this.apiService.delete(this.id).subscribe({
|
||||
next: () => this.router.navigate(['/catalog']),
|
||||
next: () => {
|
||||
if (redirectTarget) {
|
||||
this.router.navigate(['/catalog', redirectTarget.id]);
|
||||
} else {
|
||||
this.router.navigate(this.backUrl());
|
||||
}
|
||||
},
|
||||
error: () => this.deleting.set(false),
|
||||
});
|
||||
}
|
||||
@@ -605,12 +614,14 @@ export class ApiDetailComponent implements OnInit, OnDestroy {
|
||||
badgeClass(status: string): string {
|
||||
if (status === 'GENERATED') return 'fr-badge fr-badge--success';
|
||||
if (status === 'ERROR') return 'fr-badge fr-badge--error';
|
||||
if (status === 'NO_YAML') return 'fr-badge fr-badge--warning';
|
||||
return 'fr-badge fr-badge--info';
|
||||
}
|
||||
|
||||
statusLabel(status: string): string {
|
||||
if (status === 'GENERATED') return 'Générée';
|
||||
if (status === 'ERROR') return 'Erreur';
|
||||
if (status === 'NO_YAML') return 'Sans fichier';
|
||||
return 'En attente';
|
||||
}
|
||||
|
||||
|
||||
@@ -208,6 +208,7 @@ export class CatalogComponent implements OnInit {
|
||||
const base = 'fr-badge';
|
||||
if (status === 'GENERATED') return `${base} fr-badge--success`;
|
||||
if (status === 'ERROR') return `${base} fr-badge--error`;
|
||||
if (status === 'NO_YAML') return `${base} fr-badge--warning`;
|
||||
return `${base} fr-badge--info`;
|
||||
}
|
||||
|
||||
|
||||
@@ -393,7 +393,9 @@ import { ApiEntry, Category, API_CONVENTION_LABELS, ApiConvention } from '@datac
|
||||
<div class="fr-col-12 fr-col-md-6">
|
||||
<dt class="fr-text--bold">Fichier{{ selectedFiles().length > 1 ? 's' : '' }}</dt>
|
||||
<dd>
|
||||
@if (selectedFiles().length === 1) {
|
||||
@if (selectedFiles().length === 0) {
|
||||
<span class="fr-badge fr-badge--warning fr-badge--sm">Aucun fichier</span>
|
||||
} @else if (selectedFiles().length === 1) {
|
||||
{{ selectedFiles()[0].name }}
|
||||
} @else {
|
||||
@for (f of selectedFiles(); track f.name) {
|
||||
@@ -568,7 +570,7 @@ export class UploadComponent implements OnInit {
|
||||
return !!(this.metaForm.get('versionPatch')?.invalid && this.metaForm.get('versionPatch')?.touched);
|
||||
}
|
||||
canProceedFromStep1 = computed(() => {
|
||||
if (this.selectedFiles().length === 0) return false;
|
||||
if (this.selectedFiles().length === 0) return true; // fichier optionnel
|
||||
if (this.validating()) return false;
|
||||
return this.validationDone() && this.validationErrors().length === 0;
|
||||
});
|
||||
@@ -627,6 +629,7 @@ export class UploadComponent implements OnInit {
|
||||
|
||||
onFileChange(event: Event) {
|
||||
this.fileError.set(null);
|
||||
this.uploadError.set(null);
|
||||
this.detectedType.set(null);
|
||||
this.detectedMainFilename.set(null);
|
||||
this.validationErrors.set([]);
|
||||
@@ -725,7 +728,12 @@ export class UploadComponent implements OnInit {
|
||||
onSubmit() {
|
||||
const files = this.selectedFiles();
|
||||
this.metaForm.markAllAsTouched();
|
||||
if (files.length === 0 || this.metaForm.invalid) return;
|
||||
if (this.metaForm.invalid) {
|
||||
/* Naviguer vers l'étape avec les erreurs pour les rendre visibles */
|
||||
const infoInvalid = this.INFO_FIELDS.some((f) => this.metaForm.get(f)?.invalid);
|
||||
this.step.set(infoInvalid ? 2 : 3);
|
||||
return;
|
||||
}
|
||||
|
||||
this.uploading.set(true);
|
||||
this.uploadError.set(null);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export type ApiType = 'ASYNCAPI' | 'OPENAPI';
|
||||
|
||||
export type ApiStatus = 'PENDING' | 'GENERATED' | 'ERROR';
|
||||
export type ApiStatus = 'PENDING' | 'GENERATED' | 'ERROR' | 'NO_YAML';
|
||||
|
||||
export type ApiConvention = 'CONSULTER' | 'ENREGISTRER' | 'ETRE_NOTIFIE';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user