Skip to content

How to dynamically/statically add a field when using processor plugin? #87

Open
@jaeyson

Description

@jaeyson

Hi @aj-foster 👋🏾,

Tldr

I wanted to add another/dynamic key in %Field{...} in OpenAPI.Processor, but I'm certain I'm doing the wrong way ☹️

Why did I do this?

Everytime I generate my spec file, defaults that are described aren't picked up in defstruct:

# generated
...
  defstruct [
    :default_sorting_field,
    :enable_nested_fields,
    :fields,
    :name,
    :symbols_to_index,
    :token_separators
  ]
...

I was expecting:

# generated
...
  defstruct [
    :fields,
    :name,
    default_sorting_field: "",
    enable_nested_fields: false,
    symbols_to_index: [],
    token_separators: []
  ]
...

Then I saw this doc statement for run/1 function:

Warning

This functions is used by the main OpenAPI module. It is unlikely that you will call this
function directly, unless you are reimplementing one of the core functions. If this happens,
please share your use-case with the maintainers; a plugin might be warranted.

I'm just confused how to do it using the provided callbacks, so that I can use the plugin way.

Here's what I did

  • Add mix deps {:oapi_generator, path: "/path/to/local/open-api-generator"} (was just confirming this in local dev)
  • (OpenAPI.Processor) added another key in field inside defp process_schema_fields, which is used by the parent run function. But the problem here is, "If this happens, please share your use-case with the maintainers; a plugin might be warranted."
  • Add also that key in OpenAPI.Processor.Schema.Field

The codes

processor.ex

I edited the processor.ex inside the deps folder

  defmodule OpenAPI.Processor do
    ...

    alias OpenAPI.Processor.Format
    alias OpenAPI.Processor.Operation
    alias OpenAPI.Processor.Schema.Field
    alias OpenAPI.Processor.Operation.Param
    alias OpenAPI.Processor.Schema
    # ...reduced for brevity

    # around this line
    @spec process_schema_fields(State.t(), SchemaSpec.t(), reference, MapSet.t()) ::
            {State.t(), [Field.t()]}
    defp process_schema_fields(state, schema_spec, parent_ref, seen_refs) do
      %SchemaSpec{properties: properties, required: required} = schema_spec

      for {field_name, field_spec} <- properties, reduce: {state, []} do
        {state, fields} ->
+         default =
+           case field_spec do
+             %SchemaSpec{default: default} -> default
+             rest -> rest
+           end

          nullable? =
            case field_spec do
              %SchemaSpec{nullable: nullable?} -> nullable?
              {:ref, _} -> false
            end

          # ...reduced for brevity

          field = %Field{
            name: field_name,
            nullable: nullable?,
            private: false,
            required: required?,
            type: type,
+           default: default
          }

          {state, [field | fields]}
      end
    end
  end
end

schema.ex

defmodule OpenAPI.Renderer.Schema do
  ...

  @spec render_struct(State.t(), [Schema.t()]) :: Macro.t()
  def render_struct(state, schemas) do
    fields =
      Enum.map(schemas, & &1.fields)
      |> List.insert_at(0, extra_fields(state))
      |> List.flatten()
+     |> Enum.map(fn field ->
+       if not is_nil(field.default) do
+         {String.to_atom(field.name), field.default}
+       else
+         String.to_atom(field.name)
+       end
+     end)
      |> Enum.sort()
      |> Enum.dedup()

    quote do
      defstruct unquote(fields)
    end
    |> Util.put_newlines()
  end

  ...
end

field.ex

  defmodule OpenAPI.Processor.Schema.Field do
    ...
    alias OpenAPI.Processor.Type

    @typedoc "Processed field data used by the renderer"
    @type t :: %__MODULE__{
            name: String.t(),
            nullable: boolean,
            private: boolean,
            required: boolean,
            type: Type.t(),
+           default: any()
          }

    defstruct name: nil,
              nullable: false,
              private: false,
              required: false,
              type: nil,
+             default: nil
  end

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions