Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide a way to export/present Flop and Flop.Meta data #370

Open
hgg opened this issue Jul 10, 2023 · 7 comments
Open

Provide a way to export/present Flop and Flop.Meta data #370

hgg opened this issue Jul 10, 2023 · 7 comments

Comments

@hgg
Copy link

hgg commented Jul 10, 2023

Is your feature request related to a problem? Please describe.
I'm using Flop in a small project where the intended output is JSON, to be consumed by a FE app. When introducing pagination, filtering and ordering to the API, I have to introduce code on my end to deal with parsing the Flop.Meta structure properly so that it can be returned to the FE.

Describe the solution you'd like
I think ideally the lib would provide clear ways to present information about what kind of filters, orderings and pagination are active. I believe this can easily be achieved with functions on the Meta module.
I don't know if there are any standards for something like this.
My suggestion is a function Meta.output_config(%Meta{}) :: %{filtering: map() | nil, ordering: map() | nil, pagination: map() | nil}. With something like this, people could then take the information and adapt it to whatever their API response structure is.
I'm not sure if you would rather place this on the flop_phoenix library.

Describe alternatives you've considered
We could also go for a more phoenix approach and create a JSON view for the Meta object. This would probably make more sense in the flop_phoenix lib. The downside I see is I think the base lib should be capable of "exporting" this information.

Another, simpler, approach could be to just add a JSON encoder to the Meta struct. If it's coupled to a specific JSON lib, that could definitely be a downside. If it's not, I don't see much the difference from the initial suggestion.

Additional context
I'm happy to work on this if you'll have my help. Been looking to doing more open-source contributions and this seems like an approachable one, if it does go forward.

@woylie
Copy link
Owner

woylie commented Jul 10, 2023

I don't quite understand what you have in mind. Can you give me a concrete example for what format you want to return to the front end?

@hgg
Copy link
Author

hgg commented Jul 10, 2023

Something like

%{
  filter: [%{value: "john ", op: :ilike, field: :name}],
  limit: 10,
  order: %{order_by: [:name], order_directions: [:asc]},
  pagination: %{
    current_page: 2,
    has_next_page?: false,
    has_previous_page?: true,
    next_page: nil,
    page_size: 2,
    previous_page: 1,
    total_pages: 2
  }
}

Basically something that tells the FE exactly what happened with the query on the backend in terms of filtering, sorting and pagination so that the FE can react to it. I think the best example is pagination: if I don't tell the FE something, how does it know it can ask for more pages?

EDIT: I generated this map from information inside the %Flop.Meta{} I get from running the query.

@woylie
Copy link
Owner

woylie commented Jul 10, 2023

Well, the Meta struct has all that information, and the cast and validated Flop struct is under meta.flop. You just want it in some other arbitrary format.

If you just wanted convert the Meta struct into JSON as it is, in the easiest case, you could convert the Meta struct, the nested Flop struct and the nested Filter structs into maps with Map.from_struct/1 and pass the result to Jason.encode/1. I don't want Flop to depend on a JSON library, but you can also implement the Jason.Encode protocol for these structs using defimpl instead of using @derive (see https://hexdocs.pm/jason/Jason.Encoder.html#module-example).

@hgg
Copy link
Author

hgg commented Jul 10, 2023

I see your point 👍🏼
I think what made me think this would be interesting was the fact that we have ways to use Flop out-of-the-box on HTML views, so I thought JSON views could have the same kind of support. Otherwise people using this with JSON need to figure out each and every field that are relevant for their use-case.
For instance in my case, I ended up creating a module to convert the structure. But I'm not really sure I even got all the relevant fields, and it feels weird to go get data from inside the structure like this. Almost like I'm breaking the boundary of the lib. I'll paste it here just to give you context.

defmodule MyApp.MetaJSON do
  def data(%Flop.Meta{} = meta) do
    %{
      order: order_data(meta),
      filter: filter_data(meta),
      pagination: pagination_data(meta),
      limit: limit_data(meta)
    }
  end

  defp order_data(%Flop.Meta{} = meta) do
    %{order_by: meta.flop.order_by, order_directions: meta.flop.order_directions}
  end

  defp filter_data(%Flop.Meta{} = meta) do
    Enum.map(meta.flop.filters, &Map.from_struct/1)
  end

  defp pagination_data(%Flop.Meta{start_cursor: start_cursor} = meta)
       when is_binary(start_cursor) do
    Map.take(meta, [:start_cursor, :end_cursor])
  end

  defp pagination_data(%Flop.Meta{flop: %Flop{page: current_page}} = meta)
       when is_integer(current_page) do
    Map.take(meta, [
      :current_page,
      :has_next_page?,
      :has_previous_page?,
      :next_page,
      :page_size,
      :previous_page,
      :total_pages
    ])
  end

  defp pagination_data(%Flop.Meta{flop: %Flop{offset: current_offset}} = meta)
       when is_integer(current_offset) do
    Map.take(meta, [
      :current_offset,
      :next_offset,
      :previous_offset,
      :total_count
    ])
  end

  defp pagination_data(_meta), do: %{}

  defp limit_data(%Flop.Meta{} = meta) do
    meta.flop.limit ||
      meta.opts[:for]
      |> struct()
      |> Flop.Schema.default_limit()
  end
end

And this is being called from other JSON view files.

Anyways, thanks for the quick responses. Feel free to close this 😄

@woylie
Copy link
Owner

woylie commented Jul 11, 2023

Maybe we can add a to_map function to the Meta module. But only as a convenience to get the meta and the nested structs as maps, without any transformations (maybe nil values can be removed).

@hgg
Copy link
Author

hgg commented Jul 24, 2023

@woylie let me know if that's something you'd like help with. I'm happy to contribute.

@woylie
Copy link
Owner

woylie commented Jul 24, 2023

@hgg Sure, if you want to contribute, go ahead!

@woylie woylie added this to the 0.23.0 milestone Aug 3, 2023
@woylie woylie removed this from the 0.23.0 milestone Nov 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants