Skip to content

Горшенин Дмитрий Лаб. 2 Группа 6511#7

Open
dmgorshenin wants to merge 16 commits intoitsecd:mainfrom
dmgorshenin:Lab2
Open

Горшенин Дмитрий Лаб. 2 Группа 6511#7
dmgorshenin wants to merge 16 commits intoitsecd:mainfrom
dmgorshenin:Lab2

Conversation

@dmgorshenin
Copy link

ФИО: Горшенин Дмитрий
Номер группы: 6511
Номер лабораторной: 2
Номер варианта: 4
Краткое описание предметной области: Генератор кредитной заявки
Краткое описание добавленных фич: Реализация Gateway, настройка его работы

dmgorshenin and others added 16 commits February 13, 2026 18:27
…аб. роботы

В решение добавлены проекты CreditApplication.AppHost (Aspire-хост), CreditApplication.Generator (API генерации кредитных заявок с кэшированием в Redis) и CreditApplication.ServiceDefaults (общие настройки сервисов: логирование, OpenTelemetry, resilience, health checks).
Реализована модель заявки, генератор на Bogus, сервис с кэшированием, API-эндпоинт /credit-application.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 18, 2026 12:36
@github-actions github-actions bot added In progress Код в процессе проверки Lab 2 Лабораторная №2. Балансировка нагрузки labels Feb 18, 2026
@github-actions github-actions bot requested a review from danlla February 18, 2026 12:36
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

PR implements Lab #2 for the “Credit Application Generator” microservice system by adding an API Gateway layer and running multiple generator replicas under .NET Aspire orchestration.

Changes:

  • Added API Gateway service (Ocelot) with a custom weighted-random load balancer.
  • Added Aspire AppHost orchestration to run Redis + 3 generator replicas + gateway + client.
  • Introduced a shared ServiceDefaults project to centralize Serilog/OpenTelemetry/health checks/CORS wiring.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
README.md Replaced course template README with project/lab-specific documentation and architecture notes.
CreditApplication.ServiceDefaults/Extensions.cs Added shared host defaults (logging, telemetry, health, CORS, service discovery).
CreditApplication.ServiceDefaults/CreditApplication.ServiceDefaults.csproj New shared project packaging required defaults dependencies.
CreditApplication.Generator/* New generator service (Bogus-based), Redis cache-aside service, and minimal API endpoint.
CreditApplication.Gateway/* New gateway service, Ocelot routes config, and custom WeightedRandomLoadBalancer.
CreditApplication.AppHost/* New Aspire AppHost wiring 3 generator replicas + gateway + redis (+ commander in Dev) + client.
CloudDevelopment.sln Added new projects to the solution + extra build configurations.
Client.Wasm/wwwroot/appsettings.json Pointed client configuration to the gateway endpoint.
Client.Wasm/Properties/launchSettings.json Adjusted local run ports and disabled auto-launch browser.
Client.Wasm/Components/StudentCard.razor Updated student/lab info displayed in UI.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +29 to +33
if (id <= 0)
{
logger.LogWarning("Received invalid ID: {Id}", id);
return Results.BadRequest(new { error = "ID must be a positive number" });
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API returns an English validation message ("ID must be a positive number") while the rest of the service/user-facing text is in Russian. Consider making the error message language consistent (or using a standardized error format/message across the project).

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +33
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
if (builder.Environment.IsDevelopment())
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}
else
{
var allowedOrigins = builder.Configuration
.GetSection("Cors:AllowedOrigins")
.Get<string[]>() ?? [];

policy.WithOrigins(allowedOrigins)
.WithMethods("GET")
.WithHeaders("Content-Type", "Authorization");
}
});
});

Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AddGatewayDefaults() already registers a default CORS policy via AddDefaultCors(), but the gateway also calls builder.Services.AddCors() again and redefines the default policy. This double-registration makes the effective policy order-dependent and harder to maintain; consider configuring CORS in a single place (either keep it in ServiceDefaults and remove this block, or remove CORS from AddGatewayDefaults).

Suggested change
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
if (builder.Environment.IsDevelopment())
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}
else
{
var allowedOrigins = builder.Configuration
.GetSection("Cors:AllowedOrigins")
.Get<string[]>() ?? [];
policy.WithOrigins(allowedOrigins)
.WithMethods("GET")
.WithHeaders("Content-Type", "Authorization");
}
});
});

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +57
builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);

var generatorNames = builder.Configuration.GetSection("GeneratorServices").Get<string[]>() ?? [];
var addressOverrides = new List<KeyValuePair<string, string?>>();
for (var i = 0; i < generatorNames.Length; i++)
{
var url = builder.Configuration[$"services:{generatorNames[i]}:http:0"];
if (!string.IsNullOrEmpty(url) && Uri.TryCreate(url, UriKind.Absolute, out var uri))
{
addressOverrides.Add(new($"Routes:0:DownstreamHostAndPorts:{i}:Host", uri.Host));
addressOverrides.Add(new($"Routes:0:DownstreamHostAndPorts:{i}:Port", uri.Port.ToString()));
}
}
if (addressOverrides.Count > 0)
builder.Configuration.AddInMemoryCollection(addressOverrides);

var weights = builder.Configuration
.GetSection("ReplicaWeights")
.Get<Dictionary<string, double>>() ?? new Dictionary<string, double>();

builder.Services
.AddOcelot(builder.Configuration)
.AddCustomLoadBalancer((route, serviceDiscovery) =>
new WeightedRandomLoadBalancer(serviceDiscovery, weights));
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The weights dictionary is keyed by the downstream host:port strings from config, but this file also overrides DownstreamHostAndPorts at runtime from Aspire service-discovery environment variables. After an override, the actual downstream host/port may no longer match the ReplicaWeights keys, causing the load balancer to fall back to the default weight (1.0) and ignore the intended 0.5/0.3/0.2 distribution. Consider keying weights by service name (e.g., generator-1/2/3) or rebuilding/rewriting the weight keys after applying the address overrides.

Copilot uses AI. Check for mistakes.
Comment on lines +121 to +123
.GetSection("CorsAllowedOrigins")
.Get<string[]>() ?? [];

Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AddDefaultCors() reads allowed origins from configuration section CorsAllowedOrigins, but the gateway (and typical appsettings structure) uses Cors:AllowedOrigins. Since nothing else in this PR defines CorsAllowedOrigins, production CORS settings configured under Cors:AllowedOrigins would be ignored by services using AddServiceDefaults(). Consider standardizing on one configuration path (e.g., Cors:AllowedOrigins) and updating ServiceDefaults accordingly.

Suggested change
.GetSection("CorsAllowedOrigins")
.Get<string[]>() ?? [];
.GetSection("Cors:AllowedOrigins")
.Get<string[]>();
if (allowedOrigins is null || allowedOrigins.Length == 0)
{
allowedOrigins = builder.Configuration
.GetSection("CorsAllowedOrigins")
.Get<string[]>() ?? [];
}

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +78
private static void SetStatusDependentFields(Faker faker, CreditApplicationModel app)
{
var isTerminal = _terminalStatuses.Contains(app.Status);

if (isTerminal)
{
app.DecisionDate = faker.Date.BetweenDateOnly(app.ApplicationDate, DateOnly.FromDateTime(DateTime.Today));

if (app.Status == "Одобрена")
{
var maxApproved = app.RequestedAmount;
var minApproved = maxApproved * 0.5m;
app.ApprovedAmount = Math.Round(faker.Random.Decimal(minApproved, maxApproved), 2);
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generator is described as deterministic via UseSeed(id), but DecisionDate is generated using DateTime.Today as an upper bound. This makes the same id potentially produce different results on different days (range length changes), which breaks determinism guarantees. Consider deriving DecisionDate deterministically from ApplicationDate (e.g., add a seeded random offset capped by a fixed max) rather than using the current date.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +10
<UnorderedListItem>Номер <Strong>№1 «Кэширование»</Strong></UnorderedListItem>
<UnorderedListItem>Вариант <Strong>№4 «Кредитная заявка»</Strong></UnorderedListItem>
<UnorderedListItem>Выполнена <Strong>Горшениным Дмитрием 6511</Strong> </UnorderedListItem>
<UnorderedListItem><Link To="https://github.com/dmgorshenin/cloud-development?tab=readme-ov-file">Ссылка на форк</Link></UnorderedListItem>
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This card states that lab work №1 («Кэширование») was completed, but this PR is for lab №2 (API Gateway + load balancing). If the UI is intended to reflect the current lab/PR, update the lab number/title accordingly so the client page matches the submission.

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +16
<Sdk Name="Aspire.AppHost.Sdk" Version="9.0.0" />

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting" Version="9.5.2" />
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.5.2" />
<PackageReference Include="Aspire.Hosting.Redis" Version="9.5.2" />
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aspire.AppHost.Sdk is pinned to 9.0.0 while the Aspire package references are 9.5.2. Mixing SDK and package versions can lead to subtle build/target mismatches; consider aligning the SDK version with the referenced Aspire package version (or vice versa) to keep the toolchain consistent.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

In progress Код в процессе проверки Lab 2 Лабораторная №2. Балансировка нагрузки

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments