Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions backend/Arah.Api.Bff/Journeys/BffJourneyRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@ public static string GetApiPathBase(string journeyName)
{
new("entities", "GET", "Entidades do mapa. Query: territoryId, categoryId, bounds, etc."),
new("entities/paged", "GET", "Entidades paginadas. Query: territoryId, pageNumber, pageSize."),
new("pins", "GET", "Pins do mapa. Query: territoryId, bounds."),
new("pins/paged", "GET", "Pins paginados."),
new("pins", "GET", "Pins do mapa. Query: territoryId, types, assetId, assetTypes, assetSubtypes."),
new("pins/paged", "GET", "Pins paginados. Query: territoryId, types, assetId, assetTypes, assetSubtypes, pageNumber, pageSize."),
new("entities", "POST", "Cria entidade no mapa. Body conforme API."),
new("entities/{entityId}/confirmations", "POST", "Confirma entidade."),
new("entities/{entityId}/relations", "POST", "Cria relação entre entidades.")
Expand Down Expand Up @@ -364,8 +364,8 @@ public static string GetApiPathBase(string journeyName)
{
new("entities", "GET", "Entidades do mapa."),
new("entities/paged", "GET", "Entidades paginadas."),
new("pins", "GET", "Pins do mapa."),
new("pins/paged", "GET", "Pins paginados.")
new("pins", "GET", "Pins do mapa. Query: territoryId, types, assetId, assetTypes, assetSubtypes."),
new("pins/paged", "GET", "Pins paginados. Query: territoryId, types, assetId, assetTypes, assetSubtypes, pageNumber, pageSize.")
},
[Assets] = new List<JourneyEndpoint>
{
Expand Down
4 changes: 3 additions & 1 deletion backend/Arah.Api/Contracts/Map/MapPinResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ public sealed record MapPinResponse(
Guid? MediaId,
Guid? EventId,
Guid? EntityId,
string? Status);
string? Status,
string? AssetType = null,
string? AssetSubtype = null);
12 changes: 9 additions & 3 deletions backend/Arah.Api/Controllers/Map/MapController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ public async Task<ActionResult<IEnumerable<MapPinResponse>>> GetPins(
[FromQuery] string? types,
[FromQuery] Guid? assetId,
[FromQuery] string? assetTypes,
[FromQuery] string? assetSubtypes,
CancellationToken cancellationToken)
{
var resolvedTerritoryId = await ResolveTerritoryIdAsync(territoryId, cancellationToken);
Expand All @@ -226,7 +227,8 @@ public async Task<ActionResult<IEnumerable<MapPinResponse>>> GetPins(
filters,
assetId,
ParseCsv(assetTypes),
cancellationToken);
cancellationToken,
ParseCsv(assetSubtypes));

return Ok(pins.Select(ToResponse));
}
Expand All @@ -243,6 +245,7 @@ public async Task<ActionResult<PagedResponse<MapPinResponse>>> GetPinsPaged(
[FromQuery] string? types,
[FromQuery] Guid? assetId,
[FromQuery] string? assetTypes,
[FromQuery] string? assetSubtypes,
CancellationToken cancellationToken,
[FromQuery] int pageNumber = 1,
[FromQuery] int pageSize = 20)
Expand All @@ -269,7 +272,8 @@ public async Task<ActionResult<PagedResponse<MapPinResponse>>> GetPinsPaged(
assetId,
ParseCsv(assetTypes),
pagination,
cancellationToken);
cancellationToken,
ParseCsv(assetSubtypes));

var response = new PagedResponse<MapPinResponse>(
pagedResult.Items.Select(ToResponse).ToList(),
Expand All @@ -294,7 +298,9 @@ private static MapPinResponse ToResponse(MapPin pin) =>
pin.MediaId,
pin.EventId,
pin.EntityId,
pin.Status);
pin.Status,
pin.AssetType,
pin.AssetSubtype);

private static IReadOnlyList<string>? ParseCsv(string? raw)
{
Expand Down
4 changes: 3 additions & 1 deletion backend/Arah.Application/Models/MapPin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public sealed record MapPin(
Guid? MediaId,
Guid? EventId,
Guid? EntityId,
string? Status);
string? Status,
string? AssetType = null,
string? AssetSubtype = null);
24 changes: 17 additions & 7 deletions backend/Arah.Application/Services/Map/MapPinsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ public async Task<IReadOnlyList<MapPin>> ListPinsAsync(
MapPinFilters filters,
Guid? assetId,
IReadOnlyList<string>? assetTypes,
CancellationToken cancellationToken)
CancellationToken cancellationToken,
IReadOnlyList<string>? assetSubtypes = null)
{
var pins = new List<MapPin>();

Expand All @@ -63,13 +64,16 @@ public async Task<IReadOnlyList<MapPin>> ListPinsAsync(
if (filters.Assets)
{
var assetTypeList = assetId is null ? assetTypes : null;
var assetSubtypeList = assetId is null ? assetSubtypes : null;
var assets = await _assetRepository.ListAsync(
territoryId,
assetId,
assetTypeList,
types: null,
AssetStatus.Active,
null,
cancellationToken);
cancellationToken,
subtypes: assetSubtypeList,
typesOrSubtypes: assetTypeList);

pins.AddRange(await BuildAssetPinsAsync(assets, cancellationToken));
}
Expand Down Expand Up @@ -110,7 +114,8 @@ public async Task<PagedResult<MapPin>> ListPinsPagedAsync(
Guid? assetId,
IReadOnlyList<string>? assetTypes,
PaginationParameters pagination,
CancellationToken cancellationToken)
CancellationToken cancellationToken,
IReadOnlyList<string>? assetSubtypes = null)
{
// Cada tipo usa query paged com a mesma paginação da request (contrato pré-refactor).
// Depois: merge → order by PinType → Skip/Take final. Total = tamanho do merge, não dump completo.
Expand All @@ -130,15 +135,18 @@ public async Task<PagedResult<MapPin>> ListPinsPagedAsync(
if (filters.Assets)
{
var assetTypeList = assetId is null ? assetTypes : null;
var assetSubtypeList = assetId is null ? assetSubtypes : null;
var assets = await _assetRepository.ListPagedAsync(
territoryId,
assetId,
assetTypeList,
types: null,
AssetStatus.Active,
null,
pagination.Skip,
pagination.Take,
cancellationToken);
cancellationToken,
subtypes: assetSubtypeList,
typesOrSubtypes: assetTypeList);

pins.AddRange(await BuildAssetPinsAsync(assets, cancellationToken));
}
Expand Down Expand Up @@ -218,7 +226,9 @@ private async Task<List<MapPin>> BuildAssetPinsAsync(
null,
null,
null,
asset.Status.ToString().ToUpperInvariant());
asset.Status.ToString().ToUpperInvariant(),
asset.Type,
asset.Subtype);
}).ToList();
}

Expand Down
126 changes: 62 additions & 64 deletions backend/Arah.Infrastructure/InMemory/InMemoryAssetRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,18 @@ public Task<IReadOnlyList<TerritoryAsset>> ListAsync(
IReadOnlyCollection<string>? types,
AssetStatus? status,
string? search,
CancellationToken cancellationToken)
CancellationToken cancellationToken,
IReadOnlyCollection<string>? subtypes = null,
IReadOnlyCollection<string>? typesOrSubtypes = null)
{
var query = _dataStore.TerritoryAssets
.Where(asset => asset.TerritoryId == territoryId)
.AsEnumerable();

if (assetId is not null)
{
query = query.Where(asset => asset.Id == assetId.Value);
}

if (types is not null && types.Count > 0)
{
var normalized = types.Select(type => type.Trim().ToLowerInvariant()).ToHashSet();
query = query.Where(asset => normalized.Contains(asset.Type.ToLowerInvariant()));
}

if (status is not null)
{
query = query.Where(asset => asset.Status == status);
}

if (!string.IsNullOrWhiteSpace(search))
{
query = query.Where(asset =>
asset.Name.Contains(search, StringComparison.OrdinalIgnoreCase) ||
(asset.Description is not null && asset.Description.Contains(search, StringComparison.OrdinalIgnoreCase)));
}
var query = ApplyFilters(
_dataStore.TerritoryAssets.Where(asset => asset.TerritoryId == territoryId),
assetId,
types,
status,
search,
subtypes,
typesOrSubtypes);

return Task.FromResult<IReadOnlyList<TerritoryAsset>>(query.ToList());
}
Expand Down Expand Up @@ -94,34 +78,18 @@ public Task<IReadOnlyList<TerritoryAsset>> ListPagedAsync(
string? search,
int skip,
int take,
CancellationToken cancellationToken)
CancellationToken cancellationToken,
IReadOnlyCollection<string>? subtypes = null,
IReadOnlyCollection<string>? typesOrSubtypes = null)
{
var query = _dataStore.TerritoryAssets
.Where(asset => asset.TerritoryId == territoryId)
.AsEnumerable();

if (assetId is not null)
{
query = query.Where(asset => asset.Id == assetId.Value);
}

if (types is not null && types.Count > 0)
{
var normalized = types.Select(type => type.Trim().ToLowerInvariant()).ToHashSet();
query = query.Where(asset => normalized.Contains(asset.Type.ToLowerInvariant()));
}

if (status is not null)
{
query = query.Where(asset => asset.Status == status);
}

if (!string.IsNullOrWhiteSpace(search))
{
query = query.Where(asset =>
asset.Name.Contains(search, StringComparison.OrdinalIgnoreCase) ||
(asset.Description is not null && asset.Description.Contains(search, StringComparison.OrdinalIgnoreCase)));
}
var query = ApplyFilters(
_dataStore.TerritoryAssets.Where(asset => asset.TerritoryId == territoryId),
assetId,
types,
status,
search,
subtypes,
typesOrSubtypes);

var result = query
.OrderByDescending(asset => asset.CreatedAtUtc)
Expand All @@ -138,21 +106,53 @@ public Task<int> CountAsync(
IReadOnlyCollection<string>? types,
AssetStatus? status,
string? search,
CancellationToken cancellationToken)
CancellationToken cancellationToken,
IReadOnlyCollection<string>? subtypes = null,
IReadOnlyCollection<string>? typesOrSubtypes = null)
{
var query = _dataStore.TerritoryAssets
.Where(asset => asset.TerritoryId == territoryId)
.AsEnumerable();
var query = ApplyFilters(
_dataStore.TerritoryAssets.Where(asset => asset.TerritoryId == territoryId),
assetId,
types,
status,
search,
subtypes,
typesOrSubtypes);

const int maxInt32 = int.MaxValue;
var count = query.Count();
return Task.FromResult(count > maxInt32 ? maxInt32 : count);
}

private static IEnumerable<TerritoryAsset> ApplyFilters(
IEnumerable<TerritoryAsset> query,
Guid? assetId,
IReadOnlyCollection<string>? types,
AssetStatus? status,
string? search,
IReadOnlyCollection<string>? subtypes,
IReadOnlyCollection<string>? typesOrSubtypes)
{
var normalizedTypes = TerritoryAssetTypeMatch.NormalizeFilter(types);
var normalizedSubtypes = TerritoryAssetTypeMatch.NormalizeFilter(subtypes);
var normalizedTypesOrSubtypes = TerritoryAssetTypeMatch.NormalizeFilter(typesOrSubtypes);

if (assetId is not null)
{
query = query.Where(asset => asset.Id == assetId.Value);
}

if (types is not null && types.Count > 0)
if (normalizedTypes is not null
|| normalizedSubtypes is not null
|| normalizedTypesOrSubtypes is not null)
{
var normalized = types.Select(type => type.Trim().ToLowerInvariant()).ToHashSet();
query = query.Where(asset => normalized.Contains(asset.Type.ToLowerInvariant()));
query = query.Where(asset =>
TerritoryAssetTypeMatch.Matches(
asset.Type,
asset.Subtype,
normalizedTypes,
normalizedSubtypes,
normalizedTypesOrSubtypes));
}

if (status is not null)
Expand All @@ -167,8 +167,6 @@ public Task<int> CountAsync(
(asset.Description is not null && asset.Description.Contains(search, StringComparison.OrdinalIgnoreCase)));
}

const int maxInt32 = int.MaxValue;
var count = query.Count();
return Task.FromResult(count > maxInt32 ? maxInt32 : count);
return query;
}
}
Loading
Loading