From 94746eb40ea87fd6a51c483a5c9e2154f28318c4 Mon Sep 17 00:00:00 2001 From: "LDFOUR\\luisd" Date: Mon, 21 Apr 2025 12:41:48 -0300 Subject: [PATCH 1/2] updated version references to use v2 added advice for v1 API request resolved sorting bug in UsageLogController@getApplicationLogs using sortBy and sending the correct queries. --- app/Http/Controllers/UsageLogController.php | 10 ++++-- config/app.php | 1 + routes/api.php | 16 ++++++--- storage/api-docs/api-docs.json | 2 +- tests/Feature/AlertFeedTest.php | 22 ++++++------- tests/Feature/AlertTest.php | 12 +++---- tests/Feature/ApplicationTest.php | 8 ++--- tests/Feature/RegionTest.php | 10 +++--- tests/Feature/WhatNowTest.php | 36 ++++++++++----------- 9 files changed, 65 insertions(+), 52 deletions(-) diff --git a/app/Http/Controllers/UsageLogController.php b/app/Http/Controllers/UsageLogController.php index 3b71775..5638a06 100644 --- a/app/Http/Controllers/UsageLogController.php +++ b/app/Http/Controllers/UsageLogController.php @@ -83,11 +83,15 @@ public function getApplicationLogs(Request $request) $this->validate($request, [ 'fromDate' => 'sometimes|date', 'toDate' => 'sometimes|date', + 'orderBy' => 'sometimes|string|in:name,username,estimatedUsers,requestCount', + 'sort' => 'sometimes|string|in:asc,desc', ]); try { + $orderBy = $request->query('orderBy', 'name'); + $sort = strtolower($request->query('sort', 'asc')) === 'desc'; $apps = $this->applicationRepo->allDesc(['id', 'tenant_user_id', 'name', 'estimated_users_count']); - + $usageLogs = collect([]); foreach ($apps as $app) { @@ -108,7 +112,7 @@ public function getApplicationLogs(Request $request) 'errors' => [], ], 500); } - + $usageLogs = $usageLogs->sortBy($orderBy, SORT_REGULAR, $sort)->values(); $paginated = $usageLogs->paginate(10)->toArray(); // TODO: Create custom paginator class @@ -374,7 +378,7 @@ public function getTotals(Request $request) $query = $usageLog->query(); if (isset($request->society)) { - $query->where('endpoint', 'v1/org/'.$request->society.'/whatnow'); + $query->where('endpoint', config('app.api_version') . '/org/' .$request->society.'/whatnow'); } if (isset($request->subnational)) { $query->where('subnational', $request->subnational); diff --git a/config/app.php b/config/app.php index 61b6381..757788a 100644 --- a/config/app.php +++ b/config/app.php @@ -249,4 +249,5 @@ 'bucket_container' => env('AZURE_STORAGE_CONTAINER'), + 'api_version' => 'v2', ]; diff --git a/routes/api.php b/routes/api.php index 47a160f..ff24d3d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -13,7 +13,7 @@ | */ -Route::group(['prefix' => 'v1'], function () { +Route::group(['prefix' => config('app.api_version')], function () { // Unauthenticated endpoints Route::get('alerts/rss', 'AlertController@getRss'); Route::get('alerts/{identifier}', 'AlertController@getByIdentifier'); @@ -22,14 +22,14 @@ Route::get('org/{code}/alerts/rss', 'AlertController@getRssByOrg'); }); -Route::group(['middleware' => 'BasicAuth', 'prefix' => 'v1'], function () { +Route::group(['middleware' => 'BasicAuth', 'prefix' => config('app.api_version')], function () { // Alert management Route::post('alerts', 'AlertController@post'); }); Route::group([ 'middleware' => 'ApiAuth', - 'prefix' => 'v1', + 'prefix' => config('app.api_version'), ], function () { // Endpoints requiring API key authentication Route::get('org/', 'OrganisationController@getAll'); @@ -40,7 +40,7 @@ Route::group([ 'middleware' => 'BasicAuth', - 'prefix' => 'v1', + 'prefix' => config('app.api_version'), ], function () { Route::get('/subnationals/{country_code}', 'RegionController@getAllForOrganisation'); Route::get('/subnationals/{country_code}/{code}', 'RegionController@getForCountryCode'); @@ -88,3 +88,11 @@ Route::get('/health', function () { return response()->json(['status' => 'ok']); // Or a more detailed status }); + +Route::prefix('v1')->group(function () { + Route::any('{any}', function () { + return response()->json([ + 'error' => 'API version v1 is no longer supported. Please use /v2/.' + ], 410); // 410 Gone es semánticamente correcto + })->where('any', '.*'); +}); \ No newline at end of file diff --git a/storage/api-docs/api-docs.json b/storage/api-docs/api-docs.json index 4d04f93..654eeb2 100644 --- a/storage/api-docs/api-docs.json +++ b/storage/api-docs/api-docs.json @@ -7,7 +7,7 @@ }, "servers": [ { - "url": "https://api.whatnow.jazusoft.com/v1", + "url": "https://api.whatnow.jazusoft.com/v2", "description": "API Base URL" } ], diff --git a/tests/Feature/AlertFeedTest.php b/tests/Feature/AlertFeedTest.php index b27362d..f57a88f 100644 --- a/tests/Feature/AlertFeedTest.php +++ b/tests/Feature/AlertFeedTest.php @@ -28,7 +28,7 @@ private function _postAlert($alert = null) $alert = factory(\App\Models\Alert::class)->make(); } - return $this->json('POST', '/v1/alerts', [ + return $this->json('POST', '/' . config('app.api_version') . '/alerts', [ 'country' => $alert->country_code, 'language' => $alert->language_code, 'event' => $alert->event, @@ -56,7 +56,7 @@ public function test_rss_feed_returns_alerts_filtered_by_severity() $alert = Alert::find($alert->json('id')); - $response = $this->call('GET', '/v1/alerts/rss', [ + $response = $this->call('GET', '/' . config('app.api_version') . '/alerts/rss', [ 'severity' => $alert->severity, ]); @@ -68,7 +68,7 @@ public function test_rss_feed_returns_alerts_filtered_by_severity() $this->assertEquals($alert->getPublicUrl(), (string)$item[0]); // Load the xml from disk - $path = storage_path('app/public/v1/alerts/cap12/') . $alert->getXmlPath(); + $path = storage_path('app/public/' . config('app.api_version') . '/alerts/cap12/') . $alert->getXmlPath(); $this->assertFileExists($path); $cap = simplexml_load_file($path); @@ -86,7 +86,7 @@ public function test_rss_feed_returns_alerts_filtered_by_event_type() $response = $this->call( 'GET', - '/v1/org/' . strtolower($alert->organisation->country_code) . '/alerts/rss', + '/' . config('app.api_version') . '/org/' . strtolower($alert->organisation->country_code) . '/alerts/rss', ['eventType' => $alert->event] ); @@ -104,7 +104,7 @@ public function test_json_feed_returns_alerts_filtered_by_event_type() $response = $this->call( 'GET', - '/v1/org/' . strtolower($alert->organisation->country_code) . '/alerts', + '/' . config('app.api_version') . '/org/' . strtolower($alert->organisation->country_code) . '/alerts', ['eventType' => $alert->event] ); @@ -115,7 +115,7 @@ public function test_json_feed_returns_alerts_filtered_by_event_type() public function test_json_feed_returns_alerts_filtered_by_severity() { - $response = $this->call('GET', '/v1/alerts', [ + $response = $this->call('GET', '/' . config('app.api_version') . '/alerts', [ 'severity' => 'extreme', ]); @@ -134,7 +134,7 @@ public function test_rss_item_link_self_reference_permalink_is_true() $alert = Alert::find($alert->json('id')); - $response = $this->call('GET', '/v1/alerts/rss'); + $response = $this->call('GET', '/' . config('app.api_version') . '/alerts/rss'); $xml = new SimpleXMLElement($response->content()); $item = $xml->xpath('//rss/channel/item[1]/guid/@isPermaLink'); @@ -158,7 +158,7 @@ public function test_active_filter_returns_only_active_alerts() $response = $this->call( 'GET', - '/v1/alerts/rss', + '/' . config('app.api_version') . '/alerts/rss', ['active' => 'true'] ); @@ -187,7 +187,7 @@ public function test_feed_returns_active_and_inactive_alerts_when_active_filter_ $response = $this->call( 'GET', - '/v1/alerts/rss' + '/' . config('app.api_version') . '/alerts/rss' ); $xml = new SimpleXMLElement($response->content()); @@ -211,7 +211,7 @@ public function test_date_filter_return_alerts_sent_within_specified_range() // Request last 24 hrs of alerts $response = $this->call( 'GET', - '/v1/alerts/rss', + '/' . config('app.api_version') . '/alerts/rss', [ 'startTime' => (new DateTime('now'))->sub(new DateInterval('PT24H'))->format('c'), 'endTime' => (new DateTime('now'))->format('c'), @@ -236,7 +236,7 @@ public function test_date_filter_excludes_alerts_sent_outside_specified_range() // Request alerts up to 24hrs old $response = $this->call( 'GET', - '/v1/alerts/rss', + '/' . config('app.api_version') . '/alerts/rss', [ 'startTime' => (new DateTime('now'))->sub(new DateInterval('PT24H'))->format('c'), 'endTime' => (new DateTime('now'))->format('c'), diff --git a/tests/Feature/AlertTest.php b/tests/Feature/AlertTest.php index 9a9afd7..c103d6e 100644 --- a/tests/Feature/AlertTest.php +++ b/tests/Feature/AlertTest.php @@ -23,7 +23,7 @@ private function _postAlert($alert = null) $alert = factory(\App\Models\Alert::class)->make(); } - return $this->json('POST', '/v1/alerts', [ + return $this->json('POST', '/' . config('app.api_version') . '/alerts', [ 'country' => $alert->country_code, 'language' => $alert->language_code, 'event' => $alert->event, @@ -93,7 +93,7 @@ public function test_it_generates_a_valid_cap_file() $alert = Alert::find($alert->json('id')); - $path = storage_path('app/public') . '/v1/alerts/cap12/' . $alert->getXmlPath(); + $path = storage_path('app/public') . '/' . config('app.api_version') . '/alerts/cap12/' . $alert->getXmlPath(); $this->assertFileExists($path); @@ -111,7 +111,7 @@ public function test_it_appears_in_rss_feed() $alert = Alert::find($alert->json('id')); - $response = $this->call('GET', '/v1/alerts/rss'); + $response = $this->call('GET', '/' . config('app.api_version') . '/alerts/rss'); $this->assertEquals(200, $response->status()); $xml = new SimpleXMLElement($response->content()); @@ -126,7 +126,7 @@ public function test_it_appears_in_country_feed() $alert = Alert::find($alert->json('id')); - $response = $this->call('GET', '/v1/org/' . strtolower($alert->organisation->country_code) . '/alerts/rss'); + $response = $this->call('GET', '/' . config('app.api_version') . '/org/' . strtolower($alert->organisation->country_code) . '/alerts/rss'); $this->assertEquals(200, $response->status()); $xml = new SimpleXMLElement($response->content()); @@ -141,7 +141,7 @@ public function test_it_appears_in_json_feed() $alert = Alert::find($alert->json('id')); - $response = $this->call('GET', '/v1/alerts'); + $response = $this->call('GET', '/' . config('app.api_version') . '/alerts'); $response->assertStatus(200); @@ -156,7 +156,7 @@ public function test_it_is_returned_by_identifier() $alert = Alert::find($alert->json('id')); - $response = $this->call('GET', '/v1/alerts/' . $alert->getCapIdentifier()); + $response = $this->call('GET', '/' . config('app.api_version') . '/alerts/' . $alert->getCapIdentifier()); $response->assertStatus(200); diff --git a/tests/Feature/ApplicationTest.php b/tests/Feature/ApplicationTest.php index 6fc0f14..61b1595 100644 --- a/tests/Feature/ApplicationTest.php +++ b/tests/Feature/ApplicationTest.php @@ -42,7 +42,7 @@ public function test_it_lists_apps() ], ]; - $response = $this->json('GET', '/v1/apps?userId=999'); + $response = $this->json('GET', '/' . config('app.api_version') . '/apps?userId=999'); $response->assertStatus(200); @@ -71,7 +71,7 @@ public function test_it_gets_app() ], ]; - $response = $this->json('GET', '/v1/apps/'.$app->id); + $response = $this->json('GET', '/' . config('app.api_version') . '/apps/'.$app->id); $response->assertStatus(200); @@ -91,7 +91,7 @@ public function test_it_deletes_app() 'key' => '1234567890', ]); - $this->json('DELETE', '/v1/apps/'.$app->id)->assertStatus(200); + $this->json('DELETE', '/' . config('app.api_version') . '/apps/'.$app->id)->assertStatus(200); $this->assertDatabaseHas('applications', [ 'id' => $app->id, @@ -106,7 +106,7 @@ public function test_it_creates_app() { $this->markTestSkipped('Bug Request POST parameters lost when unit testing https://github.com/laravel/lumen-framework/issues/559 upgrade to Lumen 5.4.2+'); - $this->json('POST', '/v1/apps/', [ + $this->json('POST', '/' . config('app.api_version') . '/apps/', [ 'userId' => '55', 'name' => 'Hello', ])->assertStatus(200); diff --git a/tests/Feature/RegionTest.php b/tests/Feature/RegionTest.php index 948c18a..c358dbd 100644 --- a/tests/Feature/RegionTest.php +++ b/tests/Feature/RegionTest.php @@ -46,7 +46,7 @@ public function testCreateRegionForOrganisation() ], ]; - $response = $this->json('POST', '/v1/subnationals', $data); + $response = $this->json('POST', '/' . config('app.api_version') . '/subnationals', $data); //dd($response); @@ -98,7 +98,7 @@ public function testUpdateRegion() $this->assertEquals(0, $matched); - $this->json('PUT', "/v1/subnationals/subnational/{$region->id}", $data) + $this->json('PUT', '/' . config('app.api_version') . '/subnationals/subnational/{$region->id}', $data) ->assertStatus(201); $matched = RegionTranslation::where('region_id', '=', $region->id) @@ -111,13 +111,13 @@ public function testUpdateRegion() public function testGetRegionsForOrganisation() { - $this->json('GET', '/v1/subnationals/USA') + $this->json('GET', '/' . config('app.api_version') . '/subnationals/USA') ->assertStatus(200); } public function testGetLaguageSpecificRegionsForOrganisation() { - $this->json('GET', '/v1/subnationals/USA/es') + $this->json('GET', '/' . config('app.api_version') . '/subnationals/USA/es') ->assertStatus(200); } @@ -139,7 +139,7 @@ public function testDeleteRegionForOrganisation() ], ]); - $this->json('DELETE', "/v1/subnationals/subnational/{$region->id}") + $this->json('DELETE', '/' . config('app.api_version') . '/subnationals/subnational/{$region->id}') ->assertStatus(202); } } diff --git a/tests/Feature/WhatNowTest.php b/tests/Feature/WhatNowTest.php index a65c153..a4838a0 100644 --- a/tests/Feature/WhatNowTest.php +++ b/tests/Feature/WhatNowTest.php @@ -44,7 +44,7 @@ public function test_basic_gets_entity() ])->id, ]); - $response = $this->call('GET', '/v1/whatnow/' . $translation->entity->id); + $response = $this->call('GET', '/' . config('app.api_version') . '/whatnow/' . $translation->entity->id); //dd($response->json()); @@ -64,7 +64,7 @@ public function test_gets_entity_without_any_published_revisions() 'org_id' => $organisation->id, ]); - $response = $this->call('GET', '/v1/whatnow/' . $entity->id); + $response = $this->call('GET', '/' . config('app.api_version') . '/whatnow/' . $entity->id); $response->assertStatus(404); } @@ -118,7 +118,7 @@ public function test_gets_latest_published_revision_by_default() 'entity_id' => $entity->id, ]); - $response = $this->call('GET', '/v1/whatnow/'.$entity->id); + $response = $this->call('GET', '/' . config('app.api_version') . '/whatnow/'.$entity->id); $response->assertStatus(200); $response->assertJsonStructure(['data' => $this->getEntityJsonStructure()]); @@ -185,7 +185,7 @@ public function test_gets_latest_draft_revision() 'entity_id' => $entity->id, ]); - $response = $this->call('GET', '/v1/whatnow/' . $entity->id . '/revisions/latest'); + $response = $this->call('GET', '/' . config('app.api_version') . '/whatnow/' . $entity->id . '/revisions/latest'); $response->assertStatus(200); @@ -254,7 +254,7 @@ public function test_gets_latest_draft_revision_even_when_revisions_are_publishe 'entity_id' => $entity->id, ]); - $response = $this->call('GET', '/v1/whatnow/'.$entity->id); + $response = $this->call('GET', '/' . config('app.api_version') . '/whatnow/'.$entity->id); $response->assertStatus(200); $response->assertJsonStructure(['data' => $this->getEntityJsonStructure()]); @@ -288,7 +288,7 @@ public function test_creates_new_whatnow() 'region_id' => $region->id, ]); - $response = $this->call('POST', '/v1/whatnow/', [ + $response = $this->call('POST', '/' . config('app.api_version') . '/whatnow/', [ 'countryCode' => $organisation->country_code, 'eventType' => 'Example Event', 'regionName' => $region->title, @@ -371,7 +371,7 @@ public function test_puts_whatnow() sleep(2); - $response = $this->call('PUT', '/v1/whatnow/'.$entity->id, $putRequest); + $response = $this->call('PUT', '/' . config('app.api_version') . '/whatnow/'.$entity->id, $putRequest); $data = json_decode($response->getContent(), true); @@ -418,7 +418,7 @@ public function test_creates_new_translation_for_existing_entity() sleep(1); - $response = $this->call('POST', '/v1/whatnow/'.$entity->id.'/revisions', $putRequest); + $response = $this->call('POST', '/' . config('app.api_version') . '/whatnow/'.$entity->id.'/revisions', $putRequest); $data = json_decode($response->getContent(), true); $response->assertStatus(201); @@ -445,7 +445,7 @@ public function test_patch_whatnow_to_publish_unpublished_translation() 'entity_id' => $entity->id, ]); - $response = $this->call('PATCH', '/v1/whatnow/'.$entity->id.'/revisions/'.$translation->id, [ + $response = $this->call('PATCH', '/' . config('app.api_version') . '/whatnow/'.$entity->id.'/revisions/'.$translation->id, [ 'published' => true, ]); @@ -484,7 +484,7 @@ public function test_patch_whatnow_to_publish_already_published_revision() 'entity_id' => $entity->id, ]); - $response = $this->call('PATCH', '/v1/whatnow/'.$entity->id.'/revisions/'.$translation->id, [ + $response = $this->call('PATCH', '/' . config('app.api_version') . '/whatnow/'.$entity->id.'/revisions/'.$translation->id, [ 'published' => true, ]); @@ -523,7 +523,7 @@ public function test_patch_whatnow_to_unpublish_published_revision() 'entity_id' => $entity->id, ]); - $response = $this->call('PATCH', '/v1/whatnow/'.$entity->id.'/revisions/'.$translation->id, [ + $response = $this->call('PATCH', '/' . config('app.api_version') . '/whatnow/'.$entity->id.'/revisions/'.$translation->id, [ 'published' => false, ]); @@ -584,7 +584,7 @@ public function test_patching_multiple_translations_by_id() 'entity_id' => $entity->id, ]); - $response = $this->call('POST', '/v1/whatnow/publish', [ + $response = $this->call('POST', '/' . config('app.api_version') . '/whatnow/publish', [ 'translationIds' => [ $translation->id, $translation2->id, @@ -629,10 +629,10 @@ public function test_deletes_whatnow() 'entity_id' => $entity->id, ]); - $response = $this->json('DELETE', '/v1/whatnow/' . $entity->id); + $response = $this->json('DELETE', '/' . config('app.api_version') . '/whatnow/' . $entity->id); $response->assertStatus(200); - $response = $this->json('GET', '/v1/whatnow/' . $entity->id); + $response = $this->json('GET', '/' . config('app.api_version') . '/whatnow/' . $entity->id); $response->assertStatus(404); $this->assertDatabaseMissing('whatnow_entities', [ @@ -664,7 +664,7 @@ public function test_gets_entities_by_country_code_when_none_published() 'entity_id' => $entity->id, ]); - $response = $this->call('GET', '/v1/org/'.$organisation->country_code.'/whatnow'); + $response = $this->call('GET', '/' . config('app.api_version') . '/org/'.$organisation->country_code.'/whatnow'); $data = json_decode($response->getContent(), true); @@ -711,7 +711,7 @@ public function test_gets_entities_returns_only_results_for_correct_country_code 'entity_id' => $entity2->id, ]); - $response = $this->call('GET', '/v1/org/'.$organisation->country_code.'/whatnow'); + $response = $this->call('GET', '/' . config('app.api_version') . '/org/'.$organisation->country_code.'/whatnow'); $data = json_decode($response->getContent(), true); @@ -761,7 +761,7 @@ public function test_gets_entities_with_latest_revisions() 'entity_id' => $entity->id, ]); - $response = $this->call('GET', '/v1/org/'.$organisation->country_code.'/whatnow/revisions/latest'); + $response = $this->call('GET', '/' . config('app.api_version') . '/org/'.$organisation->country_code.'/whatnow/revisions/latest'); $data = json_decode($response->getContent(), true); @@ -771,7 +771,7 @@ public function test_gets_entities_with_latest_revisions() $this->assertSame((string) $translation2->id, $data['data'][0]['translations'][$lang]['id']); - $response = $this->call('GET', '/v1/org/'.$organisation->country_code.'/'.$region->slug.'/whatnow/revisions/latest'); + $response = $this->call('GET', '/' . config('app.api_version') . '/org/'.$organisation->country_code.'/'.$region->slug.'/whatnow/revisions/latest'); $data = json_decode($response->getContent(), true); From 4dfd758150873eeeb937c1d654f3ea8581ca78d7 Mon Sep 17 00:00:00 2001 From: "LDFOUR\\luisd" Date: Mon, 21 Apr 2025 12:43:05 -0300 Subject: [PATCH 2/2] clean code. --- routes/api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/api.php b/routes/api.php index ff24d3d..6555624 100644 --- a/routes/api.php +++ b/routes/api.php @@ -93,6 +93,6 @@ Route::any('{any}', function () { return response()->json([ 'error' => 'API version v1 is no longer supported. Please use /v2/.' - ], 410); // 410 Gone es semánticamente correcto + ], 410); })->where('any', '.*'); }); \ No newline at end of file