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
- fix formatting in note/context/step contents
- add json export for data
# v0.1.5
- fix overflow on note/contexts/step contents

View File

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

View File

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

View File

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

View File

@ -8,6 +8,16 @@ defmodule Memex.Pipelines.Pipeline do
alias Ecto.{Changeset, UUID}
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}
@foreign_key_type :binary_id
schema "pipelines" do

View File

@ -7,6 +7,14 @@ defmodule Memex.Pipelines.Steps.Step do
alias Ecto.{Changeset, UUID}
alias Memex.{Accounts.User, Pipelines.Pipeline}
@derive {Jason.Encoder,
only: [
:title,
:content,
:position,
:inserted_at,
:updated_at
]}
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
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
delete "/users/settings/:id", UserSettingsController, :delete
get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email
get "/export/:mode", ExportController, :export
end
scope "/", MemexWeb do

View File

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

View File

@ -66,7 +66,7 @@ msgstr ""
msgid "delete"
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
msgid "delete user"
msgstr ""
@ -155,3 +155,8 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "send instructions to reset password"
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"
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
msgid "delete user"
msgstr ""
@ -156,3 +156,8 @@ msgstr ""
#, elixir-autogen, elixir-format, fuzzy
msgid "send instructions to reset password"
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."
msgstr ""
#: lib/memex/accounts/user.ex:129
#: lib/memex/accounts/user.ex:139
#, elixir-autogen, elixir-format
msgid "did not change"
msgstr ""
#: lib/memex/accounts/user.ex:150
#: lib/memex/accounts/user.ex:160
#, elixir-autogen, elixir-format
msgid "does not match password"
msgstr ""
#: lib/memex/accounts/user.ex:187
#: lib/memex/accounts/user.ex:197
#, elixir-autogen, elixir-format
msgid "is not valid"
msgstr ""
#: lib/memex/accounts/user.ex:85
#: lib/memex/accounts/user.ex:95
#, elixir-autogen, elixir-format
msgid "must have the @ sign and no spaces"
msgstr ""
@ -102,12 +102,12 @@ msgstr ""
msgid "oops, something went wrong! Please check the errors below"
msgstr ""
#: lib/memex/contexts/context.ex:49
#: lib/memex/contexts/context.ex:62
#: lib/memex/notes/note.ex:48
#: lib/memex/notes/note.ex:61
#: lib/memex/pipelines/pipeline.ex:50
#: lib/memex/pipelines/pipeline.ex:63
#: lib/memex/contexts/context.ex:58
#: lib/memex/contexts/context.ex:71
#: lib/memex/notes/note.ex:57
#: lib/memex/notes/note.ex:70
#: lib/memex/pipelines/pipeline.ex:60
#: lib/memex/pipelines/pipeline.ex:73
#, elixir-autogen, elixir-format
msgid "invalid format: only numbers, letters and hyphen are accepted"
msgstr ""
@ -132,9 +132,9 @@ msgstr ""
msgid "unauthorized"
msgstr ""
#: lib/memex/contexts/context.ex:75
#: lib/memex/notes/note.ex:74
#: lib/memex/pipelines/pipeline.ex:76
#: lib/memex/contexts/context.ex:84
#: lib/memex/notes/note.ex:83
#: lib/memex/pipelines/pipeline.ex:86
#, elixir-autogen, elixir-format, fuzzy
msgid "invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
msgstr ""

View File

@ -122,7 +122,7 @@ msgstr ""
msgid "are you sure you want to delete the invite for %{invite_name}?"
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
msgid "are you sure you want to delete your account?"
msgstr ""

View File

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

View File

@ -121,7 +121,7 @@ msgstr ""
msgid "are you sure you want to delete the invite for %{invite_name}?"
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
msgid "are you sure you want to delete your account?"
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