swaggest / rest

Web services with OpenAPI and JSON Schema done quick in Go
https://pkg.go.dev/github.com/swaggest/rest
MIT License
362 stars 17 forks source link

uuid.UUID howto #93

Closed chriskolenko closed 1 year ago

chriskolenko commented 1 year ago

Is there a way to support uuid.UUID as a string with format uuid? I couldn't find an example on how some of the more advanced reflect stuff worked.

I also don't have access to the type, so i can't add extra tags to the properly because it's part of an underlying framework :'(

vearutop commented 1 year ago

In order to configure schema for 3rd-party types, you need to create a mapping.

    // Create custom schema mapping for 3rd party type.
    uuidDef := jsonschema.Schema{}
    uuidDef.AddType(jsonschema.String)
    uuidDef.WithFormat("uuid")
    uuidDef.WithExamples("248df4b7-aa70-47b8-a036-33ac447e668d")
    s.OpenAPICollector.Reflector().AddTypeMapping(uuid.UUID{}, uuidDef)

Type mapping can also be done onto another type, e.g. mapping to plain string (format would be missing).

    s.OpenAPICollector.Reflector().AddTypeMapping(uuid.UUID{}, "")
chriskolenko commented 1 year ago

No way to add it as a string and format uuid without the custom type?

vearutop commented 1 year ago

What do you mean by "custom type"?

chriskolenko commented 1 year ago

With custom type

      "UuidUUID": {
        "type": "string",
        "format": "uuid",
        "example": "248df4b7-aa70-47b8-a036-33ac447e668d"
      }

      "CommandsNewOrganisation": {
        "required": [
          "aggregate_id",
          "avatar",
          "name"
        ],
        "type": "object",
        "properties": {
          "aggregate_id": {
            "$ref": "#/components/schemas/UuidUUID"
          },
          "avatar": {
            "minLength": 3,
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },

What I would like is:

      "CommandsNewOrganisation": {
        "required": [
          "aggregate_id",
          "avatar",
          "name"
        ],
        "type": "object",
        "properties": {
          "aggregate_id": {
            "type": "string",
            "format": "uuid"
          },
          "avatar": {
            "minLength": 3,
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },

I managed to get this generating using the following:

  s.OpenAPICollector.Reflector().AddTypeMapping(uuid.UUID{}, "")

  // and ...

  type NewOrganisation struct {
      AggregateId uuid.UUID `json:"aggregate_id" format:"uuid" required:"true"`
      Avatar      string    `json:"avatar" required:"true" minLength:"3"`
      Name        string    `json:"name" required:"true"`
  }

Human error kicked in and I missed a few uuid tags.

Maybe a way to add to known types in reflect would be nice in the future. https://github.com/swaggest/jsonschema-go/blob/master/reflect.go#L381-L383

vearutop commented 1 year ago

Ah, so you'd like to have inline schema in this case instead of referenced definition. Is there any particular reason for that (semantically inline schema and reference act the same)?

I'll check if it is feasible to declare custom schemas in inline mode.

Maybe a way to add to known types in reflect would be nice in the future.

The problem with uuid is that there is no definition in stdlib and there are multiple popular 3rd-party implementations, promoting them to well-known types would introduce dependencies.

vearutop commented 1 year ago

I've added Reflector.InlineDefinition (go get -u github.com/swaggest/jsonschema-go@v0.3.41), so you can declare type schemas inlineable

    // Create custom schema mapping for 3rd party type.
    uuidDef := jsonschema.Schema{}
    uuidDef.AddType(jsonschema.String)
    uuidDef.WithFormat("uuid")
    uuidDef.WithExamples("248df4b7-aa70-47b8-a036-33ac447e668d")
    s.OpenAPICollector.Reflector().AddTypeMapping(uuid.UUID{}, uuidDef)
    s.OpenAPICollector.Reflector().InlineDefinition(uuid.UUID{})  // <-----------
chriskolenko commented 1 year ago

This is perfect. Thank you for adding this.

The reason for this. I'm generating the client code using: https://github.com/deepmap/oapi-codegen

Was going crazy converting string => uuid and back again over and over.