From c96a8478cc662114cf6c3ea074e8c5951af2c9e8 Mon Sep 17 00:00:00 2001 From: Aleksandr Shinkarev Date: Fri, 15 May 2026 09:29:09 +0500 Subject: [PATCH 1/4] fix!: get rid of redundant api/ prefix in routing, it is not needed and should be handled in rewrite-target as it is now, it also removes ad-hock cleanup of URLs from UI --- .devcontainer/devcontainer.json | 2 +- .github/workflows/e2e-tests-on-pull-request.yml | 2 +- Api/Features/Internal/InternalContoller.cs | 2 +- Api/Features/Reporting/ReportingController.cs | 2 +- Api/Features/Tracking/TrackingController.cs | 2 +- Api/OpenApiConfiguration.cs | 4 ++-- README.md | 2 +- ci/values.yaml | 2 +- docker-compose.yml | 2 +- js-client/generation/generate.sh | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c8443ce2..96a051b0 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -61,7 +61,7 @@ "AUTH_SLYTHERINE_TENANT_GREGORY_GOYLE_LOGIN_WITHOUT_PERMISSIONS": "AUTH_SLYTHERINE_TENANT_GREGORY_GOYLE_LOGIN_WITHOUT_PERMISSIONS", "AUTH_SLYTHERINE_TENANT_GREGORY_GOYLE_PASSWORD_WITHOUT_PERMISSIONS": "AUTH_SLYTHERINE_TENANT_GREGORY_GOYLE_PASSWORD_WITHOUT_PERMISSIONS", "AUTH_API_ROOT_URL": "http://localhost:8507/api/auth", - "API_ROOT_URL": "http://localhost:4507/api", + "API_ROOT_URL": "http://localhost:4507", "SHOULD_USE_FAKE_EXTERNAL_DEPENDENCIES": "true", "ENABLE_EXCEPTION_DETAILS": "true" }, diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 5f40847a..8f0430f2 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -69,7 +69,7 @@ jobs: "AUTH_SLYTHERINE_TENANT_GREGORY_GOYLE_LOGIN_WITHOUT_PERMISSIONS": "goyle@tourmalinecore.com" "AUTH_SLYTHERINE_TENANT_GREGORY_GOYLE_PASSWORD_WITHOUT_PERMISSIONS": "Crucio1!" "AUTH_API_ROOT_URL": "http://localhost:30090/api/auth" - "API_ROOT_URL": "http://localhost:30090/api/time" + "API_ROOT_URL": "http://localhost:30090/time" "SHOULD_USE_FAKE_EXTERNAL_DEPENDENCIES": "false" e2e-karate-tests-in-docker-compose: diff --git a/Api/Features/Internal/InternalContoller.cs b/Api/Features/Internal/InternalContoller.cs index db9fcf20..2362fcc7 100644 --- a/Api/Features/Internal/InternalContoller.cs +++ b/Api/Features/Internal/InternalContoller.cs @@ -10,7 +10,7 @@ namespace Api.Features.Internal; [Authorize] [ApiController] -[Route("api/internal")] +[Route("internal")] public class InternalController : ControllerBase { [EndpointSummary("Get employees tracked task hours")] diff --git a/Api/Features/Reporting/ReportingController.cs b/Api/Features/Reporting/ReportingController.cs index d7cd563f..ef72e31d 100644 --- a/Api/Features/Reporting/ReportingController.cs +++ b/Api/Features/Reporting/ReportingController.cs @@ -9,7 +9,7 @@ namespace Api.Features.Reporting; [Authorize] [ApiController] -[Route("api/reporting")] +[Route("reporting")] public class ReportingController : ControllerBase { [EndpointSummary("Get all employees")] diff --git a/Api/Features/Tracking/TrackingController.cs b/Api/Features/Tracking/TrackingController.cs index 72431805..0a8a17e6 100644 --- a/Api/Features/Tracking/TrackingController.cs +++ b/Api/Features/Tracking/TrackingController.cs @@ -15,7 +15,7 @@ namespace Api.Features.Tracking; [Authorize] [ApiController] -[Route("api/tracking")] +[Route("tracking")] public class TrackingController : ControllerBase { [EndpointSummary("Get entries by period")] diff --git a/Api/OpenApiConfiguration.cs b/Api/OpenApiConfiguration.cs index dcea1bb4..d59c4c10 100644 --- a/Api/OpenApiConfiguration.cs +++ b/Api/OpenApiConfiguration.cs @@ -47,12 +47,12 @@ public static void AddConfiguredOpenApi(this IServiceCollection services) public static void AddOpenApiSchemaAndUI(this WebApplication app) { - app.MapOpenApi("api/swagger/openapi.json"); + app.MapOpenApi("swagger/openapi.json"); app.UseSwaggerUI(options => { options.SwaggerEndpoint("openapi.json", "API"); - options.RoutePrefix = "api/swagger"; + options.RoutePrefix = "swagger"; }); } } diff --git a/README.md b/README.md index 57404467..3e30e86d 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ The most useful is `PgAdmin` http://localhost:9507 (password is `postgres`). ## Swagger -You can fetch OpenApi endpoints and types contract using this URL http://localhost:4507/api/swagger/openapi.json. Swagger UI is accessible at http://localhost:4507/api/swagger/index.html. +You can fetch OpenApi endpoints and types contract using this URL http://localhost:4507/swagger/openapi.json. Swagger UI is accessible at http://localhost:4507/swagger/index.html. However, UI doesn't support requests execution, this requires adding Auth dialog to pass a token. It is a bit trickier starting from .NET 9 due to the change in support of Swagger packagies family `Swashbuckle`, read [here](https://learn.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-8.0&tabs=visual-studio) and [there](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/overview?view=aspnetcore-9.0&preserve-view=true) about that more. diff --git a/ci/values.yaml b/ci/values.yaml index 3849089c..56e905e4 100644 --- a/ci/values.yaml +++ b/ci/values.yaml @@ -92,7 +92,7 @@ ingress: annotations: cert-manager.io/cluster-issuer: letsencrypt nginx.ingress.kubernetes.io/force-ssl-redirect: "true" - nginx.ingress.kubernetes.io/rewrite-target: /api/$2 + nginx.ingress.kubernetes.io/rewrite-target: /$2 tls: true ingressClassName: nginx diff --git a/docker-compose.yml b/docker-compose.yml index 420bc9cc..1d7310f4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -128,7 +128,7 @@ services: AUTH_SLYTHERINE_TENANT_GREGORY_GOYLE_PASSWORD_WITHOUT_PERMISSIONS: "AUTH_SLYTHERINE_TENANT_GREGORY_GOYLE_PASSWORD_WITHOUT_PERMISSIONS" # here the port is 1080 because it needs to be an internal port, not an external which is 8507 in this case AUTH_API_ROOT_URL: "http://inner-circle-time-api-mock-server:1080/api/auth" - API_ROOT_URL: "http://inner-circle-time-api/api" + API_ROOT_URL: "http://inner-circle-time-api" SHOULD_USE_FAKE_EXTERNAL_DEPENDENCIES: "true" networks: - inner-circle-time-api-network diff --git a/js-client/generation/generate.sh b/js-client/generation/generate.sh index af1a4fbe..5874feec 100644 --- a/js-client/generation/generate.sh +++ b/js-client/generation/generate.sh @@ -1,5 +1,5 @@ swagger-typescript-api generate \ - --path 'http://localhost:6507/api/swagger/openapi.json' \ + --path 'http://localhost:6507/swagger/openapi.json' \ --axios \ --name index.ts \ --output /local/out \ \ No newline at end of file From 4371f3e192d30be168fa2689d246cae303b82401 Mon Sep 17 00:00:00 2001 From: Workflow Action Date: Fri, 15 May 2026 04:32:12 +0000 Subject: [PATCH 2/4] chore(js-client): Update js-client by OpenAPI --- js-client/index.ts | 56 ++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/js-client/index.ts b/js-client/index.ts index 51c7e4fc..cd03851e 100644 --- a/js-client/index.ts +++ b/js-client/index.ts @@ -348,14 +348,14 @@ export class HttpClient { export class Api< SecurityDataType extends unknown, > extends HttpClient { - api = { + tracking = { /** * No description * * @tags Tracking * @name TrackingGetEntriesByPeriod * @summary Get entries by period - * @request GET:/api/tracking/entries + * @request GET:/tracking/entries */ trackingGetEntriesByPeriod: ( query: { @@ -367,7 +367,7 @@ export class Api< params: RequestParams = {}, ) => this.request({ - path: `/api/tracking/entries`, + path: `/tracking/entries`, method: "GET", query: query, format: "json", @@ -380,14 +380,14 @@ export class Api< * @tags Tracking * @name TrackingCreateTaskEntry * @summary Create a task entry - * @request POST:/api/tracking/task-entries + * @request POST:/tracking/task-entries */ trackingCreateTaskEntry: ( data: CreateTaskEntryRequest, params: RequestParams = {}, ) => this.request({ - path: `/api/tracking/task-entries`, + path: `/tracking/task-entries`, method: "POST", body: data, type: ContentType.Json, @@ -401,14 +401,14 @@ export class Api< * @tags Tracking * @name TrackingCreateUnwellEntry * @summary Create an unwell entry - * @request POST:/api/tracking/unwell-entries + * @request POST:/tracking/unwell-entries */ trackingCreateUnwellEntry: ( data: CreateUnwellEntryRequest, params: RequestParams = {}, ) => this.request({ - path: `/api/tracking/unwell-entries`, + path: `/tracking/unwell-entries`, method: "POST", body: data, type: ContentType.Json, @@ -422,7 +422,7 @@ export class Api< * @tags Tracking * @name TrackingUpdateTaskEntry * @summary Update a task entry - * @request POST:/api/tracking/task-entries/{taskEntryId} + * @request POST:/tracking/task-entries/{taskEntryId} */ trackingUpdateTaskEntry: ( taskEntryId: number, @@ -430,7 +430,7 @@ export class Api< params: RequestParams = {}, ) => this.request({ - path: `/api/tracking/task-entries/${taskEntryId}`, + path: `/tracking/task-entries/${taskEntryId}`, method: "POST", body: data, type: ContentType.Json, @@ -443,7 +443,7 @@ export class Api< * @tags Tracking * @name TrackingUpdateUnwellEntry * @summary Update an unwell entry - * @request POST:/api/tracking/unwell-entries/{unwellEntryId} + * @request POST:/tracking/unwell-entries/{unwellEntryId} */ trackingUpdateUnwellEntry: ( unwellEntryId: number, @@ -451,7 +451,7 @@ export class Api< params: RequestParams = {}, ) => this.request({ - path: `/api/tracking/unwell-entries/${unwellEntryId}`, + path: `/tracking/unwell-entries/${unwellEntryId}`, method: "POST", body: data, type: ContentType.Json, @@ -464,7 +464,7 @@ export class Api< * @tags Tracking * @name TrackingGetEmployeeProjectsByPeriod * @summary Get employee projects by period - * @request GET:/api/tracking/task-entries/projects + * @request GET:/tracking/task-entries/projects */ trackingGetEmployeeProjectsByPeriod: ( query: { @@ -476,7 +476,7 @@ export class Api< params: RequestParams = {}, ) => this.request({ - path: `/api/tracking/task-entries/projects`, + path: `/tracking/task-entries/projects`, method: "GET", query: query, format: "json", @@ -489,11 +489,11 @@ export class Api< * @tags Tracking * @name TrackingHardDeleteEntry * @summary Deletes specific entry - * @request DELETE:/api/tracking/entries/{entryId}/hard-delete + * @request DELETE:/tracking/entries/{entryId}/hard-delete */ trackingHardDeleteEntry: (entryId: number, params: RequestParams = {}) => this.request({ - path: `/api/tracking/entries/${entryId}/hard-delete`, + path: `/tracking/entries/${entryId}/hard-delete`, method: "DELETE", ...params, }), @@ -504,7 +504,7 @@ export class Api< * @tags Tracking * @name TrackingSoftDeleteEntry * @summary Soft deletes specific entry - * @request DELETE:/api/tracking/entries/{entryId}/soft-delete + * @request DELETE:/tracking/entries/{entryId}/soft-delete */ trackingSoftDeleteEntry: ( entryId: number, @@ -512,24 +512,25 @@ export class Api< params: RequestParams = {}, ) => this.request({ - path: `/api/tracking/entries/${entryId}/soft-delete`, + path: `/tracking/entries/${entryId}/soft-delete`, method: "DELETE", body: data, type: ContentType.Json, ...params, }), - + }; + reporting = { /** * No description * * @tags Reporting * @name ReportingGetAllEmployees * @summary Get all employees - * @request GET:/api/reporting/employees + * @request GET:/reporting/employees */ reportingGetAllEmployees: (params: RequestParams = {}) => this.request({ - path: `/api/reporting/employees`, + path: `/reporting/employees`, method: "GET", format: "json", ...params, @@ -541,7 +542,7 @@ export class Api< * @tags Reporting * @name ReportingGetPersonalReport * @summary Get a personal employee report sorted by date in ascending order - * @request GET:/api/reporting/personal-report + * @request GET:/reporting/personal-report */ reportingGetPersonalReport: ( query: { @@ -555,20 +556,21 @@ export class Api< params: RequestParams = {}, ) => this.request({ - path: `/api/reporting/personal-report`, + path: `/reporting/personal-report`, method: "GET", query: query, format: "json", ...params, }), - + }; + internal = { /** * No description * * @tags Internal * @name InternalGetEmployeesTrackedTaskHours * @summary Get employees tracked task hours - * @request GET:/api/internal/projects/tracked-task-hours + * @request GET:/internal/projects/tracked-task-hours */ internalGetEmployeesTrackedTaskHours: ( query: { @@ -582,7 +584,7 @@ export class Api< params: RequestParams = {}, ) => this.request({ - path: `/api/internal/projects/tracked-task-hours`, + path: `/internal/projects/tracked-task-hours`, method: "GET", query: query, format: "json", @@ -595,11 +597,11 @@ export class Api< * @tags Internal * @name InternalGetAllProjects * @summary Get all projects - * @request GET:/api/internal/projects + * @request GET:/internal/projects */ internalGetAllProjects: (params: RequestParams = {}) => this.request({ - path: `/api/internal/projects`, + path: `/internal/projects`, method: "GET", format: "json", ...params, From 416fb6ddec6318050bc2e39a402f5d8df83b0d91 Mon Sep 17 00:00:00 2001 From: Aleksandr Shinkarev Date: Fri, 15 May 2026 09:35:43 +0500 Subject: [PATCH 3/4] test: fix path in TrackingControllerTests.cs --- Api/Features/Tracking/TrackingControllerTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Api/Features/Tracking/TrackingControllerTests.cs b/Api/Features/Tracking/TrackingControllerTests.cs index 1013b37f..35eb3773 100644 --- a/Api/Features/Tracking/TrackingControllerTests.cs +++ b/Api/Features/Tracking/TrackingControllerTests.cs @@ -26,7 +26,7 @@ public async Task CreateTaskEntryAsync_ShouldThrowValidationErrorIfAtLeastOneOfR Description = "Task description", }; - var response = await HttpClient.PostAsJsonAsync("/api/tracking/task-entries", createTaskEntryRequest); + var response = await HttpClient.PostAsJsonAsync("/tracking/task-entries", createTaskEntryRequest); Assert.NotNull(response); Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode); From 06728b7367bd7bcc1b16b2c3f4a92d846f1cb4ce Mon Sep 17 00:00:00 2001 From: Aleksandr Shinkarev Date: Fri, 15 May 2026 10:01:37 +0500 Subject: [PATCH 4/4] test: fix path in e2e-tests-on-pull-request.yml --- .github/workflows/e2e-tests-on-pull-request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 8f0430f2..5f40847a 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -69,7 +69,7 @@ jobs: "AUTH_SLYTHERINE_TENANT_GREGORY_GOYLE_LOGIN_WITHOUT_PERMISSIONS": "goyle@tourmalinecore.com" "AUTH_SLYTHERINE_TENANT_GREGORY_GOYLE_PASSWORD_WITHOUT_PERMISSIONS": "Crucio1!" "AUTH_API_ROOT_URL": "http://localhost:30090/api/auth" - "API_ROOT_URL": "http://localhost:30090/time" + "API_ROOT_URL": "http://localhost:30090/api/time" "SHOULD_USE_FAKE_EXTERNAL_DEPENDENCIES": "false" e2e-karate-tests-in-docker-compose: