diff --git a/changelog.md b/changelog.md
index 57f8d1e..e8bb92f 100644
--- a/changelog.md
+++ b/changelog.md
@@ -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
diff --git a/lib/memex/accounts/user.ex b/lib/memex/accounts/user.ex
index 411a5a3..48ce284 100644
--- a/lib/memex/accounts/user.ex
+++ b/lib/memex/accounts/user.ex
@@ -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
diff --git a/lib/memex/contexts/context.ex b/lib/memex/contexts/context.ex
index 9279adc..d4aba9b 100644
--- a/lib/memex/contexts/context.ex
+++ b/lib/memex/contexts/context.ex
@@ -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
diff --git a/lib/memex/notes/note.ex b/lib/memex/notes/note.ex
index 89ee8ce..ec3aed7 100644
--- a/lib/memex/notes/note.ex
+++ b/lib/memex/notes/note.ex
@@ -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
diff --git a/lib/memex/pipelines/pipeline.ex b/lib/memex/pipelines/pipeline.ex
index adca293..8d5a1af 100644
--- a/lib/memex/pipelines/pipeline.ex
+++ b/lib/memex/pipelines/pipeline.ex
@@ -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
diff --git a/lib/memex/pipelines/step.ex b/lib/memex/pipelines/step.ex
index 18727de..99f2c83 100644
--- a/lib/memex/pipelines/step.ex
+++ b/lib/memex/pipelines/step.ex
@@ -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
diff --git a/lib/memex_web/controllers/export_controller.ex b/lib/memex_web/controllers/export_controller.ex
new file mode 100644
index 0000000..2c5d035
--- /dev/null
+++ b/lib/memex_web/controllers/export_controller.ex
@@ -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
diff --git a/lib/memex_web/router.ex b/lib/memex_web/router.ex
index f20f3c5..8089434 100644
--- a/lib/memex_web/router.ex
+++ b/lib/memex_web/router.ex
@@ -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
diff --git a/lib/memex_web/templates/user_settings/edit.html.heex b/lib/memex_web/templates/user_settings/edit.html.heex
index ce39cf1..ec079fd 100644
--- a/lib/memex_web/templates/user_settings/edit.html.heex
+++ b/lib/memex_web/templates/user_settings/edit.html.heex
@@ -136,12 +136,22 @@
- <.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
+ href={Routes.export_path(@conn, :export, :json)}
+ class="mx-4 my-2 btn btn-primary"
+ target="_blank"
+ >
+ <%= dgettext("actions", "export data as json") %>
+
+
+ <.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") %>
+
+
diff --git a/priv/gettext/actions.pot b/priv/gettext/actions.pot
index ac3fdf3..4814d0d 100644
--- a/priv/gettext/actions.pot
+++ b/priv/gettext/actions.pot
@@ -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 ""
diff --git a/priv/gettext/de/LC_MESSAGES/actions.po b/priv/gettext/de/LC_MESSAGES/actions.po
index 3b2c9e8..2cdf02f 100644
--- a/priv/gettext/de/LC_MESSAGES/actions.po
+++ b/priv/gettext/de/LC_MESSAGES/actions.po
@@ -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 ""
diff --git a/priv/gettext/de/LC_MESSAGES/errors.po b/priv/gettext/de/LC_MESSAGES/errors.po
index b16e330..3a1bd21 100644
--- a/priv/gettext/de/LC_MESSAGES/errors.po
+++ b/priv/gettext/de/LC_MESSAGES/errors.po
@@ -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 ""
diff --git a/priv/gettext/de/LC_MESSAGES/prompts.po b/priv/gettext/de/LC_MESSAGES/prompts.po
index 5428234..814fdb4 100644
--- a/priv/gettext/de/LC_MESSAGES/prompts.po
+++ b/priv/gettext/de/LC_MESSAGES/prompts.po
@@ -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 ""
diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot
index 308c766..4b7d8cf 100644
--- a/priv/gettext/errors.pot
+++ b/priv/gettext/errors.pot
@@ -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 ""
diff --git a/priv/gettext/prompts.pot b/priv/gettext/prompts.pot
index 89db7c2..bc0fd23 100644
--- a/priv/gettext/prompts.pot
+++ b/priv/gettext/prompts.pot
@@ -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 ""
diff --git a/test/memex_web/controllers/export_controller_test.exs b/test/memex_web/controllers/export_controller_test.exs
new file mode 100644
index 0000000..053af54
--- /dev/null
+++ b/test/memex_web/controllers/export_controller_test.exs
@@ -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