add json data export
continuous-integration/drone/push Build is passing Details

This commit is contained in:
shibao 2022-12-20 19:15:08 -05:00
parent c1337ebc10
commit 7813738f91
16 changed files with 224 additions and 38 deletions

View File

@ -1,5 +1,6 @@
# v0.1.6 # v0.1.6
- fix formatting in note/context/step contents - fix formatting in note/context/step contents
- add json export for data
# v0.1.5 # v0.1.5
- fix overflow on note/contexts/step contents - fix overflow on note/contexts/step contents

View File

@ -9,6 +9,16 @@ defmodule Memex.Accounts.User do
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.Invites.Invite alias Memex.Invites.Invite
@derive {Jason.Encoder,
only: [
:id,
:email,
:confirmed_at,
:role,
:locale,
:inserted_at,
:updated_at
]}
@derive {Inspect, except: [:password]} @derive {Inspect, except: [:password]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id

View File

@ -9,6 +9,15 @@ defmodule Memex.Contexts.Context do
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.{Accounts.User, Repo} alias Memex.{Accounts.User, Repo}
@derive {Jason.Encoder,
only: [
:slug,
:content,
:tags,
:visibility,
:inserted_at,
:updated_at
]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id
schema "contexts" do schema "contexts" do

View File

@ -8,6 +8,15 @@ defmodule Memex.Notes.Note do
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.{Accounts.User, Repo} alias Memex.{Accounts.User, Repo}
@derive {Jason.Encoder,
only: [
:slug,
:content,
:tags,
:visibility,
:inserted_at,
:updated_at
]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id
schema "notes" do schema "notes" do

View File

@ -8,6 +8,16 @@ defmodule Memex.Pipelines.Pipeline do
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.{Accounts.User, Pipelines.Steps.Step, Repo} alias Memex.{Accounts.User, Pipelines.Steps.Step, Repo}
@derive {Jason.Encoder,
only: [
:slug,
:description,
:tags,
:visibility,
:inserted_at,
:steps,
:updated_at
]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id
schema "pipelines" do schema "pipelines" do

View File

@ -7,6 +7,14 @@ defmodule Memex.Pipelines.Steps.Step do
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.{Accounts.User, Pipelines.Pipeline} alias Memex.{Accounts.User, Pipelines.Pipeline}
@derive {Jason.Encoder,
only: [
:title,
:content,
:position,
:inserted_at,
:updated_at
]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id
schema "steps" do schema "steps" do

View File

@ -0,0 +1,17 @@
defmodule MemexWeb.ExportController do
use MemexWeb, :controller
alias Memex.{Contexts, Notes, Pipelines, Pipelines.Steps}
def export(%{assigns: %{current_user: current_user}} = conn, %{"mode" => "json"}) do
pipelines =
Pipelines.list_pipelines(current_user)
|> Enum.map(fn pipeline -> Steps.preload_steps(pipeline, current_user) end)
json(conn, %{
user: current_user,
notes: Notes.list_notes(current_user),
contexts: Contexts.list_contexts(current_user),
pipelines: pipelines
})
end
end

View File

@ -76,6 +76,7 @@ defmodule MemexWeb.Router do
put "/users/settings", UserSettingsController, :update put "/users/settings", UserSettingsController, :update
delete "/users/settings/:id", UserSettingsController, :delete delete "/users/settings/:id", UserSettingsController, :delete
get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email
get "/export/:mode", ExportController, :export
end end
scope "/", MemexWeb do scope "/", MemexWeb do

View File

@ -136,12 +136,22 @@
<hr class="hr" /> <hr class="hr" />
<.link <div class="flex justify-center items-center">
href={Routes.user_settings_path(@conn, :delete, @current_user)} <.link
method={:delete} href={Routes.export_path(@conn, :export, :json)}
class="btn btn-alert" class="mx-4 my-2 btn btn-primary"
data-confirm={dgettext("prompts", "are you sure you want to delete your account?")} target="_blank"
> >
<%= dgettext("actions", "delete user") %> <%= dgettext("actions", "export data as json") %>
</.link> </.link>
<.link
href={Routes.user_settings_path(@conn, :delete, @current_user)}
method={:delete}
class="mx-4 my-2 btn btn-alert"
data-confirm={dgettext("prompts", "are you sure you want to delete your account?")}
>
<%= dgettext("actions", "delete user") %>
</.link>
</div>
</div> </div>

View File

@ -66,7 +66,7 @@ msgstr ""
msgid "delete" msgid "delete"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:145 #: lib/memex_web/templates/user_settings/edit.html.heex:154
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "delete user" msgid "delete user"
msgstr "" msgstr ""
@ -155,3 +155,8 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "send instructions to reset password" msgid "send instructions to reset password"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:145
#, elixir-autogen, elixir-format
msgid "export data as json"
msgstr ""

View File

@ -67,7 +67,7 @@ msgstr ""
msgid "delete" msgid "delete"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:145 #: lib/memex_web/templates/user_settings/edit.html.heex:154
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "delete user" msgid "delete user"
msgstr "" msgstr ""
@ -156,3 +156,8 @@ msgstr ""
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "send instructions to reset password" msgid "send instructions to reset password"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:145
#, elixir-autogen, elixir-format
msgid "export data as json"
msgstr ""

View File

@ -76,22 +76,22 @@ msgstr ""
msgid "You must confirm your account and log in to access this page." msgid "You must confirm your account and log in to access this page."
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:129 #: lib/memex/accounts/user.ex:139
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "did not change" msgid "did not change"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:150 #: lib/memex/accounts/user.ex:160
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "does not match password" msgid "does not match password"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:187 #: lib/memex/accounts/user.ex:197
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "is not valid" msgid "is not valid"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:85 #: lib/memex/accounts/user.ex:95
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "must have the @ sign and no spaces" msgid "must have the @ sign and no spaces"
msgstr "" msgstr ""
@ -102,12 +102,12 @@ msgstr ""
msgid "oops, something went wrong! Please check the errors below" msgid "oops, something went wrong! Please check the errors below"
msgstr "" msgstr ""
#: lib/memex/contexts/context.ex:49 #: lib/memex/contexts/context.ex:58
#: lib/memex/contexts/context.ex:62 #: lib/memex/contexts/context.ex:71
#: lib/memex/notes/note.ex:48 #: lib/memex/notes/note.ex:57
#: lib/memex/notes/note.ex:61 #: lib/memex/notes/note.ex:70
#: lib/memex/pipelines/pipeline.ex:50 #: lib/memex/pipelines/pipeline.ex:60
#: lib/memex/pipelines/pipeline.ex:63 #: lib/memex/pipelines/pipeline.ex:73
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "invalid format: only numbers, letters and hyphen are accepted" msgid "invalid format: only numbers, letters and hyphen are accepted"
msgstr "" msgstr ""
@ -132,9 +132,9 @@ msgstr ""
msgid "unauthorized" msgid "unauthorized"
msgstr "" msgstr ""
#: lib/memex/contexts/context.ex:75 #: lib/memex/contexts/context.ex:84
#: lib/memex/notes/note.ex:74 #: lib/memex/notes/note.ex:83
#: lib/memex/pipelines/pipeline.ex:76 #: lib/memex/pipelines/pipeline.ex:86
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited" msgid "invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
msgstr "" msgstr ""

View File

@ -122,7 +122,7 @@ msgstr ""
msgid "are you sure you want to delete the invite for %{invite_name}?" msgid "are you sure you want to delete the invite for %{invite_name}?"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:143 #: lib/memex_web/templates/user_settings/edit.html.heex:152
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "are you sure you want to delete your account?" msgid "are you sure you want to delete your account?"
msgstr "" msgstr ""

View File

@ -75,22 +75,22 @@ msgstr ""
msgid "You must confirm your account and log in to access this page." msgid "You must confirm your account and log in to access this page."
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:129 #: lib/memex/accounts/user.ex:139
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "did not change" msgid "did not change"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:150 #: lib/memex/accounts/user.ex:160
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "does not match password" msgid "does not match password"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:187 #: lib/memex/accounts/user.ex:197
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "is not valid" msgid "is not valid"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:85 #: lib/memex/accounts/user.ex:95
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "must have the @ sign and no spaces" msgid "must have the @ sign and no spaces"
msgstr "" msgstr ""
@ -101,12 +101,12 @@ msgstr ""
msgid "oops, something went wrong! Please check the errors below" msgid "oops, something went wrong! Please check the errors below"
msgstr "" msgstr ""
#: lib/memex/contexts/context.ex:49 #: lib/memex/contexts/context.ex:58
#: lib/memex/contexts/context.ex:62 #: lib/memex/contexts/context.ex:71
#: lib/memex/notes/note.ex:48 #: lib/memex/notes/note.ex:57
#: lib/memex/notes/note.ex:61 #: lib/memex/notes/note.ex:70
#: lib/memex/pipelines/pipeline.ex:50 #: lib/memex/pipelines/pipeline.ex:60
#: lib/memex/pipelines/pipeline.ex:63 #: lib/memex/pipelines/pipeline.ex:73
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "invalid format: only numbers, letters and hyphen are accepted" msgid "invalid format: only numbers, letters and hyphen are accepted"
msgstr "" msgstr ""
@ -131,9 +131,9 @@ msgstr ""
msgid "unauthorized" msgid "unauthorized"
msgstr "" msgstr ""
#: lib/memex/contexts/context.ex:75 #: lib/memex/contexts/context.ex:84
#: lib/memex/notes/note.ex:74 #: lib/memex/notes/note.ex:83
#: lib/memex/pipelines/pipeline.ex:76 #: lib/memex/pipelines/pipeline.ex:86
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited" msgid "invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
msgstr "" msgstr ""

View File

@ -121,7 +121,7 @@ msgstr ""
msgid "are you sure you want to delete the invite for %{invite_name}?" msgid "are you sure you want to delete the invite for %{invite_name}?"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:143 #: lib/memex_web/templates/user_settings/edit.html.heex:152
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "are you sure you want to delete your account?" msgid "are you sure you want to delete your account?"
msgstr "" msgstr ""

View File

@ -0,0 +1,101 @@
defmodule MemexWeb.ExportControllerTest do
@moduledoc """
Tests the export function
"""
use MemexWeb.ConnCase
import Memex.{ContextsFixtures, NotesFixtures, PipelinesFixtures, StepsFixtures}
@moduletag :export_controller_test
setup %{conn: conn} do
current_user = user_fixture() |> confirm_user()
[
current_user: current_user,
conn: conn |> log_in_user(current_user)
]
end
defp add_data(%{current_user: current_user}) do
note = note_fixture(current_user)
context = context_fixture(current_user)
pipeline = pipeline_fixture(current_user)
step = step_fixture(0, pipeline, current_user)
%{
note: note,
context: context,
pipeline: pipeline,
step: step
}
end
describe "Exports data" do
setup [:add_data]
test "in JSON", %{
conn: conn,
current_user: current_user,
note: note,
context: context,
pipeline: pipeline,
step: step
} do
conn = get(conn, Routes.export_path(conn, :export, :json))
ideal_note = %{
"slug" => note.slug,
"content" => note.content,
"tags" => note.tags,
"visibility" => note.visibility |> to_string(),
"inserted_at" => note.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => note.updated_at |> NaiveDateTime.to_iso8601()
}
ideal_context = %{
"slug" => context.slug,
"content" => context.content,
"tags" => context.tags,
"visibility" => context.visibility |> to_string(),
"inserted_at" => context.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => context.updated_at |> NaiveDateTime.to_iso8601()
}
ideal_pipeline = %{
"slug" => pipeline.slug,
"description" => pipeline.description,
"tags" => pipeline.tags,
"visibility" => pipeline.visibility |> to_string(),
"inserted_at" => pipeline.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => pipeline.updated_at |> NaiveDateTime.to_iso8601(),
"steps" => [
%{
"title" => step.title,
"content" => step.content,
"position" => step.position,
"inserted_at" => step.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => step.updated_at |> NaiveDateTime.to_iso8601()
}
]
}
ideal_user = %{
"confirmed_at" =>
current_user.confirmed_at |> Jason.encode!() |> String.replace(~r/\"/, ""),
"email" => current_user.email,
"id" => current_user.id,
"locale" => current_user.locale,
"role" => to_string(current_user.role),
"inserted_at" => current_user.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => current_user.updated_at |> NaiveDateTime.to_iso8601()
}
json_resp = conn |> json_response(200)
assert %{"notes" => [^ideal_note]} = json_resp
assert %{"contexts" => [^ideal_context]} = json_resp
assert %{"pipelines" => [^ideal_pipeline]} = json_resp
assert %{"user" => ^ideal_user} = json_resp
end
end
end