Skip to content

Commit e980c0f

Browse files
committed
Update GraphQL schema and service
1 parent b6e092a commit e980c0f

File tree

3 files changed

+118
-24
lines changed

3 files changed

+118
-24
lines changed

management-portal/dab/schema.graphql

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ type Tenant {
1111
createdAt: String
1212
}
1313

14+
input Tenant_input {
15+
id: ID
16+
tenantId: String
17+
name: String
18+
domain: String
19+
tier: String
20+
status: String
21+
displayName: String
22+
cellId: String
23+
createdAt: String
24+
}
25+
1426
type Cell {
1527
id: ID!
1628
name: String
@@ -21,6 +33,38 @@ type Cell {
2133
capacityUsed: Int!
2234
}
2335

36+
extend type Query {
37+
tenants: [Tenant]
38+
tenant(id: ID!): Tenant
39+
cells: [Cell]
40+
cell(id: ID!): Cell
41+
operations: [Operation]
42+
operation(id: ID!): Operation
43+
}
44+
45+
extend type Mutation {
46+
createTenant(item: Tenant_input!): Tenant
47+
updateTenant(id: ID!, item: Tenant_input!): Tenant
48+
deleteTenant(id: ID!): Boolean
49+
createCell(item: Cell_input!): Cell
50+
updateCell(id: ID!, item: Cell_input!): Cell
51+
deleteCell(id: ID!): Boolean
52+
createOperation(item: Operation_input!): Operation
53+
updateOperation(id: ID!, item: Operation_input!): Operation
54+
deleteOperation(id: ID!): Boolean
55+
}
56+
57+
input Cell_input {
58+
id: ID
59+
cellId: String
60+
name: String
61+
region: String
62+
availabilityZone: String
63+
status: String
64+
capacityTotal: Int
65+
capacityUsed: Int
66+
}
67+
2468
type Operation {
2569
id: ID!
2670
tenantId: String
@@ -31,3 +75,13 @@ type Operation {
3175
timestamp: String
3276
details: String
3377
}
78+
79+
input Operation_input {
80+
id: ID
81+
tenantId: String
82+
type: String
83+
status: String
84+
createdAt: String
85+
timestamp: String
86+
details: String
87+
}

management-portal/docs/plan.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Plan to Resolve DAB GraphQL Query Failures
2+
3+
## Overview
4+
This plan addresses the issue of GraphQL queries failing to retrieve data from Cosmos DB via Data API Builder (DAB) in the management-portal project. It breaks down the diagnosis and resolution into actionable steps.
5+
6+
## Todo List
7+
- [ ] Verify DAB container health and logs for crash loops or startup errors using Azure CLI commands.
8+
- [ ] Compare GraphQL schema (schema.graphql) with Cosmos DB container structures for mismatches.
9+
- [ ] Test sample GraphQL queries directly against DAB endpoint to reproduce retrieval failures.
10+
- [ ] Analyze error handling in GraphQLDataService.cs and add logging for detailed failure insights.
11+
- [ ] Review Bicep deployment (management-portal.bicep) for configuration issues like ports or secrets.
12+
- [ ] Implement fixes, such as updating schema or deployment settings, and redeploy.
13+
- [ ] Validate end-to-end data retrieval in the portal UI after fixes.
14+
15+
## Next Steps
16+
Once approved, switch to debug mode to execute these steps.

management-portal/src/Portal/Services/GraphQLDataService.cs

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
using System.Text;
33
using System.Text.Json;
44
using Stamps.ManagementPortal.Models;
5+
using Microsoft.Extensions.Logging;
56

67
namespace Stamps.ManagementPortal.Services;
78

8-
public class GraphQLDataService(IHttpClientFactory httpClientFactory, IConfiguration config) : IDataService
9+
public class GraphQLDataService(IHttpClientFactory httpClientFactory, IConfiguration config, ILogger<GraphQLDataService> logger) : IDataService
910
{
1011
private readonly IHttpClientFactory _httpClientFactory = httpClientFactory;
1112
private readonly IConfiguration _config = config;
13+
private readonly ILogger<GraphQLDataService> _logger = logger;
1214

1315
private HttpClient Client => _httpClientFactory.CreateClient("GraphQL");
1416

@@ -110,49 +112,71 @@ public async Task DeleteOperationAsync(string id, string partitionKey, Cancellat
110112

111113
private async Task<IReadOnlyList<T>> QueryAsync<T>(string query, string rootField, CancellationToken ct)
112114
{
115+
_logger.LogInformation("Executing GraphQL query: {Query}", query);
113116
var payload = new { query };
114117
using var req = new HttpRequestMessage(HttpMethod.Post, "")
115118
{
116119
Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json")
117120
};
118-
using var res = await Client.SendAsync(req, ct);
119-
res.EnsureSuccessStatusCode();
120-
using var stream = await res.Content.ReadAsStreamAsync(ct);
121-
using var doc = await JsonDocument.ParseAsync(stream, cancellationToken: ct);
122-
// If GraphQL returned errors, surface them clearly
123-
if (doc.RootElement.TryGetProperty("errors", out var errs) && errs.ValueKind == JsonValueKind.Array && errs.GetArrayLength() > 0)
121+
try
124122
{
125-
throw new HttpRequestException($"GraphQL errors: {errs}");
123+
using var res = await Client.SendAsync(req, ct);
124+
res.EnsureSuccessStatusCode();
125+
var responseContent = await res.Content.ReadAsStringAsync(ct);
126+
_logger.LogDebug("GraphQL response: {Response}", responseContent);
127+
using var doc = JsonDocument.Parse(responseContent);
128+
// If GraphQL returned errors, surface them clearly
129+
if (doc.RootElement.TryGetProperty("errors", out var errs) && errs.ValueKind == JsonValueKind.Array && errs.GetArrayLength() > 0)
130+
{
131+
_logger.LogError("GraphQL errors: {Errors}", errs.ToString());
132+
throw new HttpRequestException($"GraphQL errors: {errs}");
133+
}
134+
var data = doc.RootElement.GetProperty("data").GetProperty(rootField);
135+
var list = new List<T>();
136+
foreach (var el in data.EnumerateArray())
137+
{
138+
var obj = el.Deserialize<T>(new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
139+
if (obj is not null) list.Add(obj);
140+
}
141+
return list;
126142
}
127-
var data = doc.RootElement.GetProperty("data").GetProperty(rootField);
128-
var list = new List<T>();
129-
foreach (var el in data.EnumerateArray())
143+
catch (Exception ex)
130144
{
131-
var obj = el.Deserialize<T>(new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
132-
if (obj is not null) list.Add(obj);
145+
_logger.LogError(ex, "Error executing GraphQL query: {Query}", query);
146+
throw;
133147
}
134-
return list;
135148
}
136149

137150
private async Task<T> MutationAsync<T>(string query, object variables, string rootField, CancellationToken ct)
138151
{
152+
_logger.LogInformation("Executing GraphQL mutation: {Query} with variables {Variables}", query, JsonSerializer.Serialize(variables));
139153
var payload = new { query, variables };
140154
using var req = new HttpRequestMessage(HttpMethod.Post, "")
141155
{
142156
Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json")
143157
};
144-
using var res = await Client.SendAsync(req, ct);
145-
res.EnsureSuccessStatusCode();
146-
using var stream = await res.Content.ReadAsStreamAsync(ct);
147-
using var doc = await JsonDocument.ParseAsync(stream, cancellationToken: ct);
148-
if (doc.RootElement.TryGetProperty("errors", out var errs) && errs.ValueKind == JsonValueKind.Array && errs.GetArrayLength() > 0)
158+
try
159+
{
160+
using var res = await Client.SendAsync(req, ct);
161+
res.EnsureSuccessStatusCode();
162+
var responseContent = await res.Content.ReadAsStringAsync(ct);
163+
_logger.LogDebug("GraphQL mutation response: {Response}", responseContent);
164+
using var doc = JsonDocument.Parse(responseContent);
165+
if (doc.RootElement.TryGetProperty("errors", out var errs) && errs.ValueKind == JsonValueKind.Array && errs.GetArrayLength() > 0)
166+
{
167+
_logger.LogError("GraphQL mutation errors: {Errors}", errs.ToString());
168+
throw new HttpRequestException($"GraphQL errors: {errs}");
169+
}
170+
var data = doc.RootElement.GetProperty("data").GetProperty(rootField);
171+
if (typeof(T) == typeof(object)) return default!;
172+
var result = data.Deserialize<T>(new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
173+
return result!;
174+
}
175+
catch (Exception ex)
149176
{
150-
throw new HttpRequestException($"GraphQL errors: {errs}");
177+
_logger.LogError(ex, "Error executing GraphQL mutation: {Query}", query);
178+
throw;
151179
}
152-
var data = doc.RootElement.GetProperty("data").GetProperty(rootField);
153-
if (typeof(T) == typeof(object)) return default!;
154-
var result = data.Deserialize<T>(new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
155-
return result!;
156180
}
157181

158182
public async Task<bool> ReserveDomainAsync(string domain, string ownerTenantId, CancellationToken ct = default)

0 commit comments

Comments
 (0)