-
Notifications
You must be signed in to change notification settings - Fork 50
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
Add database of shared links #1098
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
postgres 14.10 | ||
elixir 1.13.3 | ||
erlang 25.3.2 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
defmodule Cadet.SharedPrograms do | ||
@moduledoc """ | ||
The SharedPrograms context. | ||
""" | ||
|
||
import Ecto.Query, warn: false | ||
alias Cadet.Repo | ||
|
||
alias Cadet.SharedPrograms.SharedProgram | ||
|
||
@doc """ | ||
Returns the list of shared_programs. | ||
|
||
## Examples | ||
|
||
iex> list_shared_programs() | ||
[%SharedProgram{}, ...] | ||
|
||
""" | ||
def list_shared_programs do | ||
Repo.all(SharedProgram) | ||
end | ||
|
||
@doc """ | ||
Gets a single shared_program. | ||
|
||
Raises `Ecto.NoResultsError` if the Shared program does not exist. | ||
|
||
## Examples | ||
|
||
iex> get_shared_program!(123) | ||
%SharedProgram{} | ||
|
||
iex> get_shared_program!(456) | ||
** (Ecto.NoResultsError) | ||
|
||
""" | ||
def get_shared_program!(id), do: Repo.get!(SharedProgram, id) | ||
|
||
def get_shared_program_by_uuid!(uuid) do | ||
case Repo.get_by(SharedProgram, uuid: uuid) do | ||
nil -> raise "SharedProgram not found for UUID #{uuid}" | ||
shared_program -> shared_program | ||
end | ||
end | ||
|
||
@doc """ | ||
Creates a shared_program. | ||
|
||
## Examples | ||
|
||
iex> create_shared_program(%{field: value}) | ||
{:ok, %SharedProgram{}} | ||
|
||
iex> create_shared_program(%{field: bad_value}) | ||
{:error, %Ecto.Changeset{}} | ||
|
||
""" | ||
def create_shared_program(attrs \\ %{}) do | ||
%SharedProgram{} | ||
|> SharedProgram.changeset(attrs) | ||
|> Repo.insert() | ||
end | ||
|
||
@doc """ | ||
Updates a shared_program. | ||
|
||
## Examples | ||
|
||
iex> update_shared_program(shared_program, %{field: new_value}) | ||
{:ok, %SharedProgram{}} | ||
|
||
iex> update_shared_program(shared_program, %{field: bad_value}) | ||
{:error, %Ecto.Changeset{}} | ||
|
||
""" | ||
def update_shared_program(%SharedProgram{} = shared_program, attrs) do | ||
shared_program | ||
|> SharedProgram.changeset(attrs) | ||
|> Repo.update() | ||
end | ||
|
||
@doc """ | ||
Deletes a shared_program. | ||
|
||
## Examples | ||
|
||
iex> delete_shared_program(shared_program) | ||
{:ok, %SharedProgram{}} | ||
|
||
iex> delete_shared_program(shared_program) | ||
{:error, %Ecto.Changeset{}} | ||
|
||
""" | ||
def delete_shared_program(%SharedProgram{} = shared_program) do | ||
Repo.delete(shared_program) | ||
end | ||
|
||
@doc """ | ||
Returns an `%Ecto.Changeset{}` for tracking shared_program changes. | ||
|
||
## Examples | ||
|
||
iex> change_shared_program(shared_program) | ||
%Ecto.Changeset{data: %SharedProgram{}} | ||
|
||
""" | ||
def change_shared_program(%SharedProgram{} = shared_program, attrs \\ %{}) do | ||
SharedProgram.changeset(shared_program, attrs) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
defmodule Cadet.SharedPrograms.SharedProgram do | ||
@moduledoc """ | ||
Contains methods for storing frontend programs to database with uuid. | ||
""" | ||
use Ecto.Schema | ||
import Ecto.Changeset | ||
|
||
schema "shared_programs" do | ||
field(:data, :map) | ||
field(:uuid, Ecto.UUID) | ||
|
||
timestamps() | ||
end | ||
|
||
@doc false | ||
def changeset(shared_program, attrs) do | ||
shared_program | ||
Comment on lines
+8
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing definitions for required and optional fields (take a look at the other models in the codebase to see how they're implemented). |
||
|> cast(attrs, [:data]) | ||
|> generate_uuid_if_nil() | ||
|> validate_required([:uuid]) | ||
end | ||
|
||
defp generate_uuid_if_nil(changeset) do | ||
if get_change(changeset, :uuid) do | ||
changeset | ||
else | ||
put_change(changeset, :uuid, Ecto.UUID.generate()) | ||
end | ||
end | ||
|
||
defimpl String.Chars, for: Cadet.SharedPrograms.SharedProgram do | ||
def to_string(%Cadet.SharedPrograms.SharedProgram{uuid: uuid}) do | ||
"SharedProgram with UUID: #{uuid}" | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
defmodule CadetWeb.ChangesetJSON do | ||
@doc """ | ||
Renders changeset errors. | ||
""" | ||
def error(%{changeset: changeset}) do | ||
# When encoded, the changeset returns its errors | ||
# as a JSON object. So we just pass it forward. | ||
%{errors: Ecto.Changeset.traverse_errors(changeset, &translate_error/1)} | ||
end | ||
|
||
defp translate_error({msg, opts}) do | ||
# You can make use of gettext to translate error messages by | ||
# uncommenting and adjusting the following code: | ||
|
||
# if count = opts[:count] do | ||
# Gettext.dngettext(CadetWeb.Gettext, "errors", msg, msg, count, opts) | ||
# else | ||
# Gettext.dgettext(CadetWeb.Gettext, "errors", msg, opts) | ||
# end | ||
|
||
Enum.reduce(opts, msg, fn {key, value}, acc -> | ||
String.replace(acc, "%{#{key}}", fn _ -> to_string(value) end) | ||
end) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
defmodule CadetWeb.FallbackController do | ||
@moduledoc """ | ||
Translates controller action results into valid `Plug.Conn` responses. | ||
|
||
See `Phoenix.Controller.action_fallback/1` for more details. | ||
""" | ||
use CadetWeb, :controller | ||
|
||
# This clause handles errors returned by Ecto's insert/update/delete. | ||
def call(conn, {:error, %Ecto.Changeset{} = changeset}) do | ||
conn | ||
|> put_status(:unprocessable_entity) | ||
|> put_view(json: CadetWeb.ChangesetJSON) | ||
|> render(:error, changeset: changeset) | ||
end | ||
|
||
# This clause is an example of how to handle resources that cannot be found. | ||
def call(conn, {:error, :not_found}) do | ||
conn | ||
|> put_status(:not_found) | ||
|> put_view(html: CadetWeb.ErrorHTML, json: CadetWeb.ErrorJSON) | ||
|> render(:"404") | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
defmodule CadetWeb.SharedProgramController do | ||
use CadetWeb, :controller | ||
|
||
alias Cadet.SharedPrograms | ||
alias Cadet.SharedPrograms.SharedProgram | ||
|
||
action_fallback(CadetWeb.FallbackController) | ||
|
||
def index(conn, _params) do | ||
shared_programs = SharedPrograms.list_shared_programs() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should not expose this. Very dangerous and breaks privacy. |
||
render(conn, :index, shared_programs: shared_programs) | ||
end | ||
|
||
def create(conn, %{"shared_program" => shared_program_params}) do | ||
with {:ok, %SharedProgram{uuid: uuid} = shared_program} <- | ||
SharedPrograms.create_shared_program(shared_program_params) do | ||
# change to current server of source academy | ||
# server = "http://localhost:8000" | ||
# url = | ||
Comment on lines
+17
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this for? |
||
conn | ||
|> put_status(:created) | ||
|> put_resp_header("location", ~s"/api/shared_programs/#{shared_program}") | ||
# |> render(:show, shared_program: shared_program) | ||
|> render("show.json", %{uuid: uuid}) | ||
|
||
# case ping_url_shortener(uuid) do | ||
# {:ok, shortened_url} -> | ||
# conn | ||
# |> put_status(:created) | ||
# |> put_resp_header("location", ~s"/api/shared_programs/#{shared_program}") | ||
# # |> render(:show, shared_program: shared_program) | ||
# |> render("show.json", %{uuid: uuid, shortened_url: shortened_url}) | ||
# {:error, reason} -> | ||
# conn | ||
# |> put_status(:internal_server_error) | ||
# |> json(%{error: "Failed to shorten URL: #{reason}"}) | ||
# end | ||
Comment on lines
+26
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Commented code should either be explained with a comment or removed if unused. |
||
end | ||
end | ||
|
||
def show(conn, %{"id" => uuid}) do | ||
shared_program = SharedPrograms.get_shared_program_by_uuid!(uuid) | ||
render(conn, :show, shared_program: shared_program) | ||
end | ||
|
||
def update(conn, %{"id" => id, "shared_program" => shared_program_params}) do | ||
shared_program = SharedPrograms.get_shared_program!(id) | ||
|
||
with {:ok, %SharedProgram{} = shared_program} <- | ||
SharedPrograms.update_shared_program(shared_program, shared_program_params) do | ||
render(conn, :show, shared_program: shared_program) | ||
end | ||
end | ||
|
||
def delete(conn, %{"id" => id}) do | ||
shared_program = SharedPrograms.get_shared_program!(id) | ||
|
||
with {:ok, %SharedProgram{}} <- SharedPrograms.delete_shared_program(shared_program) do | ||
send_resp(conn, :no_content, "") | ||
end | ||
end | ||
|
||
defp ping_url_shortener(uuid) do | ||
url = "https://localhost:8000/yourls-api.php" | ||
|
||
params = %{ | ||
signature: "5eef899abd", | ||
action: "shorturl", | ||
format: "json", | ||
keyword: uuid, | ||
url: "http://localhost:8000/playground#uuid=#{uuid}" | ||
} | ||
|
||
case HTTPoison.post(url, {:form, params}) do | ||
{:ok, %{status_code: 200, body: body}} -> | ||
{:ok, body} | ||
|
||
{:error, %HTTPoison.Error{reason: reason}} -> | ||
{:error, reason} | ||
|
||
other -> | ||
{:error, "Unexpected response: #{inspect(other)}"} | ||
end | ||
end | ||
Comment on lines
+63
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove hardcoding. |
||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
defmodule CadetWeb.SharedProgramJSON do | ||
alias Cadet.SharedPrograms.SharedProgram | ||
|
||
@doc """ | ||
Renders a list of shared_programs. | ||
""" | ||
def index(%{shared_programs: shared_programs}) do | ||
%{data: for(shared_program <- shared_programs, do: data(shared_program))} | ||
end | ||
|
||
@doc """ | ||
Renders a single shared_program. | ||
""" | ||
def show(%{shared_program: shared_program}) do | ||
%{data: data(shared_program)} | ||
end | ||
|
||
defp data(%SharedProgram{} = shared_program) do | ||
%{ | ||
id: shared_program.id, | ||
uuid: shared_program.uuid, | ||
data: shared_program.data | ||
} | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -210,6 +210,12 @@ defmodule CadetWeb.Router do | |
# pipe_through :api | ||
# end | ||
|
||
scope "/v2", CadetWeb do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't create a separate scope is possible, since this endpoint is grouped as part of the other routes. |
||
pipe_through(:api) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should pipe through the proper authentication middleware like the other endpoints. |
||
|
||
resources("/shared_programs", SharedProgramController, only: [:index, :show, :create, :delete]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we supporting |
||
end | ||
|
||
def swagger_info do | ||
%{ | ||
info: %{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<%= Jason.encode!(%{ | ||
id: @shared_program.id, | ||
uuid: @shared_program.uuid, | ||
data: @shared_program.data, | ||
inserted_at: @shared_program.inserted_at, | ||
updated_at: @shared_program.updated_at | ||
}) %> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
defmodule CadetWeb.SharedProgramView do | ||
use CadetWeb, :view | ||
|
||
def render("index.json", %{shared_programs: shared_programs}) do | ||
%{ | ||
data: Enum.map(shared_programs, &render_shared_program/1) | ||
} | ||
end | ||
|
||
def render("show.json", %{uuid: uuid}) do | ||
%{uuid: uuid} | ||
end | ||
|
||
defp render_shared_program(shared_program) do | ||
%{ | ||
uuid: shared_program.uuid, | ||
json: shared_program.data | ||
# Add other attributes as needed | ||
} | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
defmodule Cadet.Repo.Migrations.CreateSharedPrograms do | ||
use Ecto.Migration | ||
|
||
def change do | ||
create table(:shared_programs) do | ||
add(:uuid, :uuid) | ||
add(:data, :map) | ||
Comment on lines
+6
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add constraints and/or indices to this table (e.g. non-null, unique, primary key, etc.)? |
||
|
||
timestamps() | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't commit this as this is a local development setup, thanks!