This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								lib/memex_web/controllers/export_controller.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								lib/memex_web/controllers/export_controller.ex
									
									
									
									
									
										Normal 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
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -136,12 +136,22 @@
 | 
			
		||||
 | 
			
		||||
  <hr class="hr" />
 | 
			
		||||
 | 
			
		||||
  <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="btn btn-alert"
 | 
			
		||||
      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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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 ""
 | 
			
		||||
 
 | 
			
		||||
@@ -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 ""
 | 
			
		||||
 
 | 
			
		||||
@@ -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 ""
 | 
			
		||||
 
 | 
			
		||||
@@ -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 ""
 | 
			
		||||
 
 | 
			
		||||
@@ -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 ""
 | 
			
		||||
 
 | 
			
		||||
@@ -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 ""
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										101
									
								
								test/memex_web/controllers/export_controller_test.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								test/memex_web/controllers/export_controller_test.exs
									
									
									
									
									
										Normal 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
 | 
			
		||||
		Reference in New Issue
	
	Block a user