Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions codegen/renderer.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
defmodule Cyphi.Generator.Renderer do
@moduledoc """
Adds documented `$include` relationships missing from the upstream schemas.

Relationships remain raw maps to preserve the client's existing nested-value
decoding behavior. Upstream field definitions always take precedence.
This module is compiled only in development and test environments.
"""
use OpenAPI.Renderer

alias OpenAPI.Processor.Schema
alias OpenAPI.Processor.Schema.Field

# Names are relative to output.base_module in the renderer's processed schemas.
@relationships %{
Answer => [:grade, :question, :user],
Assistant => [:user, :course],
Award => [:user, :awardee],
BadgeHistory => [:badge],
Batch => [:user],
Course => [:parent, :current_module, :organization],
CourseTemplate => [:organization],
Grade => [:user, :grader, :assessment, :course],
GroupMembership => [:user, :group],
Instructor => [:user, :course],
Learner => [
:user,
:course,
:progress,
:enrolled_by,
:unenrolled_by,
:deactivated_by,
:reactivated_by,
:transferred_by,
:transferred_from,
:transferred_to,
:order_item
],
LevelHistory => [:level],
Module => [:course],
Order => [:user, :organization, :verifier],
Organization => [:parent],
Player => [:user],
ScormAnswer => [:grade, :user],
Section => [:module, :referenced_course],
Session => [:user],
User => [:organization, :archiver, :job_title, :manager, :added_by],
Visit => [:user]
}

@impl OpenAPI.Renderer
def render_schema_types(state, schemas) do
super(state, Enum.map(schemas, &add_relationships/1))
end

@impl OpenAPI.Renderer
def render_schema_struct(state, schemas) do
super(state, Enum.map(schemas, &add_relationships/1))
end

@impl OpenAPI.Renderer
def render_schema_field_function(state, schemas) do
super(state, Enum.map(schemas, &add_relationships/1))
end

@impl OpenAPI.Renderer
def format(state, file) do
contents = super(state, file) |> IO.iodata_to_binary()

field_count =
file.schemas
|> Enum.filter(&(&1.output_format == :struct))
|> Enum.map(&add_relationships/1)
|> Enum.flat_map(& &1.fields)
|> Enum.uniq_by(& &1.name)
|> length()

# Large API structs cannot be split without changing the public response shape.
if field_count > 31 do
String.replace(contents, " defstruct ", " # credo:disable-for-next-line\n defstruct ",
global: false
)
else
contents
end
end

defp add_relationships(%Schema{output_format: :struct, type_name: :t} = schema) do
existing_names = MapSet.new(schema.fields, & &1.name)

fields =
@relationships
|> Map.get(schema.module_name, [])
|> Enum.map(&Atom.to_string/1)
|> Enum.reject(&MapSet.member?(existing_names, &1))
|> Enum.map(&%Field{name: &1, type: :map, nullable: true, required: false})

%{schema | fields: schema.fields ++ fields}
end

defp add_relationships(schema), do: schema
end
1 change: 1 addition & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ config :cyphi,
if config_env() == :dev do
config :oapi_generator,
default: [
renderer: Cyphi.Generator.Renderer,
output: [
base_module: Cyphi,
location: "lib/cyphi"
Expand Down
6 changes: 6 additions & 0 deletions lib/cyphi/answer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ defmodule Cyphi.Answer do
id: integer | nil,
points: number | nil,
position: integer | nil,
question: map | nil,
question_id: integer | nil,
response: String.t() | nil,
score: number | nil,
user: map | nil,
user_id: integer | nil
}

Expand All @@ -21,9 +23,11 @@ defmodule Cyphi.Answer do
:id,
:points,
:position,
:question,
:question_id,
:response,
:score,
:user,
:user_id
]

Expand All @@ -38,9 +42,11 @@ defmodule Cyphi.Answer do
id: :integer,
points: {:number, "float"},
position: :integer,
question: :map,
question_id: :integer,
response: :string,
score: {:number, "float"},
user: :map,
user_id: :integer
]
end
Expand Down
6 changes: 5 additions & 1 deletion lib/cyphi/assistant.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,27 @@ defmodule Cyphi.Assistant do
"""

@type t :: %__MODULE__{
course: map | nil,
course_id: integer | nil,
id: integer | nil,
last_visited_at: DateTime.t() | nil,
user: map | nil,
user_id: integer
}

defstruct [:course_id, :id, :last_visited_at, :user_id]
defstruct [:course, :course_id, :id, :last_visited_at, :user, :user_id]

@doc false
@spec __fields__(atom) :: keyword
def __fields__(type \\ :t)

def __fields__(:t) do
[
course: :map,
course_id: :integer,
id: :integer,
last_visited_at: {:string, "date-time"},
user: :map,
user_id: :integer
]
end
Expand Down
6 changes: 5 additions & 1 deletion lib/cyphi/award.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ defmodule Cyphi.Award do

@type t :: %__MODULE__{
awarded_at: DateTime.t() | nil,
awardee: map | nil,
awardee_id: integer | nil,
id: integer | nil,
note: String.t() | nil,
user: map | nil,
user_id: integer
}

defstruct [:awarded_at, :awardee_id, :id, :note, :user_id]
defstruct [:awarded_at, :awardee, :awardee_id, :id, :note, :user, :user_id]

@doc false
@spec __fields__(atom) :: keyword
Expand All @@ -20,9 +22,11 @@ defmodule Cyphi.Award do
def __fields__(:t) do
[
awarded_at: {:string, "date-time"},
awardee: :map,
awardee_id: :integer,
id: :integer,
note: :string,
user: :map,
user_id: :integer
]
end
Expand Down
11 changes: 8 additions & 3 deletions lib/cyphi/badge_history.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@ defmodule Cyphi.BadgeHistory do
Provides struct and type for a BadgeHistory
"""

@type t :: %__MODULE__{awarded_at: DateTime.t() | nil, badge_id: integer, id: integer | nil}
@type t :: %__MODULE__{
awarded_at: DateTime.t() | nil,
badge: map | nil,
badge_id: integer,
id: integer | nil
}

defstruct [:awarded_at, :badge_id, :id]
defstruct [:awarded_at, :badge, :badge_id, :id]

@doc false
@spec __fields__(atom) :: keyword
def __fields__(type \\ :t)

def __fields__(:t) do
[awarded_at: {:string, "date-time"}, badge_id: :integer, id: :integer]
[awarded_at: {:string, "date-time"}, badge: :map, badge_id: :integer, id: :integer]
end
end
3 changes: 3 additions & 0 deletions lib/cyphi/batch.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ defmodule Cyphi.Batch do
status: String.t() | nil,
successful: integer | nil,
uri: String.t() | nil,
user: map | nil,
user_id: integer
}

Expand All @@ -36,6 +37,7 @@ defmodule Cyphi.Batch do
:status,
:successful,
:uri,
:user,
:user_id
]

Expand All @@ -59,6 +61,7 @@ defmodule Cyphi.Batch do
status: :string,
successful: :integer,
uri: :string,
user: :map,
user_id: :integer
]
end
Expand Down
4 changes: 4 additions & 0 deletions lib/cyphi/client/response.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ defmodule Cyphi.Client.Response do
parse(module, map, type)
end

defp parse(_module, %{"count" => count}, _type) do
count
end

defp parse(module, map, type) do
attrs =
module.__fields__(type)
Expand Down
9 changes: 9 additions & 0 deletions lib/cyphi/course.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defmodule Cyphi.Course do
class_times: [map] | nil,
course_code: String.t() | nil,
credits: number | nil,
current_module: map | nil,
current_module_id: integer | nil,
custom_fields: map | nil,
delete_history_on_unenroll: boolean | nil,
Expand All @@ -32,8 +33,10 @@ defmodule Cyphi.Course do
must_repurchase_to_reenroll: boolean | nil,
name: String.t() | nil,
open_enrollment: boolean | nil,
organization: map | nil,
organization_id: integer | nil,
organization_name: String.t() | nil,
parent: map | nil,
parent_id: integer | nil,
path: boolean | nil,
picture: String.t() | nil,
Expand Down Expand Up @@ -68,6 +71,7 @@ defmodule Cyphi.Course do
:class_times,
:course_code,
:credits,
:current_module,
:current_module_id,
:custom_fields,
:delete_history_on_unenroll,
Expand All @@ -84,8 +88,10 @@ defmodule Cyphi.Course do
:must_repurchase_to_reenroll,
:name,
:open_enrollment,
:organization,
:organization_id,
:organization_name,
:parent,
:parent_id,
:path,
:picture,
Expand Down Expand Up @@ -124,6 +130,7 @@ defmodule Cyphi.Course do
class_times: [:map],
course_code: :string,
credits: {:number, "float"},
current_module: :map,
current_module_id: :integer,
custom_fields: :map,
delete_history_on_unenroll: :boolean,
Expand All @@ -140,8 +147,10 @@ defmodule Cyphi.Course do
must_repurchase_to_reenroll: :boolean,
name: :string,
open_enrollment: :boolean,
organization: :map,
organization_id: :integer,
organization_name: :string,
parent: :map,
parent_id: :integer,
path: :boolean,
picture: :string,
Expand Down
3 changes: 3 additions & 0 deletions lib/cyphi/course_template.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ defmodule Cyphi.CourseTemplate do
must_repurchase_to_reenroll: boolean | nil,
name: String.t() | nil,
open_enrollment: boolean | nil,
organization: map | nil,
organization_id: integer | nil,
organization_name: String.t() | nil,
path: boolean | nil,
Expand Down Expand Up @@ -68,6 +69,7 @@ defmodule Cyphi.CourseTemplate do
:must_repurchase_to_reenroll,
:name,
:open_enrollment,
:organization,
:organization_id,
:organization_name,
:path,
Expand Down Expand Up @@ -115,6 +117,7 @@ defmodule Cyphi.CourseTemplate do
must_repurchase_to_reenroll: :boolean,
name: :string,
open_enrollment: :boolean,
organization: :map,
organization_id: :integer,
organization_name: :string,
path: :boolean,
Expand Down
Loading
Loading