Skip to content

Commit

Permalink
Release - 0.2.0 (#37)
Browse files Browse the repository at this point in the history
* Update README.md

* Updated gettext errors

* Removed unused variables

* Fixed linting/formatting issues

* Fixed linting/formatting issues

* Update deploy_heroku.yml

* Rename deploy_heroku.yml to deploy_heroku_staging.yml

* Create deploy_heroku_prod.yaml

* Update deploy_heroku_staging.yml

* As a user I can sign up and sign in with a valid e-mail and password  (#28)

* [#6 #16] As a user I can sign in with a valid e-mail and password

* Remove unused files

* Remove unsused update function for User

* Add session for user after log in

* Add unique email constraint on Users table

* Add user sign in

* Add current sign in status for user

* Add user sign out functionality (not in backlog)

* Remove / refactored code

* Remove coverage check for currently unused plug

* Add controller tests

* Prepare ExMachina for testing

* User sign out now displays a message

* Change session deletion method to ensure persistence of message to user upon sign out

* Refactored password hashing function so it can be used in future tests

* Refactor fixture to use ExMachina and Faker for data generation

* Modify ExUnit tests to conform to standards

* Tidy template pages with correct formatting

* Remove comments and cleaned up code

* Merge migrations into single file for User schema

* Remove comments and cleaned up code

* Correct English used in ExUnit test case

* Move secret_key-base file to environment variable for production

* Change multiple alias identifiers from one line to multiple to satisy codebase

* Make blank line seperation more consistent in the tests

* Add feature test case for User log in

* [#6 #7 #16 #22] Add account tests for invalid cases and fixed formatting

* [#6 #7 #16 #22] Add account tests for invalid cases and fixed formatting

* [#6 #7 #16 #22] Refactor code according to feedback

* [#6 #7 #16 #22] Refactor code according to feedback

* [#6 #7 #16 #22] Remove code form AuthController to AuthHelper to reflect the functionality

* [#6 #7 #16 #22] Remove auto-generated function spec

* [#6 #7 #16 #22] Moved Account context into accounts folder and account schema into its own folder to improve structure

* [#6 #7 #16 #22] Add tests for user changesets for negative paths

* [#6 #7 #16 #22] Add tests for user changesets for negative paths

* [#6 #7 #16 #22] Change refute to assert false for testing outcomese

* [#6 #7 #16 #22] Changed from pattern matching to double equals to match exact output when required. Move value being tested against to the right side

* [#3] [UI] As a user, I can upload a CSV file containing keywords which will then be used to search on Google (#31)

* [#6 #16] As a user I can sign in with a valid e-mail and password

* Remove unused files

* Remove unsused update function for User

* Add session for user after log in

* Add unique email constraint on Users table

* Add user sign in

* Add current sign in status for user

* Add user sign out functionality (not in backlog)

* Remove / refactored code

* Remove coverage check for currently unused plug

* Add controller tests

* Prepare ExMachina for testing

* User sign out now displays a message

* Change session deletion method to ensure persistence of message to user upon sign out

* Refactored password hashing function so it can be used in future tests

* Refactor fixture to use ExMachina and Faker for data generation

* Modify ExUnit tests to conform to standards

* Tidy template pages with correct formatting

* Remove comments and cleaned up code

* Merge migrations into single file for User schema

* Remove comments and cleaned up code

* Correct English used in ExUnit test case

* Move secret_key-base file to environment variable for production

* Change multiple alias identifiers from one line to multiple to satisy codebase

* Make blank line seperation more consistent in the tests

* Add feature test case for User log in

* [#6 #7 #16 #22] Add account tests for invalid cases and fixed formatting

* [#6 #7 #16 #22] Add account tests for invalid cases and fixed formatting

* [#6 #7 #16 #22] Refactor code according to feedback

* [#6 #7 #16 #22] Refactor code according to feedback

* [#6 #7 #16 #22] Remove code form AuthController to AuthHelper to reflect the functionality

* [#6 #7 #16 #22] Remove auto-generated function spec

* [#6 #7 #16 #22] Moved Account context into accounts folder and account schema into its own folder to improve structure

* [#6 #7 #16 #22] Add tests for user changesets for negative paths

* [#6 #7 #16 #22] Add tests for user changesets for negative paths

* [#3] Add endpoint and controller for handling keyword upload

* [#3] Add template files for uploading files, including upload form

* [#3] Add link to keywords page in navigation

* [#3] Remove coverall and comments from authenticated plug to prepare for tests

* [#3] Add tests for keyword controller and test csv file

* [#3] Add tests for ensure_authenticated plug

* Resolved merge conflict

* [#3] Format code

* [#3] Change keywords fixture file name and changed template to show 1000 keywords limit

* [#3] Remove blank line and re-order assert tests for ensure_authenticated plug tests

* [#3] Add an additional test to ensure unauthenticated users are unable to upload a keywords file

* [#3] Format test

* [#18] [Backend] As a user, I can upload a CSV file containing keywords which will be stored (#33)

* [#6 #16] As a user I can sign in with a valid e-mail and password

* Remove unused files

* Remove unsused update function for User

* Add session for user after log in

* Add unique email constraint on Users table

* Add user sign in

* Add current sign in status for user

* Add user sign out functionality (not in backlog)

* Remove / refactored code

* Remove coverage check for currently unused plug

* Add controller tests

* Prepare ExMachina for testing

* User sign out now displays a message

* Change session deletion method to ensure persistence of message to user upon sign out

* Refactored password hashing function so it can be used in future tests

* Refactor fixture to use ExMachina and Faker for data generation

* Modify ExUnit tests to conform to standards

* Tidy template pages with correct formatting

* Remove comments and cleaned up code

* Merge migrations into single file for User schema

* Remove comments and cleaned up code

* Correct English used in ExUnit test case

* Move secret_key-base file to environment variable for production

* Change multiple alias identifiers from one line to multiple to satisy codebase

* Make blank line seperation more consistent in the tests

* Add feature test case for User log in

* [#6 #7 #16 #22] Add account tests for invalid cases and fixed formatting

* [#6 #7 #16 #22] Add account tests for invalid cases and fixed formatting

* [#6 #7 #16 #22] Refactor code according to feedback

* [#6 #7 #16 #22] Refactor code according to feedback

* [#6 #7 #16 #22] Remove code form AuthController to AuthHelper to reflect the functionality

* [#6 #7 #16 #22] Remove auto-generated function spec

* [#6 #7 #16 #22] Moved Account context into accounts folder and account schema into its own folder to improve structure

* [#6 #7 #16 #22] Add tests for user changesets for negative paths

* [#6 #7 #16 #22] Add tests for user changesets for negative paths

* [#3] Add endpoint and controller for handling keyword upload

* [#3] Add template files for uploading files, including upload form

* [#3] Add link to keywords page in navigation

* [#3] Remove coverall and comments from authenticated plug to prepare for tests

* [#3] Add tests for keyword controller and test csv file

* [#3] Add tests for ensure_authenticated plug

* Resolved merge conflict

* [#3] Format code

* [#3] Change keywords fixture file name and changed template to show 1000 keywords limit

* [#3] Remove blank line and re-order assert tests for ensure_authenticated plug tests

* [#3] Add an additional test to ensure unauthenticated users are unable to upload a keywords file

* [#3] Format test

* [#18] Add NimbleCSV

* [#18] Add Keyword Controller and helper function for validate/parse csv

* [#18] Add Keyword Controller tests and additional test files for invalid cases

* [#18] Change invalid file format to invalid file extension

* [#18] Initial KeywordUpload Schema, associations and tests

* [#18] Initial code to carry out the mass insertions of keywords into the table for the User. Needs refactor wip

* [#18] Refactor KeywordUpload changeset to Use __MODULE__ as default argument

* #[18] Slight refactor of keyword saving for user wip

* Refactor Keyword context name and add one test

* [#18] Change alias to fix formatting errors

* [#18] Add test to Keyword Controller to verify an uplaod of two keywords returns the correct count to the user

* [#18] Add additional empty line for csv files

* [#18] Change from using length to Enum.count() for counting list size

* [#18] Change from using string field to text for keyword html storage to remove character limit

* [#18] Add positive test result for KeywordUpload changeset

* [#18] Remove unnecessary conn.halts from keyword controller

* [#18] Remove comments and changed grammar in test cases for Keywords

* [#18] Change name and status fields of KeywordUpload to be to not accept null

* [#18] Refactor parsing of keywords into correct structure for bulk inserts wip

* [#18] Add two further KeywordUpload changeset tests to ensure a KeywordUpload has to have an existing user

* [#1] [#20] As a user, I can view a list of my previously uploaded keywords (#35)

* #[1] Add Context function to retreive list of uploaded keywords for a particular user

* #[1] Add Controller and template to show the list of uploaded keywords for the user

* #[1] Change github action trigger from Pull Request to Push to allow staging and prod

* #[1] Change github action trigger from Pull Request to Push to allow staging and prod

* [#1] Add Uploaded field to display for each KeywordUpload and format using Calendar module

* #[1] Add test for KeywordView for formatting timestamp

* #[1] Change Repo.list_all to return the inserted Keywords

* #[1] Write tests for fetching KeywordUploads for a particular user

* #[1] Add KeywordUpload Factory to tests for listing KeywordUploads for a User

* [#1] Refactor keyword test using pipe operator to make it cleaner

* [#1] Fix formatting on keywords index template file

* [#1] Clean up keyword template file

* [#1] Remove external Calendar library dependencies due to built-in functionality in Elxiir

* [#1] Made keyword test title more explicit

* Remove prod.secret.exe config import to allow deployment (#36)
  • Loading branch information
liamstevens111 authored May 19, 2022
1 parent c1e59f7 commit ba2617e
Show file tree
Hide file tree
Showing 30 changed files with 494 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ env:
jobs:
deploy:
name: Deploy to Heroku production
permissions: write-all
runs-on: ubuntu-latest

steps:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/deploy_heroku_staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ env:
jobs:
deploy:
name: Deploy to Heroku staging
permissions: write-all
runs-on: ubuntu-latest

steps:
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Test

on: pull_request
on: push

env:
OTP_VERSION: "24.2.2"
Expand All @@ -12,6 +12,7 @@ env:
jobs:
install_and_compile_dependencies:
name: Install and Compile Dependencies
permissions: write-all

runs-on: ubuntu-latest

Expand Down Expand Up @@ -69,6 +70,7 @@ jobs:

lint_codebase:
name: Linting
permissions: write-all

needs: install_and_compile_dependencies

Expand Down Expand Up @@ -153,6 +155,7 @@ jobs:

test_database_seeds:
name: Test database seeds
permissions: write-all

needs: lint_codebase

Expand Down Expand Up @@ -231,6 +234,7 @@ jobs:

unit_test:
name: Unit test
permissions: write-all

needs: lint_codebase

Expand Down Expand Up @@ -313,6 +317,7 @@ jobs:

feature_test:
name: Feature test
permissions: write-all

needs: lint_codebase

Expand Down
2 changes: 0 additions & 2 deletions config/prod.exs
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,3 @@ config :logger, level: :info
# Check `Plug.SSL` for all available options in `force_ssl`.
config :google_search_data_viewer, GoogleSearchDataViewerWeb.Endpoint,
force_ssl: [rewrite_on: [:x_forwarded_proto]]

import_config "prod.secret.exs"
3 changes: 3 additions & 0 deletions lib/google_search_data_viewer/accounts/schemas/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ defmodule GoogleSearchDataViewer.Accounts.Schemas.User do
import Ecto.Changeset

alias GoogleSearchDataViewer.Accounts.Passwords
alias GoogleSearchDataViewer.Keywords.Schemas.KeywordUpload

schema "users" do
field :email, :string
field :password, :string, virtual: true
field :hashed_password, :string

has_many :keyword_uploads, KeywordUpload

timestamps()
end

Expand Down
56 changes: 56 additions & 0 deletions lib/google_search_data_viewer/keywords/keyword.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
defmodule GoogleSearchDataViewer.Keywords.Keyword do
import Ecto.Query, warn: false

alias GoogleSearchDataViewer.Keywords.Schemas.KeywordUpload
alias GoogleSearchDataViewer.Repo

def get_keyword_uploads_for_user(user) do
KeywordUpload
|> where(user_id: ^user.id)
|> order_by(desc: :inserted_at)
|> select([:id, :user_id, :name, :status, :updated_at, :inserted_at])
|> Repo.all()
end

def insert_keyword_uploads(attrs) do
Repo.insert_all(KeywordUpload, attrs, returning: true)
end

def create_keyword_uploads(keywords, user) do
keywords
|> process_keyword_params(user)
|> insert_keyword_uploads()
end

defp process_keyword_params(keywords, user) do
keywords
|> Enum.map(fn keyword ->
create_params_for_keyword_and_user(keyword, user.id)
end)
|> Enum.map(fn params -> create_changeset_and_parse(params) end)
|> Enum.map(&Map.from_struct/1)
|> Enum.map(fn params -> Map.drop(params, [:__meta__, :user, :id]) end)
|> Enum.map(fn params -> insert_timestamps(params) end)
end

defp create_params_for_keyword_and_user(keyword, user_id) do
%{
name: keyword,
user_id: user_id
}
end

defp create_changeset_and_parse(params) do
params
|> KeywordUpload.changeset()
|> Ecto.Changeset.apply_changes()
end

defp insert_timestamps(params) do
current_date_time = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)

params
|> Map.put(:inserted_at, current_date_time)
|> Map.put(:updated_at, current_date_time)
end
end
27 changes: 27 additions & 0 deletions lib/google_search_data_viewer/keywords/schemas/keyword_upload.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule GoogleSearchDataViewer.Keywords.Schemas.KeywordUpload do
use Ecto.Schema

import Ecto.Changeset

alias GoogleSearchDataViewer.Accounts.Schemas.User

schema "keyword_uploads" do
field :name, :string
field :html, :string

field :status, Ecto.Enum,
values: [:pending, :inprogress, :completed, :failed],
default: :pending

belongs_to :user, User

timestamps()
end

def changeset(keyword_upload \\ %__MODULE__{}, attrs) do
keyword_upload
|> cast(attrs, [:name, :user_id])
|> validate_required([:name, :user_id])
|> assoc_constraint(:user)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
defmodule GoogleSearchDataViewerWeb.KeywordController do
use GoogleSearchDataViewerWeb, :controller

alias GoogleSearchDataViewer.Keywords.Keyword
alias GoogleSearchDataViewerWeb.KeywordHelper

def index(conn, _params) do
keywords = Keyword.get_keyword_uploads_for_user(conn.assigns.current_user)
render(conn, "index.html", keywords: keywords)
end

def upload(conn, %{"file" => file}) do
case KeywordHelper.validate_and_parse_keyword_file(file) do
{:ok, keywords} ->
{keyword_count, _keywords} =
Keyword.create_keyword_uploads(keywords, conn.assigns.current_user)

conn
|> put_flash(:info, "File successfully uploaded. #{keyword_count} keywords uploaded.")
|> redirect(to: Routes.keyword_path(conn, :index))

{:error, :invalid_extension} ->
conn
|> put_flash(:error, "File extension invalid, csv only")
|> redirect(to: Routes.keyword_path(conn, :index))

{:error, :invalid_length} ->
conn
|> put_flash(:error, "Length invalid. 1-1000 keywords only")
|> redirect(to: Routes.keyword_path(conn, :index))
end
end
end
34 changes: 34 additions & 0 deletions lib/google_search_data_viewer_web/helpers/keyword_helper.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
defmodule GoogleSearchDataViewerWeb.KeywordHelper do
alias NimbleCSV.RFC4180, as: CSV

@max_keyword_upload_count 1000

def validate_and_parse_keyword_file(file) do
with true <- file_valid?(file),
{:ok, keywords} <- parse_keyword_file(file) do
{:ok, keywords}
else
false -> {:error, :invalid_extension}
:error -> {:error, :invalid_length}
end
end

defp file_valid?(file), do: file.content_type == "text/csv"

defp parse_keyword_file(file) do
keywords =
file.path
|> File.stream!()
|> CSV.parse_stream(skip_headers: false)
|> Enum.to_list()
|> List.flatten()

keywords_length = Enum.count(keywords)

if keywords_length > 0 && keywords_length <= @max_keyword_upload_count do
{:ok, keywords}
else
:error
end
end
end
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# TODO: Remove # coveralls-ignore-stop

# coveralls-ignore-start
defmodule GoogleSearchDataViewerWeb.EnsureAuthenticatedPlug do
import Plug.Conn
import Phoenix.Controller
Expand All @@ -22,7 +19,7 @@ defmodule GoogleSearchDataViewerWeb.EnsureAuthenticatedPlug do
|> redirect(to: Routes.page_path(conn, :index))
|> halt()

true ->
_user ->
conn
end
end
Expand All @@ -31,5 +28,3 @@ defmodule GoogleSearchDataViewerWeb.EnsureAuthenticatedPlug do

defp get_user(user_id), do: Account.get_user(user_id)
end

# coveralls-ignore-stop
8 changes: 8 additions & 0 deletions lib/google_search_data_viewer_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ defmodule GoogleSearchDataViewerWeb.Router do
resources "/sessions", SessionController, only: [:create, :new, :delete]
end

scope "/keywords", GoogleSearchDataViewerWeb do
pipe_through [:browser, :authorized]

get "/", KeywordController, :index

post "/upload", KeywordController, :upload
end

# Other scopes may use custom stacks.
# scope "/api", GoogleSearchDataViewerWeb do
# pipe_through :api
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<.form id="keyword-upload-form" let={f} for={@conn} action={@action} multipart={true}>
<%= label f, :File %>
<%= file_input f, :file, accept: ".csv", required: true %>
<%= error_tag f, :file %>

<div>
<%= submit "Upload" %>
</div>
</.form>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<section class="phx-hero">
<h1>Keywords</h1>

<h3>Select a csv file to upload keywords.</h3>
<h3>Maximum keywords per file: 1000</h3>
<%= render "form.html", Map.put(assigns, :action, Routes.keyword_path(@conn, :upload)) %>

<h3>Keywords</h3>
<div>
<table>
<tr>
<th>Keyword</th>
<th>Status</th>
<th>Uploaded at</th>
</tr>
<%= for keyword <- @keywords do %>
<tr>
<th><%= keyword.name %></th>
<th><%= keyword.status %></th>
<th><%= format_date_time(keyword.inserted_at) %></th>
</tr>
<% end %>
</table>
</div>
</section>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<%= if @current_user do %>
<li>Signed in as <%= @current_user.email %></li>
<li><%= link "Sign out", to: Routes.session_path(@conn, :delete, @current_user), method: :delete %></li>
<li><%= link "Keywords", to: Routes.keyword_path(@conn, :index), method: :get %></li>
<% else %>
<li><%= link "Sign up", to: Routes.user_path(@conn, :new), method: :get %></li>
<li><%= link "Sign in", to: Routes.session_path(@conn, :new), method: :get %></li>
Expand Down
7 changes: 7 additions & 0 deletions lib/google_search_data_viewer_web/views/keyword_view.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule GoogleSearchDataViewerWeb.KeywordView do
use GoogleSearchDataViewerWeb, :view

def format_date_time(datetime) do
Calendar.strftime(datetime, "%d.%m.%y %H:%M:%S")
end
end
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ defmodule GoogleSearchDataViewer.MixProject do
{:gettext, "~> 0.18"},
{:jason, "~> 1.2"},
{:mimic, "~> 1.7.2", [only: :test]},
{:nimble_csv, "~> 1.1"},
{:nimble_template, "~> 4.1", only: :dev, runtime: false},
{:oban, "~> 2.12.0"},
{:phoenix, "~> 1.6.6"},
Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"mime": {:hex, :mime, "2.0.2", "0b9e1a4c840eafb68d820b0e2158ef5c49385d17fb36855ac6e7e087d4b1dcc5", [:mix], [], "hexpm", "e6a3f76b4c277739e36c2e21a2c640778ba4c3846189d5ab19f97f126df5f9b7"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"mimic": {:hex, :mimic, "1.7.2", "27007e4e0c746ddb6d56a386c40585088b35621ae2d7167160e8c3283e8cd585", [:mix], [], "hexpm", "e4d40550523841055aa469f5125d124ab89ce8b2d3686cab908b98dff5e6111b"},
"nimble_csv": {:hex, :nimble_csv, "1.2.0", "4e26385d260c61eba9d4412c71cea34421f296d5353f914afe3f2e71cce97722", [:mix], [], "hexpm", "d0628117fcc2148178b034044c55359b26966c6eaa8e2ce15777be3bbc91b12a"},
"nimble_template": {:hex, :nimble_template, "4.1.1", "ed9b223fe0cf03f07de76cdaed49b25ba5d9e884232fe6995c1f807cc4e935c7", [:make, :mix], [{:httpoison, "~> 1.7", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.6.6", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "5ddb640de99bbf8046afa17ab0fd784173d3dff51f1d9d441f0c76e28bb96378"},
"oban": {:hex, :oban, "2.12.0", "bd5a283770c6ab1284aad81e5566cfb89f4119b08f52508d92d73551283c8789", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1557b7b046b13c0b5360f55a9fb7e56975f6b5f8247e56f2c54575bd95435ca0"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
Expand Down
15 changes: 15 additions & 0 deletions priv/repo/migrations/20220511095327_create_keyword_uploads.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
defmodule GoogleSearchDataViewer.Repo.Migrations.CreateKeywordUploads do
use Ecto.Migration

def change do
create table(:keyword_uploads) do
add :name, :string, null: false
add :html, :text
add :status, :string, null: false

timestamps()
end

create index(:keyword_uploads, [:name])
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule GoogleSearchDataViewer.Repo.Migrations.KeywordUploadBelongsToUser do
use Ecto.Migration

def change do
alter table(:keyword_uploads) do
add :user_id, references(:users)
end
end
end
18 changes: 18 additions & 0 deletions test/factories/keyword_upload_factory.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
defmodule GoogleSearchDataViewer.KeywordUploadFactory do
alias Faker.Food.En
alias GoogleSearchDataViewer.Keywords.Schemas.KeywordUpload

defmacro __using__(_opts) do
quote do
def keyword_upload_factory(attrs) do
name = attrs[:name] || En.dish()
user = attrs[:user]

%KeywordUpload{
name: name,
user: user
}
end
end
end
end
Loading

0 comments on commit ba2617e

Please sign in to comment.