/// <summary> /// Creates an OpenAPI document from a CSDL document. /// </summary> /// <param name="uri">The file path of the CSDL document location.</param> /// <param name="styleOptions">Optional parameter that defines the style /// options to be used in formatting the OpenAPI document.</param> /// <returns></returns> public static OpenApiDocument CreateOpenApiDocument(string uri, OpenApiStyleOptions styleOptions = null) { if (string.IsNullOrEmpty(uri)) { return(null); } using StreamReader streamReader = new StreamReader(uri); Stream csdl = streamReader.BaseStream; var edmModel = CsdlReader.Parse(XElement.Load(csdl).CreateReader()); var settings = new OpenApiConvertSettings() { EnableKeyAsSegment = true, EnableOperationId = true, PrefixEntityTypeNameBeforeKey = true, TagDepth = 2, EnablePagination = styleOptions != null && styleOptions.EnablePagination, EnableDiscriminatorValue = styleOptions != null && styleOptions.EnableDiscriminatorValue, EnableDerivedTypesReferencesForRequestBody = styleOptions != null && styleOptions.EnableDerivedTypesReferencesForRequestBody, EnableDerivedTypesReferencesForResponses = styleOptions != null && styleOptions.EnableDerivedTypesReferencesForResponses, ShowRootPath = styleOptions != null && styleOptions.ShowRootPath }; OpenApiDocument document = edmModel.ConvertToOpenApi(settings); document = FixReferences(document); return(document); }
public void GetTypeCastPathsForModelWithDerivedTypesConstraint( bool addAnnotation, bool getNavPropModel, bool requireConstraint, bool appendBoundOperationsOnDerivedTypes, int expectedCount) { // Arrange var annotation = addAnnotation ? derivedTypeAnnotation : string.Empty; IEdmModel model = getNavPropModel ? GetNavPropModel(annotation) : GetInheritanceModel(annotation); ODataPathProvider provider = new(); var settings = new OpenApiConvertSettings { RequireDerivedTypesConstraintForODataTypeCastSegments = requireConstraint, AppendBoundOperationsOnDerivedTypeCastSegments = appendBoundOperationsOnDerivedTypes }; // Act var paths = provider.GetPaths(model, settings); // Assert Assert.NotNull(paths); Assert.Equal(expectedCount, paths.Count()); var dollarCountPathsWithCastSegment = paths.Where(x => x.Kind == ODataPathKind.DollarCount && x.Any(y => y.Kind == ODataSegmentKind.TypeCast)); if (addAnnotation || !requireConstraint) { Assert.Single(dollarCountPathsWithCastSegment); } else { Assert.Empty(dollarCountPathsWithCastSegment); } }
public void CreateEdmTypeSchemaReturnSchemaForInt64(bool isNullable, bool IEEE754Compatible) { // Arrange IEdmModel model = EdmCoreModel.Instance; OpenApiConvertSettings settings = new OpenApiConvertSettings { IEEE754Compatible = IEEE754Compatible }; ODataContext context = new ODataContext(model, settings); IEdmTypeReference edmTypeReference = EdmCoreModel.Instance.GetInt64(isNullable); // Act var schema = context.CreateEdmTypeSchema(edmTypeReference); Assert.NotNull(schema); // guard // & Assert if (IEEE754Compatible) { Assert.Null(schema.Type); Assert.NotNull(schema.AnyOf); Assert.Equal(2, schema.AnyOf.Count); Assert.Equal(new[] { "integer", "string" }, schema.AnyOf.Select(a => a.Type)); } else { Assert.Equal("integer", schema.Type); Assert.Null(schema.AnyOf); } Assert.Equal(isNullable, schema.Nullable); }
private string FunctionImportName(IEdmFunctionImport functionImport, OpenApiConvertSettings settings) { StringBuilder functionName = new StringBuilder(functionImport.Name); functionName.Append("("); // Structured or collection-valued parameters are represented as a parameter alias in the path template // and the parameters array contains a Parameter Object for the parameter alias as a query option of type string. IEdmFunction function = functionImport.Function; functionName.Append(String.Join(",", function.Parameters.Select(p => { if (p.Type.IsStructured() || p.Type.IsCollection()) { return(p.Name + "=@" + p.Name); } else { return(p.Name + "={" + p.Name + "}"); } }))); functionName.Append(")"); return(functionName.ToString()); }
public void GetPathsWithBoundFunctionOperationForContainmentNavigationPropertyPathsWorks(bool containsTarget) { // Arrange string navProp = $@"<NavigationProperty Name=""Referral"" Type=""NS.NiceCustomer"" ContainsTarget=""{containsTarget}""/>"; string boundAction = @"<Function Name=""Search"" IsBound=""true""> <Parameter Name=""bindingParameter"" Type=""NS.NiceCustomer"" /> <ReturnType Type=""Collection(NS.Customer)"" /> </Function> <EntityType Name=""NiceCustomer""> <Property Name=""Other"" Type=""Edm.Int32"" Nullable=""true"" /> </EntityType>"; IEdmModel model = GetEdmModel(boundAction, "", navProp); ODataPathProvider provider = new ODataPathProvider(); var settings = new OpenApiConvertSettings(); // Act var paths = provider.GetPaths(model, settings); // Assert Assert.NotNull(paths); if (containsTarget) { Assert.Equal(5, paths.Count()); Assert.Contains("/Customers({ID})/Referral/NS.Search()", paths.Select(p => p.GetPathItemName())); } else { Assert.Equal(4, paths.Count()); Assert.DoesNotContain("/Customers({ID})/Referral/NS.Search()", paths.Select(p => p.GetPathItemName())); } }
/// <summary> /// Gets the path item name. /// </summary> /// <param name="settings">The settings.</param> /// <returns>The string.</returns> public string GetPathItemName(OpenApiConvertSettings settings) { Utils.CheckArgumentNull(settings, nameof(settings)); StringBuilder sb = new StringBuilder(); foreach (var segment in Segments) { string pathItemName = segment.GetPathItemName(settings); if (segment.Kind == ODataSegmentKind.Key && (settings.EnableKeyAsSegment == null || !settings.EnableKeyAsSegment.Value)) { sb.Append("("); sb.Append(pathItemName); sb.Append(")"); } else // other segments { sb.Append("/"); sb.Append(pathItemName); } } return(sb.ToString()); }
public void BasicEdmModelToOpenApiJsonWorks(OpenApiSpecVersion specVersion) { // Arrange IEdmModel model = EdmModelHelper.BasicEdmModel; var openApiConvertSettings = new OpenApiConvertSettings { OpenApiSpecVersion = specVersion, ShowSchemaExamples = true // test for schema examples }; // Act string json = WriteEdmModelAsOpenApi(model, OpenApiFormat.Json, openApiConvertSettings); _output.WriteLine(json); // Assert if (specVersion == OpenApiSpecVersion.OpenApi2_0) { Assert.Equal(Resources.GetString("Basic.OpenApi.V2.json").ChangeLineBreaks(), json); } else { Assert.Equal(Resources.GetString("Basic.OpenApi.json").ChangeLineBreaks(), json); } }
internal IDictionary <string, string> GetKeyNameMapping(OpenApiConvertSettings settings, HashSet <string> parameters) { IDictionary <string, string> keyNamesMapping = new Dictionary <string, string>(); IList <IEdmStructuralProperty> keys = EntityType.Key().ToList(); if (keys.Count() == 1) { string keyName = keys.First().Name; if (settings.PrefixEntityTypeNameBeforeKey) { string name = Utils.GetUniqueName(EntityType.Name + "-" + keyName, parameters); keyNamesMapping[keyName] = name; } else { string name = Utils.GetUniqueName(keyName, parameters); keyNamesMapping[keyName] = name; } } else { foreach (var keyProperty in keys) { string name = Utils.GetUniqueName(keyProperty.Name, parameters); keyNamesMapping[keyProperty.Name] = name; } } return(keyNamesMapping); }
public void MultipleSchemasEdmModelToOpenApiYamlWorks(OpenApiSpecVersion specVersion) { // Arrange IEdmModel model = EdmModelHelper.MultipleSchemasEdmModel; var openApiConvertSettings = new OpenApiConvertSettings { OpenApiSpecVersion = specVersion, ShowLinks = true, // test Links ShowSchemaExamples = true }; // Act string yaml = WriteEdmModelAsOpenApi(model, OpenApiFormat.Yaml, openApiConvertSettings); _output.WriteLine(yaml); // Assert if (specVersion == OpenApiSpecVersion.OpenApi2_0) { Assert.Equal(Resources.GetString("Multiple.Schema.OpenApi.V2.yaml").ChangeLineBreaks(), yaml); } else { Assert.Equal(Resources.GetString("Multiple.Schema.OpenApi.yaml").ChangeLineBreaks(), yaml); } }
/// <summary> /// Initializes a new instance of <see cref="ODataContext"/> class. /// </summary> /// <param name="model">The Edm model.</param> /// <param name="settings">The convert setting.</param> public ODataContext(IEdmModel model, OpenApiConvertSettings settings) { Model = model ?? throw Error.ArgumentNull(nameof(model)); Settings = settings ?? throw Error.ArgumentNull(nameof(settings)); EdmSpatialTypeVisitor visitor = new EdmSpatialTypeVisitor(); visitor.Visit(model); IsSpatialTypeUsed = visitor.IsSpatialTypeUsed; OperationHanderProvider = new OperationHandlerProvider(); PathItemHanderProvider = new PathItemHandlerProvider(); _pathProvider = new ODataPathProvider(); if (settings.EnableKeyAsSegment != null) { // We have the global setting, use the global setting KeyAsSegment = settings.EnableKeyAsSegment.Value; } else { KeyAsSegment = false; if (model.EntityContainer != null) { var keyAsSegment = model.GetBoolean(model.EntityContainer, CapabilitiesConstants.KeyAsSegmentSupported); if (keyAsSegment != null) { KeyAsSegment = keyAsSegment.Value; } } } }
/// <summary> /// Create a map of <see cref="OpenApiPathItem"/>. /// </summary> /// <param name="context">The OData context.</param> /// <returns>The created map of <see cref="OpenApiPathItem"/>.</returns> public static IDictionary <string, OpenApiPathItem> CreatePathItems(this ODataContext context) { Utils.CheckArgumentNull(context, nameof(context)); IDictionary <string, OpenApiPathItem> pathItems = new Dictionary <string, OpenApiPathItem>(); if (context.EntityContainer == null) { return(pathItems); } OpenApiConvertSettings settings = context.Settings.Clone(); settings.EnableKeyAsSegment = context.KeyAsSegment; foreach (ODataPath path in context.AllPaths) { IPathItemHandler handler = context.PathItemHanderProvider.GetHandler(path.Kind); if (handler == null) { continue; } pathItems.Add(path.GetPathItemName(settings), handler.CreatePathItem(context, path)); } return(pathItems); }
/// <summary> /// Generate the Open Api. /// </summary> public bool Generate() { try { IEdmModel edmModel = GetEdmModel(); OpenApiConvertSettings settings = GetSettings(); settings.OpenApiSpecVersion = Version; using (FileStream fs = File.Create(Output)) { OpenApiDocument document = edmModel.ConvertToOpenApi(settings); document.Serialize(fs, settings.OpenApiSpecVersion, Format); fs.Flush(); } } catch (Exception e) { Console.WriteLine(e.Message); return(false); } return(true); }
public void CreateResponseForEdmActionWhenErrorResponsesAsDefaultIsSet(string actionName, bool errorAsDefault, string responseCode) { // Arrange IEdmModel model = EdmModelHelper.GraphBetaModel; var settings = new OpenApiConvertSettings { ErrorResponsesAsDefault = errorAsDefault, }; ODataContext context = new ODataContext(model, settings); // Act OpenApiResponses responses; IEdmOperation operation = model.SchemaElements.OfType <IEdmOperation>().First(o => o.Name == actionName); Assert.NotNull(operation); // guard ODataPath path = new(new ODataOperationSegment(operation)); responses = context.CreateResponses(operation, path); // Assert Assert.NotNull(responses); Assert.NotEmpty(responses); if (errorAsDefault) { Assert.Equal(new string[] { responseCode, "default" }, responses.Select(r => r.Key)); } else { Assert.Equal(new string[] { responseCode, "4XX", "5XX" }, responses.Select(r => r.Key)); } }
public void GetPathsWithStreamPropertyAndWithEntityHasStreamWorks(bool hasStream, string streamPropName) { // Arrange IEdmModel model = GetEdmModel(hasStream, streamPropName); ODataPathProvider provider = new ODataPathProvider(); var settings = new OpenApiConvertSettings(); // Act var paths = provider.GetPaths(model, settings); // Assert Assert.NotNull(paths); if (hasStream && !streamPropName.Equals("Content", StringComparison.OrdinalIgnoreCase)) { Assert.Equal(7, paths.Count()); Assert.Equal(new[] { "/me", "/me/photo", "/me/photo/$value", "/Todos", "/Todos({Id})", "/Todos({Id})/$value", "/Todos({Id})/Logo" }, paths.Select(p => p.GetPathItemName())); } else if ((hasStream && streamPropName.Equals("Content", StringComparison.OrdinalIgnoreCase)) || (!hasStream && streamPropName.Equals("Content", StringComparison.OrdinalIgnoreCase))) { Assert.Equal(6, paths.Count()); Assert.Equal(new[] { "/me", "/me/photo", "/me/photo/$value", "/Todos", "/Todos({Id})", "/Todos({Id})/Content" }, paths.Select(p => p.GetPathItemName())); } else // !hasStream && !streamPropName.Equals("Content") { Assert.Equal(6, paths.Count()); Assert.Equal(new[] { "/me", "/me/photo", "/me/photo/$value", "/Todos", "/Todos({Id})", "/Todos({Id})/Logo" }, paths.Select(p => p.GetPathItemName())); } }
/// <summary> /// Converts CSDL to OpenAPI /// </summary> /// <param name="csdl">The CSDL stream.</param> /// <returns>An OpenAPI document.</returns> public static async Task <OpenApiDocument> ConvertCsdlToOpenApiAsync(Stream csdl) { using var reader = new StreamReader(csdl); var csdlTxt = await reader.ReadToEndAsync(); var edmModel = CsdlReader.Parse(XElement.Parse(csdlTxt).CreateReader()); var settings = new OpenApiConvertSettings() { EnableKeyAsSegment = true, EnableOperationId = true, PrefixEntityTypeNameBeforeKey = true, TagDepth = 2, EnablePagination = true, EnableDiscriminatorValue = false, EnableDerivedTypesReferencesForRequestBody = false, EnableDerivedTypesReferencesForResponses = false, ShowRootPath = true, ShowLinks = true }; OpenApiDocument document = edmModel.ConvertToOpenApi(settings); document = FixReferences(document); return(document); }
public void GetPathsWithUnboundOperationImportWorks() { // Arrange string boundAction = @"<Function Name=""GetNearestCustomers""> <ReturnType Type=""NS.Customer"" /> </Function > <Action Name=""ResetDataSource"" />"; string unbounds = @" <FunctionImport Name=""GetNearestCustomers"" Function=""NS.GetNearestCustomers"" EntitySet=""Customers"" /> <ActionImport Name =""ResetDataSource"" Action =""NS.ResetDataSource"" />"; IEdmModel model = GetEdmModel(boundAction, unbounds); ODataPathProvider provider = new ODataPathProvider(); var settings = new OpenApiConvertSettings(); // Act var paths = provider.GetPaths(model, settings); // Assert Assert.NotNull(paths); Assert.Equal(4, paths.Count()); Assert.Contains("/GetNearestCustomers()", paths.Select(p => p.GetPathItemName())); Assert.Contains("/ResetDataSource", paths.Select(p => p.GetPathItemName())); }
public void GetPathsWithContainedNavigationPropertytWorks() { // Arrange string entityType = @"<EntityType Name=""Order""> <Key> <PropertyRef Name=""id"" /> </Key> <NavigationProperty Name=""MultipleCustomers"" Type=""Collection(NS.Customer)"" ContainsTarget=""true"" /> <NavigationProperty Name=""SingleCustomer"" Type=""NS.Customer"" ContainsTarget=""true"" /> </EntityType>"; string entitySet = @"<EntitySet Name=""Orders"" EntityType=""NS.Order"" />"; IEdmModel model = GetEdmModel(entityType, entitySet); ODataPathProvider provider = new ODataPathProvider(); var settings = new OpenApiConvertSettings(); // Act var paths = provider.GetPaths(model, settings); // Assert Assert.NotNull(paths); Assert.Equal(7, paths.Count()); var pathItems = paths.Select(p => p.GetPathItemName()).ToList(); Assert.Contains("/Orders({id})/MultipleCustomers", pathItems); Assert.Contains("/Orders({id})/MultipleCustomers({ID})", pathItems); Assert.Contains("/Orders({id})/SingleCustomer", pathItems); }
static int Main(string[] args) { // we assume the path are existed for simplicity. string path = Directory.GetCurrentDirectory(); string csdl = path + "/../../../../../docs/csdl"; string oas20 = path + "/../../../../../docs/oas_2_0"; string oas30 = path + "/../../../../../docs/oas3_0_0"; foreach (var filePath in Directory.GetFiles(csdl, "*.xml")) { IEdmModel model = LoadEdmModel(filePath); if (model == null) { continue; } FileInfo fileInfo = new FileInfo(filePath); string fileName = fileInfo.Name.Substring(0, fileInfo.Name.Length - 4); OpenApiConvertSettings settings = new OpenApiConvertSettings(); if (fileName.Contains("graph.beta")) { settings.PrefixEntityTypeNameBeforeKey = true; settings.ServiceRoot = new Uri("https://graph.microsoft.com/beta"); } else if (fileName.Contains("graph1.0")) { settings.PrefixEntityTypeNameBeforeKey = true; settings.ServiceRoot = new Uri("https://graph.microsoft.com/v1.0"); } OpenApiDocument document = model.ConvertToOpenApi(settings); string output;/* = oas20 + "/" + fileName + ".yaml"; * File.WriteAllText(output, document.SerializeAsYaml(OpenApiSpecVersion.OpenApi2_0)); * * output = oas20 + "/" + fileName + ".json"; * File.WriteAllText(output, document.SerializeAsJson(OpenApiSpecVersion.OpenApi2_0)); * * output = oas30 + "/" + fileName + ".yaml"; * File.WriteAllText(output, document.SerializeAsYaml(OpenApiSpecVersion.OpenApi3_0)); * * output = oas30 + "/" + fileName + ".json"; * File.WriteAllText(output, document.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0)); */ settings.EnableKeyAsSegment = true; settings.EnableUnqualifiedCall = true; output = oas30 + "/" + fileName + ".json"; document = model.ConvertToOpenApi(settings); File.WriteAllText(output, document.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0)); output = oas20 + "/" + fileName + ".json"; File.WriteAllText(output, document.SerializeAsJson(OpenApiSpecVersion.OpenApi2_0)); Console.WriteLine("Output [ " + fileName + " ] Succeessful!"); } Console.WriteLine("\n==> All Done!"); return(0); }
/// <inheritdoc /> public override string GetPathItemName(OpenApiConvertSettings settings, HashSet <string> parameters) { Utils.CheckArgumentNull(settings, nameof(settings)); IList <IEdmStructuralProperty> keys = EntityType.Key().ToList(); if (keys.Count() == 1) { string keyName = keys.First().Name; if (settings.PrefixEntityTypeNameBeforeKey) { string name = Utils.GetUniqueName(EntityType.Name + "-" + keyName, parameters); return("{" + name + "}"); } else { string name = Utils.GetUniqueName(keyName, parameters); return("{" + name + "}"); } } else { IList <string> keyStrings = new List <string>(); foreach (var keyProperty in keys) { string name = Utils.GetUniqueName(keyProperty.Name, parameters); keyStrings.Add(keyProperty.Name + "={" + name + "}"); } return(String.Join(",", keyStrings)); } }
/// <summary> /// Create a map of <see cref="OpenApiPathItem"/>. /// </summary> /// <param name="context">The OData context.</param> /// <returns>The created map of <see cref="OpenApiPathItem"/>.</returns> public static IDictionary <string, OpenApiPathItem> CreatePathItems(this ODataContext context) { Utils.CheckArgumentNull(context, nameof(context)); IDictionary <string, OpenApiPathItem> pathItems = new Dictionary <string, OpenApiPathItem>(); if (context.EntityContainer == null) { return(pathItems); } OpenApiConvertSettings settings = context.Settings.Clone(); settings.EnableKeyAsSegment = context.KeyAsSegment; foreach (ODataPath path in context.AllPaths) { try { IPathItemHandler handler = context.PathItemHanderProvider.GetHandler(path.Kind); if (handler == null) { continue; } pathItems.Add(path.GetPathItemName(settings), handler.CreatePathItem(context, path)); }catch (Exception ex) { System.Diagnostics.Trace.WriteLine("Error generating path:" + ex.Message); } } return(pathItems); }
private string FunctionName(IEdmFunction function, OpenApiConvertSettings settings) { StringBuilder functionName = new StringBuilder(); if (settings.EnableUnqualifiedCall) { functionName.Append(function.Name); } else { functionName.Append(function.FullName()); } functionName.Append("("); // Structured or collection-valued parameters are represented as a parameter alias in the path template // and the parameters array contains a Parameter Object for the parameter alias as a query option of type string. int skip = function.IsBound ? 1 : 0; functionName.Append(String.Join(",", function.Parameters.Skip(skip).Select(p => { if (p.Type.IsStructured() || p.Type.IsCollection()) { return(p.Name + "=@" + p.Name); } else { return(p.Name + "={" + p.Name + "}"); } }))); functionName.Append(")"); return(functionName.ToString()); }
public void TripServiceMetadataToOpenApiYamlWorks(OpenApiSpecVersion specVersion) { // Arrange IEdmModel model = EdmModelHelper.TripServiceModel; OpenApiConvertSettings settings = new OpenApiConvertSettings { EnableKeyAsSegment = true, Version = new Version(1, 0, 1), ServiceRoot = new Uri("http://services.odata.org/TrippinRESTierService"), IEEE754Compatible = true, OpenApiSpecVersion = specVersion }; // Act string yaml = WriteEdmModelAsOpenApi(model, OpenApiFormat.Yaml, settings); _output.WriteLine(yaml); // Assert if (specVersion == OpenApiSpecVersion.OpenApi2_0) { Assert.Equal(Resources.GetString("TripService.OpenApi.V2.yaml").ChangeLineBreaks(), yaml); } else { Assert.Equal(Resources.GetString("TripService.OpenApi.yaml").ChangeLineBreaks(), yaml); } }
private OpenApiDocument CreateDocument(string prefixName) { IDictionary <string, ODataPath> tempateToPathDict = new Dictionary <string, ODataPath>(); ODataOpenApiPathProvider provider = new ODataOpenApiPathProvider(); IEdmModel model = null; foreach (var endpoint in _dataSource.Endpoints) { IODataRoutingMetadata metadata = endpoint.Metadata.GetMetadata <IODataRoutingMetadata>(); if (metadata == null) { continue; } if (metadata.Prefix != prefixName) { continue; } model = metadata.Model; RouteEndpoint routeEndpoint = endpoint as RouteEndpoint; if (routeEndpoint == null) { continue; } // get rid of the prefix int length = prefixName.Length; string routePathTemplate = routeEndpoint.RoutePattern.RawText.Substring(length); routePathTemplate = routePathTemplate.StartsWith("/") ? routePathTemplate : "/" + routePathTemplate; if (tempateToPathDict.TryGetValue(routePathTemplate, out ODataPath pathValue)) { string method = GetHttpMethod(metadata, endpoint); pathValue.HttpMethods.Add(method); continue; } var path = metadata.Template.Translate(); if (path == null) { continue; } path.PathTemplate = routePathTemplate; provider.Add(path); string method1 = GetHttpMethod(metadata, endpoint); path.HttpMethods.Add(method1); tempateToPathDict[routePathTemplate] = path; } OpenApiConvertSettings settings = new OpenApiConvertSettings { PathProvider = provider, ServiceRoot = BuildAbsolute() }; return(model.ConvertToOpenApi(settings)); }
public void CreatePathsReturnsForBasicModelWithPrefix() { // Arrange IEdmModel model = EdmModelHelper.BasicEdmModel; OpenApiConvertSettings settings = new OpenApiConvertSettings { EnableKeyAsSegment = true, PathPrefix = "some/prefix" }; ODataContext context = new ODataContext(model, settings); // Act var paths = context.CreatePaths(); // Assert Assert.NotNull(paths); Assert.Equal(7, paths.Count); Assert.Contains("/some/prefix/People", paths.Keys); Assert.Contains("/some/prefix/People/{UserName}", paths.Keys); Assert.Contains("/some/prefix/City", paths.Keys); Assert.Contains("/some/prefix/City/{Name}", paths.Keys); Assert.Contains("/some/prefix/CountryOrRegion", paths.Keys); Assert.Contains("/some/prefix/CountryOrRegion/{Name}", paths.Keys); Assert.Contains("/some/prefix/Me", paths.Keys); }
public void TripServiceMetadataToOpenApiJsonWorks(OpenApiSpecVersion specVersion) { // Arrange IEdmModel model = EdmModelHelper.TripServiceModel; OpenApiConvertSettings settings = new OpenApiConvertSettings { EnableKeyAsSegment = true, Version = new Version(1, 0, 1), ServiceRoot = new Uri("http://services.odata.org/TrippinRESTierService"), IEEE754Compatible = true, OpenApiSpecVersion = specVersion, AddSingleQuotesForStringParameters = true, AddEnumDescriptionExtension = true, AppendBoundOperationsOnDerivedTypeCastSegments = true }; // Act string json = WriteEdmModelAsOpenApi(model, OpenApiFormat.Json, settings); _output.WriteLine(json); // Assert if (specVersion == OpenApiSpecVersion.OpenApi2_0) { Assert.Equal(Resources.GetString("TripService.OpenApi.V2.json").ChangeLineBreaks(), json); } else { Assert.Equal(Resources.GetString("TripService.OpenApi.json").ChangeLineBreaks(), json); } }
public void CreateNavigationPatchOperationReturnsCorrectOperation(bool enableOperationId, bool useHTTPStatusCodeClass2XX) { // Arrange IEdmModel model = EdmModelHelper.TripServiceModel; OpenApiConvertSettings settings = new OpenApiConvertSettings { EnableOperationId = enableOperationId, UseSuccessStatusCodeRange = useHTTPStatusCodeClass2XX }; ODataContext context = new ODataContext(model, settings); IEdmEntitySet people = model.EntityContainer.FindEntitySet("People"); Assert.NotNull(people); IEdmEntityType person = model.SchemaElements.OfType <IEdmEntityType>().First(c => c.Name == "Person"); IEdmNavigationProperty navProperty = person.DeclaredNavigationProperties().First(c => c.Name == "BestFriend"); ODataPath path = new ODataPath(new ODataNavigationSourceSegment(people), new ODataKeySegment(people.EntityType()), new ODataNavigationPropertySegment(navProperty)); // Act var operation = _operationHandler.CreateOperation(context, path); // Assert Assert.NotNull(operation); Assert.Equal("Update the best friend.", operation.Summary); Assert.Equal("Update an instance of a best friend.", operation.Description); Assert.NotNull(operation.Tags); var tag = Assert.Single(operation.Tags); Assert.Equal("People.Person", tag.Name); Assert.NotNull(operation.Parameters); Assert.Equal(1, operation.Parameters.Count); Assert.NotNull(operation.RequestBody); Assert.Equal("New navigation property values", operation.RequestBody.Description); Assert.Equal(2, operation.Responses.Count); var statusCode = useHTTPStatusCodeClass2XX ? "2XX" : "204"; Assert.Equal(new string[] { statusCode, "default" }, operation.Responses.Select(e => e.Key)); if (useHTTPStatusCodeClass2XX) { Assert.Single(operation.Responses[statusCode].Content); } else { Assert.Empty(operation.Responses[statusCode].Content); } if (enableOperationId) { Assert.Equal("People.UpdateBestFriend", operation.OperationId); } else { Assert.Null(operation.OperationId); } }
public void CreateSingletonPatchOperationReturnsCorrectOperation(bool enableOperationId, bool useHTTPStatusCodeClass2XX) { // Arrange string annotation = @" <Annotation Term=""Org.OData.Capabilities.V1.UpdateRestrictions""> <Record> <PropertyValue Property=""Description"" String=""Update the signed-in user."" /> <PropertyValue Property=""LongDescription"" String=""Update the signed-in user."" /> </Record> </Annotation>"; IEdmModel model = SingletonGetOperationHandlerTests.GetEdmModel(annotation); IEdmSingleton singleton = model.EntityContainer.FindSingleton("Me"); OpenApiConvertSettings settings = new OpenApiConvertSettings { EnableOperationId = enableOperationId, UseSuccessStatusCodeRange = useHTTPStatusCodeClass2XX }; ODataContext context = new ODataContext(model, settings); ODataPath path = new ODataPath(new ODataNavigationSourceSegment(singleton)); // Act var patch = _operationHandler.CreateOperation(context, path); // Assert Assert.NotNull(patch); Assert.Equal("Update the signed-in user.", patch.Summary); Assert.Equal("Update the signed-in user.", patch.Description); Assert.NotNull(patch.Tags); var tag = Assert.Single(patch.Tags); Assert.Equal("Me.Customer", tag.Name); Assert.Empty(patch.Parameters); Assert.NotNull(patch.RequestBody); Assert.NotNull(patch.Responses); Assert.Equal(2, patch.Responses.Count); var statusCode = useHTTPStatusCodeClass2XX ? "2XX" : "204"; Assert.Equal(new[] { statusCode, "default" }, patch.Responses.Select(r => r.Key)); if (useHTTPStatusCodeClass2XX) { Assert.Single(patch.Responses[statusCode].Content); } else { Assert.Empty(patch.Responses[statusCode].Content); } if (enableOperationId) { Assert.Equal("Me.Customer.UpdateCustomer", patch.OperationId); } else { Assert.Null(patch.OperationId); } }
private bool AppendBoundOperationOnDerived( IEdmOperation edmOperation, bool isCollection, IEdmEntityType bindingEntityType, OpenApiConvertSettings convertSettings) { bool found = false; bool isEscapedFunction = _model.IsUrlEscapeFunction(edmOperation); foreach (var baseType in bindingEntityType.FindAllBaseTypes()) { if (_allNavigationSources.TryGetValue(baseType, out IList <IEdmNavigationSource> baseNavigationSource)) { foreach (var ns in baseNavigationSource) { if (HasUnsatisfiedDerivedTypeConstraint( ns as IEdmVocabularyAnnotatable, baseType, convertSettings)) { continue; } if (isCollection) { if (ns is IEdmEntitySet) { ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataTypeCastSegment(bindingEntityType), new ODataOperationSegment(edmOperation, isEscapedFunction)); AppendPath(newPath); found = true; } } else { if (ns is IEdmSingleton) { ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataTypeCastSegment(bindingEntityType), new ODataOperationSegment(edmOperation, isEscapedFunction)); AppendPath(newPath); found = true; } else { ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataKeySegment(ns.EntityType()), new ODataTypeCastSegment(bindingEntityType), new ODataOperationSegment(edmOperation, isEscapedFunction)); AppendPath(newPath); found = true; } } } } } return(found); }
private bool HasUnsatisfiedDerivedTypeConstraint( IEdmVocabularyAnnotatable annotatable, IEdmEntityType baseType, OpenApiConvertSettings convertSettings) { return(convertSettings.RequireDerivedTypesConstraintForBoundOperations && !(_model.GetCollection(annotatable, "Org.OData.Validation.V1.DerivedTypeConstraint") ?? Enumerable.Empty <string>()) .Any(c => c.Equals(baseType.FullName(), StringComparison.OrdinalIgnoreCase))); }
/// <summary> /// Create a map of <see cref="OpenApiPathItem"/>. /// </summary> /// <param name="context">The OData context.</param> /// <returns>The created map of <see cref="OpenApiPathItem"/>.</returns> public static IDictionary <string, OpenApiPathItem> CreatePathItems(this ODataContext context) { Utils.CheckArgumentNull(context, nameof(context)); IDictionary <string, OpenApiPathItem> pathItems = new Dictionary <string, OpenApiPathItem>(); if (context.EntityContainer == null) { return(pathItems); } OpenApiConvertSettings settings = context.Settings.Clone(); settings.EnableKeyAsSegment = context.KeyAsSegment; foreach (ODataPath path in context.AllPaths) { IPathItemHandler handler = context.PathItemHanderProvider.GetHandler(path.Kind); if (handler == null) { continue; } if (path.PathTemplate != null) { pathItems.Add(path.PathTemplate, handler.CreatePathItem(context, path)); } else { pathItems.Add(path.GetPathItemName(settings), handler.CreatePathItem(context, path)); } } if (settings.ShowRootPath) { OpenApiPathItem rootPath = new() { Operations = new Dictionary <OperationType, OpenApiOperation> { { OperationType.Get, new OpenApiOperation { OperationId = "graphService.GetGraphService", Responses = new OpenApiResponses() { { "200", new OpenApiResponse() { Description = "OK", Links = CreateRootLinks(context.EntityContainer) } } } } } } }; pathItems.Add("/", rootPath); } return(pathItems); }