diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml new file mode 100644 index 0000000..153da7c --- /dev/null +++ b/.github/workflows/deploy-dev.yml @@ -0,0 +1,46 @@ +name: Deploy (Development) + +on: + pull_request: + branches: [ develop ] + + workflow_dispatch: + +env: + ARM_USE_OIDC: false + +jobs: + dev: + environment: dev + runs-on: ubuntu-latest + permissions: + id-token: write # This is required for requesting the OIDC JWT Token + contents: read # Required when GH token is used to authenticate with private repo + + steps: + - name: Checkout to the branch + uses: actions/checkout@v2 + + - name: Azure Login + uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + # TODO: add dotnet test step + + - name: Build and Deploy + uses: azure/container-apps-deploy-action@v2 + with: + appSourcePath: ${{ github.workspace }} + dockerfilePath: ./src/Plurish.Game.Api/Dockerfile + registryUrl: devplurish.azurecr.io + registryUsername: ${{ secrets.REGISTRY_USERNAME }} + registryPassword: ${{ secrets.REGISTRY_PASSWORD }} + containerAppName: dev-capp-api-game + resourceGroup: dev-rg-plurish + imageToBuild: devplurish.azurecr.io/api/game:${{ github.sha }} + targetPort: 8080 + _buildArgumentsKey_: | + _buildArgumentsValues_ + + # TODO: add hml stage \ No newline at end of file diff --git a/README.md b/README.md index 9c7276a..ed9e334 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Esta é uma template, que pode ser usada como base para REST APIs potencialmente ## Como testar ```bash -git clone https://github.com/plurish/api-template.git +git clone https://github.com/plurish/Plurish.Game.git dotnet run --project api-template/src/Plurish.Game.Api/Plurish.Game.Api.csproj @@ -33,7 +33,7 @@ O arquivo `Directory.Build.props` possui a prop de UserSecretsId. A intenção d ```json { - "KeyVault:Url": "https://xpto.vault.azure.net/", + "KeyVault:Url": "https://dev-kv-plurish.vault.azure.net/", "KeyVault:TenantId": "00000000-0000-0000-0000-000000000000", "KeyVault:ClientId": "00000000-0000-0000-0000-000000000000", "KeyVault:ClientSecret": "secret-xpto" @@ -76,13 +76,13 @@ scrape_configs: # Estrutura O projeto como um todo foi estruturado com base em conceitos, princípios e patterns de Clean Architecture, Vertical Slice Architecture e Domain-Driven Design (DDD), -visando promover, respectivamente, desacoplamento, coesão e manuteniblidade. +visando promover, respectivamente, desacoplamento, coesão e manutenibilidade. -Cada camada, teoricamente, deve ter responsabilidades bem claras e pouco acopladas às outras. De modo geral, cada camada normalmente terá algumas ou todas as seguintes caracaterísticas: +Cada camada, teoricamente, deve ter responsabilidades bem claras e pouco acopladas às outras. De modo geral, as camadas normalmente terão algumas ou todas as seguintes caracaterísticas: - Divisão por features - Contém diretório 'Common', para recursos compartilhados entre diferentes features - Responsabilidade própria de injeção de dependência -- Classe de configuração própria (Settings.cs), baseada no appsettings.json +- Classe de configuração própria (`Settings.cs`), baseada no `appsettings.json` ## Presentation Expõe a aplicação para agentes externos. Nesse caso, através de endpoints HTTP diff --git a/docker-compose.yml b/docker-compose.yml index cf58a1f..2e3e76b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,12 @@ version: '3.8' services: - api-template: - container_name: plurish-api-template - image: plurish/api/template + api-game: + container_name: plurish-api-game + image: plurish/api/game build: context: . - dockerfile: ./src/Api/Dockerfile - args: - KEYVAULT_URL: https://kv-plurish-dev.vault.azure.net/ + dockerfile: ./src/Plurish.Game.Api/Dockerfile ports: - 8080:8080 - 8081:8081 @@ -16,65 +14,65 @@ services: networks: - plurish - elasticsearch: - container_name: plurish-elastic - image: docker.elastic.co/elasticsearch/elasticsearch:8.12.2 - volumes: - - elastic-data:/usr/share/elasticsearch/data - environment: - - cluster.name=plurish-elastic - - discovery.type=single-node - - xpack.security.enabled=false - - xpack.security.enrollment.enabled=false - - bootstrap.memory_lock=true - - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - ports: - - 9200:9200 - ulimits: - memlock: - soft: -1 - hard: -1 - nofile: - soft: 65536 - hard: 65536 - deploy: - restart_policy: - condition: on-failure - delay: 10s - max_attempts: 3 - window: 120s - networks: - - plurish + # elasticsearch: + # container_name: plurish-elastic + # image: docker.elastic.co/elasticsearch/elasticsearch:8.12.2 + # volumes: + # - elastic-data:/usr/share/elasticsearch/data + # environment: + # - cluster.name=plurish-elastic + # - discovery.type=single-node + # - xpack.security.enabled=false + # - xpack.security.enrollment.enabled=false + # - bootstrap.memory_lock=true + # - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + # ports: + # - 9200:9200 + # ulimits: + # memlock: + # soft: -1 + # hard: -1 + # nofile: + # soft: 65536 + # hard: 65536 + # deploy: + # restart_policy: + # condition: on-failure + # delay: 10s + # max_attempts: 3 + # window: 120s + # networks: + # - plurish - kibana: - container_name: plurish-kibana - image: docker.elastic.co/kibana/kibana:8.12.2 - volumes: - - kibana-data:/usr/share/elasticsearch/data - restart: unless-stopped - ports: - - 5601:5601 - environment: - ELASTICSEARCH_URL: http://elasticsearch:9200 - ELASTICSEARCH_HOSTS: http://elasticsearch:9200 - xpack.fleet.agents.tlsCheckDisabled: true - depends_on: - - elasticsearch - networks: - - plurish + # kibana: + # container_name: plurish-kibana + # image: docker.elastic.co/kibana/kibana:8.12.2 + # volumes: + # - kibana-data:/usr/share/elasticsearch/data + # restart: unless-stopped + # ports: + # - 5601:5601 + # environment: + # ELASTICSEARCH_URL: http://elasticsearch:9200 + # ELASTICSEARCH_HOSTS: http://elasticsearch:9200 + # xpack.fleet.agents.tlsCheckDisabled: true + # depends_on: + # - elasticsearch + # networks: + # - plurish - prometheus: - container_name: plurish-prometheus - image: prom/prometheus:v2.50.1 - volumes: - - ../prometheus.yaml:/etc/prometheus/prometheus.yml - - prometheus-data:/prometheus - depends_on: - - api-template - ports: - - 9090:9090 - networks: - - plurish + # prometheus: + # container_name: plurish-prometheus + # image: prom/prometheus:v2.50.1 + # volumes: + # - ../prometheus.yaml:/etc/prometheus/prometheus.yml + # - prometheus-data:/prometheus + # depends_on: + # - api-template + # ports: + # - 9090:9090 + # networks: + # - plurish # db-log: # container_name: plurish-db-log @@ -90,13 +88,13 @@ services: # networks: # - plurish -volumes: - elastic-data: - name: plurish-elastic - kibana-data: - name: plurish-kibana - prometheus-data: - name: plurish-prometheus +# volumes: +# elastic-data: +# name: plurish-elastic +# kibana-data: +# name: plurish-kibana +# prometheus-data: +# name: plurish-prometheus networks: plurish: diff --git a/src/Plurish.Game.Api/DependencyInjection.cs b/src/Plurish.Game.Api/DependencyInjection.cs index dee2be0..e9d4b01 100644 --- a/src/Plurish.Game.Api/DependencyInjection.cs +++ b/src/Plurish.Game.Api/DependencyInjection.cs @@ -15,20 +15,14 @@ internal static class DependencyInjection { public static IServiceCollection AddPresentation( this IServiceCollection services, - IConfiguration configuration, - bool isProduction + IConfiguration configuration ) { services.AddSettings(configuration, out AuthOptions authSettings); - if (!isProduction) - { - services - .AddHealthCheckUI(authSettings) - .AddSwagger(); - } - return services + .AddHealthCheckUI(authSettings) + .AddSwagger() .AddResponseCompression() .AddVersioning() .AddMiddlewares() @@ -116,8 +110,8 @@ private static IServiceCollection AddMappers(this IServiceCollection services) .AddSingleton(); } - private static IServiceCollection AddSwagger(this IServiceCollection services) => - // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle + private static IServiceCollection AddSwagger(this IServiceCollection services) => + // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle services.AddSwaggerGen(o => { o.SwaggerDoc("v1", new OpenApiInfo diff --git a/src/Plurish.Game.Api/Dockerfile b/src/Plurish.Game.Api/Dockerfile index 6ee048f..882a80b 100644 --- a/src/Plurish.Game.Api/Dockerfile +++ b/src/Plurish.Game.Api/Dockerfile @@ -1,14 +1,13 @@ #See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build -ARG BUILD_CONFIGURATION=Release WORKDIR /src COPY ["./src/Plurish.Game.Api/Plurish.Game.Api.csproj", "./Plurish.Game.Api/"] COPY ["./src/Plurish.Game.Application/Plurish.Game.Application.csproj", "./Plurish.Game.Application/"] COPY ["./src/Plurish.Game.Domain/Plurish.Game.Domain.csproj", "./Plurish.Game.Domain/"] COPY ["./src/Plurish.Game.Infra/Plurish.Game.Infra.csproj", "./Plurish.Game.Infra/"] -COPY ["./src/Plurish.Common/Plurish.Common.csproj" "./Plurish.Common/"] +COPY ["./src/Plurish.Common/Plurish.Common.csproj", "./Plurish.Common/"] COPY ./Directory.Build.props ./ COPY ./.editorconfig ./ @@ -18,18 +17,14 @@ COPY ./src/ ./ WORKDIR "/src/Plurish.Game.Api" -RUN dotnet build "./Plurish.Game.Api.csproj" -c $BUILD_CONFIGURATION -o /app/build +RUN dotnet build "./Plurish.Game.Api.csproj" -c Release -o /app/build # TODO: adicionar dotnet test FROM build AS publish -ARG BUILD_CONFIGURATION=Release -ARG KEYVAULT_URL - -ENV KeyVault__Url=$KEYVAULT_URL RUN dotnet publish "./Plurish.Game.Api.csproj" \ - -c $BUILD_CONFIGURATION \ + -c Release \ -o /app/publish /p:UseAppHost=false FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final diff --git a/src/Plurish.Game.Api/Program.cs b/src/Plurish.Game.Api/Program.cs index 14f3c28..d09ba7b 100644 --- a/src/Plurish.Game.Api/Program.cs +++ b/src/Plurish.Game.Api/Program.cs @@ -14,10 +14,7 @@ { builder.Services .AddCommon(builder.Configuration) - .AddPresentation( - builder.Configuration, - builder.Environment.IsProduction() - ) + .AddPresentation(builder.Configuration) .AddInfrastructure(builder.Configuration) .AddApplication(); @@ -30,16 +27,19 @@ { app.UseResponseCompression(); + app + .UseSwagger(o => o.RouteTemplate = "api/{documentName}/swagger.{json|yaml}") + .UseSwaggerUI(o => + { + o.SwaggerEndpoint("/api/v1/swagger.json", "API Game v1"); + o.InjectStylesheet("/swagger.css"); + o.RoutePrefix = "docs"; + }); + if (!app.Environment.IsProduction()) - app - .UseSwagger(o => o.RouteTemplate = "api/{documentName}/swagger.{json|yaml}") - .UseSwaggerUI(o => - { - o.SwaggerEndpoint("/api/v1/swagger.json", "API Game v1"); - o.InjectStylesheet("/swagger.css"); - o.RoutePrefix = "docs"; - }) - .UseDeveloperExceptionPage(); + { + app.UseDeveloperExceptionPage(); + } app .UseRouting() @@ -54,16 +54,13 @@ }) .UseEndpoints(endpoints => { - if (!app.Environment.IsProduction()) - endpoints.MapHealthChecksUI(o => - { - o.UIPath = "/dashboard"; - o.PageTitle = "Health | Plurish-Api-Game"; - //o.AddCustomStylesheet("wwwroot/health.css"); - }); - + endpoints.MapHealthChecksUI(o => + { + o.UIPath = "/dashboard"; + o.PageTitle = "Health | Plurish-Api-Game"; + //o.AddCustomStylesheet("wwwroot/health.css"); + }); endpoints.MapMetrics("/_metrics"); - endpoints.MapControllers(); }); diff --git a/src/Plurish.Game.Api/appsettings.Development.json b/src/Plurish.Game.Api/appsettings.Development.json index dba14ef..a35dd10 100644 --- a/src/Plurish.Game.Api/appsettings.Development.json +++ b/src/Plurish.Game.Api/appsettings.Development.json @@ -1,18 +1,4 @@ { - "Api": { - "OpenWeather": { // exemplo de api externa - "Url": "https://api.openweathermap.org/", - "Token": "d2fe6b4d5d4b1f2a575390cce02bedcb", - "Timeout": 10, // segundos, - "Resilience": { - "MedianFirstRetryDelay": 1, // segundos - "RetryCount": 2 - } - }, - "Elasticsearch": { - "Url": "http://set:by@key.vault" - } - }, "Serilog": { "Using": [ "Serilog.Sinks.Console" ], // "Serilog.Sinks.Elasticsearch" "MinimumLevel": { @@ -34,7 +20,7 @@ "Name": "Elasticsearch", "Args": { "NodeUris": "http://set:by@key.vault", - "IndexFormat": "plurish-api-template-log-{0:yyyy.MM.dd}", + "IndexFormat": "plurish-api-game-log-{0:yyyy.MM.dd}", "AutoRegisterGame": true, "BufferFileSizeLimitBytes": 5242880, "QueueSizeLimit": 1000000 diff --git a/src/Plurish.Game.Api/appsettings.Production.json b/src/Plurish.Game.Api/appsettings.Production.json index 0b7a8e9..620fc1a 100644 --- a/src/Plurish.Game.Api/appsettings.Production.json +++ b/src/Plurish.Game.Api/appsettings.Production.json @@ -1,49 +1,50 @@ { - "Api": { - "Elasticsearch": { - "Url": "http://set:by@key.vault" - } - }, "Serilog": { - "Using": [ "Serilog.Sinks.Elasticsearch" ], // "Serilog.Sinks.MSSqlServer" + "Using": [ "Serilog.Sinks.Elasticsearch", "Serilog.Sinks.Console" ], // "Serilog.Sinks.MSSqlServer" "MinimumLevel": { "Default": "Warning" }, "Enrich": [ "WithThreadId", "WithMachineName", "FromLogContext" ], "WriteTo": [ + { // Referência: https://github.com/serilog/serilog-settings-configuration/blob/dev/sample/Sample/appsettings.json + "Name": "Console", + "Args": { + "outputGame": "({ThreadId}) [{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} - CorrelationId: {CorrelationId} - Hostname: {MachineName} - Logger: {SourceContext} {NewLine}{Exception}" + } + }, { // Referência: https://github.com/serilog-contrib/serilog-sinks-elasticsearch?tab=readme-ov-file#configurable-properties "Name": "Elasticsearch", "Args": { - "NodeUris": "", - "IndexFormat": "plurish-api-template-log-{0:yyyy.MM.dd}", + "NodeUris": "http://set:by@key.vault", + "IndexFormat": "plurish-api-game-log-{0:yyyy.MM.dd}", "AutoRegisterGame": true, "BufferFileSizeLimitBytes": 5242880, "QueueSizeLimit": 1000000 } - }, - { // Referência: https://github.com/serilog-mssql/serilog-sinks-mssqlserver?tab=readme-ov-file#json-microsoftextensionsconfiguration - "Name": "MSSqlServer", - "Args": { - "ConnectionString": "http://set:by@key.vault", - "SinkOptionsSection": { - "TableName": "ApiGame", - "SchemaName": "Plurish" - }, - "ColumnOptionsSection": { - "AddStandardColumns": [ "ThreadId", "MachineName" ], - "RemoveStandardColumns": [ "MessageGame", "Properties" ], - "TimeStamp": { - "ColumnName": "Date", - "ConvertToUtc": true - }, - "Message": { "DataLength": "8000" }, - "AdditionalColumns": [ - { "ColumnName": "Thread", "DataType": "int" }, - { "ColumnName": "Host", "PropertyName": "MachineName" }, - { "ColumnName": "Logger", "PropertyName": "SourceContext" }, - { "ColumnName": "TraceId", "PropertyName": "CorrelationId" } - ] - } - } - } + }//, + // { // Referência: https://github.com/serilog-mssql/serilog-sinks-mssqlserver?tab=readme-ov-file#json-microsoftextensionsconfiguration + // "Name": "MSSqlServer", + // "Args": { + // "ConnectionString": "http://set:by@key.vault", + // "SinkOptionsSection": { + // "TableName": "ApiGame", + // "SchemaName": "Plurish" + // }, + // "ColumnOptionsSection": { + // "AddStandardColumns": [ "ThreadId", "MachineName" ], + // "RemoveStandardColumns": [ "MessageGame", "Properties" ], + // "TimeStamp": { + // "ColumnName": "Date", + // "ConvertToUtc": true + // }, + // "Message": { "DataLength": "8000" }, + // "AdditionalColumns": [ + // { "ColumnName": "Thread", "DataType": "int" }, + // { "ColumnName": "Host", "PropertyName": "MachineName" }, + // { "ColumnName": "Logger", "PropertyName": "SourceContext" }, + // { "ColumnName": "TraceId", "PropertyName": "CorrelationId" } + // ] + // } + // } + // } ] } } diff --git a/src/Plurish.Game.Api/appsettings.json b/src/Plurish.Game.Api/appsettings.json index b3a81cc..dbf0f75 100644 --- a/src/Plurish.Game.Api/appsettings.json +++ b/src/Plurish.Game.Api/appsettings.json @@ -1,4 +1,18 @@ { + "Api": { + "OpenWeather": { // exemplo de api externa + "Url": "https://api.openweathermap.org/", + "Token": "d2fe6b4d5d4b1f2a575390cce02bedcb", + "Timeout": 10, // segundos, + "Resilience": { + "MedianFirstRetryDelay": 1, // segundos + "RetryCount": 2 + } + }, + "Elasticsearch": { + "Url": "http://set:by@key.vault" + } + }, "AllowedHosts": "*", "KeyVault": { "Url": "", // Definido pelo Secret Manager (localmente) ou Pipeline Variable