{"id":15436405,"url":"https://github.com/gregsdennis/graeae","last_synced_at":"2025-04-19T18:25:00.479Z","repository":{"id":186255491,"uuid":"645073836","full_name":"gregsdennis/Graeae","owner":"gregsdennis","description":"OpenAPI support powered by json-everything","archived":false,"fork":false,"pushed_at":"2024-11-07T20:16:28.000Z","size":427,"stargazers_count":4,"open_issues_count":3,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-29T11:41:27.400Z","etag":null,"topics":["openapi-specification","openapi31"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gregsdennis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-24T21:39:27.000Z","updated_at":"2025-03-12T19:09:33.000Z","dependencies_parsed_at":"2023-12-02T03:27:27.314Z","dependency_job_id":"0ba4814b-9980-424f-acfa-7eb247485980","html_url":"https://github.com/gregsdennis/Graeae","commit_stats":null,"previous_names":["gregsdennis/openapi","gregsdennis/graeae"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gregsdennis%2FGraeae","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gregsdennis%2FGraeae/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gregsdennis%2FGraeae/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gregsdennis%2FGraeae/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gregsdennis","download_url":"https://codeload.github.com/gregsdennis/Graeae/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249762398,"owners_count":21321928,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["openapi-specification","openapi31"],"created_at":"2024-10-01T18:50:26.788Z","updated_at":"2025-04-19T18:25:00.455Z","avatar_url":"https://github.com/gregsdennis.png","language":"C#","readme":"***NOTICE** This library was built as a proof-of-concept to show how _JsonSchema.Net_ could be used to support the more commonly used [_OpenApi.Net_](https://github.com/microsoft/OpenAPI.NET).*\n\n[![Build \u0026 Test](https://github.com/gregsdennis/Graeae/actions/workflows/dotnet-core.yml/badge.svg?branch=main\u0026event=push)](https://github.com/gregsdennis/Graeae/actions/workflows/dotnet-core.yml)\n[![Percentage of issues still open](http://isitmaintained.com/badge/open/gregsdennis/Graeae.svg)](http://isitmaintained.com/project/gregsdennis/Graeae \"Percentage of issues still open\")\n[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/gregsdennis/Graeae.svg)](http://isitmaintained.com/project/gregsdennis/Graeae \"Average time to resolve an issue\")\n[![License](https://img.shields.io/github/license/gregsdennis/Graeae)](https://github.com/gregsdennis/Graeae/blob/main/LICENSE)\n\u003c!-- [![Test results](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/gregsdennis/28607f2d276032f4d9a7f2c807e44df7/raw/test-results-badge.json)](https://github.com/gregsdennis/json-everything/actions?query=workflow%3A%22Build+%26+Test%22) --\u003e\n\n# Graeae.Models\n\n[![](https://img.shields.io/nuget/vpre/Graeae.Models.svg?svg=true) ![](https://img.shields.io/nuget/dt/Graeae.Models.svg?svg=true)](https://www.nuget.org/packages/Graeae.Models)\n\nOpenAPI models for System.Text.Json.  Supports specification versions v3.0.x \u0026 v3.1.\n\nThis project is supported by the [`json-everything`](https://github.com/gregsdennis/json-everything) project:\n\n- JSON Schema support provided by [JsonSchema.Net](https://www.nuget.org/packages/JsonSchema.Net)\n- YAML support provided by [Yaml2JsonNode](https://www.nuget.org/packages/Yaml2JsonNode)\n\n## Why \"Graeae\"?\n\nGraeae (pronounced \"gray-eye\") is the collective name of the sisters in Greek mythology who share an eye, often referred to as the Fates.  Because these sisters share an eye, there must be some common interface between the sisters and the eye.\n\nIn terms of web development the eye is an API, the sisters are clients, and that common interface is an OpenAPI document.\n\n## Usage\n\nThe library supports OpenAPI v3.1 (de)serialization out of the box.\n\n```c#\n// read from a file\nvar yamlText = File.ReadAllText(\"openapi.yaml\");\nvar openApiDoc = YamlSerializer.Deserialize\u003cOpenApiDocument\u003e(yamlText);\n\n// verify and resolve references\nopenApiDoc.Initialize();\n\n// back to text\nvar asText = YamlSerializer.Serialize(openApiDoc);\n```\n\n***HINT** Because YAML is a superset of JSON, the `YamlSerializer` class also supports JSON files, so you don't need to check which format the file is in.*\n\nDuring initialization, if the document contains references that cannot be resolved, a `RefResolutionException` will be thrown.\n\n### Payload Validation\n\nTo validate a payload against a JSON Schema within your OpenAPI document, use the `.EvaluatePayload()` extension method.\n\n```c#\nvar payload = JsonNode.Parse(\"\u003ccontent from HttpRequest or elsewhere\u003e\");\nvar schemaComponentLocation = JsonPointer.Parse(\"/pointer/to/schema\");\nvar results = openApiDoc.EvaluatePayload(payload, schemaComponentLocation);\n```\n\nIf your schema is under the `paths` section in the OpenAPI document, it may be easier and more readable to get the pointer using the `JsonPointer.Create()` method and passing the individual segments.  This avoids having to escape forward slashes `/`.\n\n```c#\nvar schemaInPathLocation = JsonPointer.Create(\"paths\", \"/pets/{petId}\", \"get\", \"parameters\", 0 , \"schema\");\n```\n\nOf course, if the path is well-known at dev time, you can also just access it directly:\n\n```c#\nvar schemaInPath = openApiDoc.Paths[\"/pets/{petId}\"].Get.Parameters[0].Schema;\nvar results = schemaInPath.Evaluate(payload);\n```\n\n### OpenAPI 3.0.x and JSON Schema Draft 4\n\nTo support OpenAPI v3.0.x, you'll need to enable JSON Schema draft 4 support first.  To do that, add this to your app initialization:\n\n```c#\nusing Graeae.Models.SchemaDraft4;\n\nDraft4Support.Enable();\n```\n\nWhen building schemas, you may find that the default extensions for _JsonSchema.Net_'s `JsonSchemaBuilder` are insufficient since it only supports draft 6 and after out of the box.  For example, in draft 4, `exclusiveMinimum` takes a boolean, but in draft 6 and after, it needs to be a number.\n\nTo support these differences, additional extension methods have been added to support specific draft 4 and OpenAPI functionality.  They're available for when they're needed, but otherwise the extensions that come with _JsonSchema.Net_ will work.\n\n| Extension | Function |\n|:--|:--|\n| `.OasId(Uri)`\u003cbr\u003e`.OasId(string)` | Adds the `id` (no `$`) keyword |\n| `.OasType(SchemaValueType)` | Adds a `type` keyword variant that supports the OAS notion |\n| `.Nullable(bool)` | Adds the `nullable` keyword |\n| `.ExclusiveMaximum(bool)` | Adds a boolean-valued `exclusiveMaximum` keyword |\n| `.ExclusiveMinimum(bool)` | Adds a boolean-valued `exclusiveMinimum` keyword |\n\nLastly, when validating a draft 4 schema, it's important to specify that it's draft 4 using the `EvaluationsOptions.EvaluateAs` property.\n\n```c#\nvar results = schema.Evaluate(instance, new EvaluationOptions\n{\n    OutputFormat = OutputFormat.List,\n    EvaluateAs = Draft4Support.Draft4Version\n});\n```\n\n### External reference resolution\n\nThe `OpenApiDocument.Initialize()` method will scan the document model and attempt to resolve any references.  References to locations within the document are automatically supported, however references to external locations are not supported by default.\n\nTo enable external reference resolution, you'll need to set the `Ref.Fetch` function property.  This static property is a function which takes a single `Uri` argument and returns an `Task\u003cJsonNode?\u003e` which will then be deserialized into the appropriate model.\n\nA (very) basic implementation that supports `http(s):` URIs is provided as the `Ref.FetchJson()` method.  It also supports YAML content.  It's likely you'll want to provide your own method for production scenarios, but this will get you started.\n\n```c#\nRef.Fetch = Ref.FetchJson;\n```\n\n### Creating OpenAPI documents inline\n\nThe models are read/write to make it simple to define an OpenAPI document in code.\n\n\u003cdetails\u003e\n\u003csummary\u003eExpand to see a code sample\u003c/summary\u003e\n\n(from https://github.com/OAI/OpenAPI-Specification/blob/main/examples/v3.0/petstore.yaml)\n\nYAML:\n```yaml\nopenapi: \"3.0.0\"\ninfo:\n  version: 1.0.0\n  title: Swagger Petstore\n  license:\n    name: MIT\nservers:\n  - url: http://petstore.swagger.io/v1\npaths:\n  /pets:\n    get:\n      summary: List all pets\n      operationId: listPets\n      tags:\n        - pets\n      parameters:\n        - name: limit\n          in: query\n          description: How many items to return at one time (max 100)\n          required: false\n          schema:\n            type: integer\n            maximum: 100\n            format: int32\n      responses:\n        '200':\n          description: A paged array of pets\n          headers:\n            x-next:\n              description: A link to the next page of responses\n              schema:\n                type: string\n          content:\n            application/json:    \n              schema:\n                $ref: \"#/components/schemas/Pets\"\n        default:\n          description: unexpected error\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Error\"\n    post:\n      summary: Create a pet\n      operationId: createPets\n      tags:\n        - pets\n      responses:\n        '201':\n          description: Null response\n        default:\n          description: unexpected error\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Error\"\n  /pets/{petId}:\n    get:\n      summary: Info for a specific pet\n      operationId: showPetById\n      tags:\n        - pets\n      parameters:\n        - name: petId\n          in: path\n          required: true\n          description: The id of the pet to retrieve\n          schema:\n            type: string\n      responses:\n        '200':\n          description: Expected response to a valid request\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Pet\"\n        default:\n          description: unexpected error\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Error\"\ncomponents:\n  schemas:\n    Pet:\n      type: object\n      required:\n        - id\n        - name\n      properties:\n        id:\n          type: integer\n          format: int64\n        name:\n          type: string\n        tag:\n          type: string\n    Pets:\n      type: array\n      maxItems: 100\n      items:\n        $ref: \"#/components/schemas/Pet\"\n    Error:\n      type: object\n      required:\n        - code\n        - message\n      properties:\n        code:\n          type: integer\n          format: int32\n        message:\n          type: string\n```\n\nEquivalent C#:\n\n```c#\nvar document = new OpenApiDocument(\"3.0.0\",\n    new(\"Swagger Petstore\", \"1.0.0\")\n    {\n        License = new(\"MIT\")\n    }\n)\n{\n    Servers = new []\n    {\n        new Server(\"http://petstore.swagger.io/v1\")\n    },\n    Paths = new()\n    {\n        [\"/pets\"] = new()\n        {\n            Get = new()\n            {\n                Summary = \"List all pets\",\n                OperationId = \"listPets\",\n                Tags = new []{\"pets\"},\n                Parameters = new []\n                {\n                    new Parameter(\"limit\", ParameterLocation.Query)\n                    {\n                        Description = \"How many items to return at one time (max 100)\",\n                        Required = false,\n                        Schema = new JsonSchemaBuilder()\n                            .Type(SchemaValueType.Integer)\n                            .Maximum(100)\n                            .Format(Formats.Int32)\n                    }\n                },\n                Responses = new()\n                {\n                    [HttpStatusCode.OK] = new(\"A paged array of pets\")\n                    {\n                        Headers = new()\n                        {\n                            [\"x-next\"] = new ()\n                            {\n                                Description = \"A link to the next page of responses\",\n                                Schema = new JsonSchemaBuilder().Type(SchemaValueType.String)\n                            }\n                        },\n                        Content = new()\n                        {\n                            [\"application/json\"] = new()\n                            {\n                                Schema = Ref.To.Schema(\"Pets\")\n                            }\n                        }\n                    },\n                    Default = new(\"unexpected error\")\n                    {\n                        Content = new()\n                        {\n                            [\"application/json\"] = new()\n                            {\n                                Schema = Ref.To.Schema(\"Error\")\n                            }\n                        }\n                    }\n                }\n            },\n            Post = new()\n            {\n                Summary = \"Create a pet\",\n                OperationId = \"createPets\",\n                Tags = new []{\"pets\"},\n                Responses = new()\n                {\n                    [HttpStatusCode.Created] = new(\"Null response\"),\n                    Default = new(\"unexpected error\")\n                    {\n                        Content = new(){\n                            [\"application/json\"] = new()\n                            {\n                                Schema = Ref.To.Schema(\"Error\")\n                            }\n                        }\n                    }\n                }\n            }\n        },\n        [\"/pets/{petId}\"] = new()\n        {\n            Get = new()\n            {\n                Summary = \"Info for a specific pet\",\n                OperationId = \"showPetById\",\n                Tags = new []{\"pets\"},\n                Parameters = new []\n                {\n                    new Parameter(\"petId\", ParameterLocation.Path)\n                    {\n                        Required = true,\n                        Description = \"The id of the pet to retrieve\",\n                        Schema = new JsonSchemaBuilder()\n                            .Type(SchemaValueType.String)\n                    }\n                },\n                Responses = new()\n                {\n                    [HttpStatusCode.OK] = new(\"Expected response to a valid request\")\n                    {\n                        Content = new()\n                        {\n                            [\"application/json\"] = new()\n                            {\n                                Schema = Ref.To.Schema(\"Pet\")\n                            }\n                        }\n                    },\n                    Default = new(\"unexpected error\")\n                    {\n                        Content = new()\n                        {\n                            [\"application/json\"] = new()\n                            {\n                                Schema = Ref.To.Schema(\"Error\")\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    },\n    Components = new()\n    {\n        Schemas = new()\n        {\n            [\"Pet\"] = new JsonSchemaBuilder()\n                .Type(SchemaValueType.Object)\n                .Required(\"id\", \"name\")\n                .Properties(\n                    (\"id\", new JsonSchemaBuilder()\n                        .Type(SchemaValueType.Integer)\n                        .Format(Formats.Int64)\n                    ),\n                    (\"name\", new JsonSchemaBuilder().Type(SchemaValueType.String)),\n                    (\"tag\", new JsonSchemaBuilder().Type(SchemaValueType.String))\n                ),\n            [\"Pets\"] = new JsonSchemaBuilder()\n                .Type(SchemaValueType.Array)\n                .MaxItems(100)\n                .Items(Ref.To.Schema(\"Pet\")),\n            [\"Error\"] = new JsonSchemaBuilder()\n                .Type(SchemaValueType.Object)\n                .Required(\"code\", \"message\")\n                .Properties(\n                    (\"code\", new JsonSchemaBuilder()\n                        .Type(SchemaValueType.Integer)\n                        .Format(Formats.Int32)\n                    ),\n                    (\"message\", new JsonSchemaBuilder().Type(SchemaValueType.String))\n                )\n        }\n    }\n};\n```\n\n\u003c/details\u003e\n\n\nThere are several more examples in the test project.\n\n## Contributing and Support\n\nThis project is in its infancy and is open for help and suggestions.  Additional functionality such as code generation is planned as extension libraries.\n\nFeel free to open issues \u0026 pull requests.\n\nRemember to follow the [Code of Conduct](./CODE_OF_CONDUCT.md) and [Contributing Guidelines](./CONTRIBUTING.md).\n\nTo chat about this project, please [join me in Slack](https://join.slack.com/t/manateeopensource/shared_invite/enQtMzU4MjgzMjgyNzU3LWZjYzAzYzY3NjY1MjY3ODI0ZGJiZjc3Nzk1MDM5NTNlMjMyOTE0MzMxYWVjMjdiOGU1NDY5OGVhMGQ5YzY4Zjg).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgregsdennis%2Fgraeae","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgregsdennis%2Fgraeae","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgregsdennis%2Fgraeae/lists"}