Skip to content

Commit b3ee6e9

Browse files
committed
fix: get LookupKey to show in Swagger schemas
1 parent 5357144 commit b3ee6e9

File tree

18 files changed

+437
-310
lines changed

18 files changed

+437
-310
lines changed

.editorconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ resharper_place_expr_property_on_single_line = true
146146
resharper_place_simple_initializer_on_single_line = false
147147
resharper_trailing_comma_in_multiline_lists = true
148148
resharper_wrap_array_initializer_style = chop_if_long
149+
resharper_wrap_before_primary_constructor_declaration_rpar = true
149150
###############################
150151
# VB Coding Conventions #
151152
###############################
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using Newtonsoft.Json.Serialization;
2+
3+
namespace Intersect.Framework.Core.Serialization;
4+
5+
public sealed class AggregateContractResolver(
6+
IContractResolver fallbackContractResolver,
7+
params IRestrictedContractResolver[] contractResolvers
8+
)
9+
: IContractResolver
10+
{
11+
private readonly IContractResolver _fallbackContractResolver = fallbackContractResolver;
12+
private readonly IRestrictedContractResolver[] _contractResolvers = contractResolvers;
13+
14+
public JsonContract ResolveContract(Type type)
15+
{
16+
foreach (var contractResolver in _contractResolvers)
17+
{
18+
if (!contractResolver.SupportsType(type))
19+
{
20+
continue;
21+
}
22+
23+
return contractResolver.ResolveContract(type);
24+
}
25+
26+
return _fallbackContractResolver.ResolveContract(type);
27+
}
28+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using Newtonsoft.Json.Serialization;
2+
3+
namespace Intersect.Framework.Core.Serialization;
4+
5+
public interface IRestrictedContractResolver : IContractResolver
6+
{
7+
bool SupportsType(Type type);
8+
}

Intersect.Server/Web/Net7/ApiService.cs

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -168,33 +168,11 @@ internal partial class ApiService : ApplicationService<ServerContext, IApiServic
168168
);
169169
sgo.AddDocumentFilterInstance(documentFilterTokenRequest);
170170
sgo.AddSchemaFilterInstance(documentFilterTokenRequest.CreateSchemaFilter());
171-
sgo.AddSchemaFilterInstance(new GameObjectTypeSchemaFilter());
171+
sgo.SchemaFilter<GameObjectTypeSchemaFilter>();
172+
sgo.SchemaFilter<LookupKeySchemaFilter>();
172173
sgo.EnableAnnotations(enableAnnotationsForInheritance: true, enableAnnotationsForPolymorphism: true);
173174
sgo.UseOneOfForPolymorphism();
174175
sgo.UseAllOfForInheritance();
175-
sgo.MapType<LookupKey>(
176-
() => new OpenApiSchema
177-
{
178-
Type = "string",
179-
Description = $"An id or a name",
180-
// TODO: Multiple examples? It doesn't look like the C# API exposes this but it exists according to the Swagger documentation
181-
// Example = new OpenApiString($"test or {new Guid("01234567-89ab-cdef-0123-456789abcdef")}"),
182-
Example = new OpenApiString("test"),
183-
OneOf =
184-
[
185-
new OpenApiSchema
186-
{
187-
Type = "string", Format = "uuid",
188-
},
189-
new OpenApiSchema
190-
{
191-
Type = "string",
192-
Format = "username",
193-
Pattern = Strings.Regex.Username,
194-
},
195-
],
196-
}
197-
);
198176
});
199177
builder.Services.AddSwaggerGenNewtonsoftSupport();
200178

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
using System.Net;
22
using System.Security.Claims;
33
using Intersect.Security.Claims;
4+
using Intersect.Server.Web.RestApi.Types;
45
using Microsoft.AspNetCore.Mvc;
56
using IntersectUser = Intersect.Server.Database.PlayerData.User;
67

78
namespace Intersect.Server.Web;
89

910
public abstract class IntersectController : Controller
1011
{
11-
public const int PAGE_SIZE_MAX = 100;
12-
13-
public const int PAGE_SIZE_MIN = 5;
14-
1512
private IntersectUser? _intersectUser;
1613

17-
public IntersectUser? IntersectUser
14+
protected IntersectUser? IntersectUser
1815
{
1916
get
2017
{
@@ -37,32 +34,43 @@ public IntersectUser? IntersectUser
3734
}
3835

3936
[NonAction]
40-
public virtual ObjectResult StatusCode(HttpStatusCode statusCode, object? data = default)
41-
{
42-
return StatusCode((int)statusCode, data);
43-
}
37+
protected virtual IActionResult StatusCodeMessage(HttpStatusCode statusCode, string message) => StatusCode(
38+
(int)statusCode,
39+
new StatusMessageResponseBody(message)
40+
);
4441

4542
[NonAction]
46-
public virtual ObjectResult Forbidden(object? data = default)
47-
{
48-
return StatusCode(HttpStatusCode.Forbidden, data);
49-
}
43+
protected virtual IActionResult Forbidden(string message) => StatusCodeMessage(HttpStatusCode.Forbidden, message);
5044

5145
[NonAction]
52-
public virtual ObjectResult Gone()
53-
{
54-
return StatusCode(HttpStatusCode.Gone);
55-
}
46+
protected virtual IActionResult InternalServerError(string message) =>
47+
StatusCodeMessage(HttpStatusCode.InternalServerError, message);
5648

5749
[NonAction]
58-
public virtual ObjectResult InternalServerError(object? data = default)
59-
{
60-
return StatusCode(HttpStatusCode.InternalServerError, data);
61-
}
50+
protected virtual IActionResult NotImplemented(string message) =>
51+
StatusCodeMessage(HttpStatusCode.NotImplemented, message);
6252

6353
[NonAction]
64-
public virtual ObjectResult NotImplemented(object? data = default)
65-
{
66-
return StatusCode(HttpStatusCode.NotImplemented, data);
67-
}
54+
protected virtual IActionResult BadRequest(string message) => StatusCodeMessage(HttpStatusCode.BadRequest, message);
55+
56+
[NonAction]
57+
protected virtual IActionResult NotFound(string message) => StatusCodeMessage(HttpStatusCode.NotFound, message);
58+
59+
[NonAction]
60+
protected virtual ObjectResult StatusCode(HttpStatusCode statusCode, object? data = default) =>
61+
StatusCode((int)statusCode, data);
62+
63+
[NonAction]
64+
protected virtual ObjectResult Forbidden(object? data = default) => StatusCode(HttpStatusCode.Forbidden, data);
65+
66+
[NonAction]
67+
protected virtual ObjectResult Gone() => StatusCode(HttpStatusCode.Gone);
68+
69+
[NonAction]
70+
protected virtual ObjectResult InternalServerError(object? data = default) =>
71+
StatusCode(HttpStatusCode.InternalServerError, data);
72+
73+
[NonAction]
74+
protected virtual ObjectResult NotImplemented(object? data = default) =>
75+
StatusCode(HttpStatusCode.NotImplemented, data);
6876
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Intersect.Framework.Reflection;
2+
using Intersect.Server.Web.RestApi.Types;
3+
using Microsoft.OpenApi.Models;
4+
using Swashbuckle.AspNetCore.SwaggerGen;
5+
6+
namespace Intersect.Server.Web.Swagger.Filters;
7+
8+
public sealed class DataPageSchemaFilter : ISchemaFilter
9+
{
10+
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
11+
{
12+
var contextType = context.Type;
13+
if (!typeof(DataPage<>).ExtendedBy(contextType))
14+
{
15+
return;
16+
}
17+
18+
var generator = context.SchemaGenerator;
19+
var repository = context.SchemaRepository;
20+
21+
if (!repository.TryLookupByType(typeof(IDataPage<>), out var genericTypeDefinitionSchema))
22+
{
23+
genericTypeDefinitionSchema = generator.GenerateSchema(typeof(IDataPage<>), repository);
24+
}
25+
26+
if (contextType.IsGenericType)
27+
{
28+
schema.Properties = new Dictionary<string, OpenApiSchema>();
29+
schema.AllOf =
30+
[
31+
new OpenApiSchema
32+
{
33+
Reference = genericTypeDefinitionSchema.Reference ??
34+
throw new InvalidOperationException(
35+
$"Missing schema reference for {typeof(DataPage<>).Name}"
36+
),
37+
},
38+
];
39+
return;
40+
}
41+
42+
contextType.ToString();
43+
}
44+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Microsoft.OpenApi.Models;
2+
using Swashbuckle.AspNetCore.SwaggerGen;
3+
4+
namespace Intersect.Server.Web.Swagger.Filters;
5+
6+
public sealed class EnumSchemaFilter : ISchemaFilter
7+
{
8+
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
9+
{
10+
if (!context.Type.IsEnum)
11+
{
12+
return;
13+
}
14+
}
15+
}
Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
using Intersect.Server.Localization;
12
using Intersect.Server.Web.RestApi.Payloads;
3+
using Microsoft.OpenApi.Any;
24
using Microsoft.OpenApi.Models;
35
using Swashbuckle.AspNetCore.SwaggerGen;
46

@@ -13,33 +15,54 @@ public void Apply(OpenApiSchema schema, SchemaFilterContext context)
1315
return;
1416
}
1517

16-
var foundId = false;
17-
var foundName = false;
18-
foreach (var oneOfSchema in schema.OneOf)
19-
{
20-
if (oneOfSchema.Type != "string")
21-
{
22-
continue;
23-
}
18+
schema.Type = "string";
19+
schema.Description = "An id or a name";
2420

25-
if (oneOfSchema.Format == "uuid")
21+
// TODO: Multiple examples? It doesn't look like the C# API exposes this but it exists according to the Swagger documentation
22+
// Example = new OpenApiString($"test or {new Guid("01234567-89ab-cdef-0123-456789abcdef")}"),
23+
schema.Example = new OpenApiString("test");
24+
25+
schema.OneOf =
26+
[
27+
new OpenApiSchema
2628
{
27-
foundId = true;
28-
}
29-
else if (string.IsNullOrWhiteSpace(oneOfSchema.Format))
29+
Type = "string", Format = "uuid",
30+
},
31+
new OpenApiSchema
3032
{
31-
foundName = true;
32-
}
33-
}
33+
Type = "string", Format = "username", Pattern = Strings.Regex.Username,
34+
},
35+
];
3436

35-
if (!foundId)
36-
{
37-
schema.OneOf.Add(new OpenApiSchema { Type = "string", Format = "uuid" });
38-
}
37+
schema.Properties = new Dictionary<string, OpenApiSchema>();
3938

40-
if (!foundName)
41-
{
42-
schema.OneOf.Add(new OpenApiSchema { Type = "string", Format = "username"});
43-
}
39+
// var foundId = false;
40+
// var foundName = false;
41+
// foreach (var oneOfSchema in schema.OneOf)
42+
// {
43+
// if (oneOfSchema.Type != "string")
44+
// {
45+
// continue;
46+
// }
47+
//
48+
// if (oneOfSchema.Format == "uuid")
49+
// {
50+
// foundId = true;
51+
// }
52+
// else if (string.IsNullOrWhiteSpace(oneOfSchema.Format))
53+
// {
54+
// foundName = true;
55+
// }
56+
// }
57+
//
58+
// if (!foundId)
59+
// {
60+
// schema.OneOf.Add(new OpenApiSchema { Type = "string", Format = "uuid" });
61+
// }
62+
//
63+
// if (!foundName)
64+
// {
65+
// schema.OneOf.Add(new OpenApiSchema { Type = "string", Format = "username"});
66+
// }
4467
}
4568
}
Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
namespace Intersect.Server.Web.RestApi.Payloads
1+
namespace Intersect.Server.Web.RestApi.Payloads;
2+
3+
public struct PagingInfo()
24
{
3-
public partial class PagingInfo
4-
{
5-
public int Page { get; set; }
5+
public const int MaxPageSize = 100;
6+
public const int MinPageSize = 5;
7+
public const int DefaultPageSize = 10;
8+
9+
public int Page { get; set; } = 0;
610

7-
public int Count { get; set; } = 10;
8-
}
9-
}
11+
public int Count { get; set; } = 10;
12+
}

Intersect.Server/Web/RestApi/Routes/V1/GameObjectController.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,17 @@ public IActionResult List(GameObjectType gameObjectType, [FromQuery] PagingInfo
3737
.Take(pageInfo.Count)
3838
.ToArray();
3939

40+
var total = gameObjectType == GameObjectType.Event
41+
? lookup.Count(obj => ((EventBase)obj.Value).CommonEvent)
42+
: lookup.Count;
4043
return Ok(
41-
new DataPage<IDatabaseObject>
42-
{
43-
Total = gameObjectType == GameObjectType.Event
44-
? lookup.Count(obj => ((EventBase)obj.Value).CommonEvent)
45-
: lookup.Count,
46-
Page = pageInfo.Page,
47-
Count = entries.Length,
48-
Values = entries,
49-
}
44+
new DataPage<IDatabaseObject>(
45+
Total: total,
46+
Page: pageInfo.Page,
47+
PageSize: pageInfo.Count,
48+
Count: entries.Length,
49+
Values: entries
50+
)
5051
);
5152
}
5253

0 commit comments

Comments
 (0)