/// <summary> /// Creates an ODataAction or ODataFunction from a operation import. /// </summary> /// <param name="metadataDocumentUri">The metadata document uri.</param> /// <param name="metadataReferencePropertyName">The metadata reference property name.</param> /// <param name="edmOperation">The operation to create the ODataOperation for.</param> /// <param name="isAction">true if the created ODataOperation is an ODataAction, false otherwise.</param> /// <returns>The created ODataAction or ODataFunction.</returns> internal static ODataOperation CreateODataOperation(Uri metadataDocumentUri, string metadataReferencePropertyName, IEdmOperation edmOperation, out bool isAction) { Debug.Assert(metadataDocumentUri != null, "metadataDocumentUri != null"); Debug.Assert(!string.IsNullOrEmpty(metadataReferencePropertyName), "!string.IsNullOrEmpty(metadataReferencePropertyName)"); Debug.Assert(edmOperation != null, "edmOperation != null"); isAction = edmOperation.IsAction(); ODataOperation operation = isAction ? (ODataOperation) new ODataAction() : new ODataFunction(); // Note that the property name can be '#name' which is not a valid Uri. We need to prepend the metadata document uri in that case. int parameterStartIndex = 0; if (isAction && (parameterStartIndex = metadataReferencePropertyName.IndexOf(JsonLightConstants.FunctionParameterStart)) > 0) { metadataReferencePropertyName = metadataReferencePropertyName.Substring(0, parameterStartIndex); } operation.Metadata = GetAbsoluteUriFromMetadataReferencePropertyName(metadataDocumentUri, metadataReferencePropertyName); return(operation); }
/// <inheritdoc /> protected override bool IsOperationParameterMeet(IEdmOperation operation, ActionModel action) { Contract.Assert(operation != null); Contract.Assert(action != null); if (!operation.IsAction()) { return(false); } // So far, we use the "ODataActionParameters" and "ODataUntypedActionParameters" to hold the action parameter values. // TODO: consider to use [FromODataBody] to seperate the parameters to each corresponding if (operation.Parameters.Count() > 1) { if (!action.Parameters.Any(p => p.ParameterType == typeof(ODataActionParameters) || p.ParameterType == typeof(ODataUntypedActionParameters))) { return(false); } } return(true); }
/// <summary> /// Initializes a new instance of the <see cref="FunctionSegmentTemplate" /> class. /// </summary> /// <param name="operationSegment">The operation segment, it should be a function segment and the parameters are template.</param> public ActionSegmentTemplate(OperationSegment operationSegment) { if (operationSegment == null) { throw Error.ArgumentNull(nameof(operationSegment)); } IEdmOperation operation = operationSegment.Operations.FirstOrDefault(); if (!operation.IsAction()) { throw new ODataException(Error.Format(SRResources.SegmentShouldBeKind, "Action", "ActionSegmentTemplate")); } Action = (IEdmAction)operation; if (Action.ReturnType != null) { IsSingle = Action.ReturnType.TypeKind() != EdmTypeKind.Collection; EdmType = Action.ReturnType.Definition; } Segment = operationSegment; }
/// <summary> /// Create the <see cref="OpenApiResponses"/> for a <see cref="IEdmOperation"/> /// </summary> /// <param name="context">The OData context.</param> /// <param name="operation">The Edm operation.</param> /// <returns>The created <see cref="OpenApiResponses"/>.</returns> public static OpenApiResponses CreateResponses(this ODataContext context, IEdmOperation operation) { Utils.CheckArgumentNull(context, nameof(context)); Utils.CheckArgumentNull(operation, nameof(operation)); OpenApiResponses responses = new OpenApiResponses(); if (operation.IsAction() && operation.ReturnType == null) { responses.Add(Constants.StatusCode204, Constants.StatusCode204.GetResponse()); responses.Add(Constants.StatusCode200, Constants.StatusCode204.GetResponse()); } else { OpenApiResponse response = new OpenApiResponse { Description = "Success", Content = new Dictionary <string, OpenApiMediaType> { { Constants.ApplicationJsonMediaType, new OpenApiMediaType { Schema = context.CreateEdmTypeSchema(operation.ReturnType) } } } }; responses.Add(Constants.StatusCode200, response); } // both action & function has the default response. responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); return(responses); }
/// <summary> /// Add the template to the action /// </summary> /// <param name="context">The context.</param> /// <param name="edmOperation">The Edm operation.</param> /// <param name="hasKeyParameter">Has key parameter or not.</param> /// <param name="entityType">The entity type.</param> /// <param name="navigationSource">The navigation source.</param> /// <param name="castType">The type cast.</param> protected static void AddSelector(ODataControllerActionContext context, IEdmOperation edmOperation, bool hasKeyParameter, IEdmEntityType entityType, IEdmNavigationSource navigationSource, IEdmEntityType castType) { Contract.Assert(context != null); Contract.Assert(entityType != null); Contract.Assert(navigationSource != null); Contract.Assert(edmOperation != null); // Now, let's add the selector model. IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate>(); if (context.EntitySet != null) { segments.Add(new EntitySetSegmentTemplate(context.EntitySet)); if (hasKeyParameter) { segments.Add(new KeySegmentTemplate(entityType, navigationSource)); } } else { segments.Add(new SingletonSegmentTemplate(context.Singleton)); } if (castType != null) { if (context.Singleton != null || !hasKeyParameter) { segments.Add(new CastSegmentTemplate(castType, entityType, navigationSource)); } else { segments.Add(new CastSegmentTemplate(new EdmCollectionType(castType.ToEdmTypeReference(false)), new EdmCollectionType(entityType.ToEdmTypeReference(false)), navigationSource)); } } IEdmNavigationSource targetset = null; if (edmOperation.ReturnType != null) { targetset = edmOperation.GetTargetEntitySet(navigationSource, context.Model); } string httpMethod; if (edmOperation.IsAction()) { segments.Add(new ActionSegmentTemplate((IEdmAction)edmOperation, targetset)); httpMethod = "post"; } else { ISet <String> required = GetRequiredFunctionParamters(edmOperation, context.Action); segments.Add(new FunctionSegmentTemplate((IEdmFunction)edmOperation, targetset, required)); httpMethod = "get"; } ODataPathTemplate template = new ODataPathTemplate(segments); context.Action.AddSelector(httpMethod, context.Prefix, context.Model, template); }
/// <summary> /// Create the <see cref="OpenApiResponses"/> for a <see cref="IEdmOperation"/> /// </summary> /// <param name="context">The OData context.</param> /// <param name="operation">The Edm operation.</param> /// <param name="path">The OData path.</param> /// <returns>The created <see cref="OpenApiResponses"/>.</returns> public static OpenApiResponses CreateResponses(this ODataContext context, IEdmOperation operation, ODataPath path) { Utils.CheckArgumentNull(context, nameof(context)); Utils.CheckArgumentNull(operation, nameof(operation)); Utils.CheckArgumentNull(path, nameof(path)); OpenApiResponses responses = new(); if (operation.IsAction() && operation.ReturnType == null) { responses.Add(Constants.StatusCode204, Constants.StatusCode204.GetResponse()); } else { OpenApiSchema schema; if (operation.ReturnType.IsCollection()) { // Get the entity type of the previous segment IEdmEntityType entityType = path.Segments.Reverse().Skip(1)?.Take(1)?.FirstOrDefault()?.EntityType; schema = new OpenApiSchema { Title = entityType == null ? null : $"Collection of {entityType.Name}", Type = "object", Properties = new Dictionary <string, OpenApiSchema> { { "value", context.CreateEdmTypeSchema(operation.ReturnType) } } }; } else if (operation.ReturnType.IsPrimitive()) { // A property or operation response that is of a primitive type is represented as an object with a single name/value pair, // whose name is value and whose value is a primitive value. schema = new OpenApiSchema { Type = "object", Properties = new Dictionary <string, OpenApiSchema> { { "value", context.CreateEdmTypeSchema(operation.ReturnType) } } }; } else { schema = context.CreateEdmTypeSchema(operation.ReturnType); } string mediaType = Constants.ApplicationJsonMediaType; if (operation.ReturnType.AsPrimitive()?.PrimitiveKind() == EdmPrimitiveTypeKind.Stream) { // Responses of types Edm.Stream should be application/octet-stream mediaType = Constants.ApplicationOctetStreamMediaType; } OpenApiResponse response = new() { Description = "Success", Content = new Dictionary <string, OpenApiMediaType> { { mediaType, new OpenApiMediaType { Schema = schema } } } }; responses.Add(context.Settings.UseSuccessStatusCodeRange ? Constants.StatusCodeClass2XX : Constants.StatusCode200, response); } if (context.Settings.ErrorResponsesAsDefault) { responses.Add(Constants.StatusCodeDefault, Constants.StatusCodeDefault.GetResponse()); } else { responses.Add(Constants.StatusCodeClass4XX, Constants.StatusCodeClass4XX.GetResponse()); responses.Add(Constants.StatusCodeClass5XX, Constants.StatusCodeClass5XX.GetResponse()); } return(responses); }
private void WriteMethod(OdcmClass odcmClass, IEdmOperation operation, IEdmOperationImport operationImport = null) { var parameters = operation.IsBound ? (from parameter in operation.Parameters where parameter != operation.Parameters.First() select parameter) : (operation.Parameters); var isBoundToCollection = operation.IsBound && operation.Parameters.First().Type.IsCollection(); var odcmMethod = new OdcmMethod(operation.Name, odcmClass.Namespace) { IsComposable = operation.IsFunction() && ((IEdmFunction)operation).IsComposable, IsBoundToCollection = isBoundToCollection, Verbs = operation.IsAction() ? OdcmAllowedVerbs.Post : OdcmAllowedVerbs.Any, Class = odcmClass }; AddVocabularyAnnotations(odcmMethod, operation); if (operationImport != null) { AddVocabularyAnnotations(odcmMethod, operationImport); } odcmClass.Methods.Add(odcmMethod); if (operation.ReturnType != null) { odcmMethod.ReturnType = ResolveType(operation.ReturnType); odcmMethod.IsCollection = operation.ReturnType.IsCollection(); } var callingConvention = operation.IsAction() ? OdcmCallingConvention.InHttpMessageBody : OdcmCallingConvention.InHttpRequestUri; foreach (var parameter in parameters) { var odcmParameter = new OdcmParameter(parameter.Name) { CallingConvention = callingConvention, Type = ResolveType(parameter.Type), IsCollection = parameter.Type.IsCollection(), IsNullable = parameter.Type.IsNullable }; AddVocabularyAnnotations(odcmParameter, parameter); odcmMethod.Parameters.Add(odcmParameter); } }
/// <summary> /// Creates an ODataAction or ODataFunction from a operation import. /// </summary> /// <param name="metadataDocumentUri">The metadata document uri.</param> /// <param name="metadataReferencePropertyName">The metadata reference property name.</param> /// <param name="edmOperation">The operation to create the ODataOperation for.</param> /// <param name="isAction">true if the created ODataOperation is an ODataAction, false otherwise.</param> /// <returns>The created ODataAction or ODataFunction.</returns> internal static ODataOperation CreateODataOperation(Uri metadataDocumentUri, string metadataReferencePropertyName, IEdmOperation edmOperation, out bool isAction) { Debug.Assert(metadataDocumentUri != null, "metadataDocumentUri != null"); Debug.Assert(!string.IsNullOrEmpty(metadataReferencePropertyName), "!string.IsNullOrEmpty(metadataReferencePropertyName)"); Debug.Assert(edmOperation != null, "edmOperation != null"); isAction = edmOperation.IsAction(); ODataOperation operation = isAction ? (ODataOperation)new ODataAction() : new ODataFunction(); // Note that the property name can be '#name' which is not a valid Uri. We need to prepend the metadata document uri in that case. int parameterStartIndex = 0; if (isAction && (parameterStartIndex = metadataReferencePropertyName.IndexOf(JsonLightConstants.FunctionParameterStart)) > 0) { metadataReferencePropertyName = metadataReferencePropertyName.Substring(0, parameterStartIndex); } operation.Metadata = GetAbsoluteUriFromMetadataReferencePropertyName(metadataDocumentUri, metadataReferencePropertyName); return operation; }