+
-
- ./tests/Unit
-
-
- ./tests/Feature
+
+ ./tests/Api
-
-
- ./app
-
-
+
+
+ ./app
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/storage/inco/.gitkeep b/resources/documents/arcadia/marque.xsd
similarity index 100%
rename from storage/inco/.gitkeep
rename to resources/documents/arcadia/marque.xsd
diff --git a/resources/documents/inco/incollector.xsd b/resources/documents/inco/incollector.xsd
new file mode 100644
index 0000000..6c9fe89
--- /dev/null
+++ b/resources/documents/inco/incollector.xsd
@@ -0,0 +1,270 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/seeders/xml_inco/fakeimage1.png b/resources/seeders/xml_inco/fakeimage1.png
new file mode 100644
index 0000000..98a37a9
Binary files /dev/null and b/resources/seeders/xml_inco/fakeimage1.png differ
diff --git a/resources/seeders/xml_inco/fakeimage2.png b/resources/seeders/xml_inco/fakeimage2.png
new file mode 100644
index 0000000..7d98a4c
Binary files /dev/null and b/resources/seeders/xml_inco/fakeimage2.png differ
diff --git a/resources/views/emails/invalidxml.blade.php b/resources/views/mails/invalidxml.blade.php
similarity index 84%
rename from resources/views/emails/invalidxml.blade.php
rename to resources/views/mails/invalidxml.blade.php
index e212653..a6b96f2 100644
--- a/resources/views/emails/invalidxml.blade.php
+++ b/resources/views/mails/invalidxml.blade.php
@@ -1,11 +1,11 @@
- {{ $appName }} Exception Handler: Unvalid XML
+ XML Exception Handler: Invalid XML
- {{ $appName }} à reçu un fichier xml non valide !!
+ {{ config('app.name') }} à reçu un fichier xml non valide
{{ $strError }}
FICHIER: `{{ $filename }}`
{{ $exception->getMessage() }}
@@ -15,7 +15,7 @@
file : {{$exception->getFile() }}
line : {{$exception->getLine() }}
- Merci de corriger le flux, puis de renvoyer le fichier en provoquant un mouvement EAI.
+ Merci de corriger les données, puis de renvoyer le fichier en provoquant un mouvement EAI.
Pour rappel : Il est recommandé de valider un fichier XML à l'aide d'un DTD
https://www.w3schools.com/xml/xml_dtd.asp
ou bien XSD https://www.w3schools.com/xml/schema_intro.asp
diff --git a/routes/api.php b/routes/api.php
index 89f34a8..789bc2e 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -1,19 +1,19 @@
name('apiV1.')->group(function () {
-Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
- return $request->user();
+ // * PUBLIC
+ Route::get('/ping', [V1ApiController::class, 'ping'])->name('ping');
+
+ // * PRIVATE
+ Route::middleware('auth:api')->group(function () {
+ Route::prefix('inco')->name('incos.')->group(function () {
+ Route::get('/', [V1XmlIncoController::class, 'index'])->name('index');
+ Route::get('/{xmlinco}', [V1XmlIncoController::class, 'show'])->name('show');
+ });
+ });
});
diff --git a/routes/channels.php b/routes/channels.php
deleted file mode 100644
index b75e9e1..0000000
--- a/routes/channels.php
+++ /dev/null
@@ -1,18 +0,0 @@
-id === (int)$id;
-});
diff --git a/routes/console.php b/routes/console.php
deleted file mode 100644
index e216465..0000000
--- a/routes/console.php
+++ /dev/null
@@ -1,19 +0,0 @@
-comment(Inspiring::quote());
-})->purpose('Display an inspiring quote');
diff --git a/storage/.gitignore b/storage/.gitignore
deleted file mode 100644
index e3a4197..0000000
--- a/storage/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-inco/*
diff --git a/storage/inco/xsd/incollector.xsd b/storage/inco/xsd/incollector.xsd
deleted file mode 100644
index 54069fe..0000000
--- a/storage/inco/xsd/incollector.xsd
+++ /dev/null
@@ -1,273 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/storage/inco/xsd/.gitkeep b/storage/marque/archives/.gitkeep
similarity index 100%
rename from storage/inco/xsd/.gitkeep
rename to storage/marque/archives/.gitkeep
diff --git a/storage/marque/invalid/.gitkeep b/storage/marque/invalid/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/test.sh b/test.sh
new file mode 100755
index 0000000..65347d2
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+#composer runtestmigration >/dev/null
+composer runserve
+# If argument is passed run with coverage
+if [ -z "$1" ]; then
+ echo 'no coverage'
+ composer runtest
+else
+ echo 'with coverage'
+ composer runtestWithCoverage
+fi
+composer killserve
diff --git a/tests/Api/V1/ApiBase.php b/tests/Api/V1/ApiBase.php
new file mode 100644
index 0000000..1f268e2
--- /dev/null
+++ b/tests/Api/V1/ApiBase.php
@@ -0,0 +1,77 @@
+assertFragment(['status' => $status->value]);
+ }
+
+ /**
+ * Raw Function to call api
+ *
+ * @param string $method
+ * @param string $uri
+ * @param array $data
+ * @param array $headers
+ * @return \Illuminate\Testing\TestResponse
+ */
+ protected function callApi(string $method, string $uri, array $data = [], array $headers = []): TestResponse
+ {
+ if (!isset($data['timestamp'])) {
+ $data['timestamp'] = \time();
+ }
+ return $this->json($method, $uri, $data, $headers);
+ }
+
+ /**
+ * Raw Function to call api with Token
+ *
+ * @param Service $service
+ * @param string $method
+ * @param string $uri
+ * @param array $data
+ * @param array $headers
+ * @return \Illuminate\Testing\TestResponse
+ */
+ protected function callApiWithToken(
+ Service $service,
+ string $method,
+ string $uri,
+ array $data = [],
+ array $headers = []
+ ): TestResponse {
+ $data['bearer_token'] = $service->bearer_token;
+ $data['timestamp'] = \gmdate('Y-m-d H:i:s');
+
+ $data['signature'] = Helper::signRequest(
+ $service->bearer_pwd,
+ $uri,
+ $method,
+ $data
+ );
+
+ return $this->callApi($method, $uri, $data, $headers);
+ }
+}
diff --git a/tests/Api/V1/ApiTest.php b/tests/Api/V1/ApiTest.php
new file mode 100644
index 0000000..a69b13c
--- /dev/null
+++ b/tests/Api/V1/ApiTest.php
@@ -0,0 +1,186 @@
+call('get', route('apiV1.ping'), [
+ 'timestamp' => \gmdate('Y-m-d H:i:s')
+ ]);
+ $response->assertStatus(400);
+ }
+
+ /**
+ * Test timestamp middleware works as expected.
+ *
+ * @return void
+ */
+ public function testTimestampMiddleware()
+ {
+ $response = $this->json('get', route('apiV1.ping'), [
+ 'timestamp' => \gmdate('Y-m-d H:i:s')
+ ]);
+ $json = $response->decodeResponseJson();
+ $this->assertApiResponseStatusIs($json, ApiStatus::OK());
+ }
+
+ /**
+ * Timestamp middleware should fail if timestamp parameter is missing.
+ *
+ * @return void
+ */
+ public function testTimestampMiddlewareCheckFailMissing()
+ {
+ $response = $this->json('get', route('apiV1.ping'), []);
+ $json = $response->decodeResponseJson();
+ $this->assertApiResponseStatusIs($json, ApiStatus::KO());
+ $json->assertFragment(['data' => [
+ 'code' => ApiError::wrongTimeStamp()->value,
+ 'message' => ApiError::wrongTimeStamp()->label
+ ]]);
+ }
+
+ /**
+ * Timestamp middleware should fail if timestamp parameter has wrong format.
+ *
+ * @return void
+ */
+ public function testTimestampMiddlewareCheckFailWrongFormat()
+ {
+ $response = $this->json('get', route('apiV1.ping'), ['timestamp' => \gmdate('Y/m/d')]);
+ $json = $response->decodeResponseJson();
+ $this->assertApiResponseStatusIs($json, ApiStatus::KO());
+ $json->assertFragment(['data' => [
+ 'code' => ApiError::wrongTimeStamp()->value,
+ 'message' => ApiError::wrongTimeStamp()->label
+ ]]);
+ }
+
+ /**
+ * Timestamp middleware should fail if timestamp is older than "config('api.timestampTimeout', 10)".
+ *
+ * @return void
+ */
+ public function testTimestampMiddlewareCheckFailIfTooOld()
+ {
+ $response = $this->json('get', route('apiV1.ping'), [
+ 'timestamp' => \gmdate('Y-m-d H:i:s', \time() - config('api.timestampTimeout', 10))
+ ]);
+ $json = $response->decodeResponseJson();
+ $this->assertApiResponseStatusIs($json, ApiStatus::KO());
+ $json->assertFragment(['data' => [
+ 'code' => ApiError::wrongTimeStamp()->value,
+ 'message' => ApiError::wrongTimeStamp()->label
+ ]]);
+ }
+
+ /**
+ * Test api route throttle.
+ *
+ * ! This has to be the last test of this class.
+ *
+ * @return void
+ */
+ public function testApiThrottle()
+ {
+ for ($i = config('api.apiRequestRatePerMinute', 100); $i >= 0; $i--) {
+ $this->json('get', route('apiV1.ping'), [
+ 'timestamp' => \gmdate('Y-m-d H:i:s')
+ ]);
+ }
+
+ $response = $this->json('get', route('apiV1.ping'), [
+ 'timestamp' => \gmdate('Y-m-d H:i:s')
+ ]);
+ $json = $response->decodeResponseJson();
+ $this->assertApiResponseStatusIs($json, ApiStatus::KO());
+ $response->assertStatus(429);
+ }
+
+ /**
+ * Test wrong http method call
+ *
+ * @return void
+ */
+ public function testWrongMethodCall()
+ {
+ $response = $this->json('post', route('apiV1.ping'), [
+ 'timestamp' => \gmdate('Y-m-d H:i:s', \time())
+ ]);
+ $json = $response->decodeResponseJson();
+ $this->assertApiResponseStatusIs($json, ApiStatus::KO());
+ $this->assertEquals(ApiError::methodNotAllowed()->value, $json->json('data.code'));
+ }
+
+ /**
+ * Test Auth fail when no token
+ *
+ * @return void
+ */
+ public function testAuthFailNoToken()
+ {
+ $response = $this->json('get', route('apiV1.incos.show', [
+ 'xmlinco' => Str::uuid()
+ ]), [
+ 'timestamp' => \gmdate('Y-m-d H:i:s', \time())
+ ]);
+ $json = $response->decodeResponseJson();
+ $this->assertApiResponseStatusIs($json, ApiStatus::KO());
+ $this->assertEquals(ApiError::apiAuthFailed()->value, $json->json('data.code'));
+ }
+
+ /**
+ * Test Auth fail when unknown token
+ *
+ * @return void
+ */
+ public function testAuthFailUnknownToken()
+ {
+ $response = $this->json('get', route('apiV1.incos.show', [
+ 'xmlinco' => Str::uuid()
+ ]), [
+ 'bearer_token' => 'qsdqsdqsd',
+ 'timestamp' => \gmdate('Y-m-d H:i:s', \time())
+ ]);
+ $json = $response->decodeResponseJson();
+ $this->assertApiResponseStatusIs($json, ApiStatus::KO());
+ $this->assertEquals(ApiError::apiAuthFailed()->value, $json->json('data.code'));
+ }
+
+ /**
+ * Test Auth fail when wrong signature
+ *
+ * @return void
+ */
+ public function testAuthFailWrongSignature()
+ {
+ $service = Service::query()->firstOrFail();
+ $response = $this->json('get', route('apiV1.incos.show', [
+ 'xmlinco' => Str::uuid()
+ ]), [
+ 'bearer_token' => $service->bearer_token,
+ 'timestamp' => \gmdate('Y-m-d H:i:s', \time()),
+ 'signature' => 'sdqdqsdqd'
+ ]);
+ $json = $response->decodeResponseJson();
+ $this->assertApiResponseStatusIs($json, ApiStatus::KO());
+ $this->assertEquals(ApiError::apiAuthFailed()->value, $json->json('data.code'));
+ }
+}
diff --git a/tests/Api/V1/XmlIncoTest.php b/tests/Api/V1/XmlIncoTest.php
new file mode 100644
index 0000000..c7efcc8
--- /dev/null
+++ b/tests/Api/V1/XmlIncoTest.php
@@ -0,0 +1,161 @@
+firstOrFail();
+ $arcadiaBrand = $xmlInco->arcadiaBrand;
+ $this->assertInstanceOf(ArcadiaBrand::class, $arcadiaBrand);
+ }
+
+ /**
+ * Test customer Authentification ok.
+ * route: apiV2.customers.authenticate
+ *
+ * @return void
+ */
+ public function testXmlIncoIndex()
+ {
+ /** @var \App\Models\Service $service */
+ extract($this->getService(true));
+
+ $response = $this->callApiWithToken(
+ $service,
+ 'get',
+ route('apiV1.incos.index'),
+ ['per_page' => '2']
+ );
+
+ $json = $response->decodeResponseJson();
+
+ $this->assertApiResponseStatusIs($json, ApiStatus::OK());
+ $response->assertJsonStructure([
+ 'data' => [
+ '*' => [
+ 'id',
+ 'prod_saison',
+ 'allegations',
+ 'descriptif',
+ 'pictures' => [
+ '*'
+ ],
+ 'astuces',
+ 'lib_conso',
+ 'nb_portion',
+ 'typ_portion',
+ 'pds_moy_theo',
+ 'conseil_prepa',
+ 'arg_vente',
+ 'lst_ing',
+ 'lib_allergene',
+ 'nutritional_values' => [
+ 'val_energ_kj_r',
+ 'val_energ_kcal_r',
+ 'mat_grass_r',
+ 'ags_r',
+ 'glucides_r',
+ 'sucre_r',
+ 'fib_alim_r',
+ 'sel_r',
+ 'sodium_r',
+ 'fruits_r',
+ 'fruits_coque_sech_r',
+ 'pourcent_apport_ref_e14_r',
+ 'pourcent_apport_ref_kjo_r',
+ 'pourcent_apport_ref_fat_r',
+ 'pourcent_apport_ref_fatsat_r',
+ 'pourcent_apport_ref_choval_r',
+ 'pourcent_apport_ref_sugar_r',
+ 'pourcent_apport_ref_fibtg_r',
+ 'pourcent_apport_ref_pro_r',
+ 'pourcent_apport_ref_salteq_r',
+ 'taille_port',
+ 'val_energ_kj_p',
+ 'val_energ_kcal_p',
+ 'mat_grass_p',
+ 'ags_p',
+ 'glucides_p',
+ 'sucre_p',
+ 'fib_alim_p',
+ 'proteine_p',
+ 'sodium_p',
+ 'fruits_p',
+ 'sel_p',
+ 'fruits_coque_sech_p',
+ 'pourcent_apport_ref_e14_p',
+ 'pourcent_apport_ref_kjo_p',
+ 'pourcent_apport_ref_fat_p',
+ 'pourcent_apport_ref_fatsat_p',
+ 'pourcent_apport_ref_choval_p',
+ 'pourcent_apport_ref_sugar_p',
+ 'pourcent_apport_ref_fibtg_p',
+ 'pourcent_apport_ref_pro_p',
+ 'pourcent_apport_ref_salteq_p',
+ 'apport_port',
+ 'part_p',
+ 'part_g',
+ 'part_l',
+ 'p_sur_l',
+ 'pourcent_ags',
+ 'pourcent_agi',
+ 'pourcent_ss',
+ 'pourcent_amidon',
+ 'composante_n',
+ 'composante_p',
+ 'pourcent_fruits',
+ 'score_nutri',
+ 'classement',
+ 'mat_grass_r_prec',
+ 'ags_r_prec',
+ 'glucides_r_prec',
+ 'sucre_r_prec',
+ 'fib_alim_r_prec',
+ 'proteine_r_prec',
+ 'sel_r_prec',
+ 'sodium_r_prec',
+ 'fruits_r_prec',
+ 'fruits_coque_sech_r_prec',
+ ],
+ ]
+ ]
+ ]);
+ }
+
+ /**
+ * Get Customer models with given status (active or not)
+ *
+ * @param boolean $random Randomize the customer to get.
+ * @return array
+ */
+ protected function getService(bool $random = true): array
+ {
+ if ($random) {
+ /** @var \App\Models\Service $service */
+ $service = Service::inRandomOrder()->whereHas('arcadiaBrands')->firstOrFail();
+ } else {
+ /** @var \App\Models\Service $service */
+ $service = Service::query()->whereHas('arcadiaBrands')->firstOrFail();
+ }
+
+ return compact('service');
+ }
+}
diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php
deleted file mode 100644
index 0894c60..0000000
--- a/tests/Feature/ExampleTest.php
+++ /dev/null
@@ -1,20 +0,0 @@
-get('/');
-
- $response->assertStatus(200);
- }
-}
diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php
deleted file mode 100644
index 62e8c3c..0000000
--- a/tests/Unit/ExampleTest.php
+++ /dev/null
@@ -1,18 +0,0 @@
-assertTrue(true);
- }
-}