Skip to content

Commit 6022115

Browse files
committed
Add support for JSON file input using body.treatAsFile property
1 parent 6cec9d8 commit 6022115

17 files changed

Lines changed: 211 additions & 55 deletions

src/Dibix.Sdk.CodeGeneration/Lookup/ActionTargetDefinitionResolver.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,17 @@ protected ActionTargetDefinitionResolver(ISchemaRegistry schemaRegistry, ILogger
2222
#endregion
2323

2424
#region Abstract Methods
25-
public abstract bool TryResolve<T>(string targetName, SourceLocation sourceLocation, IReadOnlyDictionary<string, ExplicitParameter> explicitParameters, IReadOnlyDictionary<string, PathParameter> readOnlyDictionary, ICollection<string> bodyParameters, ActionRequestBody requestBody, Func<ActionTarget, T> actionTargetDefinitionFactory, out T actionTargetDefinition) where T : ActionTargetDefinition;
25+
public bool TryResolve<T>(string targetName, SourceLocation sourceLocation, IReadOnlyDictionary<string, ExplicitParameter> explicitParameters, IReadOnlyDictionary<string, PathParameter> readOnlyDictionary, ICollection<string> bodyParameters, ActionRequestBody requestBody, Func<ActionTarget, T> actionTargetDefinitionFactory, out T actionTargetDefinition) where T : ActionTargetDefinition
26+
{
27+
bool result = TryResolveTarget(targetName, sourceLocation, explicitParameters, readOnlyDictionary, bodyParameters, requestBody, actionTargetDefinitionFactory, out actionTargetDefinition);
28+
if (result)
29+
{
30+
CollectAdditionalParameters(requestBody, actionTargetDefinition);
31+
}
32+
return result;
33+
}
34+
35+
protected abstract bool TryResolveTarget<T>(string targetName, SourceLocation sourceLocation, IReadOnlyDictionary<string, ExplicitParameter> explicitParameters, IReadOnlyDictionary<string, PathParameter> readOnlyDictionary, ICollection<string> bodyParameters, ActionRequestBody requestBody, Func<ActionTarget, T> actionTargetDefinitionFactory, out T actionTargetDefinition) where T : ActionTargetDefinition;
2636
#endregion
2737

2838
#region Protected Methods
@@ -209,6 +219,29 @@ private ActionParameter CreateActionParameter(string name, TypeReference type, b
209219
bool isRequired = this.IsParameterRequired(type, location, defaultValue);
210220
return new ActionParameter(apiParameterName, internalParameterName, type, location, isRequired, isOutput, defaultValue, description, source, sourceLocation);
211221
}
222+
223+
private static void CollectAdditionalParameters(ActionRequestBody requestBody, ActionTargetDefinition actionTargetDefinition)
224+
{
225+
if (requestBody == null)
226+
return;
227+
228+
if (!requestBody.IsStream(out SourceLocation isStreamLocation))
229+
return;
230+
231+
if (requestBody.MediaType == HttpMediaType.Binary && actionTargetDefinition.Parameters.All(x => x.ApiParameterName != SpecialHttpParameterName.MediaType))
232+
{
233+
PrimitiveTypeReference mediaTypeParameterType = new PrimitiveTypeReference(PrimitiveType.String, isNullable: true, isEnumerable: false);
234+
ActionParameter mediaTypeParameter = new ActionParameter(SpecialHttpParameterName.MediaType, SpecialHttpParameterName.MediaType, mediaTypeParameterType, ActionParameterLocation.Body, isRequired: false, isOutput: false, defaultValue: new NullValueReference(mediaTypeParameterType, default), description: null, source: null, isStreamLocation);
235+
actionTargetDefinition.Parameters.Add(mediaTypeParameter);
236+
}
237+
238+
if (actionTargetDefinition.Parameters.All(x => x.ApiParameterName != SpecialHttpParameterName.FileName))
239+
{
240+
PrimitiveTypeReference fileNameParameterType = new PrimitiveTypeReference(PrimitiveType.String, isNullable: true, isEnumerable: false);
241+
ActionParameter fileNameParameter = new ActionParameter(SpecialHttpParameterName.FileName, SpecialHttpParameterName.FileName, fileNameParameterType, ActionParameterLocation.Body, isRequired: false, isOutput: false, defaultValue: new NullValueReference(fileNameParameterType, default), description: null, source: null, isStreamLocation);
242+
actionTargetDefinition.Parameters.Add(fileNameParameter);
243+
}
244+
}
212245
#endregion
213246
}
214247
}

src/Dibix.Sdk.CodeGeneration/Lookup/ExternalReflectionActionTargetDefinitionResolver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public ExternalReflectionActionTargetDefinitionResolver(ISchemaRegistry schemaRe
1515
this._lockEntryManager = lockEntryManager;
1616
}
1717

18-
public override bool TryResolve<T>(string targetName, SourceLocation sourceLocation, IReadOnlyDictionary<string, ExplicitParameter> explicitParameters, IReadOnlyDictionary<string, PathParameter> pathParameters, ICollection<string> bodyParameters, ActionRequestBody requestBody, Func<ActionTarget, T> actionTargetDefinitionFactory, out T actionTargetDefinition)
18+
protected override bool TryResolveTarget<T>(string targetName, SourceLocation sourceLocation, IReadOnlyDictionary<string, ExplicitParameter> explicitParameters, IReadOnlyDictionary<string, PathParameter> pathParameters, ICollection<string> bodyParameters, ActionRequestBody requestBody, Func<ActionTarget, T> actionTargetDefinitionFactory, out T actionTargetDefinition)
1919
{
2020
string[] parts = targetName.Split(',');
2121
if (parts.Length != 2)

src/Dibix.Sdk.CodeGeneration/Lookup/SqlStatementDefinitionActionTargetDefinitionResolver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ string productName
3535
#endregion
3636

3737
#region Overrides
38-
public override bool TryResolve<T>(string targetName, SourceLocation sourceLocation, IReadOnlyDictionary<string, ExplicitParameter> explicitParameters, IReadOnlyDictionary<string, PathParameter> pathParameters, ICollection<string> bodyParameters, ActionRequestBody requestBody, Func<ActionTarget, T> actionTargetDefinitionFactory, out T actionTargetDefinition)
38+
protected override bool TryResolveTarget<T>(string targetName, SourceLocation sourceLocation, IReadOnlyDictionary<string, ExplicitParameter> explicitParameters, IReadOnlyDictionary<string, PathParameter> pathParameters, ICollection<string> bodyParameters, ActionRequestBody requestBody, Func<ActionTarget, T> actionTargetDefinitionFactory, out T actionTargetDefinition)
3939
{
4040
if (!TryGetStatementDefinitionByProbing(targetName, out SqlStatementDefinition statementDefinition))
4141
{

src/Dibix.Sdk.CodeGeneration/Mapping/PrimitiveTypeMap.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ internal static class PrimitiveTypeMap
7070
, [PrimitiveType.Decimal] = () => new OpenApiSchema { Type = "number", Format = "double" }
7171
, [PrimitiveType.Binary] = () => new OpenApiSchema { Type = "string", Format = "byte" }
7272
, [PrimitiveType.Stream] = () => new OpenApiSchema { Type = "string", Format = "binary" }
73-
, [PrimitiveType.Date] = () => new OpenApiSchema { Type = "string", Format = "date" }
74-
, [PrimitiveType.Time] = () => new OpenApiSchema { Type = "string", Format = "time" }
73+
, [PrimitiveType.Date] = () => new OpenApiSchema { Type = "string", Format = "date" }
74+
, [PrimitiveType.Time] = () => new OpenApiSchema { Type = "string", Format = "time" }
7575
, [PrimitiveType.DateTime] = () => new OpenApiSchema { Type = "string", Format = "date-time" }
7676
, [PrimitiveType.DateTimeOffset] = () => new OpenApiSchema { Type = "string", Format = "date-time" }
7777
, [PrimitiveType.String] = () => new OpenApiSchema { Type = "string" }

src/Dibix.Sdk.CodeGeneration/Model/ActionRequestBody.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ public sealed class ActionRequestBody
55
public string MediaType { get; } = HttpMediaType.Json;
66
public TypeReference Contract { get; }
77
public string Binder { get; }
8+
public SourceLocation? TreatAsFile { get; }
89
public SourceLocation Location { get; }
910

1011
public ActionRequestBody(TypeReference contract, SourceLocation location)
@@ -16,9 +17,10 @@ public ActionRequestBody(TypeReference contract, SourceLocation location, string
1617
{
1718
MediaType = mediaType;
1819
}
19-
public ActionRequestBody(TypeReference contract, SourceLocation location, string mediaType, string binder) : this(contract, location, mediaType)
20+
public ActionRequestBody(TypeReference contract, SourceLocation location, string mediaType, string binder, SourceLocation? treatAsFile) : this(contract, location, mediaType)
2021
{
2122
Binder = binder;
23+
TreatAsFile = treatAsFile;
2224
}
2325
}
2426
}

src/Dibix.Sdk.CodeGeneration/Model/SchemaExtensions.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,22 @@ bool MatchesOutputFilter(SchemaDefinition schema)
2828
IEnumerable<SchemaDefinition> schemas = model.Schemas.Where(MatchesOutputFilter);
2929
return schemas;
3030
}
31+
32+
public static bool IsStream(this ActionRequestBody requestBody, out SourceLocation location)
33+
{
34+
if (requestBody.Contract != null && requestBody.Contract.IsStream(out location))
35+
{
36+
return true;
37+
}
38+
39+
if (requestBody.TreatAsFile != null)
40+
{
41+
location = requestBody.TreatAsFile.Value;
42+
return true;
43+
}
44+
45+
location = default;
46+
return false;
47+
}
3148
}
3249
}

src/Dibix.Sdk.CodeGeneration/OpenApi/OpenApiGenerator.cs

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -176,19 +176,20 @@ private static OpenApiParameter AppendParameter(OpenApiDocument document, OpenAp
176176
In = parameterLocation,
177177
Required = actionParameter.IsRequired,
178178
Name = actionParameter.ApiParameterName,
179-
Schema = CreateSchema(document, parameterType, isEnumerable, actionParameter.DefaultValue, rootNamespace, supportOpenApiNullableReferenceTypes, schemaRegistry, logger)
179+
Schema = CreateSchema(document, parameterType, isEnumerable, actionParameter.DefaultValue, rootNamespace, supportOpenApiNullableReferenceTypes, treatAsFile: false, schemaRegistry, logger)
180180
};
181181
operation.Parameters.Add(apiParameter);
182182
return apiParameter;
183183
}
184184

185185
private static void AppendBody(OpenApiDocument document, OpenApiOperation operation, ActionDefinition action, string rootNamespace, bool supportOpenApiNullableReferenceTypes, ISchemaRegistry schemaRegistry, ILogger logger)
186186
{
187-
if (action.RequestBody == null)
187+
ActionRequestBody requestBody = action.RequestBody;
188+
if (requestBody == null)
188189
return;
189190

190191
OpenApiRequestBody body = new OpenApiRequestBody { Required = true };
191-
AppendContent(document, body.Content, action.RequestBody.MediaType, action.RequestBody.Contract, rootNamespace, supportOpenApiNullableReferenceTypes, schemaRegistry, logger);
192+
AppendContent(document, body.Content, requestBody.MediaType, requestBody.Contract, rootNamespace, supportOpenApiNullableReferenceTypes, requestBody.TreatAsFile != null, schemaRegistry, logger);
192193
operation.RequestBody = body;
193194
}
194195

@@ -199,7 +200,7 @@ private static void AppendResponses(OpenApiDocument document, OpenApiOperation o
199200
OpenApiResponse apiResponse = new OpenApiResponse();
200201

201202
if (actionResponse.ResultType != null)
202-
AppendContent(document, apiResponse.Content, actionResponse.MediaType, actionResponse.ResultType, rootNamespace, supportOpenApiNullableReferenceTypes, schemaRegistry, logger);
203+
AppendContent(document, apiResponse.Content, actionResponse.MediaType, actionResponse.ResultType, rootNamespace, supportOpenApiNullableReferenceTypes, treatAsFile: false, schemaRegistry, logger);
203204

204205
StringBuilder sb = new StringBuilder(actionResponse.Description);
205206
if (actionResponse.Errors.Any())
@@ -230,9 +231,10 @@ private static void AppendResponses(OpenApiDocument document, OpenApiOperation o
230231
}
231232
}
232233

233-
private static void AppendContent(OpenApiDocument document, IDictionary<string, OpenApiMediaType> target, string mediaType, TypeReference typeReference, string rootNamespace, bool supportOpenApiNullableReferenceTypes, ISchemaRegistry schemaRegistry, ILogger logger)
234+
private static void AppendContent(OpenApiDocument document, IDictionary<string, OpenApiMediaType> target, string mediaType, TypeReference typeReference, string rootNamespace, bool supportOpenApiNullableReferenceTypes, bool treatAsFile, ISchemaRegistry schemaRegistry, ILogger logger)
234235
{
235-
OpenApiMediaType content = new OpenApiMediaType { Schema = CreateSchema(document, typeReference, rootNamespace, supportOpenApiNullableReferenceTypes, schemaRegistry, logger) };
236+
OpenApiSchema schema = CreateSchema(document, typeReference, rootNamespace, supportOpenApiNullableReferenceTypes, treatAsFile, schemaRegistry, logger);
237+
OpenApiMediaType content = new OpenApiMediaType { Schema = schema };
236238
target.Add(mediaType, content);
237239
}
238240

@@ -300,10 +302,10 @@ private static void AppendSecuritySchemes(OpenApiDocument document, IEnumerable<
300302
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null)
301303
};
302304

303-
private static OpenApiSchema CreateSchema(OpenApiDocument document, TypeReference typeReference, string rootNamespace, bool supportOpenApiNullableReferenceTypes, ISchemaRegistry schemaRegistry, ILogger logger) => CreateSchema(document, typeReference, typeReference.IsEnumerable, defaultValue: null, rootNamespace: rootNamespace, supportOpenApiNullableReferenceTypes: supportOpenApiNullableReferenceTypes, schemaRegistry: schemaRegistry, logger: logger);
304-
private static OpenApiSchema CreateSchema(OpenApiDocument document, TypeReference typeReference, bool isEnumerable, ValueReference defaultValue, string rootNamespace, bool supportOpenApiNullableReferenceTypes, ISchemaRegistry schemaRegistry, ILogger logger)
305+
private static OpenApiSchema CreateSchema(OpenApiDocument document, TypeReference typeReference, string rootNamespace, bool supportOpenApiNullableReferenceTypes, bool treatAsFile, ISchemaRegistry schemaRegistry, ILogger logger) => CreateSchema(document, typeReference, typeReference.IsEnumerable, defaultValue: null, rootNamespace, supportOpenApiNullableReferenceTypes, treatAsFile, schemaRegistry, logger);
306+
private static OpenApiSchema CreateSchema(OpenApiDocument document, TypeReference typeReference, bool isEnumerable, ValueReference defaultValue, string rootNamespace, bool supportOpenApiNullableReferenceTypes, bool treatAsFile, ISchemaRegistry schemaRegistry, ILogger logger)
305307
{
306-
OpenApiSchema schema = CreateSchemaCore(document, typeReference, defaultValue, rootNamespace, supportOpenApiNullableReferenceTypes, schemaRegistry, logger);
308+
OpenApiSchema schema = CreateSchemaCore(document, typeReference, defaultValue, rootNamespace, supportOpenApiNullableReferenceTypes, treatAsFile, schemaRegistry, logger);
307309

308310
if (isEnumerable)
309311
{
@@ -317,20 +319,27 @@ private static OpenApiSchema CreateSchema(OpenApiDocument document, TypeReferenc
317319
return schema;
318320
}
319321

320-
private static OpenApiSchema CreateSchemaCore(OpenApiDocument document, TypeReference typeReference, ValueReference defaultValue, string rootNamespace, bool supportOpenApiNullableReferenceTypes, ISchemaRegistry schemaRegistry, ILogger logger)
322+
private static OpenApiSchema CreateSchemaCore(OpenApiDocument document, TypeReference typeReference, ValueReference defaultValue, string rootNamespace, bool supportOpenApiNullableReferenceTypes, bool treatAsFile, ISchemaRegistry schemaRegistry, ILogger logger)
321323
{
322-
switch (typeReference)
324+
if (treatAsFile)
323325
{
324-
case PrimitiveTypeReference primitiveContractPropertyType: return CreatePrimitiveTypeSchema(primitiveContractPropertyType, defaultValue, schemaRegistry, logger);
325-
case SchemaTypeReference contractPropertyTypeReference: return CreateReferenceSchema(document, contractPropertyTypeReference, rootNamespace, supportOpenApiNullableReferenceTypes, schemaRegistry, logger);
326-
default: throw new ArgumentOutOfRangeException(nameof(typeReference), typeReference, $"Unexpected property type: {typeReference}");
326+
return CreatePrimitiveTypeSchema(typeReference, PrimitiveType.Stream, defaultValue, schemaRegistry, logger);
327327
}
328+
329+
OpenApiSchema schema = typeReference switch
330+
{
331+
PrimitiveTypeReference primitiveContractPropertyType => CreatePrimitiveTypeSchema(primitiveContractPropertyType, defaultValue, schemaRegistry, logger),
332+
SchemaTypeReference contractPropertyTypeReference => CreateReferenceSchema(document, contractPropertyTypeReference, rootNamespace, supportOpenApiNullableReferenceTypes, schemaRegistry, logger),
333+
_ => throw new ArgumentOutOfRangeException(nameof(typeReference), typeReference, $"Unexpected property type: {typeReference}")
334+
};
335+
return schema;
328336
}
329337

330-
private static OpenApiSchema CreatePrimitiveTypeSchema(PrimitiveTypeReference typeReference, ValueReference defaultValue, ISchemaRegistry schemaRegistry, ILogger logger)
338+
private static OpenApiSchema CreatePrimitiveTypeSchema(PrimitiveTypeReference typeReference, ValueReference defaultValue, ISchemaRegistry schemaRegistry, ILogger logger) => CreatePrimitiveTypeSchema(typeReference, typeReference.Type, defaultValue, schemaRegistry, logger);
339+
private static OpenApiSchema CreatePrimitiveTypeSchema(TypeReference typeReference, PrimitiveType type, ValueReference defaultValue, ISchemaRegistry schemaRegistry, ILogger logger)
331340
{
332-
if (!PrimitiveTypeMap.TryGetOpenApiFactory(typeReference.Type, out Func<OpenApiSchema> schemaFactory))
333-
throw new InvalidOperationException($"Unexpected primitive type: {typeReference.Type}");
341+
if (!PrimitiveTypeMap.TryGetOpenApiFactory(type, out Func<OpenApiSchema> schemaFactory))
342+
throw new InvalidOperationException($"Unexpected primitive type: {type}");
334343

335344
OpenApiSchema schema = schemaFactory();
336345
schema.Nullable = typeReference.IsNullable;
@@ -440,7 +449,7 @@ private static void AppendObjectSchema(OpenApiDocument document, string schemaNa
440449
continue;
441450

442451
string propertyName = StringExtensions.ToCamelCase(property.Name);
443-
OpenApiSchema propertySchema = CreateSchema(document, property.Type, rootNamespace, supportOpenApiNullableReferenceTypes, schemaRegistry, logger);
452+
OpenApiSchema propertySchema = CreateSchema(document, property.Type, rootNamespace, supportOpenApiNullableReferenceTypes, treatAsFile: false, schemaRegistry, logger);
444453
schema.Properties.Add(propertyName, propertySchema);
445454

446455
if (property.SerializationBehavior == SerializationBehavior.Always && !property.IsOptional)

0 commit comments

Comments
 (0)