feat(inco-compositions): add IncoCompositionShow feature
- Implemented IncoCompositionShowController to handle requests for inco composition details. - Created IncoCompositionDto for response data structure. - Added IncoCompositionShowRequest for request validation. - Developed IncoCompositionShowResponse for formatting API responses. - Introduced IncoCompositionShowPresenter and IncoCompositionShowView for presentation logic. - Added tests for IncoCompositionShow feature and OpenAPI conformance.
This commit is contained in:
parent
f667532ff7
commit
91114cb67b
26 changed files with 1359 additions and 879 deletions
|
|
@ -0,0 +1,19 @@
|
|||
info:
|
||||
name: Get Inco Composition
|
||||
type: http
|
||||
seq: 1
|
||||
|
||||
http:
|
||||
method: GET
|
||||
url: "{{ENDPOINT}}/api/{{APIV1}}/inco-sheets/:incoSheet/composition"
|
||||
params:
|
||||
- name: incoSheet
|
||||
value: "{{INCOSHEET_ID}}"
|
||||
type: path
|
||||
auth: inherit
|
||||
|
||||
settings:
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
followRedirects: true
|
||||
maxRedirects: 5
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
info:
|
||||
name: IncoCompositions
|
||||
seq: 3
|
||||
|
||||
folder:
|
||||
auth: inherit
|
||||
|
|
@ -5,9 +5,9 @@ info:
|
|||
|
||||
http:
|
||||
method: GET
|
||||
url: "{{ENDPOINT}}/api/{{APIV1}}/inco-sheets/:employeeId"
|
||||
url: "{{ENDPOINT}}/api/{{APIV1}}/inco-sheets/:incoSheet"
|
||||
params:
|
||||
- name: employeeId
|
||||
- name: incoSheet
|
||||
value: "{{INCOSHEET_ID}}"
|
||||
type: path
|
||||
auth: inherit
|
||||
|
|
|
|||
|
|
@ -1000,7 +1000,7 @@ components:
|
|||
pattern: '^\d{1,}$'
|
||||
example: "12345"
|
||||
identification:
|
||||
$ref: "#/components/schemas/IncoCompoIdentification"
|
||||
$ref: "#/components/schemas/IncoCompositionIdentification"
|
||||
energy:
|
||||
$ref: "#/components/schemas/IncoCompoEnergy"
|
||||
referenceMacronutrients:
|
||||
|
|
@ -1077,7 +1077,7 @@ components:
|
|||
type: string
|
||||
example: "0"
|
||||
|
||||
IncoCompoIdentification:
|
||||
IncoCompositionIdentification:
|
||||
type: object
|
||||
description: Informations d'identification de la composition INCO.
|
||||
properties:
|
||||
|
|
|
|||
|
|
@ -1,819 +0,0 @@
|
|||
openapi: 3.1.1
|
||||
info:
|
||||
title: Arcadia Genesis API (for PHP client)
|
||||
version: 1.0.0
|
||||
summary: Proposed HTTP API contract consumed by arcadia-genesis-php-client
|
||||
description: |
|
||||
This specification describes the API surface implied by the PHP client.
|
||||
It models employee and INCO sheet use cases exposed by the client services.
|
||||
|
||||
servers:
|
||||
- url: https://api.example.com/gaston/api
|
||||
description: Example production server
|
||||
|
||||
security:
|
||||
- ArcadiaAuth: []
|
||||
|
||||
tags:
|
||||
- name: Employees
|
||||
- name: IncoSheets
|
||||
- name: IncoComposition
|
||||
|
||||
paths:
|
||||
/employees:
|
||||
get:
|
||||
tags: [Employees]
|
||||
operationId: listEmployees
|
||||
summary: List employees
|
||||
description: |
|
||||
Returns a paginated list of employees.
|
||||
Supports optional filtering by update timestamp and site ids.
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/Page'
|
||||
- $ref: '#/components/parameters/PageSize'
|
||||
- $ref: '#/components/parameters/Since'
|
||||
- $ref: '#/components/parameters/EmployeeSortBy'
|
||||
- $ref: '#/components/parameters/SortDirection'
|
||||
- name: siteIds
|
||||
in: query
|
||||
required: false
|
||||
description: Filter on one or many site ids
|
||||
style: form
|
||||
explode: true
|
||||
schema:
|
||||
type: array
|
||||
minItems: 1
|
||||
items:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Employees page
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EmployeeListResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'429':
|
||||
$ref: '#/components/responses/TooManyRequests'
|
||||
'503':
|
||||
$ref: '#/components/responses/ServiceUnavailable'
|
||||
|
||||
/employees/{employeeId}:
|
||||
get:
|
||||
tags: [Employees]
|
||||
operationId: getEmployeeById
|
||||
summary: Get an employee by id
|
||||
parameters:
|
||||
- name: employeeId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Employee detail
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EmployeeItemResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
description: Employee not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NotFoundResponse'
|
||||
'429':
|
||||
$ref: '#/components/responses/TooManyRequests'
|
||||
'503':
|
||||
$ref: '#/components/responses/ServiceUnavailable'
|
||||
|
||||
/inco-sheets:
|
||||
get:
|
||||
tags: [IncoSheets]
|
||||
operationId: listIncoSheets
|
||||
summary: List INCO sheets
|
||||
description: |
|
||||
Returns paginated INCO sheets.
|
||||
Supports brand filtering and optional updated_at lower bound.
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/Page'
|
||||
- $ref: '#/components/parameters/PageSize'
|
||||
- $ref: '#/components/parameters/Since'
|
||||
- $ref: '#/components/parameters/IncoSortBy'
|
||||
- $ref: '#/components/parameters/SortDirection'
|
||||
- name: brands
|
||||
in: query
|
||||
required: false
|
||||
description: Filter on one or many brand codes
|
||||
style: form
|
||||
explode: true
|
||||
schema:
|
||||
type: array
|
||||
minItems: 1
|
||||
items:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: INCO sheets page
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/IncoListResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'429':
|
||||
$ref: '#/components/responses/TooManyRequests'
|
||||
'503':
|
||||
$ref: '#/components/responses/ServiceUnavailable'
|
||||
|
||||
/inco-sheets/short:
|
||||
get:
|
||||
tags: [IncoSheets]
|
||||
operationId: listIncoSheetsShort
|
||||
summary: List INCO sheets (short payload)
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/Page'
|
||||
- $ref: '#/components/parameters/PageSize'
|
||||
- $ref: '#/components/parameters/IncoSortBy'
|
||||
- $ref: '#/components/parameters/SortDirection'
|
||||
- name: brands
|
||||
in: query
|
||||
required: true
|
||||
description: One or many brand codes
|
||||
style: form
|
||||
explode: true
|
||||
schema:
|
||||
type: array
|
||||
minItems: 1
|
||||
items:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: INCO short page
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/IncoShortListResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'429':
|
||||
$ref: '#/components/responses/TooManyRequests'
|
||||
'503':
|
||||
$ref: '#/components/responses/ServiceUnavailable'
|
||||
|
||||
/inco-sheets/search:
|
||||
get:
|
||||
tags: [IncoSheets]
|
||||
operationId: searchIncoSheetsByIdLike
|
||||
summary: Search INCO sheets by id-like pattern
|
||||
parameters:
|
||||
- name: incoIdLike
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Pattern used with SQL-like matching rules
|
||||
- name: brands
|
||||
in: query
|
||||
required: true
|
||||
style: form
|
||||
explode: true
|
||||
schema:
|
||||
type: array
|
||||
minItems: 1
|
||||
items:
|
||||
type: string
|
||||
- name: short
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
default: false
|
||||
description: Return short projection when true
|
||||
responses:
|
||||
'200':
|
||||
description: Search result (first page, size 50)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/IncoListResponse'
|
||||
- $ref: '#/components/schemas/IncoShortListResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'429':
|
||||
$ref: '#/components/responses/TooManyRequests'
|
||||
'503':
|
||||
$ref: '#/components/responses/ServiceUnavailable'
|
||||
|
||||
/inco-sheets/{incoId}:
|
||||
get:
|
||||
tags: [IncoSheets]
|
||||
operationId: getIncoSheetById
|
||||
summary: Get INCO sheet by id
|
||||
parameters:
|
||||
- name: incoId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^\\d{1,}$'
|
||||
responses:
|
||||
'200':
|
||||
description: INCO sheet detail
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/IncoItemResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
description: INCO not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NotFoundResponse'
|
||||
'429':
|
||||
$ref: '#/components/responses/TooManyRequests'
|
||||
'503':
|
||||
$ref: '#/components/responses/ServiceUnavailable'
|
||||
|
||||
/inco-sheets/{incoId}/composition:
|
||||
get:
|
||||
tags: [IncoComposition]
|
||||
operationId: getIncoCompositionByIncoId
|
||||
summary: Get composition by INCO id
|
||||
parameters:
|
||||
- name: incoId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^\\d{1,}$'
|
||||
responses:
|
||||
'200':
|
||||
description: Composition detail
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/IncoCompoItemResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
description: Composition not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NotFoundResponse'
|
||||
'429':
|
||||
$ref: '#/components/responses/TooManyRequests'
|
||||
'503':
|
||||
$ref: '#/components/responses/ServiceUnavailable'
|
||||
|
||||
/inco-compositions/{incoCompoId}:
|
||||
get:
|
||||
tags: [IncoComposition]
|
||||
operationId: getIncoCompositionById
|
||||
summary: Get composition by composition id
|
||||
parameters:
|
||||
- name: incoCompoId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^\\d{1,}$'
|
||||
responses:
|
||||
'200':
|
||||
description: Composition detail
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/IncoCompoItemResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'404':
|
||||
description: Composition not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/NotFoundResponse'
|
||||
'429':
|
||||
$ref: '#/components/responses/TooManyRequests'
|
||||
'503':
|
||||
$ref: '#/components/responses/ServiceUnavailable'
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
ArcadiaAuth:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-API-Key
|
||||
description: |
|
||||
Requests also require `X-API-Signature` and `X-Timestamp` headers.
|
||||
Signature is computed from method, URI, body payload, shared secret, and timestamp.
|
||||
|
||||
parameters:
|
||||
Page:
|
||||
name: page
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 1
|
||||
default: 1
|
||||
description: 1-based page index
|
||||
|
||||
PageSize:
|
||||
name: page_size
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 1
|
||||
default: 50
|
||||
maximum: 10000
|
||||
description: Max records per page
|
||||
|
||||
Since:
|
||||
name: since
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
description: RFC3339 timestamp lower bound (>=)
|
||||
|
||||
SortDirection:
|
||||
name: sortDirection
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
enum: [asc, desc]
|
||||
default: desc
|
||||
|
||||
EmployeeSortBy:
|
||||
name: sortBy
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- arcadiaId
|
||||
- startDate
|
||||
- endDate
|
||||
- plannedEndDate
|
||||
- createdAt
|
||||
- updatedAt
|
||||
|
||||
IncoSortBy:
|
||||
name: sortBy
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- id
|
||||
- createdAt
|
||||
- updatedAt
|
||||
|
||||
responses:
|
||||
BadRequest:
|
||||
description: Invalid request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiErrorResponse'
|
||||
|
||||
Unauthorized:
|
||||
description: Authentication failed
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiErrorResponse'
|
||||
|
||||
TooManyRequests:
|
||||
description: Rate limited
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiErrorResponse'
|
||||
|
||||
ServiceUnavailable:
|
||||
description: Upstream service unavailable
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiErrorResponse'
|
||||
|
||||
schemas:
|
||||
Employee:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- matriculePublique
|
||||
- lastname
|
||||
- firstname
|
||||
- start_date
|
||||
- establishment
|
||||
- site
|
||||
- service
|
||||
- created_at
|
||||
properties:
|
||||
matriculePublique:
|
||||
type: string
|
||||
lastname:
|
||||
type: string
|
||||
firstname:
|
||||
type: string
|
||||
start_date:
|
||||
type: string
|
||||
format: date-time
|
||||
end_date:
|
||||
type: [string, 'null']
|
||||
format: date-time
|
||||
planned_end_date:
|
||||
type: [string, 'null']
|
||||
format: date-time
|
||||
establishment:
|
||||
type: string
|
||||
site:
|
||||
type: string
|
||||
service:
|
||||
type: string
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
updated_at:
|
||||
type: [string, 'null']
|
||||
format: date-time
|
||||
|
||||
IncoBrand:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [code, label]
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
label:
|
||||
type: string
|
||||
|
||||
Inco:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- libelle
|
||||
- description
|
||||
- allegations
|
||||
- sellArguments
|
||||
- preparationAdvices
|
||||
- ingredients
|
||||
- allergens
|
||||
- brand
|
||||
- isSeasonal
|
||||
- additives
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
pattern: '^\\d{1,}$'
|
||||
name:
|
||||
type: string
|
||||
libelle:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
allegations:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
sellArguments:
|
||||
type: string
|
||||
preparationAdvices:
|
||||
type: string
|
||||
tips:
|
||||
type: [string, 'null']
|
||||
sellFamilly:
|
||||
type: [string, 'null']
|
||||
ingredients:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
portions:
|
||||
type: [object, 'null']
|
||||
additionalProperties: true
|
||||
allergens:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
brand:
|
||||
$ref: '#/components/schemas/IncoBrand'
|
||||
isSeasonal:
|
||||
type: boolean
|
||||
weight:
|
||||
type: [object, 'null']
|
||||
additionalProperties: true
|
||||
nutriscore:
|
||||
type: [object, 'null']
|
||||
additionalProperties: true
|
||||
storageCondition:
|
||||
type: [object, 'null']
|
||||
additionalProperties: true
|
||||
additives:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
compo:
|
||||
type: [string, 'null']
|
||||
pattern: '^\\d{1,}$'
|
||||
created_at:
|
||||
type: [string, 'null']
|
||||
format: date-time
|
||||
updated_at:
|
||||
type: [string, 'null']
|
||||
format: date-time
|
||||
|
||||
IncoShort:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- libelle
|
||||
- description
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
pattern: '^\\d{1,}$'
|
||||
name:
|
||||
type: string
|
||||
libelle:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
created_at:
|
||||
type: [string, 'null']
|
||||
format: date-time
|
||||
updated_at:
|
||||
type: [string, 'null']
|
||||
format: date-time
|
||||
|
||||
IncoCompo:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- id
|
||||
- idInco
|
||||
- identification
|
||||
- energie
|
||||
- macronutrimentsRef
|
||||
- macronutrimentsPortion
|
||||
- fnl
|
||||
- apportsReference
|
||||
- nutriScore
|
||||
- pointsNutriScore
|
||||
- mineraux
|
||||
- vitamines
|
||||
- glucidesDetail
|
||||
- acideGras
|
||||
- acidesAmines
|
||||
- composesSpeciaux
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
pattern: '^\\d{1,}$'
|
||||
idInco:
|
||||
type: string
|
||||
pattern: '^\\d{1,}$'
|
||||
identification:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
energie:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
macronutrimentsRef:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
macronutrimentsPortion:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
fnl:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
apportsReference:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
nutriScore:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
pointsNutriScore:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
mineraux:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
vitamines:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
glucidesDetail:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
acideGras:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
acidesAmines:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
composesSpeciaux:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
|
||||
Pagination:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- page
|
||||
- pageSize
|
||||
- totalItems
|
||||
- totalPages
|
||||
- links
|
||||
properties:
|
||||
page:
|
||||
type: integer
|
||||
minimum: 1
|
||||
pageSize:
|
||||
type: integer
|
||||
minimum: 1
|
||||
totalItems:
|
||||
type: integer
|
||||
minimum: 0
|
||||
totalPages:
|
||||
type: integer
|
||||
minimum: 0
|
||||
links:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [first, last]
|
||||
properties:
|
||||
first:
|
||||
type: string
|
||||
format: uri
|
||||
last:
|
||||
type: string
|
||||
format: uri
|
||||
|
||||
EmployeeListResponse:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/SuccessEnvelope'
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Employee'
|
||||
pagination:
|
||||
$ref: '#/components/schemas/Pagination'
|
||||
|
||||
EmployeeItemResponse:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/SuccessEnvelope'
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/Employee'
|
||||
|
||||
IncoListResponse:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/SuccessEnvelope'
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Inco'
|
||||
pagination:
|
||||
$ref: '#/components/schemas/Pagination'
|
||||
|
||||
IncoShortListResponse:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/SuccessEnvelope'
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/IncoShort'
|
||||
pagination:
|
||||
$ref: '#/components/schemas/Pagination'
|
||||
|
||||
IncoItemResponse:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/SuccessEnvelope'
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/Inco'
|
||||
|
||||
IncoCompoItemResponse:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/SuccessEnvelope'
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/IncoCompo'
|
||||
|
||||
SuccessEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, data]
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
const: true
|
||||
data: {}
|
||||
pagination:
|
||||
$ref: '#/components/schemas/Pagination'
|
||||
|
||||
ApiErrorCode:
|
||||
type: string
|
||||
enum:
|
||||
- '0004'
|
||||
- ERR_UNAUTHORIZED_PARAMETER
|
||||
- ERR_DEFAULT
|
||||
- ERR_LIST_SIZE_MISMATCH
|
||||
- ERR_METAEXCEPTION
|
||||
- ERR_SORT_LIST_SIZE_MISMATCH
|
||||
- ERR_TABLE_NAME_MISSING
|
||||
- ERR_UNAUTHORIZED_OPERATOR
|
||||
- ERR_BAD_DATE_FORMAT
|
||||
- ERR_PAGE_TYPE_MISMATCH
|
||||
- ERR_PAGE_SIZE_TYPE_MISMATCH
|
||||
- ERR_TOO_MANY_RECORDS
|
||||
- ERR_INVALID_API_KEY
|
||||
- ERR_INVALID_API_SIGNATURE
|
||||
- ERR_TOO_MANY_REQUESTS
|
||||
- UNKNOWN
|
||||
|
||||
ApiErrorObject:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
required: [code]
|
||||
properties:
|
||||
code:
|
||||
$ref: '#/components/schemas/ApiErrorCode'
|
||||
message:
|
||||
type: string
|
||||
|
||||
ApiErrorResponse:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, error]
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
const: false
|
||||
error:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/ApiErrorObject'
|
||||
- type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ApiErrorObject'
|
||||
|
||||
NotFoundResponse:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [success, error]
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
const: false
|
||||
error:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [code, message]
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
enum: [NOT_FOUND]
|
||||
message:
|
||||
type: string
|
||||
|
|
@ -6,10 +6,21 @@ use Illuminate\Support\Facades\Config;
|
|||
use Illuminate\Support\Facades\Route;
|
||||
use Presentation\Api\V1\Http\Controllers\Employees\EmployeeShowController;
|
||||
use Presentation\Api\V1\Http\Controllers\Employees\EmployeesIndexController;
|
||||
use Presentation\Api\V1\Http\Controllers\IncoCompositions\IncoCompositionShowController;
|
||||
use Presentation\Api\V1\Http\Controllers\IncoSheets\IncoSheetsIndexController;
|
||||
use Presentation\Api\V1\Http\Controllers\IncoSheets\IncoSheetsSearchController;
|
||||
use Presentation\Api\V1\Http\Controllers\IncoSheets\IncoSheetsShowController;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| API V1 Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Version 1 of the Arcadia API.
|
||||
| All routes are prefixed with /api/v1 (configured in bootstrap/app.php).
|
||||
|
|
||||
*/
|
||||
|
||||
// * ROOT
|
||||
Route::get('/', fn () => response()->json([
|
||||
'message' => Config::string('app.name') . ' v1',
|
||||
|
|
@ -25,14 +36,15 @@ Route::get('/openapi', function () {
|
|||
})->name('openapi');
|
||||
|
||||
// * EMPLOYEES
|
||||
Route::prefix('employees')->group(function () {
|
||||
Route::prefix('employees')->group(function (): void {
|
||||
Route::get('/', EmployeesIndexController::class)->name('employees.index');
|
||||
Route::get('/{employeeId}', EmployeeShowController::class)->name('employees.show');
|
||||
});
|
||||
|
||||
// * INCO SHEETS
|
||||
Route::prefix('inco-sheets')->group(function () {
|
||||
Route::prefix('inco-sheets')->group(function (): void {
|
||||
Route::get('/', IncoSheetsIndexController::class)->name('inco-sheets.index');
|
||||
Route::get('/search', IncoSheetsSearchController::class)->name('inco-sheets.search');
|
||||
Route::get('/{incoId}', IncoSheetsShowController::class)->name('inco-sheets.show');
|
||||
Route::get('/{incoId}/composition', IncoCompositionShowController::class)->name('inco-sheets.composition');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Application\Usecase\IncoCompositions\Find;
|
||||
|
||||
use Application\Exceptions\NotFoundException;
|
||||
use Domain\Ports\Directories\IncoCompositionDirectory;
|
||||
use Shared\DDD\Usecase\Input as BaseInput;
|
||||
use Shared\DDD\Usecase\Usecase;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/** @implements Usecase<\Shared\DDD\Usecase\Input, \Shared\DDD\Usecase\Output> */
|
||||
final readonly class FindIncoComposition implements Usecase
|
||||
{
|
||||
public function __construct(
|
||||
private IncoCompositionDirectory $directory
|
||||
) {
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function execute(BaseInput $input): Output
|
||||
{
|
||||
Assert::isInstanceOf($input, Input::class);
|
||||
|
||||
$incoComposition = $this->directory->findByIncoId($input->incoId);
|
||||
|
||||
if ($incoComposition === null) {
|
||||
throw NotFoundException::forEntity('IncoComposition', (string) $input->incoId->value);
|
||||
}
|
||||
|
||||
return new Output(
|
||||
id: $incoComposition->id(),
|
||||
idInco: $incoComposition->idInco,
|
||||
identification: $incoComposition->identification,
|
||||
energy: $incoComposition->energy,
|
||||
referenceMacronutrients: $incoComposition->referenceMacronutrients,
|
||||
portionMacronutrients: $incoComposition->portionMacronutrients,
|
||||
freshProduceAndNuts: $incoComposition->freshProduceAndNuts,
|
||||
referenceIntakes: $incoComposition->referenceIntakes,
|
||||
nutriScore: $incoComposition->nutriScore,
|
||||
nutriScorePoints: $incoComposition->nutriScorePoints,
|
||||
minerals: $incoComposition->minerals,
|
||||
vitamins: $incoComposition->vitamins,
|
||||
carbohydratesDetail: $incoComposition->carbohydratesDetail,
|
||||
fattyAcids: $incoComposition->fattyAcids,
|
||||
aminoAcids: $incoComposition->aminoAcids,
|
||||
specialCompounds: $incoComposition->specialCompounds
|
||||
);
|
||||
}
|
||||
}
|
||||
16
src/Application/Usecase/IncoCompositions/Find/Input.php
Normal file
16
src/Application/Usecase/IncoCompositions/Find/Input.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Application\Usecase\IncoCompositions\Find;
|
||||
|
||||
use Domain\Models\ValueObjects\Inco\IncoId;
|
||||
use Shared\DDD\Usecase\Input as UsecaseInput;
|
||||
|
||||
final readonly class Input implements UsecaseInput
|
||||
{
|
||||
public function __construct(
|
||||
public IncoId $incoId,
|
||||
) {
|
||||
}
|
||||
}
|
||||
46
src/Application/Usecase/IncoCompositions/Find/Output.php
Normal file
46
src/Application/Usecase/IncoCompositions/Find/Output.php
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Application\Usecase\IncoCompositions\Find;
|
||||
|
||||
use Domain\Models\ValueObjects\Inco\Composition\AminoAcids;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\CarbohydratesDetail;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\Energy;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\FattyAcids;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\FreshProduceAndNuts;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\Identification;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\IncoCompositionId;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\Minerals;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\NutriScore;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\NutriScorePoints;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\PortionMacronutrients;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\ReferenceIntakes;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\ReferenceMacronutrients;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\SpecialCompounds;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\Vitamins;
|
||||
use Domain\Models\ValueObjects\Inco\IncoId;
|
||||
use Shared\DDD\Usecase\Output as UsecaseOutput;
|
||||
|
||||
final readonly class Output implements UsecaseOutput
|
||||
{
|
||||
public function __construct(
|
||||
public IncoCompositionId $id,
|
||||
public IncoId $idInco,
|
||||
public Identification $identification,
|
||||
public Energy $energy,
|
||||
public ReferenceMacronutrients $referenceMacronutrients,
|
||||
public PortionMacronutrients $portionMacronutrients,
|
||||
public FreshProduceAndNuts $freshProduceAndNuts,
|
||||
public ReferenceIntakes $referenceIntakes,
|
||||
public NutriScore $nutriScore,
|
||||
public NutriScorePoints $nutriScorePoints,
|
||||
public Minerals $minerals,
|
||||
public Vitamins $vitamins,
|
||||
public CarbohydratesDetail $carbohydratesDetail,
|
||||
public FattyAcids $fattyAcids,
|
||||
public AminoAcids $aminoAcids,
|
||||
public SpecialCompounds $specialCompounds,
|
||||
) {
|
||||
}
|
||||
}
|
||||
18
src/Domain/Ports/Directories/IncoCompositionDirectory.php
Normal file
18
src/Domain/Ports/Directories/IncoCompositionDirectory.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Domain\Ports\Directories;
|
||||
|
||||
use Domain\Models\Entities\Inco\Composition\IncoComposition;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\IncoCompositionId;
|
||||
use Domain\Models\ValueObjects\Inco\IncoId;
|
||||
use Shared\DDD\Directory;
|
||||
|
||||
/** @extends Directory<IncoCompositionId, IncoComposition> */
|
||||
interface IncoCompositionDirectory extends Directory
|
||||
{
|
||||
public function find(IncoCompositionId $id): ?IncoComposition;
|
||||
|
||||
public function findByIncoId(IncoId $incoId): ?IncoComposition;
|
||||
}
|
||||
|
|
@ -0,0 +1,392 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Infra\Adapters\Directories;
|
||||
|
||||
use Domain\Models\Entities\Inco\Composition\IncoComposition;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\AminoAcids;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\CarbohydratesDetail;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\Energy;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\EnergyValues;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\FattyAcids;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\FreshProduceAndNuts;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\FruitAndNutAmount;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\Identification;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\IncoCompositionId;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\Minerals;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\NutrientValue;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\NutriScore;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\NutriScorePoints;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\PercentageValue;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\PortionMacronutrients;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\ReferenceIntakes;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\ReferenceMacronutrients;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\SpecialCompounds;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\Vitamins;
|
||||
use Domain\Models\ValueObjects\Inco\IncoId;
|
||||
use Domain\Ports\Directories\IncoCompositionDirectory;
|
||||
use Infra\Database\Models\IncoCompo as IncoCompoModel;
|
||||
|
||||
final class EloquentIncoCompositionDirectory implements IncoCompositionDirectory
|
||||
{
|
||||
#[\Override]
|
||||
public function find(IncoCompositionId $id): ?IncoComposition
|
||||
{
|
||||
$model = IncoCompoModel::query()
|
||||
->where('id', $id->value)
|
||||
->first();
|
||||
|
||||
if ($model === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->toDomain($model);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function findByIncoId(IncoId $incoId): ?IncoComposition
|
||||
{
|
||||
$model = IncoCompoModel::query()
|
||||
->where('id_inco', $incoId->value)
|
||||
->first();
|
||||
|
||||
if ($model === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->toDomain($model);
|
||||
}
|
||||
|
||||
private function toDomain(IncoCompoModel $model): IncoComposition
|
||||
{
|
||||
return new IncoComposition(
|
||||
id: new IncoCompositionId((string) $model->id),
|
||||
idInco: new IncoId((string) $model->id_inco),
|
||||
identification: $this->identificationFrom($model),
|
||||
energy: $this->energyFrom($model),
|
||||
referenceMacronutrients: $this->refMacronutrientsFrom($model),
|
||||
portionMacronutrients: $this->portionMacronutrientsFrom($model),
|
||||
freshProduceAndNuts: $this->freshProduceAndNutsFrom($model),
|
||||
referenceIntakes: $this->refIntakesFrom($model),
|
||||
nutriScore: $this->nutriScoreFrom($model),
|
||||
nutriScorePoints: $this->nutriScorePointsFrom($model),
|
||||
minerals: $this->mineralsFrom($model),
|
||||
vitamins: $this->vitaminsFrom($model),
|
||||
carbohydratesDetail: $this->carbohydratesDetailFrom($model),
|
||||
fattyAcids: $this->fattyAcidsFrom($model),
|
||||
aminoAcids: $this->aminoAcidsFrom($model),
|
||||
specialCompounds: $this->specialCompoundsFrom($model),
|
||||
);
|
||||
}
|
||||
|
||||
private function identificationFrom(IncoCompoModel $model): Identification
|
||||
{
|
||||
$raw = is_array($model->identification) ? $model->identification : [];
|
||||
|
||||
return new Identification(
|
||||
name: $this->getString($raw, 'name', 'Inconnu'),
|
||||
number: $this->getString($raw, 'number', (string) $model->id_inco),
|
||||
source: $this->getString($raw, 'source', 'System'),
|
||||
);
|
||||
}
|
||||
|
||||
private function energyFrom(IncoCompoModel $model): Energy
|
||||
{
|
||||
$energyRaw = is_array($model->energy) ? $model->energy : [];
|
||||
$refArr = is_array($energyRaw['reference'] ?? null) ? $energyRaw['reference'] : [];
|
||||
$porArr = is_array($energyRaw['portion'] ?? null) ? $energyRaw['portion'] : [];
|
||||
|
||||
$refEnergy = new EnergyValues(
|
||||
size: $this->getStringWithFallback($refArr, 'size', 'portionAmount', '100g'),
|
||||
energyKj: $this->getStringWithFallback($refArr, 'energyKj', 'kilojoules'),
|
||||
energyKcal: $this->getStringWithFallback($refArr, 'energyKcal', 'kilocalories'),
|
||||
);
|
||||
$porEnergy = new EnergyValues(
|
||||
size: $this->getStringWithFallback($porArr, 'size', 'portionAmount', '100g'),
|
||||
energyKj: $this->getStringWithFallback($porArr, 'energyKj', 'kilojoules'),
|
||||
energyKcal: $this->getStringWithFallback($porArr, 'energyKcal', 'kilocalories'),
|
||||
);
|
||||
|
||||
return new Energy($refEnergy, $porEnergy);
|
||||
}
|
||||
|
||||
private function refMacronutrientsFrom(IncoCompoModel $model): ReferenceMacronutrients
|
||||
{
|
||||
$raw = is_array($model->reference_macronutrients) ? $model->reference_macronutrients : [];
|
||||
|
||||
return new ReferenceMacronutrients(
|
||||
fat: $this->getNutrient($raw, 'fat'),
|
||||
saturatedFat: $this->getNutrient($raw, 'saturatedFat'),
|
||||
carbohydrates: $this->getNutrient($raw, 'carbohydrates'),
|
||||
sugars: $this->getNutrient($raw, 'sugars'),
|
||||
fiber: $this->getNutrient($raw, 'fiber'),
|
||||
protein: $this->getNutrient($raw, 'protein'),
|
||||
salt: $this->getNutrient($raw, 'salt'),
|
||||
);
|
||||
}
|
||||
|
||||
private function portionMacronutrientsFrom(IncoCompoModel $model): PortionMacronutrients
|
||||
{
|
||||
$raw = is_array($model->portion_macronutrients) ? $model->portion_macronutrients : [];
|
||||
|
||||
return new PortionMacronutrients(
|
||||
fat: $this->getNutrient($raw, 'fat'),
|
||||
saturatedFat: $this->getNutrient($raw, 'saturatedFat'),
|
||||
carbohydrates: $this->getNutrient($raw, 'carbohydrates'),
|
||||
sugars: $this->getNutrient($raw, 'sugars'),
|
||||
fiber: $this->getNutrient($raw, 'fiber'),
|
||||
protein: $this->getNutrient($raw, 'protein'),
|
||||
salt: $this->getNutrient($raw, 'salt'),
|
||||
);
|
||||
}
|
||||
|
||||
private function freshProduceAndNutsFrom(IncoCompoModel $model): FreshProduceAndNuts
|
||||
{
|
||||
$raw = is_array($model->fresh_produce_and_nuts) ? $model->fresh_produce_and_nuts : [];
|
||||
$fruitAmount = new FruitAndNutAmount();
|
||||
|
||||
return new FreshProduceAndNuts(
|
||||
reference: $fruitAmount,
|
||||
portion: $fruitAmount,
|
||||
freshFnlPercentage: $this->getPercentage($raw, 'freshFnlPercentage'),
|
||||
fruitsPercentage: $this->getPercentage($raw, 'fruitsPercentage'),
|
||||
);
|
||||
}
|
||||
|
||||
private function refIntakesFrom(IncoCompoModel $model): ReferenceIntakes
|
||||
{
|
||||
$raw = is_array($model->reference_intakes) ? $model->reference_intakes : [];
|
||||
|
||||
return new ReferenceIntakes(
|
||||
energyE14: $this->getPercentage($raw, 'energyE14'),
|
||||
energyKj: $this->getPercentage($raw, 'energyKj'),
|
||||
fat: $this->getPercentage($raw, 'fat'),
|
||||
saturatedFat: $this->getPercentage($raw, 'saturatedFat'),
|
||||
carbohydrates: $this->getPercentage($raw, 'carbohydrates'),
|
||||
sugars: $this->getPercentage($raw, 'sugars'),
|
||||
protein: $this->getPercentage($raw, 'protein'),
|
||||
salt: $this->getPercentage($raw, 'salt'),
|
||||
portionAmount: $this->getString($raw, 'portionAmount', '100g'),
|
||||
);
|
||||
}
|
||||
|
||||
private function nutriScoreFrom(IncoCompoModel $model): NutriScore
|
||||
{
|
||||
$raw = is_array($model->nutri_score) ? $model->nutri_score : [];
|
||||
|
||||
return new NutriScore(
|
||||
negativeComponent: $this->getString($raw, 'negativeComponent'),
|
||||
score: $this->getString($raw, 'score'),
|
||||
grade: $this->getString($raw, 'grade', 'A'),
|
||||
);
|
||||
}
|
||||
|
||||
private function nutriScorePointsFrom(IncoCompoModel $model): NutriScorePoints
|
||||
{
|
||||
$raw = is_array($model->nutri_score_points) ? $model->nutri_score_points : [];
|
||||
|
||||
return new NutriScorePoints(
|
||||
energyPoints: $this->getStringWithFallback($raw, 'energyPoints', 'energy'),
|
||||
saturatedFatPoints: $this->getStringWithFallback($raw, 'saturatedFatPoints', 'saturatedFat'),
|
||||
sugarsPoints: $this->getStringWithFallback($raw, 'sugarsPoints', 'sugars'),
|
||||
saltPoints: $this->getStringWithFallback($raw, 'saltPoints', 'sodium'),
|
||||
fiberPoints: $this->getStringWithFallback($raw, 'fiberPoints', 'fiber'),
|
||||
proteinPoints: $this->getStringWithFallback($raw, 'proteinPoints', 'proteins'),
|
||||
freshFnlPoints: $this->getStringWithFallback($raw, 'freshFnlPoints', 'fruitsNuts'),
|
||||
);
|
||||
}
|
||||
|
||||
private function mineralsFrom(IncoCompoModel $model): Minerals
|
||||
{
|
||||
$raw = is_array($model->minerals) ? $model->minerals : [];
|
||||
|
||||
return new Minerals(
|
||||
calcium: $this->getString($raw, 'calcium'),
|
||||
magnesium: $this->getString($raw, 'magnesium'),
|
||||
potassium: $this->getString($raw, 'potassium'),
|
||||
phosphorus: $this->getString($raw, 'phosphorus'),
|
||||
chloride: $this->getString($raw, 'chloride'),
|
||||
iron: $this->getString($raw, 'iron'),
|
||||
zinc: $this->getString($raw, 'zinc'),
|
||||
copper: $this->getString($raw, 'copper'),
|
||||
manganese: $this->getString($raw, 'manganese'),
|
||||
selenium: $this->getString($raw, 'selenium'),
|
||||
iodine: $this->getString($raw, 'iodine'),
|
||||
chromium: $this->getString($raw, 'chromium'),
|
||||
molybdenum: $this->getString($raw, 'molybdenum'),
|
||||
sulfur: $this->getString($raw, 'sulfur'),
|
||||
nitrates: $this->getString($raw, 'nitrates'),
|
||||
);
|
||||
}
|
||||
|
||||
private function vitaminsFrom(IncoCompoModel $model): Vitamins
|
||||
{
|
||||
$raw = is_array($model->vitamins) ? $model->vitamins : [];
|
||||
|
||||
return new Vitamins(
|
||||
vitaminA: $this->getString($raw, 'vitaminA'),
|
||||
vitaminB6: $this->getString($raw, 'vitaminB6'),
|
||||
vitaminB12: $this->getString($raw, 'vitaminB12'),
|
||||
vitaminC: $this->getString($raw, 'vitaminC'),
|
||||
vitaminD: $this->getString($raw, 'vitaminD'),
|
||||
vitaminE: $this->getString($raw, 'vitaminE'),
|
||||
vitaminK: $this->getString($raw, 'vitaminK'),
|
||||
cholecalciferol: $this->getString($raw, 'cholecalciferol'),
|
||||
niacin: $this->getString($raw, 'niacin'),
|
||||
riboflavin: $this->getString($raw, 'riboflavin'),
|
||||
thiamine: $this->getString($raw, 'thiamine'),
|
||||
pantothenicAcid: $this->getString($raw, 'pantothenicAcid'),
|
||||
biotin: $this->getString($raw, 'biotin'),
|
||||
folates: $this->getString($raw, 'folates'),
|
||||
);
|
||||
}
|
||||
|
||||
private function carbohydratesDetailFrom(IncoCompoModel $model): CarbohydratesDetail
|
||||
{
|
||||
$raw = is_array($model->carbohydrates_detail) ? $model->carbohydrates_detail : [];
|
||||
|
||||
return new CarbohydratesDetail(
|
||||
starch: $this->getString($raw, 'starch'),
|
||||
maltodextrin: $this->getString($raw, 'maltodextrin'),
|
||||
sucrose: $this->getString($raw, 'sucrose'),
|
||||
glucose: $this->getString($raw, 'glucose'),
|
||||
lactose: $this->getString($raw, 'lactose'),
|
||||
maltose: $this->getString($raw, 'maltose'),
|
||||
galactose: $this->getString($raw, 'galactose'),
|
||||
carbohydrateHydrates: $this->getString($raw, 'carbohydrateHydrates'),
|
||||
boundFructose: $this->getString($raw, 'boundFructose'),
|
||||
freeFructose: $this->getString($raw, 'freeFructose'),
|
||||
oligosaccharides: $this->getString($raw, 'oligosaccharides'),
|
||||
polyols: $this->getString($raw, 'polyols'),
|
||||
solubleFiber: $this->getString($raw, 'solubleFiber'),
|
||||
inositol: $this->getString($raw, 'inositol'),
|
||||
);
|
||||
}
|
||||
|
||||
private function fattyAcidsFrom(IncoCompoModel $model): FattyAcids
|
||||
{
|
||||
$raw = is_array($model->fatty_acids) ? $model->fatty_acids : [];
|
||||
|
||||
return new FattyAcids(
|
||||
fd: $this->getString($raw, 'fd'),
|
||||
cholesterol: $this->getString($raw, 'cholesterol'),
|
||||
transFat: $this->getString($raw, 'transFat'),
|
||||
formic: $this->getString($raw, 'formic'),
|
||||
acetic: $this->getString($raw, 'acetic'),
|
||||
propionic: $this->getString($raw, 'propionic'),
|
||||
butyric: $this->getString($raw, 'butyric'),
|
||||
valeric: $this->getString($raw, 'valeric'),
|
||||
caproic: $this->getString($raw, 'caproic'),
|
||||
enanthic: $this->getString($raw, 'enanthic'),
|
||||
caprylic: $this->getString($raw, 'caprylic'),
|
||||
pelargonic: $this->getString($raw, 'pelargonic'),
|
||||
capric: $this->getString($raw, 'capric'),
|
||||
undecylic: $this->getString($raw, 'undecylic'),
|
||||
lauric: $this->getString($raw, 'lauric'),
|
||||
tridecylic: $this->getString($raw, 'tridecylic'),
|
||||
myristic: $this->getString($raw, 'myristic'),
|
||||
pentadecylic: $this->getString($raw, 'pentadecylic'),
|
||||
f16d0: $this->getString($raw, 'f16d0'),
|
||||
margaric: $this->getString($raw, 'margaric'),
|
||||
stearic: $this->getString($raw, 'stearic'),
|
||||
arachidic: $this->getString($raw, 'arachidic'),
|
||||
heneicosylic: $this->getString($raw, 'heneicosylic'),
|
||||
behenic: $this->getString($raw, 'behenic'),
|
||||
tricosylic: $this->getString($raw, 'tricosylic'),
|
||||
lignoceric: $this->getString($raw, 'lignoceric'),
|
||||
carboceric: $this->getString($raw, 'carboceric'),
|
||||
cerotic: $this->getString($raw, 'cerotic'),
|
||||
ceroplastic: $this->getString($raw, 'ceroplastic'),
|
||||
montanic: $this->getString($raw, 'montanic'),
|
||||
nonacosylic: $this->getString($raw, 'nonacosylic'),
|
||||
melissic: $this->getString($raw, 'melissic'),
|
||||
laceric: $this->getString($raw, 'laceric'),
|
||||
hentriacontylic: $this->getString($raw, 'hentriacontylic'),
|
||||
hextriacontylic: $this->getString($raw, 'hextriacontylic'),
|
||||
heptatriacontylic: $this->getString($raw, 'heptatriacontylic'),
|
||||
octatriacontylic: $this->getString($raw, 'octatriacontylic'),
|
||||
nonadylie: $this->getString($raw, 'nonadylie'),
|
||||
psyllic: $this->getString($raw, 'psyllic'),
|
||||
palmitic: $this->getString($raw, 'palmitic'),
|
||||
oleic: $this->getString($raw, 'oleic'),
|
||||
f18d: $this->getString($raw, 'f18d'),
|
||||
f19d: $this->getString($raw, 'f19d'),
|
||||
f11eico: $this->getString($raw, 'f11eico'),
|
||||
geddic: $this->getString($raw, 'geddic'),
|
||||
erucic: $this->getString($raw, 'erucic'),
|
||||
nervonic: $this->getString($raw, 'nervonic'),
|
||||
hyenic: $this->getString($raw, 'hyenic'),
|
||||
amscis: $this->getString($raw, 'amscis'),
|
||||
f18d2cn6: $this->getString($raw, 'f18d2cn6'),
|
||||
f18d3n3: $this->getString($raw, 'f18d3n3'),
|
||||
f20d4: $this->getString($raw, 'f20d4'),
|
||||
f20d5n3: $this->getString($raw, 'f20d5n3'),
|
||||
f22d6n3: $this->getString($raw, 'f22d6n3'),
|
||||
fapun3: $this->getString($raw, 'fapun3'),
|
||||
fapun6: $this->getString($raw, 'fapun6'),
|
||||
fapun9: $this->getString($raw, 'fapun9'),
|
||||
fapucis: $this->getString($raw, 'fapucis'),
|
||||
fapulc: $this->getString($raw, 'fapulc'),
|
||||
);
|
||||
}
|
||||
|
||||
private function aminoAcidsFrom(IncoCompoModel $model): AminoAcids
|
||||
{
|
||||
$raw = is_array($model->amino_acids) ? $model->amino_acids : [];
|
||||
|
||||
return new AminoAcids(
|
||||
essentialAminoAcids: $this->getString($raw, 'essentialAminoAcids'),
|
||||
caseins: $this->getString($raw, 'caseins'),
|
||||
wheyProtein: $this->getString($raw, 'wheyProtein'),
|
||||
);
|
||||
}
|
||||
|
||||
private function specialCompoundsFrom(IncoCompoModel $model): SpecialCompounds
|
||||
{
|
||||
$raw = is_array($model->special_compounds) ? $model->special_compounds : [];
|
||||
|
||||
return new SpecialCompounds(
|
||||
choline: $this->getString($raw, 'choline'),
|
||||
carnitine: $this->getString($raw, 'carnitine'),
|
||||
taurine: $this->getString($raw, 'taurine'),
|
||||
nucleotides: $this->getString($raw, 'nucleotides'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $data
|
||||
*/
|
||||
private function getString(array $data, string $key, string $default = '0'): string
|
||||
{
|
||||
$val = $data[$key] ?? null;
|
||||
|
||||
return is_scalar($val) ? (string) $val : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $data
|
||||
*/
|
||||
private function getStringWithFallback(array $data, string $key, string $fallbackKey, string $default = '0'): string
|
||||
{
|
||||
$val = $data[$key] ?? $data[$fallbackKey] ?? null;
|
||||
|
||||
return is_scalar($val) ? (string) $val : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $data
|
||||
*/
|
||||
private function getNutrient(array $data, string $key, string $default = '0'): NutrientValue
|
||||
{
|
||||
return new NutrientValue($this->getString($data, $key, $default));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $data
|
||||
*/
|
||||
private function getPercentage(array $data, string $key, string $default = '0'): PercentageValue
|
||||
{
|
||||
return new PercentageValue($this->getString($data, $key, $default));
|
||||
}
|
||||
}
|
||||
|
|
@ -134,8 +134,8 @@ final class EloquentIncoSheetDirectory implements IncoSheetDirectory
|
|||
allegations: $allegations,
|
||||
sellArguments: (string) $model->sell_arguments,
|
||||
preparationAdvices: (string) $model->preparation_advices,
|
||||
tips: $model->tips !== null ? (string) $model->tips : null,
|
||||
sellFamilly: $model->sell_familly !== null ? (string) $model->sell_familly : null,
|
||||
tips: $this->toNullableString($model->tips),
|
||||
sellFamilly: $this->toNullableString($model->sell_familly),
|
||||
ingredients: $ingredients,
|
||||
portions: $portions,
|
||||
allergens: $allergens,
|
||||
|
|
@ -153,50 +153,38 @@ final class EloquentIncoSheetDirectory implements IncoSheetDirectory
|
|||
|
||||
private function allegationsFrom(IncoSheetModel $model): Allegations
|
||||
{
|
||||
$allegationsRaw = is_array($model->allegations) ? $model->allegations : [];
|
||||
|
||||
/** @var array<string> $allegationsList */
|
||||
$allegationsList = is_array($allegationsRaw['allegations'] ?? null) ? $allegationsRaw['allegations'] : [];
|
||||
/** @var array<string> $healthList */
|
||||
$healthList = is_array($allegationsRaw['health'] ?? null) ? $allegationsRaw['health'] : [];
|
||||
/** @var array<string> $nutritionList */
|
||||
$nutritionList = is_array($allegationsRaw['nutrition'] ?? null) ? $allegationsRaw['nutrition'] : [];
|
||||
/** @var array<string> $otherList */
|
||||
$otherList = is_array($allegationsRaw['other'] ?? null) ? $allegationsRaw['other'] : [];
|
||||
$raw = is_array($model->allegations) ? $model->allegations : [];
|
||||
|
||||
return new Allegations(
|
||||
allegations: $allegationsList,
|
||||
health: $healthList,
|
||||
nutrition: $nutritionList,
|
||||
other: $otherList,
|
||||
allegations: $this->getArrayOfStrings($raw, 'allegations'),
|
||||
health: $this->getArrayOfStrings($raw, 'health'),
|
||||
nutrition: $this->getArrayOfStrings($raw, 'nutrition'),
|
||||
other: $this->getArrayOfStrings($raw, 'other'),
|
||||
);
|
||||
}
|
||||
|
||||
private function ingredientsFrom(IncoSheetModel $model): Ingredients
|
||||
{
|
||||
$ingredientsRaw = is_array($model->ingredients) ? $model->ingredients : [];
|
||||
$raw = is_array($model->ingredients) ? $model->ingredients : [];
|
||||
|
||||
/** @var array<int|string, array<string>> $ingredientsGroups */
|
||||
$ingredientsGroups = $ingredientsRaw;
|
||||
$ingredientsGroups = $raw;
|
||||
|
||||
return new Ingredients($ingredientsGroups);
|
||||
}
|
||||
|
||||
private function portionsFrom(IncoSheetModel $model): ?PortionAmount
|
||||
{
|
||||
$portionsRaw = is_array($model->portions) ? $model->portions : null;
|
||||
$raw = is_array($model->portions) ? $model->portions : null;
|
||||
|
||||
if ($portionsRaw === null) {
|
||||
if ($raw === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$lowPartVal = is_numeric($portionsRaw['lowPart'] ?? null) ? (float) $portionsRaw['lowPart'] : 0.0;
|
||||
$highPartVal = is_numeric($portionsRaw['highPart'] ?? null) ? (float) $portionsRaw['highPart'] : 0.0;
|
||||
|
||||
return new PortionAmount(
|
||||
lowPart: $lowPartVal,
|
||||
highPart: $highPartVal,
|
||||
approximative: (bool) ($portionsRaw['approximative'] ?? false),
|
||||
lowPart: $this->getFloat($raw, 'lowPart'),
|
||||
highPart: $this->getFloat($raw, 'highPart'),
|
||||
approximative: (bool) ($raw['approximative'] ?? false),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -217,52 +205,52 @@ final class EloquentIncoSheetDirectory implements IncoSheetDirectory
|
|||
|
||||
private function brandFrom(IncoSheetModel $model): Brand
|
||||
{
|
||||
$brandRaw = is_array($model->brand) ? $model->brand : ['code' => 'M01', 'label' => 'Standard'];
|
||||
$raw = is_array($model->brand) ? $model->brand : [];
|
||||
|
||||
return new Brand(
|
||||
code: is_scalar($brandRaw['code'] ?? null) ? (string) $brandRaw['code'] : 'M01',
|
||||
label: is_scalar($brandRaw['label'] ?? null) ? (string) $brandRaw['label'] : 'Standard',
|
||||
code: $this->getString($raw, 'code', 'M01'),
|
||||
label: $this->getString($raw, 'label', 'Standard'),
|
||||
);
|
||||
}
|
||||
|
||||
private function weightFrom(IncoSheetModel $model): ?IncoWeight
|
||||
{
|
||||
$weightRaw = is_array($model->weight) ? $model->weight : null;
|
||||
$raw = is_array($model->weight) ? $model->weight : null;
|
||||
|
||||
if ($weightRaw === null) {
|
||||
if ($raw === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new IncoWeight(
|
||||
netWeight: is_numeric($weightRaw['netWeight'] ?? null) ? (int) $weightRaw['netWeight'] : 0,
|
||||
grossWeight: is_numeric($weightRaw['grossWeight'] ?? null) ? (int) $weightRaw['grossWeight'] : 0,
|
||||
theoreticalAverage: is_numeric($weightRaw['theoreticalAverage'] ?? null) ? (int) $weightRaw['theoreticalAverage'] : 0,
|
||||
netWeight: $this->getInt($raw, 'netWeight'),
|
||||
grossWeight: $this->getInt($raw, 'grossWeight'),
|
||||
theoreticalAverage: $this->getInt($raw, 'theoreticalAverage'),
|
||||
);
|
||||
}
|
||||
|
||||
private function nutriscoreFrom(IncoSheetModel $model): ?Nutriscore
|
||||
{
|
||||
$nutriscoreRaw = is_array($model->nutriscore) ? $model->nutriscore : null;
|
||||
$raw = is_array($model->nutriscore) ? $model->nutriscore : null;
|
||||
|
||||
if ($nutriscoreRaw === null) {
|
||||
if ($raw === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Nutriscore(
|
||||
grade: is_scalar($nutriscoreRaw['grade'] ?? null) ? (string) $nutriscoreRaw['grade'] : 'A',
|
||||
grade: $this->getString($raw, 'grade', 'A'),
|
||||
);
|
||||
}
|
||||
|
||||
private function storageConditionFrom(IncoSheetModel $model): ?StorageCondition
|
||||
{
|
||||
$storageRaw = is_array($model->storage_condition) ? $model->storage_condition : null;
|
||||
$raw = is_array($model->storage_condition) ? $model->storage_condition : null;
|
||||
|
||||
if ($storageRaw === null) {
|
||||
if ($raw === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new StorageCondition(
|
||||
instructions: is_scalar($storageRaw['instructions'] ?? null) ? (string) $storageRaw['instructions'] : '',
|
||||
instructions: $this->getString($raw, 'instructions', ''),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -281,6 +269,63 @@ final class EloquentIncoSheetDirectory implements IncoSheetDirectory
|
|||
return $additives;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $data
|
||||
*/
|
||||
private function getString(array $data, string $key, string $default = ''): string
|
||||
{
|
||||
$val = $data[$key] ?? null;
|
||||
|
||||
return is_scalar($val) ? (string) $val : $default;
|
||||
}
|
||||
|
||||
private function toNullableString(mixed $val): ?string
|
||||
{
|
||||
return is_scalar($val) ? (string) $val : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $data
|
||||
* @return array<string>
|
||||
*/
|
||||
private function getArrayOfStrings(array $data, string $key): array
|
||||
{
|
||||
$val = $data[$key] ?? null;
|
||||
|
||||
if (! is_array($val)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($val as $item) {
|
||||
if (is_string($item)) {
|
||||
$result[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $data
|
||||
*/
|
||||
private function getInt(array $data, string $key, int $default = 0): int
|
||||
{
|
||||
$val = $data[$key] ?? null;
|
||||
|
||||
return is_numeric($val) ? (int) $val : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $data
|
||||
*/
|
||||
private function getFloat(array $data, string $key, float $default = 0.0): float
|
||||
{
|
||||
$val = $data[$key] ?? null;
|
||||
|
||||
return is_numeric($val) ? (float) $val : $default;
|
||||
}
|
||||
|
||||
private function toDateTime(\DateTimeInterface | string $value): \DateTimeInterface
|
||||
{
|
||||
if ($value instanceof \DateTimeInterface) {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@ declare(strict_types=1);
|
|||
namespace Infra\Providers;
|
||||
|
||||
use Domain\Ports\Directories\EmployeeDirectory;
|
||||
use Domain\Ports\Directories\IncoCompositionDirectory;
|
||||
use Domain\Ports\Directories\IncoSheetDirectory;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Infra\Adapters\Directories\EloquentEmployeeDirectory;
|
||||
use Infra\Adapters\Directories\EloquentIncoCompositionDirectory;
|
||||
use Infra\Adapters\Directories\EloquentIncoSheetDirectory;
|
||||
use Infra\Usecase\LaravelExecutor;
|
||||
use Shared\DDD\Usecase\Builder\UsecaseExecutor;
|
||||
|
|
@ -19,6 +21,7 @@ class DomainServiceProvider extends ServiceProvider
|
|||
{
|
||||
$this->app->bind(EmployeeDirectory::class, EloquentEmployeeDirectory::class);
|
||||
$this->app->bind(IncoSheetDirectory::class, EloquentIncoSheetDirectory::class);
|
||||
$this->app->bind(IncoCompositionDirectory::class, EloquentIncoCompositionDirectory::class);
|
||||
$this->app->singleton(UsecaseExecutor::class, LaravelExecutor::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Presentation\Api\V1\Dto\IncoCompositions;
|
||||
|
||||
use Presentation\Api\V1\Contracts\ApiResponseDto;
|
||||
|
||||
final readonly class IncoCompositionDto implements ApiResponseDto
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $identification
|
||||
* @param array<string, mixed> $energy
|
||||
* @param array<string, mixed> $referenceMacronutrients
|
||||
* @param array<string, mixed> $portionMacronutrients
|
||||
* @param array<string, mixed> $freshProduceAndNuts
|
||||
* @param array<string, mixed> $referenceIntakes
|
||||
* @param array<string, mixed> $nutriScore
|
||||
* @param array<string, mixed> $nutriScorePoints
|
||||
* @param array<string, mixed> $minerals
|
||||
* @param array<string, mixed> $vitamins
|
||||
* @param array<string, mixed> $carbohydratesDetail
|
||||
* @param array<string, mixed> $fattyAcids
|
||||
* @param array<string, mixed> $aminoAcids
|
||||
* @param array<string, mixed> $specialCompounds
|
||||
*/
|
||||
public function __construct(
|
||||
public string $id,
|
||||
public string $incoId,
|
||||
public array $identification,
|
||||
public array $energy,
|
||||
public array $referenceMacronutrients,
|
||||
public array $portionMacronutrients,
|
||||
public array $freshProduceAndNuts,
|
||||
public array $referenceIntakes,
|
||||
public array $nutriScore,
|
||||
public array $nutriScorePoints,
|
||||
public array $minerals,
|
||||
public array $vitamins,
|
||||
public array $carbohydratesDetail,
|
||||
public array $fattyAcids,
|
||||
public array $aminoAcids,
|
||||
public array $specialCompounds,
|
||||
) {
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
#[\Override]
|
||||
public function toArray(): array
|
||||
{
|
||||
/** @var array<string, mixed> */
|
||||
return get_object_vars($this);
|
||||
}
|
||||
}
|
||||
|
|
@ -25,16 +25,18 @@ final readonly class EmployeeShowController
|
|||
|
||||
public function __invoke(EmployeeShowRequest $request): ApiResponse
|
||||
{
|
||||
$input = $this->inputFrom($request);
|
||||
|
||||
$response = UsecaseBuilder::using($this->executor)
|
||||
->do($this->usecase)
|
||||
->from($this->toInput($request))
|
||||
->from($input)
|
||||
->with($this->presenter)
|
||||
->run();
|
||||
|
||||
return new EmployeeShowResponse($response);
|
||||
}
|
||||
|
||||
private function toInput(EmployeeShowRequest $request): Input
|
||||
private function inputFrom(EmployeeShowRequest $request): Input
|
||||
{
|
||||
return new Input(
|
||||
id: $request->employeeId(),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Presentation\Api\V1\Http\Controllers\IncoCompositions;
|
||||
|
||||
use Application\Usecase\IncoCompositions\Find\FindIncoComposition;
|
||||
use Application\Usecase\IncoCompositions\Find\Input;
|
||||
use Presentation\Api\V1\Contracts\ApiResponse;
|
||||
use Presentation\Api\V1\Http\Requests\IncoCompositions\IncoCompositionShowRequest;
|
||||
use Presentation\Api\V1\Http\Responses\IncoCompositions\IncoCompositionShowResponse;
|
||||
use Presentation\Api\V1\Presenters\IncoCompositions\IncoCompositionShowPresenter;
|
||||
use Shared\DDD\Usecase\Builder\UsecaseBuilder;
|
||||
use Shared\DDD\Usecase\Builder\UsecaseExecutor;
|
||||
|
||||
final readonly class IncoCompositionShowController
|
||||
{
|
||||
/** @param \Shared\DDD\Usecase\Builder\UsecaseExecutor<\Shared\DDD\Usecase\Input, \Shared\DDD\Usecase\Output> $executor */
|
||||
public function __construct(
|
||||
private FindIncoComposition $usecase,
|
||||
private UsecaseExecutor $executor,
|
||||
private IncoCompositionShowPresenter $presenter,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(IncoCompositionShowRequest $request): ApiResponse
|
||||
{
|
||||
$input = $this->inputFrom($request);
|
||||
|
||||
$view = UsecaseBuilder::using($this->executor)
|
||||
->do($this->usecase)
|
||||
->from($input)
|
||||
->with($this->presenter)
|
||||
->run();
|
||||
|
||||
return new IncoCompositionShowResponse($view);
|
||||
}
|
||||
|
||||
private function inputFrom(IncoCompositionShowRequest $request): Input
|
||||
{
|
||||
return new Input(
|
||||
incoId: $request->incoId(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -25,11 +25,7 @@ final readonly class IncoSheetsIndexController
|
|||
|
||||
public function __invoke(IncoSheetsIndexRequest $request): ApiResponse
|
||||
{
|
||||
$input = new Input(
|
||||
pagination: $request->pagination(),
|
||||
sorter: $request->sorter(),
|
||||
filter: $request->filter(),
|
||||
);
|
||||
$input = $this->inputFrom($request);
|
||||
|
||||
$response = UsecaseBuilder::using($this->executor)
|
||||
->do($this->usecase)
|
||||
|
|
@ -39,4 +35,13 @@ final readonly class IncoSheetsIndexController
|
|||
|
||||
return new IncoSheetsIndexResponse($response, $request->toLeagueUri());
|
||||
}
|
||||
|
||||
private function inputFrom(IncoSheetsIndexRequest $request): Input
|
||||
{
|
||||
return new Input(
|
||||
pagination: $request->pagination(),
|
||||
sorter: $request->sorter(),
|
||||
filter: $request->filter(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,10 +24,7 @@ final readonly class IncoSheetsSearchController
|
|||
|
||||
public function __invoke(IncoSheetsSearchRequest $request): ApiResponse
|
||||
{
|
||||
$input = new SearchInput(
|
||||
$request->incoIdSearch(),
|
||||
...$request->brands()
|
||||
);
|
||||
$input = $this->inputFrom($request);
|
||||
|
||||
$view = UsecaseBuilder::using($this->executor)
|
||||
->do($this->usecase)
|
||||
|
|
@ -40,4 +37,12 @@ final readonly class IncoSheetsSearchController
|
|||
baseUri: $request->toLeagueUri(),
|
||||
);
|
||||
}
|
||||
|
||||
private function inputFrom(IncoSheetsSearchRequest $request): SearchInput
|
||||
{
|
||||
return new SearchInput(
|
||||
$request->incoIdSearch(),
|
||||
...$request->brands()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,9 +25,7 @@ final readonly class IncoSheetsShowController
|
|||
|
||||
public function __invoke(IncoSheetsShowRequest $request): ApiResponse
|
||||
{
|
||||
$input = new Input(
|
||||
id: $request->incoId(),
|
||||
);
|
||||
$input = $this->inputFrom($request);
|
||||
|
||||
$response = UsecaseBuilder::using($this->executor)
|
||||
->do($this->usecase)
|
||||
|
|
@ -37,4 +35,11 @@ final readonly class IncoSheetsShowController
|
|||
|
||||
return new IncoSheetsShowResponse($response);
|
||||
}
|
||||
|
||||
private function inputFrom(IncoSheetsShowRequest $request): Input
|
||||
{
|
||||
return new Input(
|
||||
id: $request->incoId(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Presentation\Api\V1\Http\Requests\IncoCompositions;
|
||||
|
||||
use Domain\Models\ValueObjects\Inco\IncoId;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class IncoCompositionShowRequest extends FormRequest
|
||||
{
|
||||
/** @return array<string, array<int, mixed>> */
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'incoId' => ['required', 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$this->merge([
|
||||
'incoId' => $this->route('incoId'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function incoId(): IncoId
|
||||
{
|
||||
return new IncoId((string) $this->string('incoId')->value());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Presentation\Api\V1\Http\Responses\IncoCompositions;
|
||||
|
||||
use Application\Usecase\IncoCompositions\Find\Output;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Presentation\Api\V1\Contracts\ApiResponse;
|
||||
use Presentation\Api\V1\Dto\IncoCompositions\IncoCompositionDto;
|
||||
use Presentation\Api\V1\Presenters\IncoCompositions\IncoCompositionShowView;
|
||||
|
||||
final readonly class IncoCompositionShowResponse implements ApiResponse
|
||||
{
|
||||
public function __construct(
|
||||
private IncoCompositionShowView $view,
|
||||
) {
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function toResponse($request): JsonResponse
|
||||
{
|
||||
return $this->present();
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function present(): JsonResponse
|
||||
{
|
||||
return response()->json([
|
||||
'composition' => $this->fromOutput($this->view->output)->toArray(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function fromOutput(Output $output): IncoCompositionDto
|
||||
{
|
||||
return new IncoCompositionDto(
|
||||
id: (string) $output->id,
|
||||
incoId: (string) $output->idInco,
|
||||
identification: [
|
||||
'name' => $output->identification->name,
|
||||
'number' => $output->identification->number,
|
||||
'source' => $output->identification->source,
|
||||
],
|
||||
energy: [
|
||||
'reference' => [
|
||||
'size' => $output->energy->reference->size,
|
||||
'energyKj' => $output->energy->reference->energyKj,
|
||||
'energyKcal' => $output->energy->reference->energyKcal,
|
||||
],
|
||||
'portion' => [
|
||||
'size' => $output->energy->portion->size,
|
||||
'energyKj' => $output->energy->portion->energyKj,
|
||||
'energyKcal' => $output->energy->portion->energyKcal,
|
||||
],
|
||||
],
|
||||
referenceMacronutrients: [
|
||||
'fat' => [
|
||||
'amount' => $output->referenceMacronutrients->fat->amount,
|
||||
'precision' => $output->referenceMacronutrients->fat->precision,
|
||||
],
|
||||
'saturatedFat' => [
|
||||
'amount' => $output->referenceMacronutrients->saturatedFat->amount,
|
||||
'precision' => $output->referenceMacronutrients->saturatedFat->precision,
|
||||
],
|
||||
'carbohydrates' => [
|
||||
'amount' => $output->referenceMacronutrients->carbohydrates->amount,
|
||||
'precision' => $output->referenceMacronutrients->carbohydrates->precision,
|
||||
],
|
||||
'sugars' => [
|
||||
'amount' => $output->referenceMacronutrients->sugars->amount,
|
||||
'precision' => $output->referenceMacronutrients->sugars->precision,
|
||||
],
|
||||
'fiber' => [
|
||||
'amount' => $output->referenceMacronutrients->fiber->amount,
|
||||
'precision' => $output->referenceMacronutrients->fiber->precision,
|
||||
],
|
||||
'protein' => [
|
||||
'amount' => $output->referenceMacronutrients->protein->amount,
|
||||
'precision' => $output->referenceMacronutrients->protein->precision,
|
||||
],
|
||||
'salt' => [
|
||||
'amount' => $output->referenceMacronutrients->salt->amount,
|
||||
'precision' => $output->referenceMacronutrients->salt->precision,
|
||||
],
|
||||
'sodium' => '0',
|
||||
],
|
||||
portionMacronutrients: [
|
||||
'fat' => [
|
||||
'amount' => $output->portionMacronutrients->fat->amount,
|
||||
'precision' => $output->portionMacronutrients->fat->precision,
|
||||
],
|
||||
'saturatedFat' => [
|
||||
'amount' => $output->portionMacronutrients->saturatedFat->amount,
|
||||
'precision' => $output->portionMacronutrients->saturatedFat->precision,
|
||||
],
|
||||
'carbohydrates' => [
|
||||
'amount' => $output->portionMacronutrients->carbohydrates->amount,
|
||||
'precision' => $output->portionMacronutrients->carbohydrates->precision,
|
||||
],
|
||||
'sugars' => [
|
||||
'amount' => $output->portionMacronutrients->sugars->amount,
|
||||
'precision' => $output->portionMacronutrients->sugars->precision,
|
||||
],
|
||||
'fiber' => [
|
||||
'amount' => $output->portionMacronutrients->fiber->amount,
|
||||
'precision' => $output->portionMacronutrients->fiber->precision,
|
||||
],
|
||||
'protein' => [
|
||||
'amount' => $output->portionMacronutrients->protein->amount,
|
||||
'precision' => $output->portionMacronutrients->protein->precision,
|
||||
],
|
||||
'salt' => [
|
||||
'amount' => $output->portionMacronutrients->salt->amount,
|
||||
'precision' => $output->portionMacronutrients->salt->precision,
|
||||
],
|
||||
'sodium' => '0',
|
||||
],
|
||||
freshProduceAndNuts: [
|
||||
'reference' => [
|
||||
'fruits' => $output->freshProduceAndNuts->reference->fruits,
|
||||
'nuts' => $output->freshProduceAndNuts->reference->nuts,
|
||||
'freshFnl' => $output->freshProduceAndNuts->reference->freshFnl,
|
||||
],
|
||||
'portion' => [
|
||||
'fruits' => $output->freshProduceAndNuts->portion->fruits,
|
||||
'nuts' => $output->freshProduceAndNuts->portion->nuts,
|
||||
'freshFnl' => $output->freshProduceAndNuts->portion->freshFnl,
|
||||
],
|
||||
'freshFnlPercentage' => [
|
||||
'value' => $output->freshProduceAndNuts->freshFnlPercentage->value,
|
||||
],
|
||||
'fruitsPercentage' => [
|
||||
'value' => $output->freshProduceAndNuts->fruitsPercentage->value,
|
||||
],
|
||||
],
|
||||
referenceIntakes: [
|
||||
'energyE14' => ['value' => $output->referenceIntakes->energyE14->value],
|
||||
'energyKj' => ['value' => $output->referenceIntakes->energyKj->value],
|
||||
'fat' => ['value' => $output->referenceIntakes->fat->value],
|
||||
'saturatedFat' => ['value' => $output->referenceIntakes->saturatedFat->value],
|
||||
'carbohydrates' => ['value' => $output->referenceIntakes->carbohydrates->value],
|
||||
'sugars' => ['value' => $output->referenceIntakes->sugars->value],
|
||||
'protein' => ['value' => $output->referenceIntakes->protein->value],
|
||||
'salt' => ['value' => $output->referenceIntakes->salt->value],
|
||||
'portionAmount' => $output->referenceIntakes->portionAmount,
|
||||
'proteinToLipidRatio' => $output->referenceIntakes->proteinToLipidRatio,
|
||||
],
|
||||
nutriScore: [
|
||||
'negativeComponent' => $output->nutriScore->negativeComponent,
|
||||
'negativeComponent2022' => $output->nutriScore->negativeComponent2022 ?? '0',
|
||||
'positiveComponent' => $output->nutriScore->positiveComponent,
|
||||
'positiveComponent2022' => $output->nutriScore->positiveComponent2022 ?? '0',
|
||||
'score' => $output->nutriScore->score,
|
||||
'score2022' => $output->nutriScore->score2022 ?? '0',
|
||||
'grade' => $output->nutriScore->grade,
|
||||
'grade2022' => $output->nutriScore->grade2022 ?? 'A',
|
||||
'isGradeAArticle' => $output->nutriScore->isGradeAArticle ?? '0',
|
||||
'isRedMeat' => $output->nutriScore->isRedMeat ?? '0',
|
||||
],
|
||||
nutriScorePoints: [
|
||||
'energyPoints' => $output->nutriScorePoints->energyPoints,
|
||||
'saturatedFatPoints' => $output->nutriScorePoints->saturatedFatPoints,
|
||||
'sugarsPoints' => $output->nutriScorePoints->sugarsPoints,
|
||||
'saltPoints' => $output->nutriScorePoints->saltPoints,
|
||||
'fiberPoints' => $output->nutriScorePoints->fiberPoints,
|
||||
'proteinPoints' => $output->nutriScorePoints->proteinPoints,
|
||||
'freshFnlPoints' => $output->nutriScorePoints->freshFnlPoints,
|
||||
],
|
||||
minerals: [
|
||||
'calcium' => $output->minerals->calcium,
|
||||
'magnesium' => $output->minerals->magnesium,
|
||||
'potassium' => $output->minerals->potassium,
|
||||
'phosphorus' => $output->minerals->phosphorus,
|
||||
'chloride' => $output->minerals->chloride,
|
||||
'iron' => $output->minerals->iron,
|
||||
'zinc' => $output->minerals->zinc,
|
||||
'copper' => $output->minerals->copper,
|
||||
'manganese' => $output->minerals->manganese,
|
||||
'selenium' => $output->minerals->selenium,
|
||||
'iodine' => $output->minerals->iodine,
|
||||
'chromium' => $output->minerals->chromium,
|
||||
'molybdenum' => $output->minerals->molybdenum,
|
||||
'sulfur' => $output->minerals->sulfur,
|
||||
'nitrates' => $output->minerals->nitrates,
|
||||
],
|
||||
vitamins: [
|
||||
'vitaminA' => $output->vitamins->vitaminA,
|
||||
'vitaminB6' => $output->vitamins->vitaminB6,
|
||||
'vitaminB12' => $output->vitamins->vitaminB12,
|
||||
'vitaminC' => $output->vitamins->vitaminC,
|
||||
'vitaminD' => $output->vitamins->vitaminD,
|
||||
'vitaminE' => $output->vitamins->vitaminE,
|
||||
'vitaminK' => $output->vitamins->vitaminK,
|
||||
'cholecalciferol' => $output->vitamins->cholecalciferol,
|
||||
'niacin' => $output->vitamins->niacin,
|
||||
],
|
||||
carbohydratesDetail: [
|
||||
'starch' => $output->carbohydratesDetail->starch,
|
||||
'maltodextrin' => $output->carbohydratesDetail->maltodextrin,
|
||||
'sucrose' => $output->carbohydratesDetail->sucrose,
|
||||
'glucose' => $output->carbohydratesDetail->glucose,
|
||||
'lactose' => $output->carbohydratesDetail->lactose,
|
||||
'maltose' => $output->carbohydratesDetail->maltose,
|
||||
'galactose' => $output->carbohydratesDetail->galactose,
|
||||
'carbohydrateHydrates' => $output->carbohydratesDetail->carbohydrateHydrates,
|
||||
'boundFructose' => $output->carbohydratesDetail->boundFructose,
|
||||
'freeFructose' => $output->carbohydratesDetail->freeFructose,
|
||||
'oligosaccharides' => $output->carbohydratesDetail->oligosaccharides,
|
||||
'polyols' => $output->carbohydratesDetail->polyols,
|
||||
'solubleFiber' => $output->carbohydratesDetail->solubleFiber,
|
||||
'inositol' => $output->carbohydratesDetail->inositol,
|
||||
],
|
||||
fattyAcids: [
|
||||
'nutFd' => $output->fattyAcids->fd,
|
||||
'nutChol' => $output->fattyAcids->cholesterol,
|
||||
'nutAgt' => $output->fattyAcids->transFat,
|
||||
'nutFformiq' => $output->fattyAcids->formic,
|
||||
'nutFacetiq' => $output->fattyAcids->acetic,
|
||||
'nutFpropioniq' => $output->fattyAcids->propionic,
|
||||
'nutFbutyriq' => $output->fattyAcids->butyric,
|
||||
'nutFvaleriq' => $output->fattyAcids->valeric,
|
||||
'nutFcaproiq' => $output->fattyAcids->caproic,
|
||||
'nutFenatiq' => $output->fattyAcids->enanthic,
|
||||
'nutFcapryliq' => $output->fattyAcids->caprylic,
|
||||
'nutFpelargoniq' => $output->fattyAcids->pelargonic,
|
||||
'nutFcapriq' => $output->fattyAcids->capric,
|
||||
'nutFundecyliq' => $output->fattyAcids->undecylic,
|
||||
'nutFlauriq' => $output->fattyAcids->lauric,
|
||||
'nutFtridecyliq' => $output->fattyAcids->tridecylic,
|
||||
'nutFmyristiq' => $output->fattyAcids->myristic,
|
||||
'nutFpentadecyliq' => $output->fattyAcids->pentadecylic,
|
||||
'nutF16d0' => $output->fattyAcids->f16d0,
|
||||
'nutFmargariq' => $output->fattyAcids->margaric,
|
||||
'nutFsteariq' => $output->fattyAcids->stearic,
|
||||
'nutFarachidiq' => $output->fattyAcids->arachidic,
|
||||
'nutFheneicosyliq' => $output->fattyAcids->heneicosylic,
|
||||
'nutFbeheniq' => $output->fattyAcids->behenic,
|
||||
'nutFtricosiliq' => $output->fattyAcids->tricosylic,
|
||||
'nutFlignoceriq' => $output->fattyAcids->lignoceric,
|
||||
'nutFcarboceriq' => $output->fattyAcids->carboceric,
|
||||
'nutFcerotiq' => $output->fattyAcids->cerotic,
|
||||
'nutFceroplastiq' => $output->fattyAcids->ceroplastic,
|
||||
'nutFmontaniq' => $output->fattyAcids->montanic,
|
||||
'nutFnonacosyliq' => $output->fattyAcids->nonacosylic,
|
||||
'nutFmelissiq' => $output->fattyAcids->melissic,
|
||||
'nutFlaceroiq' => $output->fattyAcids->laceric,
|
||||
'nutFhentriacontyliq' => $output->fattyAcids->hentriacontylic,
|
||||
'nutFhextriacontyliq' => $output->fattyAcids->hextriacontylic,
|
||||
'nutFheptatriacontyliq' => $output->fattyAcids->heptatriacontylic,
|
||||
'nutFoctatriacontyliq' => $output->fattyAcids->octatriacontylic,
|
||||
'nutFnonadylique' => $output->fattyAcids->nonadylie,
|
||||
'nutFpsylliq' => $output->fattyAcids->psyllic,
|
||||
'nutFpalm' => $output->fattyAcids->palmitic,
|
||||
'nutFoleiq' => $output->fattyAcids->oleic,
|
||||
'nutF18d' => $output->fattyAcids->f18d,
|
||||
'nutF19d' => $output->fattyAcids->f19d,
|
||||
'nutF11eico' => $output->fattyAcids->f11eico,
|
||||
'nutFgeddiq' => $output->fattyAcids->geddic,
|
||||
'nutFeruciq' => $output->fattyAcids->erucic,
|
||||
'nutFnervoniq' => $output->fattyAcids->nervonic,
|
||||
'nutFhyeniq' => $output->fattyAcids->hyenic,
|
||||
'nutFamscis' => $output->fattyAcids->amscis,
|
||||
'nutF18d2cn6' => $output->fattyAcids->f18d2cn6,
|
||||
'nutF18d3n3' => $output->fattyAcids->f18d3n3,
|
||||
'nutF20d4' => $output->fattyAcids->f20d4,
|
||||
'nutF20d5n3' => $output->fattyAcids->f20d5n3,
|
||||
'nutF22d6n3' => $output->fattyAcids->f22d6n3,
|
||||
'nutFapun3' => $output->fattyAcids->fapun3,
|
||||
'nutFapun6' => $output->fattyAcids->fapun6,
|
||||
'nutFapun9' => $output->fattyAcids->fapun9,
|
||||
'nutFapucis' => $output->fattyAcids->fapucis,
|
||||
'nutFapulc' => $output->fattyAcids->fapulc,
|
||||
],
|
||||
aminoAcids: [
|
||||
'essentialAminoAcids' => $output->aminoAcids->essentialAminoAcids,
|
||||
'caseins' => $output->aminoAcids->caseins,
|
||||
'wheyProtein' => $output->aminoAcids->wheyProtein,
|
||||
],
|
||||
specialCompounds: [
|
||||
'choline' => $output->specialCompounds->choline,
|
||||
'carnitine' => $output->specialCompounds->carnitine,
|
||||
'taurine' => $output->specialCompounds->taurine,
|
||||
'nucleotides' => $output->specialCompounds->nucleotides,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Presentation\Api\V1\Presenters\IncoCompositions;
|
||||
|
||||
use Application\Usecase\IncoCompositions\Find\Output;
|
||||
use Shared\DDD\Usecase\Output as UsecaseOutput;
|
||||
use Shared\DDD\Usecase\Presenter;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/** @implements Presenter<\Shared\DDD\Usecase\Output, IncoCompositionShowView> */
|
||||
final readonly class IncoCompositionShowPresenter implements Presenter
|
||||
{
|
||||
#[\Override]
|
||||
public function present(UsecaseOutput $output): object
|
||||
{
|
||||
Assert::isInstanceOf($output, Output::class);
|
||||
|
||||
return IncoCompositionShowView::fromOutput($output);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Presentation\Api\V1\Presenters\IncoCompositions;
|
||||
|
||||
use Application\Usecase\IncoCompositions\Find\Output;
|
||||
|
||||
final readonly class IncoCompositionShowView
|
||||
{
|
||||
public function __construct(
|
||||
public Output $output,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function fromOutput(Output $output): self
|
||||
{
|
||||
return new self($output);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Api\V1\IncoCompositions;
|
||||
|
||||
use Infra\Database\Models\IncoCompo;
|
||||
use Infra\Database\Models\IncoSheet;
|
||||
|
||||
describe('IncoCompositions Show Feature API', function (): void {
|
||||
it('returns 200 OK with inco composition details by inco sheet ID', function (): void {
|
||||
// GIVEN
|
||||
/** @var \Tests\TestCase $test */
|
||||
$test = $this;
|
||||
|
||||
$sheet = IncoSheet::factory()->create([
|
||||
'id' => '12345',
|
||||
'compo_id' => '67890',
|
||||
]);
|
||||
|
||||
IncoCompo::factory()->create([
|
||||
'id' => '67890',
|
||||
'id_inco' => '12345',
|
||||
'identification' => [
|
||||
'name' => 'Poulet Rôti 1kg Test',
|
||||
'number' => '12345',
|
||||
'source' => 'LDC',
|
||||
],
|
||||
]);
|
||||
|
||||
// WHEN
|
||||
$response = $test->getJson("/api/v1/inco-sheets/{$sheet->id}/composition");
|
||||
|
||||
// THEN
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'composition' => [
|
||||
'id' => '67890',
|
||||
'incoId' => '12345',
|
||||
'identification' => [
|
||||
'name' => 'Poulet Rôti 1kg Test',
|
||||
],
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns 404 Not Found when composition does not exist', function (): void {
|
||||
// GIVEN
|
||||
/** @var \Tests\TestCase $test */
|
||||
$test = $this;
|
||||
|
||||
// WHEN
|
||||
$response = $test->getJson('/api/v1/inco-sheets/999999/composition');
|
||||
|
||||
// THEN
|
||||
$response->assertStatus(404);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\OpenApi\V1\IncoCompositions;
|
||||
|
||||
use Infra\Database\Models\IncoCompo;
|
||||
use Infra\Database\Models\IncoSheet;
|
||||
|
||||
describe('IncoComposition OpenAPI Conformance', function (): void {
|
||||
it('validates GET /api/v1/inco-sheets/{incoId}/composition response against OpenAPI spec', function (): void {
|
||||
// GIVEN
|
||||
/** @var \Tests\TestCase $test */
|
||||
$test = $this;
|
||||
|
||||
$sheet = IncoSheet::factory()->create([
|
||||
'id' => '12345',
|
||||
'compo_id' => '67890',
|
||||
]);
|
||||
|
||||
IncoCompo::factory()->create([
|
||||
'id' => '67890',
|
||||
'id_inco' => '12345',
|
||||
'identification' => [
|
||||
'name' => 'Poulet Rôti 1kg Test',
|
||||
'number' => '12345',
|
||||
'source' => 'LDC',
|
||||
],
|
||||
]);
|
||||
|
||||
// WHEN
|
||||
$response = $test->getJson("/api/v1/inco-sheets/{$sheet->id}/composition");
|
||||
|
||||
// THEN
|
||||
$response->assertStatus(200);
|
||||
$response->assertValidRequest();
|
||||
$response->assertValidResponse();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Application\Usecase\IncoCompositions;
|
||||
|
||||
use Application\Exceptions\NotFoundException;
|
||||
use Application\Usecase\IncoCompositions\Find\FindIncoComposition;
|
||||
use Application\Usecase\IncoCompositions\Find\Input;
|
||||
use Application\Usecase\IncoCompositions\Find\Output;
|
||||
use Domain\Models\Entities\Inco\Composition\IncoComposition;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\AminoAcids;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\CarbohydratesDetail;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\Energy;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\EnergyValues;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\FattyAcids;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\FreshProduceAndNuts;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\FruitAndNutAmount;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\Identification;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\IncoCompositionId;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\Minerals;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\NutrientValue;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\NutriScore;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\NutriScorePoints;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\PercentageValue;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\PortionMacronutrients;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\ReferenceIntakes;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\ReferenceMacronutrients;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\SpecialCompounds;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\Vitamins;
|
||||
use Domain\Models\ValueObjects\Inco\IncoId;
|
||||
use Domain\Ports\Directories\IncoCompositionDirectory;
|
||||
use Mockery;
|
||||
|
||||
covers(FindIncoComposition::class);
|
||||
|
||||
describe('FindIncoComposition Usecase Unit Tests', function (): void {
|
||||
it('executes successfully and returns Output when composition exists', function (): void {
|
||||
// GIVEN
|
||||
$incoId = new IncoId('12345');
|
||||
$compoId = new IncoCompositionId('67890');
|
||||
$identification = new Identification('Poulet Rôti 1kg Test', '12345', 'LDC');
|
||||
|
||||
$energyValues = new EnergyValues('100g', '0', '0');
|
||||
$energy = new Energy($energyValues, $energyValues);
|
||||
|
||||
$nutrient = new NutrientValue('0');
|
||||
$refMacro = new ReferenceMacronutrients($nutrient, $nutrient, $nutrient, $nutrient, $nutrient, $nutrient, $nutrient);
|
||||
$porMacro = new PortionMacronutrients($nutrient, $nutrient, $nutrient, $nutrient, $nutrient, $nutrient, $nutrient);
|
||||
|
||||
$fnAmount = new FruitAndNutAmount();
|
||||
$pct = new PercentageValue('0');
|
||||
$fresh = new FreshProduceAndNuts($fnAmount, $fnAmount, $pct, $pct);
|
||||
|
||||
$refIntakes = new ReferenceIntakes($pct, $pct, $pct, $pct, $pct, $pct, $pct, $pct, '100g');
|
||||
$nutriScore = new NutriScore('0', '0', 'A');
|
||||
$nutriScorePoints = new NutriScorePoints('0', '0', '0', '0', '0', '0', '0');
|
||||
|
||||
$minerals = new Minerals('0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0');
|
||||
$vitamins = new Vitamins('0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0');
|
||||
$carbs = new CarbohydratesDetail('0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0');
|
||||
$fatty = new FattyAcids('0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0');
|
||||
$amino = new AminoAcids('0', '0', '0');
|
||||
$special = new SpecialCompounds('0', '0', '0', '0');
|
||||
|
||||
$composition = new IncoComposition(
|
||||
id: $compoId,
|
||||
idInco: $incoId,
|
||||
identification: $identification,
|
||||
energy: $energy,
|
||||
referenceMacronutrients: $refMacro,
|
||||
portionMacronutrients: $porMacro,
|
||||
freshProduceAndNuts: $fresh,
|
||||
referenceIntakes: $refIntakes,
|
||||
nutriScore: $nutriScore,
|
||||
nutriScorePoints: $nutriScorePoints,
|
||||
minerals: $minerals,
|
||||
vitamins: $vitamins,
|
||||
carbohydratesDetail: $carbs,
|
||||
fattyAcids: $fatty,
|
||||
aminoAcids: $amino,
|
||||
specialCompounds: $special,
|
||||
);
|
||||
|
||||
$directory = Mockery::mock(IncoCompositionDirectory::class);
|
||||
$directory->shouldReceive('findByIncoId')->with($incoId)->andReturn($composition);
|
||||
|
||||
$usecase = new FindIncoComposition($directory);
|
||||
$input = new Input($incoId);
|
||||
|
||||
// WHEN
|
||||
$output = $usecase->execute($input);
|
||||
|
||||
// THEN
|
||||
expect($output)->toBeInstanceOf(Output::class)
|
||||
->and($output->id)->toBe($compoId)
|
||||
->and($output->idInco)->toBe($incoId);
|
||||
});
|
||||
|
||||
it('throws NotFoundException when composition does not exist', function (): void {
|
||||
// GIVEN
|
||||
$incoId = new IncoId('99999');
|
||||
|
||||
$directory = Mockery::mock(IncoCompositionDirectory::class);
|
||||
$directory->shouldReceive('findByIncoId')->with($incoId)->andReturn(null);
|
||||
|
||||
$usecase = new FindIncoComposition($directory);
|
||||
$input = new Input($incoId);
|
||||
|
||||
// WHEN & THEN
|
||||
expect(fn () => $usecase->execute($input))->toThrow(NotFoundException::class);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue