public void OperationRestrictionsTermWorksToCreateOperationForEdmFunctionImport(bool enableAnnotation)
        {
            string template = @"<?xml version=""1.0"" encoding=""utf-8""?>
<edmx:Edmx Version=""4.0"" xmlns:edmx=""http://docs.oasis-open.org/odata/ns/edmx"">
  <edmx:DataServices>
    <Schema Namespace=""NS"" xmlns=""http://docs.oasis-open.org/odata/ns/edm"">
      <Function Name=""GetNearestAirport"">
        <Parameter Name=""lat"" Type=""Edm.Double"" Nullable=""false"" />
        <Parameter Name=""lon"" Type=""Edm.Double"" Nullable=""false"" />
        <ReturnType Type=""Edm.String"" />
      </Function>
      <EntityContainer Name=""GraphService"">
        <FunctionImport Name=""GetNearestAirport"" Function=""NS.GetNearestAirport"" >
         {0}
        </FunctionImport>
      </EntityContainer>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>
";

            string annotation = @"<Annotation Term=""Org.OData.Capabilities.V1.OperationRestrictions"">
  <Record>
    <PropertyValue Property=""CustomHeaders"">
      <Collection>
        <Record>
          <PropertyValue Property=""Name"" String=""myhead1"" />
          <PropertyValue Property=""Required"" Bool=""true"" />
        </Record>
      </Collection>
    </PropertyValue>
    <PropertyValue Property=""Permissions"">
      <Collection>
        <Record>
          <PropertyValue Property=""SchemeName"" String=""Delegated (work or school account)"" />
          <PropertyValue Property=""Scopes"">
            <Collection>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.ReadBasic.All"" />
              </Record>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.Read.All"" />
              </Record>
              <Record>
                <PropertyValue Property=""Scope"" String=""Directory.Read.All"" />
              </Record>
            </Collection>
          </PropertyValue>
        </Record>
        <Record>
          <PropertyValue Property=""SchemeName"" String=""Application"" />
          <PropertyValue Property=""Scopes"">
            <Collection>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.Read.All"" />
              </Record>
              <Record>
                <PropertyValue Property=""Scope"" String=""Directory.Read.All"" />
              </Record>
            </Collection>
          </PropertyValue>
        </Record>
      </Collection>
    </PropertyValue>
  </Record>
</Annotation>";

            // Arrange
            string csdl = string.Format(template, enableAnnotation ? annotation : "");

            var edmModel = CsdlReader.Parse(XElement.Parse(csdl).CreateReader());

            Assert.NotNull(edmModel);
            IEdmOperationImport operationImport = edmModel.EntityContainer.FindOperationImports("GetNearestAirport").FirstOrDefault();

            Assert.NotNull(operationImport);

            ODataContext context = new ODataContext(edmModel);

            ODataPath path = new ODataPath(new ODataOperationImportSegment(operationImport));

            // Act
            var operation = _operationHandler.CreateOperation(context, path);

            // Assert
            Assert.NotNull(operation);
            Assert.NotNull(operation.Security);

            if (enableAnnotation)
            {
                Assert.Equal(2, operation.Security.Count);

                string json = operation.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
                Assert.Contains(@"
  ""security"": [
    {
      ""Delegated (work or school account)"": [
        ""User.ReadBasic.All"",
        ""User.Read.All"",
        ""Directory.Read.All""
      ]
    },
    {
      ""Application"": [
        ""User.Read.All"",
        ""Directory.Read.All""
      ]
    }
  ],".ChangeLineBreaks(), json);

                Assert.Contains(@"
    {
      ""name"": ""myhead1"",
      ""in"": ""header"",
      ""required"": true,
      ""schema"": {
        ""type"": ""string""
      }
    }
".ChangeLineBreaks(), json);
            }
            else
            {
                Assert.Empty(operation.Security);
            }
        }
        /// <summary>
        /// Create the collection of <see cref="OpenApiTag"/> object.
        /// </summary>
        /// <param name="context">The OData context.</param>
        /// <returns>The created collection of <see cref="OpenApiTag"/> object.</returns>
        public static IList <OpenApiTag> CreateTags(this ODataContext context)
        {
            Utils.CheckArgumentNull(context, nameof(context));

            // The value of tags is an array of Tag Objects.
            // For an OData service the natural groups are entity sets and singletons,
            // so the tags array contains one Tag Object per entity set and singleton in the entity container.

            // A Tag Object has to contain the field name, whose value is the name of the entity set or singleton,
            // and it optionally can contain the field description, whose value is the value of the unqualified annotation
            // Core.Description of the entity set or singleton.
            if (context.Tags != null)
            {
                return(context.Tags);
            }

            IList <OpenApiTag> tags = new List <OpenApiTag>();

            if (context.EntityContainer != null)
            {
                foreach (IEdmEntityContainerElement element in context.Model.EntityContainer.Elements)
                {
                    switch (element.ContainerElementKind)
                    {
                    case EdmContainerElementKind.EntitySet:     // entity set
                        IEdmEntitySet entitySet = (IEdmEntitySet)element;
                        tags.Add(new OpenApiTag
                        {
                            Name        = entitySet.Name,
                            Description = context.Model.GetDescriptionAnnotation(entitySet)
                        });
                        break;

                    case EdmContainerElementKind.Singleton:     // singleton
                        IEdmSingleton singleton = (IEdmSingleton)element;
                        tags.Add(new OpenApiTag
                        {
                            Name        = singleton.Name,
                            Description = context.Model.GetDescriptionAnnotation(singleton)
                        });
                        break;

                    // The tags array can contain additional Tag Objects for other logical groups,
                    // e.g. for action imports or function imports that are not associated with an entity set.
                    case EdmContainerElementKind.ActionImport:     // Action Import
                        OpenApiTag actionImportTag = context.CreateOperationImportTag((IEdmActionImport)element);
                        if (actionImportTag != null)
                        {
                            tags.Add(actionImportTag);
                        }
                        break;

                    case EdmContainerElementKind.FunctionImport:     // Function Import
                        OpenApiTag functionImportTag = context.CreateOperationImportTag((IEdmFunctionImport)element);
                        if (functionImportTag != null)
                        {
                            tags.Add(functionImportTag);
                        }
                        break;
                    }
                }
            }

            return(tags);
        }
        private void VerifyMediaEntityPutOperation(string annotation, bool enableOperationId)
        {
            // Arrange
            IEdmModel model = MediaEntityGetOperationHandlerTests.GetEdmModel(annotation);
            OpenApiConvertSettings settings = new OpenApiConvertSettings
            {
                EnableOperationId = enableOperationId
            };

            ODataContext  context = new ODataContext(model, settings);
            IEdmEntitySet todos   = model.EntityContainer.FindEntitySet("Todos");
            IEdmSingleton me      = model.EntityContainer.FindSingleton("me");

            Assert.NotNull(todos);

            IEdmEntityType         todo = model.SchemaElements.OfType <IEdmEntityType>().First(c => c.Name == "Todo");
            IEdmStructuralProperty sp   = todo.StructuralProperties().First(c => c.Name == "Logo");
            ODataPath path = new ODataPath(new ODataNavigationSourceSegment(todos),
                                           new ODataKeySegment(todos.EntityType()),
                                           new ODataStreamPropertySegment(sp.Name));

            IEdmEntityType         user        = model.SchemaElements.OfType <IEdmEntityType>().First(c => c.Name == "user");
            IEdmNavigationProperty navProperty = user.NavigationProperties().First(c => c.Name == "photo");
            ODataPath path2 = new ODataPath(new ODataNavigationSourceSegment(me),
                                            new ODataNavigationPropertySegment(navProperty),
                                            new ODataStreamContentSegment());

            // Act
            var putOperation  = _operationalHandler.CreateOperation(context, path);
            var putOperation2 = _operationalHandler.CreateOperation(context, path2);

            // Assert
            Assert.NotNull(putOperation);
            Assert.NotNull(putOperation2);
            Assert.Equal("Update Logo for Todo in Todos", putOperation.Summary);
            Assert.Equal("Update media content for the navigation property photo in me", putOperation2.Summary);
            Assert.NotNull(putOperation.Tags);
            Assert.NotNull(putOperation2.Tags);

            var tag  = Assert.Single(putOperation.Tags);
            var tag2 = Assert.Single(putOperation2.Tags);

            Assert.Equal("Todos.Todo", tag.Name);
            Assert.Equal("me.profilePhoto", tag2.Name);

            Assert.NotNull(putOperation.Responses);
            Assert.NotNull(putOperation2.Responses);
            Assert.Equal(2, putOperation.Responses.Count);
            Assert.Equal(2, putOperation2.Responses.Count);
            Assert.Equal(new[] { "204", "default" }, putOperation.Responses.Select(r => r.Key));
            Assert.Equal(new[] { "204", "default" }, putOperation2.Responses.Select(r => r.Key));

            if (!string.IsNullOrEmpty(annotation))
            {
                Assert.Equal(2, putOperation.RequestBody.Content.Keys.Count);
                Assert.True(putOperation.RequestBody.Content.ContainsKey("image/png"));
                Assert.True(putOperation.RequestBody.Content.ContainsKey("image/jpeg"));
                Assert.Equal("The logo image.", putOperation.Description);

                Assert.Equal(1, putOperation2.RequestBody.Content.Keys.Count);
                Assert.True(putOperation2.RequestBody.Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
            }
            else
            {
                Assert.Equal(1, putOperation.RequestBody.Content.Keys.Count);
                Assert.Equal(1, putOperation2.RequestBody.Content.Keys.Count);
                Assert.True(putOperation.RequestBody.Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
                Assert.True(putOperation2.RequestBody.Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
            }

            if (enableOperationId)
            {
                Assert.Equal("Todos.Todo.UpdateLogo", putOperation.OperationId);
                Assert.Equal("me.UpdatePhotoContent", putOperation2.OperationId);
            }
            else
            {
                Assert.Null(putOperation.OperationId);
                Assert.Null(putOperation2.OperationId);
            }
        }
        protected override void Initialize(ODataContext context, ODataPath path)
        {
            base.Initialize(context, path);

            _readRestrictions = Context.Model.GetRecord <ReadRestrictionsType>(EntitySet, CapabilitiesConstants.ReadRestrictions);
        }
Exemple #5
0
        public void CreateEntityTypeWithBaseSchemaReturnCorrectSchema()
        {
            // Arrange
            IEdmModel      model   = EdmModelHelper.MultipleInheritanceEdmModel;
            ODataContext   context = new ODataContext(model);
            IEdmEntityType entity  = model.SchemaElements.OfType <IEdmEntityType>().First(t => t.Name == "Human");

            Assert.NotNull(entity); // Guard

            // Act
            var schema = context.CreateStructuredTypeSchema(entity);

            // Assert
            Assert.NotNull(schema);
            Assert.True(String.IsNullOrEmpty(schema.Type));

            Assert.NotNull(schema.AllOf);
            Assert.Null(schema.AnyOf);
            Assert.Null(schema.OneOf);
            Assert.Null(schema.Properties);

            Assert.Equal(2, schema.AllOf.Count);
            var baseSchema = schema.AllOf.First();

            Assert.NotNull(baseSchema.Reference);
            Assert.Equal(ReferenceType.Schema, baseSchema.Reference.Type);
            Assert.Equal("NS.Animal", baseSchema.Reference.Id);

            var declaredSchema = schema.AllOf.Last();

            Assert.Equal("object", declaredSchema.Type);
            Assert.Null(declaredSchema.AllOf);
            Assert.Null(declaredSchema.AnyOf);
            Assert.Null(declaredSchema.OneOf);

            Assert.NotNull(declaredSchema.Properties);
            Assert.Equal(1, declaredSchema.Properties.Count);
            var property = Assert.Single(declaredSchema.Properties);

            Assert.Equal("Name", property.Key);
            Assert.Equal("string", property.Value.Type);
            Assert.Null(property.Value.OneOf);

            Assert.Equal("Entity type 'Human' description.", declaredSchema.Description);
            Assert.Equal("Human", declaredSchema.Title);

            // Act
            string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);

            _output.WriteLine(json);
            // Assert
            Assert.NotNull(json);
            Assert.Equal(@"{
  ""allOf"": [
    {
      ""$ref"": ""#/components/schemas/NS.Animal""
    },
    {
      ""title"": ""Human"",
      ""type"": ""object"",
      ""properties"": {
        ""Name"": {
          ""type"": ""string""
        }
      },
      ""description"": ""Entity type 'Human' description.""
    }
  ],
  ""example"": {
    ""Id"": ""integer (identifier)"",
    ""Age"": ""integer"",
    ""Name"": ""string""
  }
}"
                         .ChangeLineBreaks(), json);
        }
 public static OpenApiSchema CreateSchemaTypeDefinitionSchema(this ODataContext context, IEdmTypeDefinition typeDefinition)
 {
     return(context.CreateSchema(typeDefinition.UnderlyingType));
 }
 public BaseController(ODataContext context)
 {
     _Context = context;
 }
        public void OperationRestrictionsTermWorksToCreateOperationForEdmActionImport(bool enableAnnotation)
        {
            string template = @"<?xml version=""1.0"" encoding=""utf-8""?>
<edmx:Edmx Version=""4.0"" xmlns:edmx=""http://docs.oasis-open.org/odata/ns/edmx"">
  <edmx:DataServices>
    <Schema Namespace=""NS"" xmlns=""http://docs.oasis-open.org/odata/ns/edm"">
      <Action Name=""ResetDataSource"" />
      <EntityContainer Name=""GraphService"">
        <ActionImport Name=""ResetDataSource"" Action=""NS.ResetDataSource"" >
         {0}
        </ActionImport>
      </EntityContainer>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>
";

            string annotation = @"<Annotation Term=""Org.OData.Capabilities.V1.OperationRestrictions"">
  <Record>
    <PropertyValue Property=""Permissions"">
      <Collection>
        <Record>
          <PropertyValue Property=""SchemeName"" String=""Delegated (work or school account)"" />
          <PropertyValue Property=""Scopes"">
            <Collection>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.ReadBasic.All"" />
              </Record>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.Read.All"" />
              </Record>
              <Record>
                <PropertyValue Property=""Scope"" String=""Directory.Read.All"" />
              </Record>
            </Collection>
          </PropertyValue>
        </Record>
        <Record>
          <PropertyValue Property=""SchemeName"" String=""Application"" />
          <PropertyValue Property=""Scopes"">
            <Collection>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.Read.All"" />
              </Record>
              <Record>
                <PropertyValue Property=""Scope"" String=""Directory.Read.All"" />
              </Record>
            </Collection>
          </PropertyValue>
        </Record>
      </Collection>
    </PropertyValue>
    <PropertyValue Property=""CustomQueryOptions"">
      <Collection>
        <Record>
          <PropertyValue Property=""Name"" String=""odata-debug"" />
          <PropertyValue Property=""Description"" String=""Debug support for OData services"" />
          <PropertyValue Property=""Required"" Bool=""false"" />
          <PropertyValue Property=""ExampleValues"">
            <Collection>
              <Record>
                <PropertyValue Property=""Value"" String=""html"" />
                <PropertyValue Property=""Description"" String=""Service responds with self-contained..."" />
              </Record>
              <Record>
                <PropertyValue Property=""Value"" String=""json"" />
                <PropertyValue Property=""Description"" String=""Service responds with JSON document..."" />
              </Record>
            </Collection>
          </PropertyValue>
        </Record>
      </Collection>
    </PropertyValue>
  </Record>
</Annotation>";

            // Arrange
            string csdl = string.Format(template, enableAnnotation ? annotation : "");

            var edmModel = CsdlReader.Parse(XElement.Parse(csdl).CreateReader());

            Assert.NotNull(edmModel);
            IEdmOperationImport operationImport = edmModel.EntityContainer.FindOperationImports("ResetDataSource").FirstOrDefault();

            Assert.NotNull(operationImport);

            ODataContext context = new ODataContext(edmModel);

            ODataPath path = new ODataPath(new ODataOperationImportSegment(operationImport));

            // Act
            var operation = _operationHandler.CreateOperation(context, path);

            // Assert
            Assert.NotNull(operation);
            Assert.NotNull(operation.Security);

            if (enableAnnotation)
            {
                Assert.Equal(2, operation.Security.Count);

                string json = operation.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
                Assert.Contains(@"
  ""security"": [
    {
      ""Delegated (work or school account)"": [
        ""User.ReadBasic.All"",
        ""User.Read.All"",
        ""Directory.Read.All""
      ]
    },
    {
      ""Application"": [
        ""User.Read.All"",
        ""Directory.Read.All""
      ]
    }
  ],".ChangeLineBreaks(), json);

                // with custom query options
                Assert.Contains(@"
  ""parameters"": [
    {
      ""name"": ""odata-debug"",
      ""in"": ""query"",
      ""description"": ""Debug support for OData services"",
      ""schema"": {
        ""type"": ""string""
      },
      ""examples"": {
        ""example-1"": {
          ""description"": ""Service responds with self-contained..."",
          ""value"": ""html""
        },
        ""example-2"": {
          ""description"": ""Service responds with JSON document..."",
          ""value"": ""json""
        }
      }
    }
  ],".ChangeLineBreaks(), json);
            }
            else
            {
                Assert.Empty(operation.Security);
            }
        }
        /// <summary>
        /// Create the collection of <see cref="OpenApiLink"/> object for an entity or collection of entities.
        /// </summary>
        /// <param name="context">The OData context.</param>
        /// <param name="entityType">The Entity type.</param>
        /// <param name ="entityName">The name of the source of the <see cref="IEdmEntityType"/> object.</param>
        /// <param name="entityKind">"The kind of the source of the <see cref="IEdmEntityType"/> object.</param>
        /// <param name="path">The OData path of the operation the links are to be generated for.</param>
        /// <param name="parameters">"Optional: The list of parameters of the incoming operation.</param>
        /// <param name="navPropOperationId">Optional: The operation id of the source of the NavigationProperty object.</param>
        /// <returns>The created dictionary of <see cref="OpenApiLink"/> object.</returns>
        public static IDictionary <string, OpenApiLink> CreateLinks(this ODataContext context,
                                                                    IEdmEntityType entityType, string entityName, string entityKind, ODataPath path,
                                                                    IList <OpenApiParameter> parameters = null, string navPropOperationId = null)
        {
            Utils.CheckArgumentNull(context, nameof(context));
            Utils.CheckArgumentNull(entityType, nameof(entityType));
            Utils.CheckArgumentNullOrEmpty(entityName, nameof(entityName));
            Utils.CheckArgumentNullOrEmpty(entityKind, nameof(entityKind));
            Utils.CheckArgumentNull(path, nameof(path));

            List <string> pathKeyNames = new();

            // Fetch defined Id(s) from incoming parameters (if any)
            if (parameters != null)
            {
                foreach (var parameter in parameters)
                {
                    if (!string.IsNullOrEmpty(parameter.Description) &&
                        parameter.Description.ToLower().Contains("key"))
                    {
                        pathKeyNames.Add(parameter.Name);
                    }
                }
            }

            Dictionary <string, OpenApiLink> links = new();
            bool lastSegmentIsColNavProp           = (path.LastSegment as ODataNavigationPropertySegment)?.NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many;

            // Valid only for non collection-valued navigation properties
            if (!lastSegmentIsColNavProp)
            {
                foreach (IEdmNavigationProperty navProp in entityType.NavigationProperties())
                {
                    string navPropName = navProp.Name;
                    string operationId;
                    string operationPrefix;

                    switch (entityKind)
                    {
                    case "Navigation":     // just for contained navigations
                        operationPrefix = navPropOperationId;
                        break;

                    default:     // EntitySet, Entity, Singleton
                        operationPrefix = entityName;
                        break;
                    }

                    if (navProp.TargetMultiplicity() == EdmMultiplicity.Many)
                    {
                        operationId = operationPrefix + ".List" + Utils.UpperFirstChar(navPropName);
                    }
                    else
                    {
                        operationId = operationPrefix + ".Get" + Utils.UpperFirstChar(navPropName);
                    }

                    OpenApiLink link = new OpenApiLink
                    {
                        OperationId = operationId,
                        Parameters  = new Dictionary <string, RuntimeExpressionAnyWrapper>()
                    };

                    if (pathKeyNames.Any())
                    {
                        foreach (var pathKeyName in pathKeyNames)
                        {
                            link.Parameters[pathKeyName] = new RuntimeExpressionAnyWrapper
                            {
                                Any = new OpenApiString("$request.path." + pathKeyName)
                            };
                        }
                    }

                    links[navProp.Name] = link;
                }
            }

            // Get the Operations and OperationImport paths bound to this (collection of) entity.
            IEnumerable <ODataPath> operationPaths = context.AllPaths.Where(p => (p.Kind.Equals(ODataPathKind.Operation) || p.Kind.Equals(ODataPathKind.OperationImport)) &&
                                                                            path.GetPathItemName().Equals(p.Clone().Pop().GetPathItemName()));

            // Generate links to the Operations and OperationImport operations.
            if (operationPaths.Any())
            {
                foreach (var operationPath in operationPaths)
                {
                    OpenApiLink link = new()
                    {
                        OperationId = string.Join(".", operationPath.Segments.Select(x =>
                        {
                            return(x.Kind.Equals(ODataSegmentKind.Key) ? x.EntityType.Name : x.Identifier);
                        }))
                    };

                    links[operationPath.LastSegment.Identifier] = link;
                }
            }

            return(links);
        }
    }
Exemple #10
0
        private void VerifyEntitySetPostOperation(string annotation, bool enableOperationId, bool hasStream, bool useHTTPStatusCodeClass2XX)
        {
            // Arrange
            IEdmModel              model     = GetEdmModel(annotation, hasStream);
            IEdmEntitySet          entitySet = model.EntityContainer.FindEntitySet("Customers");
            OpenApiConvertSettings settings  = new OpenApiConvertSettings
            {
                EnableOperationId         = enableOperationId,
                UseSuccessStatusCodeRange = useHTTPStatusCodeClass2XX
            };
            ODataContext context = new ODataContext(model, settings);
            ODataPath    path    = new ODataPath(new ODataNavigationSourceSegment(entitySet));

            // Act
            var post = _operationHandler.CreateOperation(context, path);

            // Assert
            Assert.NotNull(post);
            Assert.Equal("Create customer.", post.Summary);
            Assert.Equal("Create a new customer.", post.Description);
            Assert.NotNull(post.Tags);
            var tag = Assert.Single(post.Tags);

            Assert.Equal("Customers.Customer", tag.Name);

            Assert.Empty(post.Parameters);
            Assert.NotNull(post.RequestBody);

            Assert.NotNull(post.Responses);
            Assert.Equal(2, post.Responses.Count);

            var statusCode = useHTTPStatusCodeClass2XX ? Constants.StatusCodeClass2XX : Constants.StatusCode201;

            if (hasStream)
            {
                Assert.NotNull(post.RequestBody);

                if (!string.IsNullOrEmpty(annotation))
                {
                    // RequestBody
                    Assert.Equal(2, post.RequestBody.Content.Keys.Count);
                    Assert.True(post.RequestBody.Content.ContainsKey("application/todo"));
                    Assert.True(post.RequestBody.Content.ContainsKey(Constants.ApplicationJsonMediaType));

                    // Response
                    Assert.Equal(2, post.Responses[statusCode].Content.Keys.Count);
                    Assert.True(post.Responses[statusCode].Content.ContainsKey("application/todo"));
                    Assert.True(post.Responses[statusCode].Content.ContainsKey(Constants.ApplicationJsonMediaType));
                }
                else
                {
                    // RequestBody
                    Assert.Equal(2, post.RequestBody.Content.Keys.Count);
                    Assert.True(post.RequestBody.Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
                    Assert.True(post.RequestBody.Content.ContainsKey(Constants.ApplicationJsonMediaType));

                    // Response
                    Assert.Equal(2, post.Responses[statusCode].Content.Keys.Count);
                    Assert.True(post.Responses[statusCode].Content.ContainsKey(Constants.ApplicationOctetStreamMediaType));
                    Assert.True(post.Responses[statusCode].Content.ContainsKey(Constants.ApplicationJsonMediaType));
                }
            }
            else
            {
                // RequestBody
                Assert.NotNull(post.RequestBody);
                Assert.Equal(1, post.RequestBody.Content.Keys.Count);
                Assert.True(post.RequestBody.Content.ContainsKey(Constants.ApplicationJsonMediaType));

                // Response
                Assert.Equal(1, post.Responses[Constants.StatusCode201].Content.Keys.Count);
                Assert.True(post.Responses[Constants.StatusCode201].Content.ContainsKey(Constants.ApplicationJsonMediaType));
            }

            if (enableOperationId)
            {
                Assert.Equal("Customers.Customer.CreateCustomer", post.OperationId);
            }
            else
            {
                Assert.Null(post.OperationId);
            }
        }
Exemple #11
0
        /// <summary>
        /// Create the list of <see cref="OpenApiSecurityRequirement"/> object.
        /// </summary>
        /// <param name="context">The OData to Open API context.</param>
        /// <param name="securitySchemes">The securitySchemes.</param>
        /// <returns>The created <see cref="OpenApiSecurityRequirement"/> collection.</returns>
        public static IEnumerable <OpenApiSecurityRequirement> CreateSecurityRequirements(this ODataContext context,
                                                                                          IList <SecurityScheme> securitySchemes)
        {
            Utils.CheckArgumentNull(context, nameof(context));

            if (securitySchemes != null)
            {
                foreach (var securityScheme in securitySchemes)
                {
                    yield return(new OpenApiSecurityRequirement
                    {
                        [
                            new OpenApiSecurityScheme
                            {
                                Reference = new OpenApiReference
                                {
                                    Type = ReferenceType.SecurityScheme,
                                    Id = securityScheme.AuthorizationSchemeName
                                }
                            }
                        ] = new List <string>(securityScheme.RequiredScopes ?? new List <string>())
                    });
                }
            }
        }
 public PhonesServices(ODataContext context)
 {
     this.context = context;
 }
 /// <inheritdoc/>
 protected override void Initialize(ODataContext context, ODataPath path)
 {
     ComplexPropertySegment = path.LastSegment as ODataComplexPropertySegment ?? throw Error.ArgumentNull(nameof(path));
 }
        /// <summary>
        /// Create a <see cref="OpenApiSchema"/> for a <see cref="IEdmPrimitiveType"/>.
        /// </summary>
        /// <param name="context">The OData context.</param>
        /// <param name="primitiveType">The Edm primitive type.</param>
        /// <returns>The created <see cref="OpenApiSchema"/>.</returns>
        public static OpenApiSchema CreateSchema(this ODataContext context, IEdmPrimitiveType primitiveType)
        {
            Utils.CheckArgumentNull(context, nameof(context));
            Utils.CheckArgumentNull(primitiveType, nameof(primitiveType));

            // Spec has different configure for double, AnyOf or OneOf?
            OpenApiSchema schema = new OpenApiSchema
            {
                AllOf = null,
                OneOf = null,
                AnyOf = null
            };

            switch (primitiveType.PrimitiveKind)
            {
            case EdmPrimitiveTypeKind.Binary:     // binary
                schema.Type   = "string";
                schema.Format = "base64url";
                break;

            case EdmPrimitiveTypeKind.Boolean:     // boolean
                schema.Type    = "boolean";
                schema.Default = new OpenApiBoolean(false);
                break;

            case EdmPrimitiveTypeKind.Byte:     // byte
                schema.Type   = "integer";
                schema.Format = "uint8";
                break;

            case EdmPrimitiveTypeKind.DateTimeOffset:     // datetime offset
                schema.Type    = "string";
                schema.Format  = "date-time";
                schema.Pattern = "^[0-9]{4,}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]([.][0-9]{1,12})?(Z|[+-][0-9][0-9]:[0-9][0-9])$";
                break;

            case EdmPrimitiveTypeKind.Decimal:     // decimal
                if (context.Settings.IEEE754Compatible)
                {
                    schema.AnyOf = new List <OpenApiSchema>
                    {
                        new OpenApiSchema {
                            Type = "number"
                        },
                        new OpenApiSchema {
                            Type = "string"
                        },
                    };
                }
                else
                {
                    schema.Type = "number";
                }
                schema.Format = "decimal";
                break;

            case EdmPrimitiveTypeKind.Double:     // double
                if (context.Settings.IEEE754Compatible)
                {
                    schema.AnyOf = new List <OpenApiSchema>
                    {
                        new OpenApiSchema {
                            Type = "number"
                        },
                        new OpenApiSchema {
                            Type = "string"
                        },
                        new OpenApiSchema
                        {
                            Enum = new List <IOpenApiAny>
                            {
                                new OpenApiString("-INF"),
                                new OpenApiString("INF"),
                                new OpenApiString("NaN")
                            }
                        }
                    };
                }
                else
                {
                    schema.Type = "number";
                }
                schema.Format = "double";
                break;

            case EdmPrimitiveTypeKind.Single:     // single
                if (context.Settings.IEEE754Compatible)
                {
                    schema.AnyOf = new List <OpenApiSchema>
                    {
                        new OpenApiSchema {
                            Type = "number"
                        },
                        new OpenApiSchema {
                            Type = "string"
                        },
                        new OpenApiSchema
                        {
                            Enum = new List <IOpenApiAny>
                            {
                                new OpenApiString("-INF"),
                                new OpenApiString("INF"),
                                new OpenApiString("NaN")
                            }
                        }
                    };
                }
                else
                {
                    schema.Type = "number";
                }
                schema.Format = "float";
                break;

            case EdmPrimitiveTypeKind.Guid:     // guid
                schema.Type    = "string";
                schema.Format  = "uuid";
                schema.Pattern = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$";
                break;

            case EdmPrimitiveTypeKind.Int16:
                schema.Type    = "integer";
                schema.Format  = "int16";
                schema.Minimum = Int16.MinValue;     // -32768
                schema.Maximum = Int16.MaxValue;     // 32767
                break;

            case EdmPrimitiveTypeKind.Int32:
                schema.Type    = "integer";
                schema.Format  = "int32";
                schema.Minimum = Int32.MinValue;     // -2147483648
                schema.Maximum = Int32.MaxValue;     // 2147483647
                break;

            case EdmPrimitiveTypeKind.Int64:
                if (context.Settings.IEEE754Compatible)
                {
                    schema.AnyOf = new List <OpenApiSchema>
                    {
                        new OpenApiSchema {
                            Type = "integer"
                        },
                        new OpenApiSchema {
                            Type = "string"
                        }
                    };
                }
                else
                {
                    schema.Type = "integer";
                }

                schema.Format = "int64";
                break;

            case EdmPrimitiveTypeKind.SByte:
                schema.Type    = "integer";
                schema.Format  = "int8";
                schema.Minimum = SByte.MinValue;     // -128
                schema.Maximum = SByte.MaxValue;     // 127
                break;

            case EdmPrimitiveTypeKind.String:     // string
                schema.Type = "string";
                break;

            case EdmPrimitiveTypeKind.Stream:     // stream
                schema.Type   = "string";
                schema.Format = "base64url";
                break;

            case EdmPrimitiveTypeKind.Duration:     // duration
                schema.Type    = "string";
                schema.Format  = "duration";
                schema.Pattern = "^-?P([0-9]+D)?(T([0-9]+H)?([0-9]+M)?([0-9]+([.][0-9]+)?S)?)?$";
                break;

            case EdmPrimitiveTypeKind.Date:
                schema.Type    = "string";
                schema.Format  = "date";
                schema.Pattern = "^[0-9]{4,}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$";
                break;

            case EdmPrimitiveTypeKind.TimeOfDay:
                schema.Type    = "string";
                schema.Format  = "time";
                schema.Pattern = "^([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]([.][0-9]{1,12})?$";
                break;

            case EdmPrimitiveTypeKind.Geography:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.Geography"
                };
                break;

            case EdmPrimitiveTypeKind.GeographyPoint:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.GeographyPoint"
                };
                break;

            case EdmPrimitiveTypeKind.GeographyLineString:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.GeographyLineString"
                };
                break;

            case EdmPrimitiveTypeKind.GeographyPolygon:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.GeographyPolygon"
                };
                break;

            case EdmPrimitiveTypeKind.GeographyCollection:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.GeographyCollection"
                };
                break;

            case EdmPrimitiveTypeKind.GeographyMultiPolygon:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.GeographyMultiPolygon"
                };
                break;

            case EdmPrimitiveTypeKind.GeographyMultiLineString:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.GeographyMultiLineString"
                };
                break;

            case EdmPrimitiveTypeKind.GeographyMultiPoint:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.GeographyMultiPoint"
                };
                break;

            case EdmPrimitiveTypeKind.Geometry:     // Geometry
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.Geometry"
                };
                break;

            case EdmPrimitiveTypeKind.GeometryPoint:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.GeometryPoint"
                };
                break;

            case EdmPrimitiveTypeKind.GeometryLineString:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.GeometryLineString"
                };
                break;

            case EdmPrimitiveTypeKind.GeometryPolygon:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.GeometryPolygon"
                };
                break;

            case EdmPrimitiveTypeKind.GeometryCollection:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.GeometryCollection"
                };
                break;

            case EdmPrimitiveTypeKind.GeometryMultiPolygon:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.GeometryMultiPolygon"
                };
                break;

            case EdmPrimitiveTypeKind.GeometryMultiLineString:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.GeometryMultiLineString"
                };
                break;

            case EdmPrimitiveTypeKind.GeometryMultiPoint:
                schema.Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id   = "Edm.GeometryMultiPoint"
                };
                break;

            case EdmPrimitiveTypeKind.None:
            default:
                throw new OpenApiException(String.Format(SRResource.NotSupportedEdmTypeKind, primitiveType.PrimitiveKind));
            }

            return(schema);
        }
Exemple #15
0
        public void OperationRestrictionsTermWorksToCreateOperationForEdmAction(bool enableAnnotation)
        {
            string template = @"<?xml version=""1.0"" encoding=""utf-8""?>
<edmx:Edmx Version=""4.0"" xmlns:edmx=""http://docs.oasis-open.org/odata/ns/edmx"">
  <edmx:DataServices>
    <Schema Namespace=""NS"" xmlns=""http://docs.oasis-open.org/odata/ns/edm"">
      <EntityType Name=""user"" />
      <Action Name=""getMemberGroups"" IsBound=""true"">
        <Parameter Name=""bindingParameter"" Type=""NS.user"" Nullable=""false"" />
          {0}
      </Action>
      <EntityContainer Name=""GraphService"">
        <Singleton Name=""me"" Type=""NS.user"" />
      </EntityContainer>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>
";

            string annotation = @"<Annotation Term=""Org.OData.Capabilities.V1.OperationRestrictions"">
  <Record>
    <PropertyValue Property=""Permissions"">
      <Collection>
        <Record>
          <PropertyValue Property=""SchemeName"" String=""Delegated (work or school account)"" />
          <PropertyValue Property=""Scopes"">
            <Collection>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.ReadBasic.All"" />
              </Record>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.Read.All"" />
              </Record>
              <Record>
                <PropertyValue Property=""Scope"" String=""Directory.Read.All"" />
              </Record>
            </Collection>
          </PropertyValue>
        </Record>
        <Record>
          <PropertyValue Property=""SchemeName"" String=""Application"" />
          <PropertyValue Property=""Scopes"">
            <Collection>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.Read.All"" />
              </Record>
              <Record>
                <PropertyValue Property=""Scope"" String=""Directory.Read.All"" />
              </Record>
            </Collection>
          </PropertyValue>
        </Record>
      </Collection>
    </PropertyValue>
  </Record>
</Annotation>";

            // Arrange
            string csdl = string.Format(template, enableAnnotation ? annotation : "");

            var edmModel = CsdlReader.Parse(XElement.Parse(csdl).CreateReader());

            Assert.NotNull(edmModel);
            IEdmSingleton me = edmModel.EntityContainer.FindSingleton("me");

            Assert.NotNull(me);
            IEdmAction action = edmModel.SchemaElements.OfType <IEdmAction>().SingleOrDefault();

            Assert.NotNull(action);
            Assert.Equal("getMemberGroups", action.Name);

            ODataContext context = new ODataContext(edmModel);

            ODataPath path = new ODataPath(new ODataNavigationSourceSegment(me),
                                           new ODataOperationSegment(action));

            // Act
            var operation = _operationHandler.CreateOperation(context, path);

            // Assert
            Assert.NotNull(operation);
            Assert.NotNull(operation.Security);

            if (enableAnnotation)
            {
                Assert.Equal(2, operation.Security.Count);

                string json = operation.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
                Assert.Contains(@"
  ""security"": [
    {
      ""Delegated (work or school account)"": [
        ""User.ReadBasic.All"",
        ""User.Read.All"",
        ""Directory.Read.All""
      ]
    },
    {
      ""Application"": [
        ""User.Read.All"",
        ""Directory.Read.All""
      ]
    }
  ],".ChangeLineBreaks(), json);
            }
            else
            {
                Assert.Empty(operation.Security);
            }
        }
Exemple #16
0
        /// <summary>
        /// Create the dictionary of <see cref="OpenApiSecurityScheme"/> object.
        /// The name of each pair is the name of authorization. The value of each pair is a <see cref="OpenApiSecurityScheme"/>.
        /// </summary>
        /// <param name="context">The OData to Open API context.</param>
        /// <returns>The string/security scheme dictionary.</returns>
        public static IDictionary <string, OpenApiSecurityScheme> CreateSecuritySchemes(this ODataContext context)
        {
            Utils.CheckArgumentNull(context, nameof(context));

            if (context.Model == null || context.Model.EntityContainer == null)
            {
                return(null);
            }

            IDictionary <string, OpenApiSecurityScheme> securitySchemes = new Dictionary <string, OpenApiSecurityScheme>();
            var authorizations = context.Model.GetAuthorizations(context.EntityContainer);

            if (authorizations == null)
            {
                return(securitySchemes);
            }

            foreach (var authorization in authorizations)
            {
                OpenApiSecurityScheme scheme = new OpenApiSecurityScheme
                {
                    Type        = authorization.SchemeType,
                    Description = authorization.Description
                };

                switch (authorization.SchemeType)
                {
                case SecuritySchemeType.ApiKey:     // ApiKey
                    AppendApiKey(scheme, (ApiKey)authorization);
                    break;

                case SecuritySchemeType.Http:     // Http
                    AppendHttp(scheme, (Http)authorization);
                    break;

                case SecuritySchemeType.OpenIdConnect:     // OpenIdConnect
                    AppendOpenIdConnect(scheme, (OpenIDConnect)authorization);
                    break;

                case SecuritySchemeType.OAuth2:     // OAuth2
                    AppendOAuth2(scheme, (OAuthAuthorization)authorization);
                    break;
                }

                securitySchemes[authorization.Name] = scheme;
            }

            return(securitySchemes);
        }
        /// <summary>
        /// Create a map of string/<see cref="OpenApiSchema"/> map for a <see cref="IEdmStructuredType"/>'s all properties.
        /// </summary>
        /// <param name="context">The OData context.</param>
        /// <param name="structuredType">The Edm structured type.</param>
        /// <returns>The created map of <see cref="OpenApiSchema"/>.</returns>
        public static IDictionary <string, OpenApiSchema> CreateStructuredTypePropertiesSchema(this ODataContext context, IEdmStructuredType structuredType)
        {
            Utils.CheckArgumentNull(context, nameof(context));
            Utils.CheckArgumentNull(structuredType, nameof(structuredType));

            // The name is the property name, the value is a Schema Object describing the allowed values of the property.
            IDictionary <string, OpenApiSchema> properties = new Dictionary <string, OpenApiSchema>();

            // structure properties
            foreach (var property in structuredType.DeclaredStructuralProperties())
            {
                // OpenApiSchema propertySchema = property.Type.CreateSchema();
                // propertySchema.Default = property.DefaultValueString != null ? new OpenApiString(property.DefaultValueString) : null;
                properties.Add(property.Name, context.CreatePropertySchema(property));
            }

            // navigation properties
            foreach (var property in structuredType.DeclaredNavigationProperties())
            {
                OpenApiSchema propertySchema = context.CreateEdmTypeSchema(property.Type);
                propertySchema.Description = context.Model.GetDescriptionAnnotation(property);
                properties.Add(property.Name, propertySchema);
            }

            return(properties);
        }
Exemple #18
0
 protected ODataTestes()
 {
     context = CreateContext();
 }
        private static OpenApiSchema CreateStructuredTypeSchema(this ODataContext context, IEdmStructuredType structuredType, bool processBase, bool processExample)
        {
            Debug.Assert(context != null);
            Debug.Assert(structuredType != null);

            if (processBase && structuredType.BaseType != null)
            {
                // A structured type with a base type is represented as a Schema Object
                // that contains the keyword allOf whose value is an array with two items:
                return(new OpenApiSchema
                {
                    AllOf = new List <OpenApiSchema>
                    {
                        // 1. a JSON Reference to the Schema Object of the base type
                        new OpenApiSchema
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.Schema,
                                Id = structuredType.BaseType.FullTypeName()
                            }
                        },

                        // 2. a Schema Object describing the derived type
                        context.CreateStructuredTypeSchema(structuredType, false, false)
                    },

                    AnyOf = null,
                    OneOf = null,
                    Properties = null,
                    Example = CreateStructuredTypePropertiesExample(context, structuredType)
                });
            }
            else
            {
                // A structured type without a base type is represented as a Schema Object of type object
                OpenApiSchema schema = new OpenApiSchema
                {
                    Title = (structuredType as IEdmSchemaElement)?.Name,

                    Type = "object",

                    // Each structural property and navigation property is represented
                    // as a name/value pair of the standard OpenAPI properties object.
                    Properties = context.CreateStructuredTypePropertiesSchema(structuredType),

                    // make others null
                    AllOf = null,
                    OneOf = null,
                    AnyOf = null
                };

                // It optionally can contain the field description,
                // whose value is the value of the unqualified annotation Core.Description of the structured type.
                if (structuredType.TypeKind == EdmTypeKind.Complex)
                {
                    IEdmComplexType complex = (IEdmComplexType)structuredType;
                    schema.Description = context.Model.GetDescriptionAnnotation(complex);
                }
                else if (structuredType.TypeKind == EdmTypeKind.Entity)
                {
                    IEdmEntityType entity = (IEdmEntityType)structuredType;
                    schema.Description = context.Model.GetDescriptionAnnotation(entity);
                }

                if (processExample)
                {
                    schema.Example = CreateStructuredTypePropertiesExample(context, structuredType);
                }

                return(schema);
            }
        }
Exemple #20
0
        protected override void Initialize(ODataContext context, ODataPath path)
        {
            base.Initialize(context, path);

            _updateRestrictions = Context.Model.GetRecord <UpdateRestrictionsType>(Singleton, CapabilitiesConstants.UpdateRestrictions);
        }
Exemple #21
0
 public PeopleServices(ODataContext context)
 {
     this.context = context;
 }
 /// <summary>
 /// Initialize the handler.
 /// It should be call ahead of in derived class.
 /// </summary>
 /// <param name="context">The context.</param>
 /// <param name="path">The path.</param>
 protected virtual void Initialize(ODataContext context, ODataPath path)
 {
 }
Exemple #23
0
        public void CreateComplexTypeWithBaseSchemaReturnCorrectSchema()
        {
            // Arrange
            IEdmModel    model   = EdmModelHelper.MultipleInheritanceEdmModel;
            ODataContext context = new ODataContext(model, new OpenApiConvertSettings
            {
                IEEE754Compatible = true
            });
            IEdmComplexType complex = model.SchemaElements.OfType <IEdmComplexType>().First(t => t.Name == "Tree");

            Assert.NotNull(complex); // Guard

            // Act
            var schema = context.CreateStructuredTypeSchema(complex);

            // Assert
            Assert.NotNull(schema);
            Assert.True(String.IsNullOrEmpty(schema.Type));

            Assert.NotNull(schema.AllOf);
            Assert.Null(schema.AnyOf);
            Assert.Null(schema.OneOf);
            Assert.Null(schema.Properties);

            Assert.Equal(2, schema.AllOf.Count);
            var baseSchema = schema.AllOf.First();

            Assert.NotNull(baseSchema.Reference);
            Assert.Equal(ReferenceType.Schema, baseSchema.Reference.Type);
            Assert.Equal("NS.LandPlant", baseSchema.Reference.Id);

            var declaredSchema = schema.AllOf.Last();

            Assert.Equal("object", declaredSchema.Type);
            Assert.Null(declaredSchema.AllOf);
            Assert.Null(declaredSchema.AnyOf);
            Assert.Null(declaredSchema.OneOf);

            Assert.NotNull(declaredSchema.Properties);
            Assert.Equal(1, declaredSchema.Properties.Count);
            var property = Assert.Single(declaredSchema.Properties);

            Assert.Equal("Price", property.Key);
            Assert.Equal("decimal", property.Value.Format);
            Assert.NotNull(property.Value.AnyOf);
            Assert.Equal(new string[] { "number", "string" }, property.Value.AnyOf.Select(e => e.Type));

            Assert.Equal("Complex type 'Tree' description.", declaredSchema.Description);
            Assert.Equal("Tree", declaredSchema.Title);

            // Act
            string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);

            // Assert
            Assert.NotNull(json);
            Assert.Equal(@"{
  ""allOf"": [
    {
      ""$ref"": ""#/components/schemas/NS.LandPlant""
    },
    {
      ""title"": ""Tree"",
      ""type"": ""object"",
      ""properties"": {
        ""Price"": {
          ""multipleOf"": 1,
          ""anyOf"": [
            {
              ""type"": ""number""
            },
            {
              ""type"": ""string""
            }
          ],
          ""format"": ""decimal""
        }
      },
      ""description"": ""Complex type 'Tree' description.""
    }
  ],
  ""example"": {
    ""Color"": {
      ""@odata.type"": ""NS.Color""
    },
    ""Continent"": {
      ""@odata.type"": ""NS.Continent""
    },
    ""Name"": ""string"",
    ""Price"": ""decimal""
  }
}"
                         .ChangeLineBreaks(), json);
        }
Exemple #24
0
        public void CreateEntitySetGetOperationReturnsSecurityForReadRestrictions(bool enableAnnotation)
        {
            string annotation = @"<Annotation Term=""Org.OData.Capabilities.V1.ReadRestrictions"">
  <Record>
    <PropertyValue Property=""Permissions"">
      <Collection>
        <Record>
          <PropertyValue Property=""SchemeName"" String=""Delegated (work or school account)"" />
          <PropertyValue Property=""Scopes"">
            <Collection>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.ReadBasic.All"" />
              </Record>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.Read.All"" />
              </Record>
            </Collection>
          </PropertyValue>
        </Record>
        <Record>
          <PropertyValue Property=""SchemeName"" String=""Application"" />
          <PropertyValue Property=""Scopes"">
            <Collection>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.Read.All"" />
              </Record>
              <Record>
                <PropertyValue Property=""Scope"" String=""Directory.Read.All"" />
              </Record>
            </Collection>
          </PropertyValue>
        </Record>
      </Collection>
    </PropertyValue>
    <PropertyValue Property=""CustomHeaders"">
      <Collection>
        <Record>
          <PropertyValue Property=""Name"" String=""odata-debug"" />
          <PropertyValue Property=""Description"" String=""Debug support for OData services"" />
          <PropertyValue Property=""Required"" Bool=""false"" />
          <PropertyValue Property=""ExampleValues"">
            <Collection>
              <Record>
                <PropertyValue Property=""Value"" String=""html"" />
                <PropertyValue Property=""Description"" String=""Service responds with self-contained..."" />
              </Record>
              <Record>
                <PropertyValue Property=""Value"" String=""json"" />
                <PropertyValue Property=""Description"" String=""Service responds with JSON document..."" />
              </Record>
            </Collection>
          </PropertyValue>
        </Record>
      </Collection>
    </PropertyValue>
  </Record>
</Annotation>";

            // Arrange
            IEdmModel     model     = GetEdmModel(enableAnnotation ? annotation : "");
            ODataContext  context   = new ODataContext(model);
            IEdmEntitySet customers = model.EntityContainer.FindEntitySet("Customers");

            Assert.NotNull(customers); // guard
            ODataPath path = new ODataPath(new ODataNavigationSourceSegment(customers));

            // Act
            var get = _operationHandler.CreateOperation(context, path);

            // Assert
            Assert.NotNull(get);
            Assert.NotNull(get.Security);

            if (enableAnnotation)
            {
                Assert.Equal(2, get.Security.Count);

                string json = get.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
                Assert.Contains(@"
  ""security"": [
    {
      ""Delegated (work or school account)"": [
        ""User.ReadBasic.All"",
        ""User.Read.All""
      ]
    },
    {
      ""Application"": [
        ""User.Read.All"",
        ""Directory.Read.All""
      ]
    }
  ]".ChangeLineBreaks(), json);
            }
            else
            {
                Assert.Empty(get.Security);
            }
        }
 public PersonNumbersServices(ODataContext context)
 {
     this.context = context;
 }
        /// <summary>
        /// Create $orderby parameter for the <see cref="IEdmEntitySet"/>.
        /// </summary>
        /// <param name="context">The OData context.</param>
        /// <param name="target">The Edm annotation target.</param>
        /// <returns>The created <see cref="OpenApiParameter"/> or null.</returns>
        public static OpenApiParameter CreateOrderBy(this ODataContext context, IEdmVocabularyAnnotatable target, IEdmEntityType entityType)
        {
            Utils.CheckArgumentNull(context, nameof(context));
            Utils.CheckArgumentNull(target, nameof(target));
            Utils.CheckArgumentNull(entityType, nameof(entityType));

            SortRestrictionsType sort = context.Model.GetRecord <SortRestrictionsType>(target, CapabilitiesConstants.SortRestrictions);

            if (sort != null && !sort.IsSortable)
            {
                return(null);
            }

            IList <IOpenApiAny> orderByItems = new List <IOpenApiAny>();

            foreach (var property in entityType.StructuralProperties())
            {
                if (sort != null && sort.IsNonSortableProperty(property.Name))
                {
                    continue;
                }

                bool isAscOnly = sort != null?sort.IsAscendingOnlyProperty(property.Name) : false;

                bool isDescOnly = sort != null?sort.IsDescendingOnlyProperty(property.Name) : false;

                if (isAscOnly || isDescOnly)
                {
                    if (isAscOnly)
                    {
                        orderByItems.Add(new OpenApiString(property.Name));
                    }
                    else
                    {
                        orderByItems.Add(new OpenApiString(property.Name + " desc"));
                    }
                }
                else
                {
                    orderByItems.Add(new OpenApiString(property.Name));
                    orderByItems.Add(new OpenApiString(property.Name + " desc"));
                }
            }

            return(new OpenApiParameter
            {
                Name = "$orderby",
                In = ParameterLocation.Query,
                Description = "Order items by property values",
                Schema = new OpenApiSchema
                {
                    Type = "array",
                    UniqueItems = true,
                    Items = new OpenApiSchema
                    {
                        Type = "string",
                        Enum = orderByItems
                    }
                },
                Style = ParameterStyle.Form
            });
        }
        /// <summary>
        /// Create the collection of <see cref="OpenApiLink"/> object.
        /// </summary>
        /// <param name="context">The OData context.</param>
        /// <param name="entityType">The Entity type.</param>
        /// <param name ="entityName">The name of the source of the <see cref="IEdmEntityType"/> object.</param>
        /// <param name="entityKind">"The kind of the source of the <see cref="IEdmEntityType"/> object.</param>
        /// <param name="parameters">"The list of parameters of the incoming operation.</param>
        /// <param name="navPropOperationId">Optional parameter: The operation id of the source of the NavigationProperty object.</param>
        /// <returns>The created dictionary of <see cref="OpenApiLink"/> object.</returns>
        public static IDictionary <string, OpenApiLink> CreateLinks(this ODataContext context,
                                                                    IEdmEntityType entityType, string entityName, string entityKind,
                                                                    IList <OpenApiParameter> parameters, string navPropOperationId = null)
        {
            IDictionary <string, OpenApiLink> links = new Dictionary <string, OpenApiLink>();

            Utils.CheckArgumentNull(context, nameof(context));
            Utils.CheckArgumentNull(entityType, nameof(entityType));
            Utils.CheckArgumentNullOrEmpty(entityName, nameof(entityName));
            Utils.CheckArgumentNullOrEmpty(entityKind, nameof(entityKind));
            Utils.CheckArgumentNull(parameters, nameof(parameters));

            List <string> pathKeyNames = new List <string>();

            // Fetch defined Id(s) from incoming parameters (if any)
            foreach (var parameter in parameters)
            {
                if (!string.IsNullOrEmpty(parameter.Description) &&
                    parameter.Description.ToLower().Contains("key"))
                {
                    pathKeyNames.Add(parameter.Name);
                }
            }

            foreach (IEdmNavigationProperty navProp in entityType.NavigationProperties())
            {
                string navPropName = navProp.Name;
                string operationId;
                string operationPrefix;

                switch (entityKind)
                {
                case "Navigation":     // just for contained navigations
                    operationPrefix = navPropOperationId;
                    break;

                default:     // EntitySet, Entity, Singleton
                    operationPrefix = entityName;
                    break;
                }

                if (navProp.TargetMultiplicity() == EdmMultiplicity.Many)
                {
                    operationId = operationPrefix + ".List" + Utils.UpperFirstChar(navPropName);
                }
                else
                {
                    operationId = operationPrefix + ".Get" + Utils.UpperFirstChar(navPropName);
                }

                OpenApiLink link = new OpenApiLink
                {
                    OperationId = operationId,
                    Parameters  = new Dictionary <string, RuntimeExpressionAnyWrapper>()
                };

                if (pathKeyNames.Any())
                {
                    foreach (var pathKeyName in pathKeyNames)
                    {
                        link.Parameters[pathKeyName] = new RuntimeExpressionAnyWrapper
                        {
                            Any = new OpenApiString("$request.path." + pathKeyName)
                        };
                    }
                }

                links[navProp.Name] = link;
            }

            return(links);
        }
        private static OpenApiSchema CreateStructuredTypeSchema(this ODataContext context, IEdmStructuredType structuredType, bool processBase, bool processExample,
                                                                IEnumerable <IEdmEntityType> derivedTypes = null)
        {
            Debug.Assert(context != null);
            Debug.Assert(structuredType != null);

            if (context.Settings.EnableDiscriminatorValue && derivedTypes == null)
            {
                derivedTypes = context.Model.FindDirectlyDerivedTypes(structuredType).OfType <IEdmEntityType>();
            }

            if (processBase && structuredType.BaseType != null)
            {
                // The x-ms-discriminator-value extension is added to structured types which are derived types.
                Dictionary <string, IOpenApiExtension> extension = null;
                if (context.Settings.EnableDiscriminatorValue && !derivedTypes.Any())
                {
                    extension = new Dictionary <string, IOpenApiExtension>
                    {
                        { Constants.xMsDiscriminatorValue, new OpenApiString("#" + structuredType.FullTypeName()) }
                    };
                }

                // A structured type with a base type is represented as a Schema Object
                // that contains the keyword allOf whose value is an array with two items:
                return(new OpenApiSchema
                {
                    Extensions = extension,

                    AllOf = new List <OpenApiSchema>
                    {
                        // 1. a JSON Reference to the Schema Object of the base type
                        new OpenApiSchema
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.Schema,
                                Id = structuredType.BaseType.FullTypeName()
                            }
                        },

                        // 2. a Schema Object describing the derived type
                        context.CreateStructuredTypeSchema(structuredType, false, false, derivedTypes)
                    },

                    AnyOf = null,
                    OneOf = null,
                    Properties = null,
                    Example = CreateStructuredTypePropertiesExample(context, structuredType)
                });
            }
            else
            {
                // The discriminator object is added to structured types which have derived types.
                OpenApiDiscriminator discriminator = null;
                if (context.Settings.EnableDiscriminatorValue && derivedTypes.Any() && structuredType.BaseType != null)
                {
                    discriminator = new OpenApiDiscriminator
                    {
                        PropertyName = "@odata.type"
                    };
                }

                // A structured type without a base type is represented as a Schema Object of type object
                OpenApiSchema schema = new OpenApiSchema
                {
                    Title = (structuredType as IEdmSchemaElement)?.Name,

                    Type = "object",

                    Discriminator = discriminator,

                    // Each structural property and navigation property is represented
                    // as a name/value pair of the standard OpenAPI properties object.
                    Properties = context.CreateStructuredTypePropertiesSchema(structuredType),

                    // make others null
                    AllOf = null,
                    OneOf = null,
                    AnyOf = null
                };

                // It optionally can contain the field description,
                // whose value is the value of the unqualified annotation Core.Description of the structured type.
                if (structuredType.TypeKind == EdmTypeKind.Complex)
                {
                    IEdmComplexType complex = (IEdmComplexType)structuredType;
                    schema.Description = context.Model.GetDescriptionAnnotation(complex);
                }
                else if (structuredType.TypeKind == EdmTypeKind.Entity)
                {
                    IEdmEntityType entity = (IEdmEntityType)structuredType;
                    schema.Description = context.Model.GetDescriptionAnnotation(entity);
                }

                if (processExample)
                {
                    schema.Example = CreateStructuredTypePropertiesExample(context, structuredType);
                }

                return(schema);
            }
        }
Exemple #29
0
        /// <summary>
        /// Adds a mapping of custom extension values against custom attribute values for a given element to the provided
        /// extensions object.
        /// </summary>
        /// <param name="extensions">The target extensions object in which the mapped extensions and custom attribute
        /// values will be added to.</param>
        /// <param name="context">The OData context.</param>
        /// <param name="element">The target element.</param>
        internal static void AddCustomAtributesToExtensions(this IDictionary <string, IOpenApiExtension> extensions, ODataContext context, IEdmElement element)
        {
            if (extensions == null ||
                context == null ||
                element == null)
            {
                return;
            }

            Dictionary <string, string> atrributesValueMap = GetCustomXMLAtrributesValueMapping(context.Model, element, context.Settings.CustomXMLAttributesMapping);

            if (atrributesValueMap?.Any() ?? false)
            {
                foreach (var item in atrributesValueMap)
                {
                    extensions.TryAdd(item.Key, new OpenApiString(item.Value));
                }
            }
        }
        public void ReadRestrictionsTermWorksToCreateOperationForSingletonGetOperation(bool enableAnnotation)
        {
            string annotation = @"<Annotation Term=""Org.OData.Capabilities.V1.ReadRestrictions"">
  <Record>
    <PropertyValue Property=""Permissions"">
      <Collection>
        <Record>
          <PropertyValue Property=""SchemeName"" String=""Delegated (work or school account)"" />
          <PropertyValue Property=""Scopes"">
            <Collection>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.ReadBasic.All"" />
              </Record>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.Read.All"" />
              </Record>
            </Collection>
          </PropertyValue>
        </Record>
        <Record>
          <PropertyValue Property=""SchemeName"" String=""Application"" />
          <PropertyValue Property=""Scopes"">
            <Collection>
              <Record>
                <PropertyValue Property=""Scope"" String=""User.Read.All"" />
              </Record>
              <Record>
                <PropertyValue Property=""Scope"" String=""Directory.Read.All"" />
              </Record>
            </Collection>
          </PropertyValue>
        </Record>
      </Collection>
    </PropertyValue>
    <PropertyValue Property=""Description"" String=""A brief description of GET '/me' request."" />
    <PropertyValue Property=""CustomHeaders"">
      <Collection>
        <Record>
          <PropertyValue Property=""Name"" String=""odata-debug"" />
          <PropertyValue Property=""Description"" String=""Debug support for OData services"" />
          <PropertyValue Property=""Required"" Bool=""false"" />
          <PropertyValue Property=""ExampleValues"">
            <Collection>
              <Record>
                <PropertyValue Property=""Value"" String=""html"" />
                <PropertyValue Property=""Description"" String=""Service responds with self-contained..."" />
              </Record>
              <Record>
                <PropertyValue Property=""Value"" String=""json"" />
                <PropertyValue Property=""Description"" String=""Service responds with JSON document..."" />
              </Record>
            </Collection>
          </PropertyValue>
        </Record>
      </Collection>
    </PropertyValue>
  </Record>
</Annotation>";

            // Arrange
            var edmModel = GetEdmModel(enableAnnotation ? annotation : "");

            Assert.NotNull(edmModel);
            IEdmSingleton me = edmModel.EntityContainer.FindSingleton("Me");

            Assert.NotNull(me);

            ODataContext context = new ODataContext(edmModel);

            ODataPath path = new ODataPath(new ODataNavigationSourceSegment(me));

            // Act
            var operation = _operationHandler.CreateOperation(context, path);

            // Assert
            Assert.NotNull(operation);
            Assert.NotNull(operation.Security);

            if (enableAnnotation)
            {
                Assert.Equal(2, operation.Security.Count);

                string json = operation.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
                Assert.Contains(@"
  ""security"": [
    {
      ""Delegated (work or school account)"": [
        ""User.ReadBasic.All"",
        ""User.Read.All""
      ]
    },
    {
      ""Application"": [
        ""User.Read.All"",
        ""Directory.Read.All""
      ]
    }
  ],".ChangeLineBreaks(), json);

                // with custom header
                Assert.Contains(@"
  ""parameters"": [
    {
      ""name"": ""odata-debug"",
      ""in"": ""header"",
      ""description"": ""Debug support for OData services"",
      ""schema"": {
        ""type"": ""string""
      },
      ""examples"": {
        ""example-1"": {
          ""description"": ""Service responds with self-contained..."",
          ""value"": ""html""
        },
        ""example-2"": {
          ""description"": ""Service responds with JSON document..."",
          ""value"": ""json""
        }
      }
    },
    {".ChangeLineBreaks(), json);
            }
            else
            {
                Assert.Empty(operation.Security);
            }
        }