feat(skeleton) : remove skeleton JSON field references — use structure API field directly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-03-12 18:11:07 +01:00
parent 4a3bceffa1
commit 9fef009610
4 changed files with 31 additions and 91 deletions

View File

@@ -390,7 +390,7 @@ const loadProductType = async () => {
// Try using the expanded typeProduct from entity response first
const embedded = product.value?.typeProduct
if (embedded && typeof embedded === 'object' && embedded.id) {
const embeddedStructure = embedded.structure ?? embedded.productSkeleton ?? null
const embeddedStructure = embedded.structure ?? null
if (embeddedStructure) {
productType.value = embedded
structure.value = normalizeProductStructureForSave(embeddedStructure)
@@ -406,7 +406,7 @@ const loadProductType = async () => {
try {
const type = await getModelType(product.value.typeProductId)
productType.value = type
structure.value = normalizeProductStructureForSave(type?.structure ?? type?.productSkeleton ?? null)
structure.value = normalizeProductStructureForSave(type?.structure ?? null)
} catch (error) {
console.error('Erreur lors du chargement du type de produit:', error)
productType.value = embedded ?? null

View File

@@ -46,9 +46,6 @@ export interface ModelType extends BaseModelTypePayload {
updatedAt: string;
category: ModelCategory;
structure: ModelTypeStructure;
componentSkeleton?: ComponentModelStructure | null;
pieceSkeleton?: PieceModelStructure | null;
productSkeleton?: ProductModelStructure | null;
}
export interface ModelTypeListParams {
@@ -86,42 +83,9 @@ const normalizeModelType = (item: any): ModelType => {
if (!item || typeof item !== 'object') {
return item as ModelType;
}
if (!item.structure) {
if (item.category === 'COMPONENT' && item.componentSkeleton) {
item.structure = item.componentSkeleton;
} else if (item.category === 'PIECE' && item.pieceSkeleton) {
item.structure = item.pieceSkeleton;
} else if (item.category === 'PRODUCT' && item.productSkeleton) {
item.structure = item.productSkeleton;
}
}
return item as ModelType;
};
const mapStructureToSkeleton = <T extends Record<string, any>>(payload: T): T => {
if (!payload || typeof payload !== 'object') {
return payload;
}
if (!('structure' in payload)) {
return payload;
}
const structure = (payload as any).structure;
if (!structure) {
return payload;
}
const category = (payload as any).category;
const next = { ...payload } as Record<string, any>;
if (category === 'COMPONENT') {
next.componentSkeleton = structure;
} else if (category === 'PIECE') {
next.pieceSkeleton = structure;
} else if (category === 'PRODUCT') {
next.productSkeleton = structure;
}
delete next.structure;
return next as T;
};
export async function listModelTypes(params: ModelTypeListParams = {}, opts: { signal?: AbortSignal } = {}) {
const requestFetch = useRequestFetch();
const query: Record<string, string | number> = {};
@@ -178,28 +142,26 @@ export async function listModelTypes(params: ModelTypeListParams = {}, opts: { s
export function createModelType(payload: ModelTypePayload, opts: { signal?: AbortSignal } = {}) {
const requestFetch = useRequestFetch();
const mappedPayload = mapStructureToSkeleton(payload);
return requestFetch<ModelType>(ENDPOINT, createOptions({
method: 'POST',
headers: {
'Content-Type': 'application/ld+json',
Accept: 'application/ld+json',
},
body: mappedPayload,
body: payload,
signal: opts.signal,
})).then(normalizeModelType);
}
export function updateModelType(id: string, payload: Partial<ModelTypePayload>, opts: { signal?: AbortSignal } = {}) {
const requestFetch = useRequestFetch();
const mappedPayload = mapStructureToSkeleton(payload);
return requestFetch<ModelType>(`${ENDPOINT}/${id}`, createOptions({
method: 'PATCH',
headers: {
'Content-Type': 'application/merge-patch+json',
Accept: 'application/ld+json',
},
body: mappedPayload,
body: payload,
signal: opts.signal,
})).then(normalizeModelType);
}

View File

@@ -196,34 +196,19 @@ export const getProductDisplay = (
const structuralCandidates = [
source.products,
source.productSkeleton,
(source.definition as AnyRecord)?.products,
(source.definition as AnyRecord)?.productSkeleton,
((source.definition as AnyRecord)?.structure as AnyRecord)?.products,
((source.definition as AnyRecord)?.structure as AnyRecord)?.productSkeleton,
(source.structure as AnyRecord)?.products,
(source.structure as AnyRecord)?.productSkeleton,
(source.requirement as AnyRecord)?.products,
(source.requirement as AnyRecord)?.productSkeleton,
((source.requirement as AnyRecord)?.structure as AnyRecord)?.products,
((source.requirement as AnyRecord)?.structure as AnyRecord)?.productSkeleton,
((source.requirement as AnyRecord)?.componentSkeleton as AnyRecord)?.products,
(source.typeComposant as AnyRecord)?.products,
(source.typeComposant as AnyRecord)?.productSkeleton,
((source.typeComposant as AnyRecord)?.structure as AnyRecord)?.products,
((source.typeComposant as AnyRecord)?.structure as AnyRecord)?.productSkeleton,
(source.originalComposant as AnyRecord)?.products,
(source.originalComposant as AnyRecord)?.productSkeleton,
((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.products,
((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.productSkeleton,
(((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.products,
(((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.productSkeleton,
(source.originalComponent as AnyRecord)?.products,
(source.originalComponent as AnyRecord)?.productSkeleton,
((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.products,
((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.productSkeleton,
(((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.products,
(((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.productSkeleton,
]
const structuralProducts = structuralCandidates

View File

@@ -50,60 +50,55 @@ beforeEach(() => {
// normalizeModelType (tested via getModelType which calls .then(normalizeModelType))
// ---------------------------------------------------------------------------
describe('normalizeModelType (via getModelType)', () => {
it('maps componentSkeleton to structure for COMPONENT', async () => {
const skeleton = { customFields: [{ name: 'Weight' }] }
it('returns structure as-is for COMPONENT', async () => {
const structure = { customFields: [{ name: 'Weight' }] }
mockFetch.mockResolvedValue(fakeModelType({
category: 'COMPONENT',
structure: null,
componentSkeleton: skeleton as any,
structure: structure as any,
}))
const result = await getModelType('mt-1')
expect(result.structure).toEqual(skeleton)
expect(result.structure).toEqual(structure)
})
it('maps pieceSkeleton to structure for PIECE', async () => {
const skeleton = { customFields: [{ name: 'Size' }] }
it('returns structure as-is for PIECE', async () => {
const structure = { customFields: [{ name: 'Size' }] }
mockFetch.mockResolvedValue(fakeModelType({
category: 'PIECE',
structure: null,
pieceSkeleton: skeleton as any,
structure: structure as any,
}))
const result = await getModelType('mt-1')
expect(result.structure).toEqual(skeleton)
expect(result.structure).toEqual(structure)
})
it('maps productSkeleton to structure for PRODUCT', async () => {
const skeleton = { customFields: [{ name: 'Brand' }] }
it('returns structure as-is for PRODUCT', async () => {
const structure = { customFields: [{ name: 'Brand' }] }
mockFetch.mockResolvedValue(fakeModelType({
category: 'PRODUCT',
structure: null,
productSkeleton: skeleton as any,
structure: structure as any,
}))
const result = await getModelType('mt-1')
expect(result.structure).toEqual(skeleton)
expect(result.structure).toEqual(structure)
})
it('does not override existing structure', async () => {
const existing = { customFields: [{ name: 'Existing' }] }
it('preserves null structure', async () => {
mockFetch.mockResolvedValue(fakeModelType({
category: 'COMPONENT',
structure: existing as any,
componentSkeleton: { customFields: [{ name: 'Skeleton' }] } as any,
structure: null,
}))
const result = await getModelType('mt-1')
expect(result.structure).toEqual(existing)
expect(result.structure).toBeNull()
})
})
// ---------------------------------------------------------------------------
// createModelType — maps structure to skeleton
// createModelType — sends structure directly
// ---------------------------------------------------------------------------
describe('createModelType', () => {
it('sends POST with componentSkeleton for COMPONENT', async () => {
it('sends POST with structure for COMPONENT', async () => {
const structure = { customFields: [] }
mockFetch.mockResolvedValue(fakeModelType())
@@ -120,11 +115,10 @@ describe('createModelType', () => {
const [endpoint, options] = mockFetch.mock.calls[0]
expect(endpoint).toBe('/model_types')
expect(options.method).toBe('POST')
expect(options.body.componentSkeleton).toEqual(structure)
expect(options.body.structure).toBeUndefined()
expect(options.body.structure).toEqual(structure)
})
it('sends POST with pieceSkeleton for PIECE', async () => {
it('sends POST with structure for PIECE', async () => {
const structure = { customFields: [], products: [] }
mockFetch.mockResolvedValue(fakeModelType({ category: 'PIECE' }))
@@ -136,11 +130,10 @@ describe('createModelType', () => {
})
const [, options] = mockFetch.mock.calls[0]
expect(options.body.pieceSkeleton).toEqual(structure)
expect(options.body.structure).toBeUndefined()
expect(options.body.structure).toEqual(structure)
})
it('sends POST with productSkeleton for PRODUCT', async () => {
it('sends POST with structure for PRODUCT', async () => {
const structure = { customFields: [] }
mockFetch.mockResolvedValue(fakeModelType({ category: 'PRODUCT' }))
@@ -152,15 +145,15 @@ describe('createModelType', () => {
})
const [, options] = mockFetch.mock.calls[0]
expect(options.body.productSkeleton).toEqual(structure)
expect(options.body.structure).toEqual(structure)
})
})
// ---------------------------------------------------------------------------
// updateModelType — maps structure to skeleton
// updateModelType — sends structure directly
// ---------------------------------------------------------------------------
describe('updateModelType', () => {
it('sends PATCH with correct endpoint and skeleton', async () => {
it('sends PATCH with correct endpoint and structure', async () => {
const structure = { customFields: [{ name: 'Updated' }] }
mockFetch.mockResolvedValue(fakeModelType())
@@ -176,10 +169,10 @@ describe('updateModelType', () => {
expect(endpoint).toBe('/model_types/mt-1')
expect(options.method).toBe('PATCH')
expect(options.headers['Content-Type']).toBe('application/merge-patch+json')
expect(options.body.componentSkeleton).toEqual(structure)
expect(options.body.structure).toEqual(structure)
})
it('sends payload without skeleton when no structure', async () => {
it('sends payload without structure when not provided', async () => {
mockFetch.mockResolvedValue(fakeModelType())
await updateModelType('mt-1', {
@@ -189,7 +182,7 @@ describe('updateModelType', () => {
})
const [, options] = mockFetch.mock.calls[0]
expect(options.body.componentSkeleton).toBeUndefined()
expect(options.body.structure).toBeUndefined()
expect(options.body.name).toBe('Just Name')
})
})