mirror of
https://github.com/spacebarchat/server.git
synced 2026-03-30 18:15:41 +00:00
352 lines
13 KiB
C#
352 lines
13 KiB
C#
using System.Collections.Frozen;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
|
|
namespace Spacebar.AdminApi.TestClient.Classes.OpenAPI;
|
|
|
|
public class OpenApiSchema {
|
|
[JsonPropertyName("openapi")]
|
|
public string Version { get; set; } = null!;
|
|
|
|
[JsonPropertyName("info")]
|
|
public OpenApiInfo Info { get; set; } = null!;
|
|
|
|
[JsonPropertyName("externalDocs")]
|
|
public OpenApiExternalDocs? ExternalDocs { get; set; }
|
|
|
|
[JsonPropertyName("paths")]
|
|
public Dictionary<string, OpenApiPath> Paths { get; set; } = null!;
|
|
|
|
[JsonPropertyName("servers")]
|
|
public List<OpenApiServer> Servers { get; set; } = null!;
|
|
|
|
[JsonPropertyName("components")]
|
|
public OpenApiComponents? Components { get; set; } = null!;
|
|
|
|
public class OpenApiComponents {
|
|
[JsonPropertyName("schemas")]
|
|
public Dictionary<string, OpenApiSchemaRef>? Schemas { get; set; } = null!;
|
|
}
|
|
}
|
|
|
|
public class OpenApiServer {
|
|
[JsonPropertyName("url")]
|
|
public string Url { get; set; } = null!;
|
|
|
|
[JsonPropertyName("description")]
|
|
public string? Description { get; set; }
|
|
}
|
|
|
|
public class OpenApiPath {
|
|
public FrozenSet<string> GetAvailableMethods() {
|
|
List<string> methods = new();
|
|
if (Get != null) methods.Add("GET");
|
|
if (Post != null) methods.Add("POST");
|
|
if (Put != null) methods.Add("PUT");
|
|
if (Delete != null) methods.Add("DELETE");
|
|
if (Patch != null) methods.Add("PATCH");
|
|
if (Options != null) methods.Add("OPTIONS");
|
|
return methods.ToFrozenSet();
|
|
}
|
|
|
|
public bool HasMethod(string method) {
|
|
return method.ToLower() switch {
|
|
"get" => Get != null,
|
|
"post" => Post != null,
|
|
"put" => Put != null,
|
|
"delete" => Delete != null,
|
|
"patch" => Patch != null,
|
|
"options" => Options != null,
|
|
_ => false
|
|
};
|
|
}
|
|
|
|
public OpenApiOperation? GetOperation(string method) {
|
|
if (!HasMethod(method)) return null;
|
|
return method.ToLower() switch {
|
|
"get" => Get,
|
|
"post" => Post,
|
|
"put" => Put,
|
|
"delete" => Delete,
|
|
"patch" => Patch,
|
|
"options" => Options,
|
|
_ => null
|
|
};
|
|
}
|
|
|
|
[JsonPropertyName("get")]
|
|
public OpenApiOperation? Get { get; set; }
|
|
|
|
[JsonPropertyName("post")]
|
|
public OpenApiOperation? Post { get; set; }
|
|
|
|
[JsonPropertyName("put")]
|
|
public OpenApiOperation? Put { get; set; }
|
|
|
|
[JsonPropertyName("delete")]
|
|
public OpenApiOperation? Delete { get; set; }
|
|
|
|
[JsonPropertyName("patch")]
|
|
public OpenApiOperation? Patch { get; set; }
|
|
|
|
[JsonPropertyName("options")]
|
|
public OpenApiOperation? Options { get; set; }
|
|
|
|
public class OpenApiOperation {
|
|
[JsonPropertyName("description")]
|
|
public string Description { get; set; } = null!;
|
|
|
|
[JsonPropertyName("parameters")]
|
|
public List<OpenApiParameter>? Parameters { get; set; }
|
|
|
|
[JsonPropertyName("requestBody")]
|
|
public OpenApiRequestBody? RequestBody { get; set; }
|
|
|
|
public class OpenApiParameter {
|
|
[JsonPropertyName("name")]
|
|
public string Name { get; set; } = null!;
|
|
|
|
[JsonPropertyName("in")]
|
|
public string In { get; set; } = null!;
|
|
|
|
[JsonPropertyName("required")]
|
|
public bool Required { get; set; }
|
|
|
|
[JsonPropertyName("schema")]
|
|
public OpenApiSchemaRef Schema { get; set; } = null!;
|
|
|
|
[JsonPropertyName("description")]
|
|
public string? Description { get; set; }
|
|
}
|
|
}
|
|
}
|
|
|
|
public class OpenApiExternalDocs {
|
|
[JsonPropertyName("description")]
|
|
public string Description { get; set; } = null!;
|
|
|
|
[JsonPropertyName("url")]
|
|
public string Url { get; set; } = null!;
|
|
}
|
|
|
|
public class OpenApiInfo {
|
|
[JsonPropertyName("title")]
|
|
public string Title { get; set; } = null!;
|
|
|
|
[JsonPropertyName("version")]
|
|
public string Version { get; set; } = null!;
|
|
|
|
[JsonPropertyName("description")]
|
|
public string Description { get; set; } = null!;
|
|
|
|
[JsonPropertyName("license")]
|
|
public OpenApiLicense License { get; set; } = null!;
|
|
|
|
public class OpenApiLicense {
|
|
[JsonPropertyName("name")]
|
|
public string Name { get; set; } = null!;
|
|
|
|
[JsonPropertyName("url")]
|
|
public string Url { get; set; } = null!;
|
|
}
|
|
}
|
|
|
|
public class OpenApiRequestBody {
|
|
[JsonPropertyName("content")]
|
|
public OpenApiContent Content { get; set; } = null!;
|
|
|
|
[JsonPropertyName("required")]
|
|
public bool Required { get; set; }
|
|
}
|
|
|
|
public class OpenApiContent {
|
|
[JsonPropertyName("application/json")]
|
|
public OpenApiSchemaContainer? ApplicationJson { get; set; }
|
|
|
|
public class OpenApiSchemaContainer {
|
|
[JsonPropertyName("schema")]
|
|
public OpenApiSchemaRef Schema { get; set; } = null!;
|
|
}
|
|
}
|
|
|
|
[JsonConverter(typeof(OpenApiSchemaRefConverter))]
|
|
public class OpenApiSchemaRef {
|
|
public string? Description { get; set; }
|
|
public string? Type { get; set; } = null!;
|
|
public List<string>? Types { get; set; } = null!;
|
|
public string? Ref { get; set; } = null!;
|
|
public Dictionary<string, OpenApiSchemaRef>? Properties { get; set; }
|
|
public List<string>? Required { get; set; }
|
|
public int? MinLength { get; set; }
|
|
public int? MaxLength { get; set; }
|
|
public int? MinItems { get; set; }
|
|
public int? MaxItems { get; set; }
|
|
public object? Constant { get; set; }
|
|
public object? Default { get; set; }
|
|
public bool Nullable { get; set; }
|
|
public List<OpenApiSchemaRef>? AnyOf { get; set; }
|
|
public List<object>? Enum { get; set; }
|
|
public string? Format { get; set; }
|
|
|
|
public OpenApiSchemaRef? GetReferencedSchema(OpenApiSchema schema) {
|
|
if (Ref == null) return null;
|
|
string refKey = Ref.Replace("#/components/schemas/", "");
|
|
if (schema.Components?.Schemas != null && schema.Components.Schemas.TryGetValue(refKey, out var referencedSchema)) {
|
|
return referencedSchema;
|
|
}
|
|
|
|
throw new KeyNotFoundException($"Referenced schema '{refKey}' not found.");
|
|
}
|
|
}
|
|
|
|
public class OpenApiSchemaRefConverter : JsonConverter<OpenApiSchemaRef> {
|
|
public override OpenApiSchemaRef? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
|
|
if (reader.TokenType != JsonTokenType.StartObject) {
|
|
throw new JsonException("Expected StartObject token");
|
|
}
|
|
|
|
using var jsonDoc = JsonDocument.ParseValue(ref reader);
|
|
var jsonObject = jsonDoc.RootElement;
|
|
var schemaRef = new OpenApiSchemaRef();
|
|
|
|
foreach (var property in jsonObject.EnumerateObject()) {
|
|
switch (property.Name) {
|
|
case "type":
|
|
if (property.Value.ValueKind == JsonValueKind.String) {
|
|
schemaRef.Type = property.Value.GetString();
|
|
}
|
|
else if (property.Value.ValueKind == JsonValueKind.Array) {
|
|
var types = new List<string>();
|
|
foreach (var item in property.Value.EnumerateArray()) {
|
|
if (item.ValueKind == JsonValueKind.String) {
|
|
types.Add(item.GetString()!);
|
|
}
|
|
else throw new JsonException("Expected string in type array");
|
|
}
|
|
|
|
schemaRef.Types = types;
|
|
}
|
|
|
|
break;
|
|
case "$ref":
|
|
schemaRef.Ref = property.Value.GetString();
|
|
break;
|
|
case "description":
|
|
schemaRef.Description = property.Value.GetString();
|
|
break;
|
|
case "properties":
|
|
schemaRef.Properties = property.Value.EnumerateObject().ToDictionary(x => x.Name, x => x.Value.Deserialize<OpenApiSchemaRef>(options)!);
|
|
break;
|
|
case "required":
|
|
schemaRef.Required = property.Value.EnumerateArray().Select(item => item.GetString()!).ToList();
|
|
break;
|
|
case "minLength":
|
|
schemaRef.MinLength = property.Value.GetInt32();
|
|
break;
|
|
case "maxLength":
|
|
schemaRef.MaxLength = property.Value.GetInt32();
|
|
break;
|
|
case "minItems":
|
|
schemaRef.MinItems = property.Value.GetInt32();
|
|
break;
|
|
case "maxItems":
|
|
schemaRef.MaxItems = property.Value.GetInt32();
|
|
break;
|
|
case "const":
|
|
if (property.Value.ValueKind == JsonValueKind.String)
|
|
schemaRef.Constant = property.Value.GetString();
|
|
else if (property.Value.ValueKind == JsonValueKind.Number)
|
|
schemaRef.Constant = property.Value.GetInt32();
|
|
else if (property.Value.ValueKind is JsonValueKind.True or JsonValueKind.False)
|
|
schemaRef.Constant = property.Value.GetBoolean();
|
|
else throw new JsonException($"Expected string|int|bool in const, got {property.Value.ValueKind}");
|
|
break;
|
|
case "default":
|
|
if (property.Value.ValueKind == JsonValueKind.String)
|
|
schemaRef.Default = property.Value.GetString();
|
|
else if (property.Value.ValueKind == JsonValueKind.Number)
|
|
schemaRef.Default = property.Value.GetInt32();
|
|
else if (property.Value.ValueKind is JsonValueKind.True or JsonValueKind.False)
|
|
schemaRef.Default = property.Value.GetBoolean();
|
|
else if (property.Value.ValueKind == JsonValueKind.Null)
|
|
schemaRef.Default = null;
|
|
else if (property.Value.ValueKind == JsonValueKind.Array)
|
|
if (property.Value.GetArrayLength() > 0) throw new JsonException("Expected empty array in default");
|
|
else schemaRef.Default = Array.Empty<object>();
|
|
else throw new JsonException($"Expected string|int|bool|null in default, got {property.Value.ValueKind}");
|
|
break;
|
|
case "enum":
|
|
var enumValues = new List<object>();
|
|
foreach (var item in property.Value.EnumerateArray()) {
|
|
if (item.ValueKind == JsonValueKind.String)
|
|
enumValues.Add(item.GetString()!);
|
|
else if (item.ValueKind == JsonValueKind.Number)
|
|
enumValues.Add(item.GetInt32());
|
|
else if (item.ValueKind is JsonValueKind.True or JsonValueKind.False)
|
|
enumValues.Add(item.GetBoolean());
|
|
else if (item.ValueKind == JsonValueKind.Null)
|
|
enumValues.Add(null!);
|
|
else throw new JsonException($"Expected string|int|bool|null in enum, got {item.ValueKind}");
|
|
}
|
|
|
|
schemaRef.Enum = enumValues;
|
|
break;
|
|
case "nullable":
|
|
schemaRef.Nullable = property.Value.GetBoolean();
|
|
break;
|
|
case "anyOf":
|
|
schemaRef.AnyOf = property.Value.EnumerateArray().Select(item => item.Deserialize<OpenApiSchemaRef>(options)!).ToList();
|
|
break;
|
|
case "format":
|
|
schemaRef.Format = property.Value.GetString();
|
|
break;
|
|
case "additionalProperties": //TODO
|
|
case "patternProperties": // Side effect of using JsonValue in typescript
|
|
break;
|
|
default:
|
|
Console.WriteLine($"Got unexpected prop {property.Name} in OpenApiSchemaRef!");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return schemaRef;
|
|
}
|
|
|
|
public override void Write(Utf8JsonWriter writer, OpenApiSchemaRef value, JsonSerializerOptions options) {
|
|
// throw new NotImplementedException("Serialization not implemented for OpenApiSchemaRef");
|
|
writer.WriteStartObject();
|
|
if (value.Type != null) {
|
|
writer.WriteString("type", value.Type);
|
|
}
|
|
else if (value.Types != null) {
|
|
writer.WritePropertyName("type");
|
|
writer.WriteStartArray();
|
|
foreach (var type in value.Types) {
|
|
writer.WriteStringValue(type);
|
|
}
|
|
|
|
writer.WriteEndArray();
|
|
}
|
|
|
|
if (value.Ref != null) {
|
|
writer.WriteString("$ref", value.Ref);
|
|
}
|
|
|
|
if (value.Description != null) {
|
|
writer.WriteString("description", value.Description);
|
|
}
|
|
|
|
if (value.Properties != null) {
|
|
writer.WritePropertyName("properties");
|
|
writer.WriteStartObject();
|
|
foreach (var prop in value.Properties) {
|
|
writer.WritePropertyName(prop.Key);
|
|
JsonSerializer.Serialize(writer, prop.Value, options);
|
|
}
|
|
|
|
writer.WriteEndObject();
|
|
}
|
|
|
|
writer.WriteEndObject();
|
|
}
|
|
} |