From 6dfbb12ba0835e346561c65b916283c6e6ec641d Mon Sep 17 00:00:00 2001 From: flxffy Date: Sun, 16 Jun 2019 22:17:47 +0800 Subject: [PATCH 01/44] Logic for jwt generation used for chatkit complete --- config/secrets.exs.example | 5 +++++ lib/cadet/chat/chat.ex | 32 ++++++++++++++++++++++++++++++++ lib/mix/tasks/something.ex | 11 +++++++++++ mix.exs | 1 + mix.lock | 1 + 5 files changed, 50 insertions(+) create mode 100644 lib/cadet/chat/chat.ex create mode 100644 lib/mix/tasks/something.ex diff --git a/config/secrets.exs.example b/config/secrets.exs.example index 0f792fdb7..53992ea46 100644 --- a/config/secrets.exs.example +++ b/config/secrets.exs.example @@ -13,6 +13,11 @@ config :cadet, ], autograder: [ lambda_name: "autograderLambdaName" + ], + chat: [ + instance_id: "CHATKIT_INSTANCE_ID", + key_id: "CHATKIT_KEY_ID", + key_secret: "CHATKIT_KEY_SECRET" ] config :sentry, diff --git a/lib/cadet/chat/chat.ex b/lib/cadet/chat/chat.ex new file mode 100644 index 000000000..a0a4f7775 --- /dev/null +++ b/lib/cadet/chat/chat.ex @@ -0,0 +1,32 @@ +defmodule Cadet.Chat do + @moduledoc """ + Contains logic to supplement ChatKit, an external service engaged for Source Academy. + ChatKit's API can be found here: https://pusher.com/docs/chatkit + """ + @instance_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) + @key_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:key_id) + @key_secret :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:key_secret) + @time_to_live 8400 + + @doc """ + Generates new bearer token for connection to ChatKit's ChatManager. + + Returns {:ok, token} on success, otherwise {:error, error_message} + """ + def get_token(username) do + curr_time_epoch = DateTime.to_unix(DateTime.utc_now()) + + payload = %{ + "instance" => @instance_id, + "iss" => "api_keys/" <> @key_id, + "exp" => curr_time_epoch + @time_to_live, + "iat" => curr_time_epoch, + "sub" => username + } + + Joken.Signer.sign( + payload, + Joken.Signer.create("HS256", @key_secret) + ) + end +end diff --git a/lib/mix/tasks/something.ex b/lib/mix/tasks/something.ex new file mode 100644 index 000000000..c54aa5301 --- /dev/null +++ b/lib/mix/tasks/something.ex @@ -0,0 +1,11 @@ +defmodule Mix.Tasks.Cadet.Something do + use Mix.Task + + alias Cadet.Chat + + def run(args) do + username = List.first(args) + {:ok, token} = Chat.get_token(username) + IO.puts(token) + end +end diff --git a/mix.exs b/mix.exs index 4e5823941..effc1d11f 100644 --- a/mix.exs +++ b/mix.exs @@ -57,6 +57,7 @@ defmodule Cadet.Mixfile do {:guardian_db, "~> 2.0"}, {:httpoison, "~> 1.0", override: true}, {:inch_ex, "~> 2.0", only: [:dev, :test]}, + {:joken, "~> 2.0"}, {:jason, "~> 1.1"}, {:jsx, "~> 2.8"}, {:phoenix, "~> 1.4.0"}, diff --git a/mix.lock b/mix.lock index 2d4c43070..691f9f02e 100644 --- a/mix.lock +++ b/mix.lock @@ -45,6 +45,7 @@ "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, "inch_ex": {:hex, :inch_ex, "2.0.0", "24268a9284a1751f2ceda569cd978e1fa394c977c45c331bb52a405de544f4de", [:mix], [{:bunt, "~> 0.2", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, + "joken": {:hex, :joken, "2.1.0", "bf21a73105d82649f617c5e59a7f8919aa47013d2519ebcc39d998d8d12adda9", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm"}, "jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"}, "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm"}, "libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"}, From 4c5026bc73bb98e0f7f1958de1fdf57ec5959bb9 Mon Sep 17 00:00:00 2001 From: flxffy Date: Mon, 17 Jun 2019 21:21:16 +0800 Subject: [PATCH 02/44] /chat/token controller --- lib/cadet_web/controllers/chat_controller.ex | 47 ++++++++++++++++++++ lib/cadet_web/router.ex | 2 + lib/cadet_web/views/chat_view.ex | 18 ++++++++ 3 files changed, 67 insertions(+) create mode 100644 lib/cadet_web/controllers/chat_controller.ex create mode 100644 lib/cadet_web/views/chat_view.ex diff --git a/lib/cadet_web/controllers/chat_controller.ex b/lib/cadet_web/controllers/chat_controller.ex new file mode 100644 index 000000000..99902e629 --- /dev/null +++ b/lib/cadet_web/controllers/chat_controller.ex @@ -0,0 +1,47 @@ +defmodule CadetWeb.ChatController do + @moduledoc """ + Provides information about a user. + """ + + use CadetWeb, :controller + use PhoenixSwagger + + import Cadet.Chat + + def index(conn, _) do + user = conn.assigns.current_user + {:ok, token} = get_token(user) + + render( + conn, + "index.json", + token: token + ) + end + + swagger_path :index do + get("/chat/token") + + summary("Get the ChatKit bearer token of a user") + + security([%{JWT: []}]) + + produces("application/json") + + response(200, "OK", Schema.ref(:TokenInfo)) + end + + def swagger_definitions do + %{ + TokenInfo: + swagger_schema do + title("ChatKit Token") + description("Token used for connection to ChatKit's server") + + properties do + token(:string, "Bearer token", required: true) + end + end + } + end +end diff --git a/lib/cadet_web/router.ex b/lib/cadet_web/router.ex index 0b7ef1a0d..c8c3cfd14 100644 --- a/lib/cadet_web/router.ex +++ b/lib/cadet_web/router.ex @@ -39,6 +39,8 @@ defmodule CadetWeb.Router do post("/grading/:submissionid/:questionid", GradingController, :update) get("/user", UserController, :index) + + get("/chat/token", ChatController, :index) end # Other scopes may use custom stacks. diff --git a/lib/cadet_web/views/chat_view.ex b/lib/cadet_web/views/chat_view.ex new file mode 100644 index 000000000..a73748b0b --- /dev/null +++ b/lib/cadet_web/views/chat_view.ex @@ -0,0 +1,18 @@ +defmodule CadetWeb.ChatView do + use CadetWeb, :view + + def render("index.json", %{user: user, grade: grade, max_grade: max_grade, xp: xp, story: story}) do + %{ + name: user.name, + role: user.role, + grade: grade, + xp: xp, + maxGrade: max_grade, + story: + transform_map_for_view(story, %{ + story: :story, + playStory: :play_story? + }) + } + end +end From 5e447cf592e0be1ed2320d2a133fc234f58ac491 Mon Sep 17 00:00:00 2001 From: flxffy Date: Mon, 17 Jun 2019 21:47:36 +0800 Subject: [PATCH 03/44] Set up API for chat token generation --- lib/cadet_web/controllers/chat_controller.ex | 2 +- lib/cadet_web/views/chat_view.ex | 13 ++----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/lib/cadet_web/controllers/chat_controller.ex b/lib/cadet_web/controllers/chat_controller.ex index 99902e629..4eb6e8064 100644 --- a/lib/cadet_web/controllers/chat_controller.ex +++ b/lib/cadet_web/controllers/chat_controller.ex @@ -10,7 +10,7 @@ defmodule CadetWeb.ChatController do def index(conn, _) do user = conn.assigns.current_user - {:ok, token} = get_token(user) + {:ok, token} = get_token(user.nusnet_id) render( conn, diff --git a/lib/cadet_web/views/chat_view.ex b/lib/cadet_web/views/chat_view.ex index a73748b0b..77234361c 100644 --- a/lib/cadet_web/views/chat_view.ex +++ b/lib/cadet_web/views/chat_view.ex @@ -1,18 +1,9 @@ defmodule CadetWeb.ChatView do use CadetWeb, :view - def render("index.json", %{user: user, grade: grade, max_grade: max_grade, xp: xp, story: story}) do + def render("index.json", %{token: token}) do %{ - name: user.name, - role: user.role, - grade: grade, - xp: xp, - maxGrade: max_grade, - story: - transform_map_for_view(story, %{ - story: :story, - playStory: :play_story? - }) + token: token } end end From 018690c21c9f1a377d5f9aac8bbeedecd5139034 Mon Sep 17 00:00:00 2001 From: flxffy Date: Mon, 17 Jun 2019 22:10:15 +0800 Subject: [PATCH 04/44] Documentation for ChatKit token generation --- lib/cadet/chat/chat.ex | 2 +- lib/cadet_web/controllers/chat_controller.ex | 3 ++- lib/mix/tasks/something.ex | 11 ----------- 3 files changed, 3 insertions(+), 13 deletions(-) delete mode 100644 lib/mix/tasks/something.ex diff --git a/lib/cadet/chat/chat.ex b/lib/cadet/chat/chat.ex index a0a4f7775..3482c895a 100644 --- a/lib/cadet/chat/chat.ex +++ b/lib/cadet/chat/chat.ex @@ -6,7 +6,7 @@ defmodule Cadet.Chat do @instance_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) @key_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:key_id) @key_secret :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:key_secret) - @time_to_live 8400 + @time_to_live 86400 @doc """ Generates new bearer token for connection to ChatKit's ChatManager. diff --git a/lib/cadet_web/controllers/chat_controller.ex b/lib/cadet_web/controllers/chat_controller.ex index 4eb6e8064..3c5024a09 100644 --- a/lib/cadet_web/controllers/chat_controller.ex +++ b/lib/cadet_web/controllers/chat_controller.ex @@ -1,6 +1,7 @@ defmodule CadetWeb.ChatController do @moduledoc """ - Provides information about a user. + Provides token for connection to ChatKit's server. + Refer to ChatKit's API here: https://pusher.com/docs/chatkit """ use CadetWeb, :controller diff --git a/lib/mix/tasks/something.ex b/lib/mix/tasks/something.ex deleted file mode 100644 index c54aa5301..000000000 --- a/lib/mix/tasks/something.ex +++ /dev/null @@ -1,11 +0,0 @@ -defmodule Mix.Tasks.Cadet.Something do - use Mix.Task - - alias Cadet.Chat - - def run(args) do - username = List.first(args) - {:ok, token} = Chat.get_token(username) - IO.puts(token) - end -end From 4691dacfb34112fb11cc618c4c447edc9e617fe1 Mon Sep 17 00:00:00 2001 From: flxffy Date: Sun, 23 Jun 2019 09:59:44 +0800 Subject: [PATCH 05/44] Changed chatkit user identifier to user.id from db --- lib/cadet/chat/chat.ex | 9 +++++++-- lib/cadet_web/controllers/chat_controller.ex | 14 +++++++++----- lib/cadet_web/router.ex | 2 +- lib/cadet_web/views/chat_view.ex | 5 +++-- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/cadet/chat/chat.ex b/lib/cadet/chat/chat.ex index 3482c895a..50b7f85b8 100644 --- a/lib/cadet/chat/chat.ex +++ b/lib/cadet/chat/chat.ex @@ -13,7 +13,7 @@ defmodule Cadet.Chat do Returns {:ok, token} on success, otherwise {:error, error_message} """ - def get_token(username) do + def get_token(user_id, su \\ false) do curr_time_epoch = DateTime.to_unix(DateTime.utc_now()) payload = %{ @@ -21,7 +21,8 @@ defmodule Cadet.Chat do "iss" => "api_keys/" <> @key_id, "exp" => curr_time_epoch + @time_to_live, "iat" => curr_time_epoch, - "sub" => username + "sub" => user_id, + "su" => su } Joken.Signer.sign( @@ -29,4 +30,8 @@ defmodule Cadet.Chat do Joken.Signer.create("HS256", @key_secret) ) end + + def get_ttl() do + @time_to_live + end end diff --git a/lib/cadet_web/controllers/chat_controller.ex b/lib/cadet_web/controllers/chat_controller.ex index 3c5024a09..3108bbe49 100644 --- a/lib/cadet_web/controllers/chat_controller.ex +++ b/lib/cadet_web/controllers/chat_controller.ex @@ -11,19 +11,21 @@ defmodule CadetWeb.ChatController do def index(conn, _) do user = conn.assigns.current_user - {:ok, token} = get_token(user.nusnet_id) + {:ok, token} = get_token(to_string(user.id)) + ttl = get_ttl() render( conn, "index.json", - token: token + access_token: token, + expires_in: ttl ) end swagger_path :index do - get("/chat/token") + post("/chat/token") - summary("Get the ChatKit bearer token of a user") + summary("Get the ChatKit bearer token of a user. Token expires in 24 hours.") security([%{JWT: []}]) @@ -40,7 +42,9 @@ defmodule CadetWeb.ChatController do description("Token used for connection to ChatKit's server") properties do - token(:string, "Bearer token", required: true) + access_token(:string, "Bearer token", required: true) + + expires_in(:string, "TTL of token", required: true) end end } diff --git a/lib/cadet_web/router.ex b/lib/cadet_web/router.ex index c8c3cfd14..6ecd6d5e6 100644 --- a/lib/cadet_web/router.ex +++ b/lib/cadet_web/router.ex @@ -40,7 +40,7 @@ defmodule CadetWeb.Router do get("/user", UserController, :index) - get("/chat/token", ChatController, :index) + post("/chat/token", ChatController, :index) end # Other scopes may use custom stacks. diff --git a/lib/cadet_web/views/chat_view.ex b/lib/cadet_web/views/chat_view.ex index 77234361c..1262ef865 100644 --- a/lib/cadet_web/views/chat_view.ex +++ b/lib/cadet_web/views/chat_view.ex @@ -1,9 +1,10 @@ defmodule CadetWeb.ChatView do use CadetWeb, :view - def render("index.json", %{token: token}) do + def render("index.json", %{access_token: token, expires_in: ttl}) do %{ - token: token + access_token: token, + expires_in: ttl } end end From 5cb5ddfd49b7a92751acd1b029f38fdefb588972 Mon Sep 17 00:00:00 2001 From: flxffy Date: Sun, 23 Jun 2019 10:16:10 +0800 Subject: [PATCH 06/44] update readme with chatkit instructions --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 21040e105..dc7bd4695 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,9 @@ $ vim config/secrets.exs `luminus_redirect_url` are required for the application to properly authenticate with LumiNUS. - A valid `cs1101s_repository`, `cs1101s_rsa_key` is required for the application to run with the `--updater` flag. Otherwise, the default values will suffice. + - A valid `instance_id`, `key_id` and `key_secret` are required to use ChatKit's services. Otherwise, the placeholder values can be left as they are. -2. Install Elixir dependencies +1. Install Elixir dependencies ```bash $ mix deps.get ``` @@ -66,6 +67,14 @@ We recommend setting up nginx to handle preflight checks using the following If you do this, do remember to point cadet-frontend to port `4001` instead of `4000` +### ChatKit + +The chat functionality replacing the previous comment field found in assignments is built on top of ChatKit. Its documentation can be found [here](https://pusher.com/docs/chatkit). + +If you are using ChatKit, obtain your instance ID, key ID and secret key from your account, and set them inInstructions to that are [here](https://pusher.com/docs/chatkit/authentication#chatkit-key-and-instance-id). + +Internet connection is required for usage. + ### Style Guide From 80fcc0bdaa06d5fc0bee8a246b30b65bd2e14895 Mon Sep 17 00:00:00 2001 From: flxffy Date: Sun, 23 Jun 2019 10:19:29 +0800 Subject: [PATCH 07/44] code readbility --- lib/cadet/chat/chat.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cadet/chat/chat.ex b/lib/cadet/chat/chat.ex index 50b7f85b8..53ce871ba 100644 --- a/lib/cadet/chat/chat.ex +++ b/lib/cadet/chat/chat.ex @@ -6,7 +6,7 @@ defmodule Cadet.Chat do @instance_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) @key_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:key_id) @key_secret :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:key_secret) - @time_to_live 86400 + @time_to_live 86_400 @doc """ Generates new bearer token for connection to ChatKit's ChatManager. @@ -31,7 +31,7 @@ defmodule Cadet.Chat do ) end - def get_ttl() do + def get_ttl do @time_to_live end end From f27ae9efbacc3203d74e218c81b61b8465cd5444 Mon Sep 17 00:00:00 2001 From: flxffy Date: Sun, 23 Jun 2019 16:09:10 +0800 Subject: [PATCH 08/44] Added private functions in chat token generator --- lib/cadet/chat/chat.ex | 32 +++++++++++++++----- lib/cadet_web/controllers/chat_controller.ex | 3 +- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/cadet/chat/chat.ex b/lib/cadet/chat/chat.ex index 53ce871ba..4c913e6a7 100644 --- a/lib/cadet/chat/chat.ex +++ b/lib/cadet/chat/chat.ex @@ -3,23 +3,43 @@ defmodule Cadet.Chat do Contains logic to supplement ChatKit, an external service engaged for Source Academy. ChatKit's API can be found here: https://pusher.com/docs/chatkit """ + + alias Cadet.Accounts.User + @instance_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) @key_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:key_id) @key_secret :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:key_secret) - @time_to_live 86_400 + @token_ttl 86_400 @doc """ - Generates new bearer token for connection to ChatKit's ChatManager. + Generates user token for connection to ChatKit's ChatManager. + Returns {:ok, token, ttl}. + """ + def get_user_token(%User{id: user_id}) do + {:ok, token} = get_token(to_string(user_id)) + {:ok, token, @token_ttl} + end + @doc """ + Generates a token for user with admin rights to enable superuser permissions. + Returns {:ok, token} + """ + def get_superuser_token() do + get_token("Admin", true) + end + + @doc """ + Generates a new token for connection to ChatKit's API. Returns {:ok, token} on success, otherwise {:error, error_message} + Note: dialyzer says it will always be successful, so no error handling required """ - def get_token(user_id, su \\ false) do + defp get_token(user_id, su \\ false) when is_binary(user_id) do curr_time_epoch = DateTime.to_unix(DateTime.utc_now()) payload = %{ "instance" => @instance_id, "iss" => "api_keys/" <> @key_id, - "exp" => curr_time_epoch + @time_to_live, + "exp" => curr_time_epoch + @token_ttl, "iat" => curr_time_epoch, "sub" => user_id, "su" => su @@ -30,8 +50,4 @@ defmodule Cadet.Chat do Joken.Signer.create("HS256", @key_secret) ) end - - def get_ttl do - @time_to_live - end end diff --git a/lib/cadet_web/controllers/chat_controller.ex b/lib/cadet_web/controllers/chat_controller.ex index 3108bbe49..b2facb314 100644 --- a/lib/cadet_web/controllers/chat_controller.ex +++ b/lib/cadet_web/controllers/chat_controller.ex @@ -11,8 +11,7 @@ defmodule CadetWeb.ChatController do def index(conn, _) do user = conn.assigns.current_user - {:ok, token} = get_token(to_string(user.id)) - ttl = get_ttl() + {:ok, token, ttl} = get_user_token(user) render( conn, From 9a3243bba2975c5509167d286b3add9ea5a6cc2b Mon Sep 17 00:00:00 2001 From: flxffy Date: Sun, 23 Jun 2019 16:19:10 +0800 Subject: [PATCH 09/44] fixed typo in superuser name --- lib/cadet/chat/chat.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cadet/chat/chat.ex b/lib/cadet/chat/chat.ex index 4c913e6a7..a82ccb504 100644 --- a/lib/cadet/chat/chat.ex +++ b/lib/cadet/chat/chat.ex @@ -25,7 +25,7 @@ defmodule Cadet.Chat do Returns {:ok, token} """ def get_superuser_token() do - get_token("Admin", true) + get_token("admin", true) end @doc """ From 472ebca52a8202658195025568ad14b4c36067ea Mon Sep 17 00:00:00 2001 From: flxffy Date: Mon, 24 Jun 2019 00:17:29 +0800 Subject: [PATCH 10/44] Main logic for script to create all users --- lib/cadet/chat/chat.ex | 4 ++-- lib/mix/tasks/lala.ex | 47 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 lib/mix/tasks/lala.ex diff --git a/lib/cadet/chat/chat.ex b/lib/cadet/chat/chat.ex index a82ccb504..92f771e9c 100644 --- a/lib/cadet/chat/chat.ex +++ b/lib/cadet/chat/chat.ex @@ -24,7 +24,7 @@ defmodule Cadet.Chat do Generates a token for user with admin rights to enable superuser permissions. Returns {:ok, token} """ - def get_superuser_token() do + def get_superuser_token do get_token("admin", true) end @@ -38,7 +38,7 @@ defmodule Cadet.Chat do payload = %{ "instance" => @instance_id, - "iss" => "api_keys/" <> @key_id, + "iss" => "api_keys/#{@key_id}", "exp" => curr_time_epoch + @token_ttl, "iat" => curr_time_epoch, "sub" => user_id, diff --git a/lib/mix/tasks/lala.ex b/lib/mix/tasks/lala.ex new file mode 100644 index 000000000..6a5a84ca8 --- /dev/null +++ b/lib/mix/tasks/lala.ex @@ -0,0 +1,47 @@ +defmodule Mix.Tasks.Cadet.Test do + @moduledoc """ + Creates ChatKit accounts for all users in the database. + User creation: https://pusher.com/docs/chatkit/reference/api-v3#create-a-user + Status codes: https://pusher.com/docs/chatkit/reference/api-v3#response-and-error-codes + """ + use Mix.Task + + import Cadet.Chat + import Mix.EctoSQL + + alias Cadet.Repo + alias Cadet.Accounts.User + + @instance_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) + + def run(_args) do + ensure_started(Repo, []) + + url = "https://us1.pusherplatform.io/services/chatkit/v4/#{@instance_id}/users" + + {:ok, token} = get_superuser_token() + headers = [Authorization: "Bearer #{token}"] + + HTTPoison.start() + + User + |> Repo.all() + |> Enum.each(fn user -> + body = Poison.encode!(%{"name" => user.name, "id" => to_string(user.id)}) + + case HTTPoison.post(url, body, headers) do + {:ok, %HTTPoison.Response{status_code: 201}} -> + :ok + + {:ok, %HTTPoison.Response{body: body}} -> + IO.puts( + "Error: #{Poison.decode!(body)["error_description"]} " <> + "(name: #{user.name}, user_id: #{user.id})" + ) + + {:error, %HTTPoison.Error{reason: reason}} -> + IO.inspect(reason) + end + end) + end +end From 2cfeae21affc1f3bf765e112debfad15bd8a545c Mon Sep 17 00:00:00 2001 From: flxffy Date: Mon, 24 Jun 2019 00:36:47 +0800 Subject: [PATCH 11/44] Script for creating all chatkit users --- lib/mix/tasks/{lala.ex => users/chat.ex} | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) rename lib/mix/tasks/{lala.ex => users/chat.ex} (69%) diff --git a/lib/mix/tasks/lala.ex b/lib/mix/tasks/users/chat.ex similarity index 69% rename from lib/mix/tasks/lala.ex rename to lib/mix/tasks/users/chat.ex index 6a5a84ca8..7b559f4e9 100644 --- a/lib/mix/tasks/lala.ex +++ b/lib/mix/tasks/users/chat.ex @@ -1,11 +1,16 @@ -defmodule Mix.Tasks.Cadet.Test do +defmodule Mix.Tasks.Cadet.Users.Chat do @moduledoc """ Creates ChatKit accounts for all users in the database. User creation: https://pusher.com/docs/chatkit/reference/api-v3#create-a-user Status codes: https://pusher.com/docs/chatkit/reference/api-v3#response-and-error-codes + + Note: this task is to be run after `import`. + Assumption: "admin" must already exist in the ChatKit instance. """ use Mix.Task + require Logger + import Cadet.Chat import Mix.EctoSQL @@ -16,14 +21,13 @@ defmodule Mix.Tasks.Cadet.Test do def run(_args) do ensure_started(Repo, []) + HTTPoison.start() url = "https://us1.pusherplatform.io/services/chatkit/v4/#{@instance_id}/users" {:ok, token} = get_superuser_token() headers = [Authorization: "Bearer #{token}"] - HTTPoison.start() - User |> Repo.all() |> Enum.each(fn user -> @@ -34,13 +38,11 @@ defmodule Mix.Tasks.Cadet.Test do :ok {:ok, %HTTPoison.Response{body: body}} -> - IO.puts( - "Error: #{Poison.decode!(body)["error_description"]} " <> - "(name: #{user.name}, user_id: #{user.id})" - ) + Logger.error("Unable to create user (name: #{user.name}, user_id: #{user.id})") + Logger.error("error: #{Poison.decode!(body)["error_description"]}") - {:error, %HTTPoison.Error{reason: reason}} -> - IO.inspect(reason) + {:error, %HTTPoison.Error{reason: error}} -> + Logger.error("error: #{inspect(error, pretty: true)}") end end) end From 8491f78772b2a877a4b1019b330433c498f06ea1 Mon Sep 17 00:00:00 2001 From: Hoang <42971570+wardetu@users.noreply.github.com> Date: Thu, 27 Jun 2019 19:25:04 +0700 Subject: [PATCH 12/44] Add comment related functions --- lib/cadet/assessments/assessments.ex | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index 69c687cae..718fa7d97 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -760,6 +760,40 @@ defmodule Cadet.Assessments do end end + def get_answer(%{submission_id: submission_id, question_id: question_id}) do + answer_query = + Answer + |> where(submission_id: ^submission_id) + |> where(question_id: ^question_id) + Repo.one(answer_query) + end + + def get_comment_from_question(%{submission_id: submission_id, question_id: question_id}) do + answer = get_answer(%{submission_id: submission_id, question_id: question_id}) + answer.comment + end + + def insert_or_update_comment(submission = %Submission{}, question = %Question{}, new_comment) do + answer = get_answer(%{submission_id: submission.id, question_id: question.id}) + + answer_changeset = + %Answer{} + |> Answer.changeset(%{ + answer: answer.answer, + comment: new_comment, + question_id: question.id, + submission_id: submission.id, + type: question.type + }) + + Repo.insert( + answer_changeset, + on_conflict: [set: [comment: get_change(answer_changeset, :comment),answer: get_change(answer_changeset, :answer)]], + conflict_target: [:submission_id, :question_id] + ) + end + + defp submissions_by_group(grader = %User{role: :staff}, submission_query) do students = Cadet.Accounts.Query.students_of(grader) From 1c3e241fa72ac5e5d6a422f126ec01e48c2c769c Mon Sep 17 00:00:00 2001 From: flxffy Date: Mon, 1 Jul 2019 01:04:48 +0800 Subject: [PATCH 13/44] Storage of ChatKit room id. Logic complete with failsafes in thevent of Chatkit's server failure. Untested. --- lib/cadet/assessments/assessments.ex | 34 ++++++++-- lib/cadet/chat/chat.ex | 77 ++++++++++++++++++++++- lib/mix/tasks/room.ex | 94 ++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 lib/mix/tasks/room.ex diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index 69c687cae..83d27c781 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -5,6 +5,7 @@ defmodule Cadet.Assessments do """ use Cadet, [:context, :display] + import Cadet.Chat import Ecto.Query alias Cadet.Accounts.User @@ -139,6 +140,12 @@ defmodule Cadet.Assessments do user = %User{role: role} ) do if Timex.after?(Timex.now(), assessment.open_at) or role in @open_all_assessment_roles do + # ChatKit - create chatrooms if they don't exist + Submission + |> where(assessment_id: ^id) + |> Repo.all() + |> Enum.each(fn submission -> create_rooms(submission) end) + answer_query = Answer |> join(:inner, [a], s in assoc(a, :submission)) @@ -153,6 +160,8 @@ defmodule Cadet.Assessments do |> order_by(:display_order) |> Repo.all() + IO.puts(questions) + assessment = Map.put(assessment, :questions, questions) {:ok, assessment} else @@ -455,7 +464,6 @@ defmodule Cadet.Assessments do xp_adjustment: 0, autograding_status: :none, autograding_results: [], - comment: nil, grader_id: nil }) |> Repo.update()} @@ -602,6 +610,9 @@ defmodule Cadet.Assessments do @spec get_answers_in_submission(integer() | String.t(), %User{}) :: {:ok, [%Answer{}]} | {:error, {:unauthorized, String.t()}} def get_answers_in_submission(id, grader = %User{role: role}) when is_ecto_id(id) do + # ChatKit - create chatrooms if they don't exist + create_rooms(Submission |> where(id: ^id) |> Repo.one()) + answer_query = Answer |> where(submission_id: ^id) @@ -743,11 +754,22 @@ defmodule Cadet.Assessments do type: question.type }) - Repo.insert( - answer_changeset, - on_conflict: [set: [answer: get_change(answer_changeset, :answer)]], - conflict_target: [:submission_id, :question_id] - ) + case Repo.insert( + answer_changeset, + on_conflict: [set: [answer: get_change(answer_changeset, :answer)]], + conflict_target: [:submission_id, :question_id] + ) do + {:error, struct} -> + {:error, struct} + + {:ok, struct} -> + # ChatKit - create chatrooms if they don't exist + create_rooms(submission) + + # Return {:ok, _} regardless of success/failure. + # Failure likely due to service's internal error. + {:ok, struct} + end end defp build_answer_content(raw_answer, question_type) do diff --git a/lib/cadet/chat/chat.ex b/lib/cadet/chat/chat.ex index 92f771e9c..64fe1ef8e 100644 --- a/lib/cadet/chat/chat.ex +++ b/lib/cadet/chat/chat.ex @@ -4,6 +4,12 @@ defmodule Cadet.Chat do ChatKit's API can be found here: https://pusher.com/docs/chatkit """ + require Logger + + import Ecto.Query + + alias Cadet.Repo + alias Cadet.Assessments.{Answer, Submission} alias Cadet.Accounts.User @instance_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) @@ -29,10 +35,67 @@ defmodule Cadet.Chat do end @doc """ - Generates a new token for connection to ChatKit's API. - Returns {:ok, token} on success, otherwise {:error, error_message} - Note: dialyzer says it will always be successful, so no error handling required + Creates a chatroom for every answer, and updates db with the chatroom id. + Takes in Submission struct """ + def create_rooms(%Submission{ + id: id, + student_id: student_id, + assessment_id: assessment_id + }) do + student = User |> where(id: ^student_id) |> Repo.one() + + Answer + |> where(submission_id: ^id) + |> Repo.all() + |> Enum.filter(fn answer -> answer.comment == "" or answer.comment == nil end) + |> Enum.each(fn answer -> + case create_room(assessment_id, answer.question_id, student) do + {:ok, %{"id" => room_id}} -> + answer + |> Answer.grading_changeset(%{ + comment: room_id + }) + |> Repo.update() + end + end) + end + + defp create_room( + assessment_id, + question_id, + %User{ + id: student_id, + nusnet_id: nusnet_id + } + ) do + HTTPoison.start() + + url = "https://us1.pusherplatform.io/services/chatkit/v4/#{@instance_id}/rooms" + + {:ok, token} = get_superuser_token() + headers = [Authorization: "Bearer #{token}"] + + body = + Poison.encode!(%{ + "name" => "#{nusnet_id}_#{assessment_id}_Q#{question_id}", + "private" => true, + "user_ids" => get_staff_admin_user_ids() ++ [to_string(student_id)] + }) + + case HTTPoison.post(url, body, headers) do + {:ok, %HTTPoison.Response{body: body, status_code: 201}} -> + Poison.decode(body) + + {:ok, _} -> + :error + + {:error, %HTTPoison.Error{reason: error}} -> + Logger.error("error: #{inspect(error, pretty: true)}") + :error + end + end + defp get_token(user_id, su \\ false) when is_binary(user_id) do curr_time_epoch = DateTime.to_unix(DateTime.utc_now()) @@ -45,9 +108,17 @@ defmodule Cadet.Chat do "su" => su } + # Note: dialyzer says signing only returns {:ok, token} Joken.Signer.sign( payload, Joken.Signer.create("HS256", @key_secret) ) end + + defp get_staff_admin_user_ids do + User + |> Repo.all() + |> Enum.filter(fn user -> user.role == :staff or user.role == :admin end) + |> Enum.map(fn user -> to_string(user.id) end) + end end diff --git a/lib/mix/tasks/room.ex b/lib/mix/tasks/room.ex new file mode 100644 index 000000000..797449259 --- /dev/null +++ b/lib/mix/tasks/room.ex @@ -0,0 +1,94 @@ +defmodule Mix.Tasks.Cadet.Room do + @moduledoc """ + Run the Cadet server. + Currently it is equivalent with `phx.server` + """ + use Mix.Task + + require Logger + + import Cadet.Chat + import Ecto.Query + import Mix.EctoSQL + + alias Cadet.Repo + alias Cadet.Assessments.{Answer, Submission} + alias Cadet.Accounts.User + + @instance_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) + + def run(_args) do + # can remove later + ensure_started(Repo, []) + + Answer + |> where(submission_id: 2) + |> Repo.all() + |> Enum.filter(fn answer -> answer.comment == "" or answer.comment == nil end) + |> Enum.each(fn answer -> + case create_room(answer) do + {:ok, %{"id" => room_id}} -> + answer + |> Answer.grading_changeset(%{ + comment: room_id + }) + |> Repo.update() + end + end) + end + + defp create_room(%Answer{submission_id: sub_id, question_id: qns_id}) do + HTTPoison.start() + + url = "https://us1.pusherplatform.io/services/chatkit/v4/#{@instance_id}/rooms" + + {:ok, token} = get_superuser_token() + headers = [Authorization: "Bearer #{token}"] + + body = + Poison.encode!(%{ + "name" => + "#{ + sub_id + |> Integer.to_string() + |> String.pad_leading(1, "0") + }_Q#{ + qns_id + |> Integer.to_string() + |> String.pad_leading(1, "0") + }", + "private" => true, + "user_ids" => get_staff_admin_user_ids() ++ get_student_id(sub_id) + }) + + case HTTPoison.post(url, body, headers) do + {:ok, %HTTPoison.Response{body: body, status_code: 201}} -> + Poison.decode(body) + + {:ok, %HTTPoison.Response{body: body}} -> + IO.puts(body) + :error + + {:error, %HTTPoison.Error{reason: error}} -> + Logger.error("error: #{inspect(error, pretty: true)}") + :error + end + end + + defp get_staff_admin_user_ids do + User + |> Repo.all() + |> Enum.filter(fn user -> user.role == :staff or user.role == :admin end) + |> Enum.map(fn user -> to_string(user.id) end) + end + + defp get_student_id(submission_id) do + %{student_id: student_id} = + Submission + |> where(id: ^submission_id) + |> select([:student_id]) + |> Repo.one() + + [to_string(student_id)] + end +end From 9e66f584361045004ba39e802bb12a140a1d43e6 Mon Sep 17 00:00:00 2001 From: flxffy Date: Mon, 1 Jul 2019 01:05:20 +0800 Subject: [PATCH 14/44] Remove test file --- lib/mix/tasks/room.ex | 94 ------------------------------------------- 1 file changed, 94 deletions(-) delete mode 100644 lib/mix/tasks/room.ex diff --git a/lib/mix/tasks/room.ex b/lib/mix/tasks/room.ex deleted file mode 100644 index 797449259..000000000 --- a/lib/mix/tasks/room.ex +++ /dev/null @@ -1,94 +0,0 @@ -defmodule Mix.Tasks.Cadet.Room do - @moduledoc """ - Run the Cadet server. - Currently it is equivalent with `phx.server` - """ - use Mix.Task - - require Logger - - import Cadet.Chat - import Ecto.Query - import Mix.EctoSQL - - alias Cadet.Repo - alias Cadet.Assessments.{Answer, Submission} - alias Cadet.Accounts.User - - @instance_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) - - def run(_args) do - # can remove later - ensure_started(Repo, []) - - Answer - |> where(submission_id: 2) - |> Repo.all() - |> Enum.filter(fn answer -> answer.comment == "" or answer.comment == nil end) - |> Enum.each(fn answer -> - case create_room(answer) do - {:ok, %{"id" => room_id}} -> - answer - |> Answer.grading_changeset(%{ - comment: room_id - }) - |> Repo.update() - end - end) - end - - defp create_room(%Answer{submission_id: sub_id, question_id: qns_id}) do - HTTPoison.start() - - url = "https://us1.pusherplatform.io/services/chatkit/v4/#{@instance_id}/rooms" - - {:ok, token} = get_superuser_token() - headers = [Authorization: "Bearer #{token}"] - - body = - Poison.encode!(%{ - "name" => - "#{ - sub_id - |> Integer.to_string() - |> String.pad_leading(1, "0") - }_Q#{ - qns_id - |> Integer.to_string() - |> String.pad_leading(1, "0") - }", - "private" => true, - "user_ids" => get_staff_admin_user_ids() ++ get_student_id(sub_id) - }) - - case HTTPoison.post(url, body, headers) do - {:ok, %HTTPoison.Response{body: body, status_code: 201}} -> - Poison.decode(body) - - {:ok, %HTTPoison.Response{body: body}} -> - IO.puts(body) - :error - - {:error, %HTTPoison.Error{reason: error}} -> - Logger.error("error: #{inspect(error, pretty: true)}") - :error - end - end - - defp get_staff_admin_user_ids do - User - |> Repo.all() - |> Enum.filter(fn user -> user.role == :staff or user.role == :admin end) - |> Enum.map(fn user -> to_string(user.id) end) - end - - defp get_student_id(submission_id) do - %{student_id: student_id} = - Submission - |> where(id: ^submission_id) - |> select([:student_id]) - |> Repo.one() - - [to_string(student_id)] - end -end From fdea41c8348dd8a76049bf26d6cb65140ae993af Mon Sep 17 00:00:00 2001 From: flxffy Date: Tue, 2 Jul 2019 20:13:47 +0800 Subject: [PATCH 15/44] Refactored chat.ex --- lib/cadet/assessments/assessments.ex | 4 +- lib/cadet/chat/{chat.ex => room.ex} | 44 ++---------------- lib/cadet/chat/token.ex | 49 ++++++++++++++++++++ lib/cadet_web/controllers/chat_controller.ex | 2 +- lib/mix/tasks/users/chat.ex | 2 +- 5 files changed, 55 insertions(+), 46 deletions(-) rename lib/cadet/chat/{chat.ex => room.ex} (64%) create mode 100644 lib/cadet/chat/token.ex diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index 83d27c781..36473dae2 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -5,7 +5,7 @@ defmodule Cadet.Assessments do """ use Cadet, [:context, :display] - import Cadet.Chat + import Cadet.Chat.Room import Ecto.Query alias Cadet.Accounts.User @@ -160,8 +160,6 @@ defmodule Cadet.Assessments do |> order_by(:display_order) |> Repo.all() - IO.puts(questions) - assessment = Map.put(assessment, :questions, questions) {:ok, assessment} else diff --git a/lib/cadet/chat/chat.ex b/lib/cadet/chat/room.ex similarity index 64% rename from lib/cadet/chat/chat.ex rename to lib/cadet/chat/room.ex index 64fe1ef8e..a617e892a 100644 --- a/lib/cadet/chat/chat.ex +++ b/lib/cadet/chat/room.ex @@ -1,11 +1,12 @@ -defmodule Cadet.Chat do +defmodule Cadet.Chat.Room do @moduledoc """ - Contains logic to supplement ChatKit, an external service engaged for Source Academy. + Contains logic pertaining to chatroom creation to supplement ChatKit, an external service engaged for Source Academy. ChatKit's API can be found here: https://pusher.com/docs/chatkit """ require Logger + import Cadet.Chat.Token import Ecto.Query alias Cadet.Repo @@ -13,26 +14,6 @@ defmodule Cadet.Chat do alias Cadet.Accounts.User @instance_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) - @key_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:key_id) - @key_secret :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:key_secret) - @token_ttl 86_400 - - @doc """ - Generates user token for connection to ChatKit's ChatManager. - Returns {:ok, token, ttl}. - """ - def get_user_token(%User{id: user_id}) do - {:ok, token} = get_token(to_string(user_id)) - {:ok, token, @token_ttl} - end - - @doc """ - Generates a token for user with admin rights to enable superuser permissions. - Returns {:ok, token} - """ - def get_superuser_token do - get_token("admin", true) - end @doc """ Creates a chatroom for every answer, and updates db with the chatroom id. @@ -96,25 +77,6 @@ defmodule Cadet.Chat do end end - defp get_token(user_id, su \\ false) when is_binary(user_id) do - curr_time_epoch = DateTime.to_unix(DateTime.utc_now()) - - payload = %{ - "instance" => @instance_id, - "iss" => "api_keys/#{@key_id}", - "exp" => curr_time_epoch + @token_ttl, - "iat" => curr_time_epoch, - "sub" => user_id, - "su" => su - } - - # Note: dialyzer says signing only returns {:ok, token} - Joken.Signer.sign( - payload, - Joken.Signer.create("HS256", @key_secret) - ) - end - defp get_staff_admin_user_ids do User |> Repo.all() diff --git a/lib/cadet/chat/token.ex b/lib/cadet/chat/token.ex new file mode 100644 index 000000000..ef0a07adc --- /dev/null +++ b/lib/cadet/chat/token.ex @@ -0,0 +1,49 @@ +defmodule Cadet.Chat.Token do + @moduledoc """ + Contains logic pertaining to the generation of tokens to supplement the usage of ChatKit, an external service engaged for Source Academy. + ChatKit's API can be found here: https://pusher.com/docs/chatkit + """ + + alias Cadet.Accounts.User + + @instance_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) + @key_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:key_id) + @key_secret :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:key_secret) + @token_ttl 86_400 + + @doc """ + Generates user token for connection to ChatKit's ChatManager. + Returns {:ok, token, ttl}. + """ + def get_user_token(%User{id: user_id}) do + {:ok, token} = get_token(to_string(user_id)) + {:ok, token, @token_ttl} + end + + @doc """ + Generates a token for user with admin rights to enable superuser permissions. + Returns {:ok, token} + """ + def get_superuser_token do + get_token("admin", true) + end + + defp get_token(user_id, su \\ false) when is_binary(user_id) do + curr_time_epoch = DateTime.to_unix(DateTime.utc_now()) + + payload = %{ + "instance" => @instance_id, + "iss" => "api_keys/#{@key_id}", + "exp" => curr_time_epoch + @token_ttl, + "iat" => curr_time_epoch, + "sub" => user_id, + "su" => su + } + + # Note: dialyzer says signing only returns {:ok, token} + Joken.Signer.sign( + payload, + Joken.Signer.create("HS256", @key_secret) + ) + end +end diff --git a/lib/cadet_web/controllers/chat_controller.ex b/lib/cadet_web/controllers/chat_controller.ex index b2facb314..2b21e83a7 100644 --- a/lib/cadet_web/controllers/chat_controller.ex +++ b/lib/cadet_web/controllers/chat_controller.ex @@ -7,7 +7,7 @@ defmodule CadetWeb.ChatController do use CadetWeb, :controller use PhoenixSwagger - import Cadet.Chat + import Cadet.Chat.Token def index(conn, _) do user = conn.assigns.current_user diff --git a/lib/mix/tasks/users/chat.ex b/lib/mix/tasks/users/chat.ex index 7b559f4e9..7a164ce89 100644 --- a/lib/mix/tasks/users/chat.ex +++ b/lib/mix/tasks/users/chat.ex @@ -11,7 +11,7 @@ defmodule Mix.Tasks.Cadet.Users.Chat do require Logger - import Cadet.Chat + import Cadet.Chat.Token import Mix.EctoSQL alias Cadet.Repo From fefc2a568db2b139c3e3098f980f48f4bc0d9bf9 Mon Sep 17 00:00:00 2001 From: flxffy Date: Tue, 2 Jul 2019 21:17:56 +0800 Subject: [PATCH 16/44] Updated moduledoc for cadet.users.chat --- lib/mix/tasks/users/chat.ex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/mix/tasks/users/chat.ex b/lib/mix/tasks/users/chat.ex index 7a164ce89..2b3667b60 100644 --- a/lib/mix/tasks/users/chat.ex +++ b/lib/mix/tasks/users/chat.ex @@ -4,8 +4,12 @@ defmodule Mix.Tasks.Cadet.Users.Chat do User creation: https://pusher.com/docs/chatkit/reference/api-v3#create-a-user Status codes: https://pusher.com/docs/chatkit/reference/api-v3#response-and-error-codes - Note: this task is to be run after `import`. - Assumption: "admin" must already exist in the ChatKit instance. + Note: + - Task is to run after `import` (i.e. db is populated) + - user_id from User is used as the unique identifier for Chatkit + + Assumption + - User with the id "admin" already exist in the ChatKit instance. """ use Mix.Task From 6bf760883ae855aaeaae56db2506743f8c8ae148 Mon Sep 17 00:00:00 2001 From: flxffy Date: Tue, 2 Jul 2019 21:57:24 +0800 Subject: [PATCH 17/44] Fixed unsubmit test --- test/cadet_web/controllers/grading_controller_test.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/cadet_web/controllers/grading_controller_test.exs b/test/cadet_web/controllers/grading_controller_test.exs index 575460bc3..decbd2c57 100644 --- a/test/cadet_web/controllers/grading_controller_test.exs +++ b/test/cadet_web/controllers/grading_controller_test.exs @@ -601,7 +601,8 @@ defmodule CadetWeb.GradingControllerTest do assert submission_db.unsubmitted_by_id === grader.id assert submission_db.unsubmitted_at != nil - assert answer_db.comment == nil + # Chatkit roomid should not be removed when a student unsubmits + assert answer_db.comment == answer.comment assert answer_db.autograding_status == :none assert answer_db.autograding_results == [] assert answer_db.grader_id == nil From 3e851e4123d7edae21ba0ed18e8149c3726fc73f Mon Sep 17 00:00:00 2001 From: flxffy Date: Tue, 2 Jul 2019 22:29:30 +0800 Subject: [PATCH 18/44] Create chatkit room when submission is retrieved only if authorised --- lib/cadet/assessments/assessments.ex | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index 36473dae2..1f92e4dcb 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -608,9 +608,6 @@ defmodule Cadet.Assessments do @spec get_answers_in_submission(integer() | String.t(), %User{}) :: {:ok, [%Answer{}]} | {:error, {:unauthorized, String.t()}} def get_answers_in_submission(id, grader = %User{role: role}) when is_ecto_id(id) do - # ChatKit - create chatrooms if they don't exist - create_rooms(Submission |> where(id: ^id) |> Repo.one()) - answer_query = Answer |> where(submission_id: ^id) @@ -620,6 +617,11 @@ defmodule Cadet.Assessments do |> join(:inner, [a, ..., s], st in assoc(s, :student)) |> preload([_, q, g, s, st], question: q, grader: g, submission: {s, student: st}) + if role in @grading_roles or role in @see_all_submissions_roles do + # ChatKit - create chatrooms if they don't exist + create_rooms(Submission |> where(id: ^id) |> Repo.one()) + end + cond do role in @grading_roles -> students = Cadet.Accounts.Query.students_of(grader) From 325e4175cfbc1bde0ed33b1c1e1b7bd0260381df Mon Sep 17 00:00:00 2001 From: flxffy Date: Tue, 2 Jul 2019 22:46:30 +0800 Subject: [PATCH 19/44] fixed answer controller test case clause error --- lib/cadet/chat/room.ex | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/cadet/chat/room.ex b/lib/cadet/chat/room.ex index a617e892a..2834a37bf 100644 --- a/lib/cadet/chat/room.ex +++ b/lib/cadet/chat/room.ex @@ -38,6 +38,9 @@ defmodule Cadet.Chat.Room do comment: room_id }) |> Repo.update() + + {:error, _} -> + nil end end) end @@ -69,11 +72,11 @@ defmodule Cadet.Chat.Room do Poison.decode(body) {:ok, _} -> - :error + {:error, nil} {:error, %HTTPoison.Error{reason: error}} -> Logger.error("error: #{inspect(error, pretty: true)}") - :error + {:error, nil} end end From 574472e059a72d613daecc5f1ad4376c418af330 Mon Sep 17 00:00:00 2001 From: flxffy Date: Tue, 2 Jul 2019 22:57:33 +0800 Subject: [PATCH 20/44] grading_controller_test.ex to accept unchanged comment field for unsubmit --- lib/cadet/assessments/assessments.ex | 9 +++++++-- mix.lock | 4 ++-- test/cadet_web/controllers/grading_controller_test.exs | 5 +++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index 72e1c063a..7052beff8 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -788,6 +788,7 @@ defmodule Cadet.Assessments do Answer |> where(submission_id: ^submission_id) |> where(question_id: ^question_id) + Repo.one(answer_query) end @@ -811,12 +812,16 @@ defmodule Cadet.Assessments do Repo.insert( answer_changeset, - on_conflict: [set: [comment: get_change(answer_changeset, :comment),answer: get_change(answer_changeset, :answer)]], + on_conflict: [ + set: [ + comment: get_change(answer_changeset, :comment), + answer: get_change(answer_changeset, :answer) + ] + ], conflict_target: [:submission_id, :question_id] ) end - defp submissions_by_group(grader = %User{role: :staff}, submission_query) do students = Cadet.Accounts.Query.students_of(grader) diff --git a/mix.lock b/mix.lock index 2eab7b587..5dec88fc4 100644 --- a/mix.lock +++ b/mix.lock @@ -13,8 +13,8 @@ "credo": {:hex, :credo, "1.1.0", "e0c07b2fd7e2109495f582430a1bc96b2c71b7d94c59dfad120529f65f19872f", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, "crontab": {:hex, :crontab, "1.1.5", "2c9439506ceb0e9045de75879e994b88d6f0be88bfe017d58cb356c66c4a5482", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, "csv": {:hex, :csv, "2.3.1", "9ce11eff5a74a07baf3787b2b19dd798724d29a9c3a492a41df39f6af686da0e", [:mix], [{:parallel_stream, "~> 1.0.4", [hex: :parallel_stream, repo: "hexpm", optional: false]}], "hexpm"}, - "db_connection": {:hex, :db_connection, "2.1.0", "122e2f62c4906bf2e49554f1e64db5030c19229aa40935f33088e7d543aa79d0", [], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"}, - "decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [], [], "hexpm"}, + "db_connection": {:hex, :db_connection, "2.1.0", "122e2f62c4906bf2e49554f1e64db5030c19229aa40935f33088e7d543aa79d0", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"}, + "decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"}, "dialyxir": {:hex, :dialyxir, "1.0.0-rc.6", "78e97d9c0ff1b5521dd68041193891aebebce52fc3b93463c0a6806874557d7d", [:mix], [{:erlex, "~> 0.2.1", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm"}, "distillery": {:hex, :distillery, "2.1.0", "5f31c7771923c12dbb79dcd8d01c5913b07222f134c327e7ab026acdabae985b", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm"}, "ecto": {:hex, :ecto, "3.0.9", "f01922a0b91a41d764d4e3a914d7f058d99a03460d3082c61dd2dcadd724c934", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"}, diff --git a/test/cadet_web/controllers/grading_controller_test.exs b/test/cadet_web/controllers/grading_controller_test.exs index 4b1793c7a..8c7b4302a 100644 --- a/test/cadet_web/controllers/grading_controller_test.exs +++ b/test/cadet_web/controllers/grading_controller_test.exs @@ -601,7 +601,7 @@ defmodule CadetWeb.GradingControllerTest do assert submission_db.unsubmitted_by_id === grader.id assert submission_db.unsubmitted_at != nil - # Chatkit roomid should not be removed when a student unsubmits + # Chatkit roomid should not be removed when a submission is unsubmitted assert answer_db.comment == answer.comment assert answer_db.autograding_status == :none assert answer_db.autograding_results == [] @@ -761,7 +761,8 @@ defmodule CadetWeb.GradingControllerTest do assert submission_db.unsubmitted_by_id === admin.id assert submission_db.unsubmitted_at != nil - assert answer_db.comment == nil + # Chatkit roomid should not be removed when a submission is unsubmitted + assert answer_db.comment == answer.comment assert answer_db.autograding_status == :none assert answer_db.autograding_results == [] assert answer_db.grader_id == nil From b442afa96847d65e67b3dbbaf41b40857e43036d Mon Sep 17 00:00:00 2001 From: aihui Date: Wed, 3 Jul 2019 22:49:52 +0800 Subject: [PATCH 21/44] Update README.md Co-Authored-By: Shuming <43642522+geshuming@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dc7bd4695..0c440198c 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ $ vim config/secrets.exs run with the `--updater` flag. Otherwise, the default values will suffice. - A valid `instance_id`, `key_id` and `key_secret` are required to use ChatKit's services. Otherwise, the placeholder values can be left as they are. -1. Install Elixir dependencies +2. Install Elixir dependencies ```bash $ mix deps.get ``` From 3d3915be3bd31d5b91b572ed2006f660ee2ed05c Mon Sep 17 00:00:00 2001 From: flxffy Date: Wed, 3 Jul 2019 23:11:52 +0800 Subject: [PATCH 22/44] Changed import to alias --- lib/cadet/assessments/assessments.ex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index 7052beff8..4112c6380 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -5,12 +5,12 @@ defmodule Cadet.Assessments do """ use Cadet, [:context, :display] - import Cadet.Chat.Room import Ecto.Query alias Cadet.Accounts.User alias Cadet.Assessments.{Answer, Assessment, Query, Question, Submission} alias Cadet.Autograder.GradingJob + alias Cadet.Chat.Room alias Ecto.Multi @xp_early_submission_max_bonus 100 @@ -144,7 +144,7 @@ defmodule Cadet.Assessments do Submission |> where(assessment_id: ^id) |> Repo.all() - |> Enum.each(fn submission -> create_rooms(submission) end) + |> Enum.each(fn submission -> Room.create_rooms(submission) end) answer_query = Answer @@ -620,7 +620,7 @@ defmodule Cadet.Assessments do if role in @grading_roles or role in @see_all_submissions_roles do # ChatKit - create chatrooms if they don't exist - create_rooms(Submission |> where(id: ^id) |> Repo.one()) + Room.create_rooms(Submission |> where(id: ^id) |> Repo.one()) end cond do @@ -765,7 +765,7 @@ defmodule Cadet.Assessments do {:ok, struct} -> # ChatKit - create chatrooms if they don't exist - create_rooms(submission) + Room.create_rooms(submission) # Return {:ok, _} regardless of success/failure. # Failure likely due to service's internal error. From 62d2f11a6837a8cab731ab79c02fd0f706a3a67c Mon Sep 17 00:00:00 2001 From: Hoang <42971570+wardetu@users.noreply.github.com> Date: Thu, 4 Jul 2019 17:52:33 +0700 Subject: [PATCH 23/44] Update assessments.ex --- lib/cadet/assessments/assessments.ex | 39 ---------------------------- 1 file changed, 39 deletions(-) diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index 4112c6380..bf3593929 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -783,45 +783,6 @@ defmodule Cadet.Assessments do end end - def get_answer(%{submission_id: submission_id, question_id: question_id}) do - answer_query = - Answer - |> where(submission_id: ^submission_id) - |> where(question_id: ^question_id) - - Repo.one(answer_query) - end - - def get_comment_from_question(%{submission_id: submission_id, question_id: question_id}) do - answer = get_answer(%{submission_id: submission_id, question_id: question_id}) - answer.comment - end - - def insert_or_update_comment(submission = %Submission{}, question = %Question{}, new_comment) do - answer = get_answer(%{submission_id: submission.id, question_id: question.id}) - - answer_changeset = - %Answer{} - |> Answer.changeset(%{ - answer: answer.answer, - comment: new_comment, - question_id: question.id, - submission_id: submission.id, - type: question.type - }) - - Repo.insert( - answer_changeset, - on_conflict: [ - set: [ - comment: get_change(answer_changeset, :comment), - answer: get_change(answer_changeset, :answer) - ] - ], - conflict_target: [:submission_id, :question_id] - ) - end - defp submissions_by_group(grader = %User{role: :staff}, submission_query) do students = Cadet.Accounts.Query.students_of(grader) From 060ec2f95b19f68bb309ee9f107d9a3739c78a5c Mon Sep 17 00:00:00 2001 From: Hoang <42971570+wardetu@users.noreply.github.com> Date: Thu, 4 Jul 2019 21:32:57 +0700 Subject: [PATCH 24/44] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0c440198c..942a9e5bf 100644 --- a/README.md +++ b/README.md @@ -67,11 +67,11 @@ We recommend setting up nginx to handle preflight checks using the following If you do this, do remember to point cadet-frontend to port `4001` instead of `4000` -### ChatKit +### Chatkit -The chat functionality replacing the previous comment field found in assignments is built on top of ChatKit. Its documentation can be found [here](https://pusher.com/docs/chatkit). +The chat functionality replacing the previous comment field found in assignments is built on top of Chatkit. Its documentation can be found [here](https://pusher.com/docs/chatkit). -If you are using ChatKit, obtain your instance ID, key ID and secret key from your account, and set them inInstructions to that are [here](https://pusher.com/docs/chatkit/authentication#chatkit-key-and-instance-id). +If you are using Chatkit, obtain your instance ID, key ID and secret key from your account, and set them inInstructions to that are [here](https://pusher.com/docs/chatkit/authentication#chatkit-key-and-instance-id). Internet connection is required for usage. From e0e84bf4bd23de3aadfdb4b07130e169a7da8542 Mon Sep 17 00:00:00 2001 From: Hoang <42971570+wardetu@users.noreply.github.com> Date: Thu, 4 Jul 2019 21:34:00 +0700 Subject: [PATCH 25/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 942a9e5bf..5b7531b3d 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ If you do this, do remember to point cadet-frontend to port `4001` instead of `4 The chat functionality replacing the previous comment field found in assignments is built on top of Chatkit. Its documentation can be found [here](https://pusher.com/docs/chatkit). -If you are using Chatkit, obtain your instance ID, key ID and secret key from your account, and set them inInstructions to that are [here](https://pusher.com/docs/chatkit/authentication#chatkit-key-and-instance-id). +If you are using Chatkit, obtain your instance ID, key ID and secret key from your account, and set them in. Instructions to that are found [here](https://pusher.com/docs/chatkit/authentication#chatkit-key-and-instance-id). Internet connection is required for usage. From 668beb5f07c44c5ae8c5c2aa9b90271e9337914f Mon Sep 17 00:00:00 2001 From: flxffy Date: Thu, 4 Jul 2019 23:48:41 +0800 Subject: [PATCH 26/44] mix task update documentation --- lib/cadet/assessments/assessments.ex | 28 ++++++---------------------- lib/mix/tasks/chatkit_room.ex | 0 lib/mix/tasks/users/chat.ex | 8 ++++---- 3 files changed, 10 insertions(+), 26 deletions(-) create mode 100644 lib/mix/tasks/chatkit_room.ex diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index bf3593929..a75ce38ee 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -140,7 +140,7 @@ defmodule Cadet.Assessments do user = %User{role: role} ) do if Timex.after?(Timex.now(), assessment.open_at) or role in @open_all_assessment_roles do - # ChatKit - create chatrooms if they don't exist + # ChatKit - create chatrooms Submission |> where(assessment_id: ^id) |> Repo.all() @@ -618,11 +618,6 @@ defmodule Cadet.Assessments do |> join(:inner, [a, ..., s], st in assoc(s, :student)) |> preload([_, q, g, s, st], question: q, grader: g, submission: {s, student: st}) - if role in @grading_roles or role in @see_all_submissions_roles do - # ChatKit - create chatrooms if they don't exist - Room.create_rooms(Submission |> where(id: ^id) |> Repo.one()) - end - cond do role in @grading_roles -> students = Cadet.Accounts.Query.students_of(grader) @@ -755,22 +750,11 @@ defmodule Cadet.Assessments do type: question.type }) - case Repo.insert( - answer_changeset, - on_conflict: [set: [answer: get_change(answer_changeset, :answer)]], - conflict_target: [:submission_id, :question_id] - ) do - {:error, struct} -> - {:error, struct} - - {:ok, struct} -> - # ChatKit - create chatrooms if they don't exist - Room.create_rooms(submission) - - # Return {:ok, _} regardless of success/failure. - # Failure likely due to service's internal error. - {:ok, struct} - end + Repo.insert( + answer_changeset, + on_conflict: [set: [answer: get_change(answer_changeset, :answer)]], + conflict_target: [:submission_id, :question_id] + ) end defp build_answer_content(raw_answer, question_type) do diff --git a/lib/mix/tasks/chatkit_room.ex b/lib/mix/tasks/chatkit_room.ex new file mode 100644 index 000000000..e69de29bb diff --git a/lib/mix/tasks/users/chat.ex b/lib/mix/tasks/users/chat.ex index 2b3667b60..d99e56a27 100644 --- a/lib/mix/tasks/users/chat.ex +++ b/lib/mix/tasks/users/chat.ex @@ -1,8 +1,8 @@ defmodule Mix.Tasks.Cadet.Users.Chat do @moduledoc """ Creates ChatKit accounts for all users in the database. - User creation: https://pusher.com/docs/chatkit/reference/api-v3#create-a-user - Status codes: https://pusher.com/docs/chatkit/reference/api-v3#response-and-error-codes + User creation: https://pusher.com/docs/chatkit/reference/api#create-a-user + Status codes: https://pusher.com/docs/chatkit/reference/api#response-and-error-codes Note: - Task is to run after `import` (i.e. db is populated) @@ -15,11 +15,11 @@ defmodule Mix.Tasks.Cadet.Users.Chat do require Logger - import Cadet.Chat.Token import Mix.EctoSQL alias Cadet.Repo alias Cadet.Accounts.User + alias Cadet.Chat.Token @instance_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) @@ -29,7 +29,7 @@ defmodule Mix.Tasks.Cadet.Users.Chat do url = "https://us1.pusherplatform.io/services/chatkit/v4/#{@instance_id}/users" - {:ok, token} = get_superuser_token() + {:ok, token} = Token.get_superuser_token() headers = [Authorization: "Bearer #{token}"] User From 42aa925a95c05c090d59f4eeece12f409473b236 Mon Sep 17 00:00:00 2001 From: flxffy Date: Fri, 5 Jul 2019 00:36:50 +0800 Subject: [PATCH 27/44] Script for creating rooms as failsafe --- lib/mix/tasks/chatkit_room.ex | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/mix/tasks/chatkit_room.ex b/lib/mix/tasks/chatkit_room.ex index e69de29bb..48da71c79 100644 --- a/lib/mix/tasks/chatkit_room.ex +++ b/lib/mix/tasks/chatkit_room.ex @@ -0,0 +1,26 @@ +defmodule Mix.Tasks.Cadet.ChatkitRoom do + @moduledoc """ + Creates ChatKit rooms for answers with empty comments in the database. + Room creation: https://pusher.com/docs/chatkit/reference/api#create-a-room + Status codes: https://pusher.com/docs/chatkit/reference/api#response-and-error-codes + + Note: + - Task is to run daily + """ + use Mix.Task + + import Mix.EctoSQL + + alias Cadet.Repo + alias Cadet.Assessments.Submission + alias Cadet.Chat.Room + + def run(_args) do + ensure_started(Repo, []) + HTTPoison.start() + + Submission + |> Repo.all() + |> Enum.each(fn submission -> Room.create_rooms(submission) end) + end +end From 2b01b523689b3609b1621d56a9f4f291b29f7758 Mon Sep 17 00:00:00 2001 From: flxffy Date: Fri, 5 Jul 2019 01:19:05 +0800 Subject: [PATCH 28/44] Scheduled script to run daily at 1am --- config/config.exs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/config.exs b/config/config.exs index 95693c72a..ba910cc3f 100644 --- a/config/config.exs +++ b/config/config.exs @@ -16,6 +16,8 @@ config :cadet, Cadet.Jobs.Scheduler, overlap: false, jobs: [ {"@hourly", {Mix.Tasks.Cadet.Assessments.Update, :run, [nil]}}, + # Create Chatkit rooms if they do not already exist at 1am + {"0 1 * * *", {Mix.Tasks.Cadet.ChatkitRoom, :run, [nil]}}, # Grade previous day's submission at 3am {"0 3 * * *", {Cadet.Autograder.GradingJob, :grade_all_due_yesterday, []}} ] From 4a628d9841b529e33243f761592ce4d23d35cfb5 Mon Sep 17 00:00:00 2001 From: flxffy Date: Fri, 5 Jul 2019 10:19:52 +0800 Subject: [PATCH 29/44] Changed import to alias --- lib/cadet/assessments/answer.ex | 6 +++++- lib/cadet/chat/room.ex | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/cadet/assessments/answer.ex b/lib/cadet/assessments/answer.ex index 9d72b280b..a777770d3 100644 --- a/lib/cadet/assessments/answer.ex +++ b/lib/cadet/assessments/answer.ex @@ -49,7 +49,7 @@ defmodule Cadet.Assessments.Answer do answer |> cast( params, - ~w(grader_id xp xp_adjustment grade adjustment autograding_results autograding_status comment)a + ~w(grader_id xp xp_adjustment grade adjustment autograding_results autograding_status)a ) |> add_belongs_to_id_from_model(:grader, params) |> foreign_key_constraint(:grader_id) @@ -63,6 +63,10 @@ defmodule Cadet.Assessments.Answer do |> validate_xp_grade_adjustment_total() end + def comment_changeset(answer, params) do + answer |> cast(params, ~w(comment)a) + end + @spec validate_xp_grade_adjustment_total(Ecto.Changeset.t()) :: Ecto.Changeset.t() defp validate_xp_grade_adjustment_total(changeset) do answer = apply_changes(changeset) diff --git a/lib/cadet/chat/room.ex b/lib/cadet/chat/room.ex index 2834a37bf..7d168199e 100644 --- a/lib/cadet/chat/room.ex +++ b/lib/cadet/chat/room.ex @@ -6,12 +6,12 @@ defmodule Cadet.Chat.Room do require Logger - import Cadet.Chat.Token import Ecto.Query alias Cadet.Repo alias Cadet.Assessments.{Answer, Submission} alias Cadet.Accounts.User + alias Cadet.Chat.Token @instance_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) @@ -34,7 +34,7 @@ defmodule Cadet.Chat.Room do case create_room(assessment_id, answer.question_id, student) do {:ok, %{"id" => room_id}} -> answer - |> Answer.grading_changeset(%{ + |> Answer.comment_changeset(%{ comment: room_id }) |> Repo.update() @@ -57,7 +57,7 @@ defmodule Cadet.Chat.Room do url = "https://us1.pusherplatform.io/services/chatkit/v4/#{@instance_id}/rooms" - {:ok, token} = get_superuser_token() + {:ok, token} = Token.get_superuser_token() headers = [Authorization: "Bearer #{token}"] body = From 039e5fae4680711150f37eb4913ff46d26788650 Mon Sep 17 00:00:00 2001 From: flxffy Date: Fri, 5 Jul 2019 11:08:44 +0800 Subject: [PATCH 30/44] Changed grading_changeset and changeset to exclude comment when updating answer --- lib/cadet/assessments/answer.ex | 5 +++-- lib/cadet/chat/room.ex | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/cadet/assessments/answer.ex b/lib/cadet/assessments/answer.ex index a777770d3..549b55664 100644 --- a/lib/cadet/assessments/answer.ex +++ b/lib/cadet/assessments/answer.ex @@ -30,7 +30,7 @@ defmodule Cadet.Assessments.Answer do end @required_fields ~w(answer submission_id question_id type)a - @optional_fields ~w(xp xp_adjustment grade comment adjustment grader_id)a + @optional_fields ~w(xp xp_adjustment grade adjustment grader_id)a def changeset(answer, params) do answer @@ -63,8 +63,9 @@ defmodule Cadet.Assessments.Answer do |> validate_xp_grade_adjustment_total() end + @spec comment_changeset(%__MODULE__{} | Ecto.Changeset.t(), map()) :: Ecto.Changeset.t() def comment_changeset(answer, params) do - answer |> cast(params, ~w(comment)a) + cast(answer, params, ~w(comment)a) end @spec validate_xp_grade_adjustment_total(Ecto.Changeset.t()) :: Ecto.Changeset.t() diff --git a/lib/cadet/chat/room.ex b/lib/cadet/chat/room.ex index 7d168199e..09d755e46 100644 --- a/lib/cadet/chat/room.ex +++ b/lib/cadet/chat/room.ex @@ -29,7 +29,9 @@ defmodule Cadet.Chat.Room do Answer |> where(submission_id: ^id) |> Repo.all() - |> Enum.filter(fn answer -> answer.comment == "" or answer.comment == nil end) + |> Enum.filter(fn answer -> + answer.comment == "" or answer.comment == nil or answer.comment == :undefined + end) |> Enum.each(fn answer -> case create_room(assessment_id, answer.question_id, student) do {:ok, %{"id" => room_id}} -> From e69e7f0ffc5e024eb480c1fe6b0edbbe2c80df8c Mon Sep 17 00:00:00 2001 From: flxffy Date: Fri, 5 Jul 2019 22:35:30 +0800 Subject: [PATCH 31/44] Optimise room creation when students access --- lib/cadet/assessments/assessments.ex | 5 +++-- lib/cadet/chat/room.ex | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index a75ce38ee..03a0ef7e5 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -143,8 +143,9 @@ defmodule Cadet.Assessments do # ChatKit - create chatrooms Submission |> where(assessment_id: ^id) - |> Repo.all() - |> Enum.each(fn submission -> Room.create_rooms(submission) end) + |> where(student_id: ^user.id) + |> Repo.one() + |> Room.create_rooms() answer_query = Answer diff --git a/lib/cadet/chat/room.ex b/lib/cadet/chat/room.ex index 09d755e46..ea55a9faf 100644 --- a/lib/cadet/chat/room.ex +++ b/lib/cadet/chat/room.ex @@ -30,7 +30,7 @@ defmodule Cadet.Chat.Room do |> where(submission_id: ^id) |> Repo.all() |> Enum.filter(fn answer -> - answer.comment == "" or answer.comment == nil or answer.comment == :undefined + answer.comment == "" or answer.comment == nil end) |> Enum.each(fn answer -> case create_room(assessment_id, answer.question_id, student) do @@ -47,6 +47,10 @@ defmodule Cadet.Chat.Room do end) end + def create_rooms(_) do + nil + end + defp create_room( assessment_id, question_id, From a9b44d5c62861f00661cb4d5bb3fed0c3f5c8f08 Mon Sep 17 00:00:00 2001 From: flxffy Date: Fri, 5 Jul 2019 23:34:37 +0800 Subject: [PATCH 32/44] Changed when rooms are created --- lib/cadet/assessments/assessments.ex | 8 +------- lib/cadet/chat/room.ex | 2 ++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index 03a0ef7e5..bbfe7bf43 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -140,13 +140,6 @@ defmodule Cadet.Assessments do user = %User{role: role} ) do if Timex.after?(Timex.now(), assessment.open_at) or role in @open_all_assessment_roles do - # ChatKit - create chatrooms - Submission - |> where(assessment_id: ^id) - |> where(student_id: ^user.id) - |> Repo.one() - |> Room.create_rooms() - answer_query = Answer |> join(:inner, [a], s in assoc(a, :submission)) @@ -361,6 +354,7 @@ defmodule Cadet.Assessments do {:status, true} <- {:status, submission.status != :submitted}, {:ok, _} <- insert_or_update_answer(submission, question, raw_answer) do update_submission_status(submission, question.assessment) + Room.create_rooms(submission) {:ok, nil} else {:question_found?, false} -> {:error, {:not_found, "Question not found"}} diff --git a/lib/cadet/chat/room.ex b/lib/cadet/chat/room.ex index ea55a9faf..0781f1eba 100644 --- a/lib/cadet/chat/room.ex +++ b/lib/cadet/chat/room.ex @@ -24,6 +24,7 @@ defmodule Cadet.Chat.Room do student_id: student_id, assessment_id: assessment_id }) do + IO.puts("yay") student = User |> where(id: ^student_id) |> Repo.one() Answer @@ -48,6 +49,7 @@ defmodule Cadet.Chat.Room do end def create_rooms(_) do + IO.puts("nil") nil end From d864c681653353b5930adefa06d395a356a18fd4 Mon Sep 17 00:00:00 2001 From: flxffy Date: Sat, 6 Jul 2019 00:37:36 +0800 Subject: [PATCH 33/44] Refactored room.ex --- lib/cadet/assessments/assessments.ex | 8 +++-- lib/cadet/chat/room.ex | 53 +++++++++++++++------------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index bbfe7bf43..dc96d5f13 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -352,9 +352,13 @@ defmodule Cadet.Assessments do {:is_open?, true} <- is_open?(question.assessment), {:ok, submission} <- find_or_create_submission(user, question.assessment), {:status, true} <- {:status, submission.status != :submitted}, - {:ok, _} <- insert_or_update_answer(submission, question, raw_answer) do + {:ok, answer} <- insert_or_update_answer(submission, question, raw_answer) do update_submission_status(submission, question.assessment) - Room.create_rooms(submission) + + if answer.comment == "" or answer.comment == nil do + Room.create_rooms(submission, answer, user) + end + {:ok, nil} else {:question_found?, false} -> {:error, {:not_found, "Question not found"}} diff --git a/lib/cadet/chat/room.ex b/lib/cadet/chat/room.ex index 0781f1eba..b98a31758 100644 --- a/lib/cadet/chat/room.ex +++ b/lib/cadet/chat/room.ex @@ -19,38 +19,41 @@ defmodule Cadet.Chat.Room do Creates a chatroom for every answer, and updates db with the chatroom id. Takes in Submission struct """ - def create_rooms(%Submission{ - id: id, - student_id: student_id, - assessment_id: assessment_id - }) do - IO.puts("yay") - student = User |> where(id: ^student_id) |> Repo.one() + def create_rooms( + submission = %Submission{ + id: submission_id, + student_id: student_id + } + ) do + user = User |> where(id: ^student_id) |> Repo.one() Answer - |> where(submission_id: ^id) + |> where(submission_id: ^submission_id) |> Repo.all() |> Enum.filter(fn answer -> answer.comment == "" or answer.comment == nil end) - |> Enum.each(fn answer -> - case create_room(assessment_id, answer.question_id, student) do - {:ok, %{"id" => room_id}} -> - answer - |> Answer.comment_changeset(%{ - comment: room_id - }) - |> Repo.update() - - {:error, _} -> - nil - end - end) + |> Enum.each(fn answer -> create_rooms(submission, answer, user) end) end - def create_rooms(_) do - IO.puts("nil") - nil + @doc """ + Creates a chatroom for every answer, and updates db with the chatroom id. + Takes in Submission, Answer and User struct + """ + def create_rooms( + %Submission{ + assessment_id: assessment_id + }, + answer = %Answer{question_id: question_id}, + user + ) do + with {:ok, %{"id" => room_id}} <- create_room(assessment_id, question_id, user) do + answer + |> Answer.comment_changeset(%{ + comment: room_id + }) + |> Repo.update() + end end defp create_room( @@ -90,8 +93,8 @@ defmodule Cadet.Chat.Room do defp get_staff_admin_user_ids do User + |> where([u], u.role in ^[:staff, :admin]) |> Repo.all() - |> Enum.filter(fn user -> user.role == :staff or user.role == :admin end) |> Enum.map(fn user -> to_string(user.id) end) end end From a9966159aeef8596c30e62806235dc1a2c00b9dc Mon Sep 17 00:00:00 2001 From: flxffy Date: Sat, 6 Jul 2019 01:04:15 +0800 Subject: [PATCH 34/44] Else clause --- lib/cadet/chat/room.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/cadet/chat/room.ex b/lib/cadet/chat/room.ex index b98a31758..07c974118 100644 --- a/lib/cadet/chat/room.ex +++ b/lib/cadet/chat/room.ex @@ -53,6 +53,8 @@ defmodule Cadet.Chat.Room do comment: room_id }) |> Repo.update() + else + {:error, _} -> nil end end From b3ba7a44f3df8d76ac3cc4fc99a37b2f95bde49a Mon Sep 17 00:00:00 2001 From: flxffy Date: Sat, 6 Jul 2019 01:10:57 +0800 Subject: [PATCH 35/44] Grading controller --- test/cadet_web/controllers/grading_controller_test.exs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/cadet_web/controllers/grading_controller_test.exs b/test/cadet_web/controllers/grading_controller_test.exs index 8c7b4302a..d00524698 100644 --- a/test/cadet_web/controllers/grading_controller_test.exs +++ b/test/cadet_web/controllers/grading_controller_test.exs @@ -488,7 +488,7 @@ defmodule CadetWeb.GradingControllerTest do assert %{ adjustment: -10, - comment: "Never gonna give you up", + comment: answer.comment, xp_adjustment: -10, grader_id: ^grader_id } = Repo.get(Answer, answer.id) @@ -548,7 +548,7 @@ defmodule CadetWeb.GradingControllerTest do assert %{ adjustment: -100, - comment: "Your awesome", + comment: answer.comment, xp_adjustment: -100, grader_id: ^mentor_id } = Repo.get(Answer, answer.id) @@ -996,7 +996,7 @@ defmodule CadetWeb.GradingControllerTest do }) assert response(conn, 200) == "OK" - assert %{adjustment: -10, comment: "Never gonna give you up"} = Repo.get(Answer, answer.id) + assert %{adjustment: -10, comment: answer.comment} = Repo.get(Answer, answer.id) end @tag authenticate: :admin From 78c41a990e1be6acc97ed41594ff7510c542f734 Mon Sep 17 00:00:00 2001 From: flxffy Date: Sat, 6 Jul 2019 01:31:05 +0800 Subject: [PATCH 36/44] Fixed tests --- test/cadet/jobs/autograder/grading_job_test.exs | 6 +++--- test/cadet_web/controllers/grading_controller_test.exs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/cadet/jobs/autograder/grading_job_test.exs b/test/cadet/jobs/autograder/grading_job_test.exs index 0ee816bf8..25c0d4869 100644 --- a/test/cadet/jobs/autograder/grading_job_test.exs +++ b/test/cadet/jobs/autograder/grading_job_test.exs @@ -250,7 +250,7 @@ defmodule Cadet.Autograder.GradingJobTest do assert answer.grade == 0 assert answer.autograding_status == :success assert answer.answer == %{"code" => "// Question not answered by student."} - assert answer.comment == "Question not attempted by student" + assert answer.comment == nil end assert Enum.empty?(JobsQueue.all()) @@ -314,7 +314,7 @@ defmodule Cadet.Autograder.GradingJobTest do assert answer.xp == 0 assert answer.autograding_status == :success assert answer.answer == %{"code" => "// Question not answered by student."} - assert answer.comment == "Question not attempted by student" + assert answer.comment == nil end end end @@ -365,7 +365,7 @@ defmodule Cadet.Autograder.GradingJobTest do assert answer.xp == 0 assert answer.autograding_status == :success assert answer.answer == %{"choice_id" => 0} - assert answer.comment == "Question not attempted by student" + assert answer.comment == nil end assert Enum.empty?(JobsQueue.all()) diff --git a/test/cadet_web/controllers/grading_controller_test.exs b/test/cadet_web/controllers/grading_controller_test.exs index d00524698..8e29d72e1 100644 --- a/test/cadet_web/controllers/grading_controller_test.exs +++ b/test/cadet_web/controllers/grading_controller_test.exs @@ -488,7 +488,7 @@ defmodule CadetWeb.GradingControllerTest do assert %{ adjustment: -10, - comment: answer.comment, + comment: comment, xp_adjustment: -10, grader_id: ^grader_id } = Repo.get(Answer, answer.id) @@ -548,7 +548,7 @@ defmodule CadetWeb.GradingControllerTest do assert %{ adjustment: -100, - comment: answer.comment, + comment: comment, xp_adjustment: -100, grader_id: ^mentor_id } = Repo.get(Answer, answer.id) @@ -996,7 +996,7 @@ defmodule CadetWeb.GradingControllerTest do }) assert response(conn, 200) == "OK" - assert %{adjustment: -10, comment: answer.comment} = Repo.get(Answer, answer.id) + assert %{adjustment: -10, comment: comment} = Repo.get(Answer, answer.id) end @tag authenticate: :admin From 19474a9aae932e43b93870fac1b5612119a5cfbb Mon Sep 17 00:00:00 2001 From: flxffy Date: Sat, 6 Jul 2019 01:39:43 +0800 Subject: [PATCH 37/44] Script only for submitted submissions --- lib/cadet/chat/room.ex | 2 -- lib/mix/tasks/chatkit_room.ex | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cadet/chat/room.ex b/lib/cadet/chat/room.ex index 07c974118..b98a31758 100644 --- a/lib/cadet/chat/room.ex +++ b/lib/cadet/chat/room.ex @@ -53,8 +53,6 @@ defmodule Cadet.Chat.Room do comment: room_id }) |> Repo.update() - else - {:error, _} -> nil end end diff --git a/lib/mix/tasks/chatkit_room.ex b/lib/mix/tasks/chatkit_room.ex index 48da71c79..4fd70fb8d 100644 --- a/lib/mix/tasks/chatkit_room.ex +++ b/lib/mix/tasks/chatkit_room.ex @@ -9,6 +9,7 @@ defmodule Mix.Tasks.Cadet.ChatkitRoom do """ use Mix.Task + import Ecto.Query import Mix.EctoSQL alias Cadet.Repo @@ -20,6 +21,7 @@ defmodule Mix.Tasks.Cadet.ChatkitRoom do HTTPoison.start() Submission + |> where(status: ^:submitted) |> Repo.all() |> Enum.each(fn submission -> Room.create_rooms(submission) end) end From ff5fbcfa5b15d6d3b5c010b74298d2f6354c17f1 Mon Sep 17 00:00:00 2001 From: flxffy Date: Sat, 6 Jul 2019 15:29:49 +0800 Subject: [PATCH 38/44] Room creation logging --- lib/cadet/chat/room.ex | 12 +++++++++--- lib/cadet/chat/token.ex | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/cadet/chat/room.ex b/lib/cadet/chat/room.ex index b98a31758..de4695967 100644 --- a/lib/cadet/chat/room.ex +++ b/lib/cadet/chat/room.ex @@ -17,7 +17,6 @@ defmodule Cadet.Chat.Room do @doc """ Creates a chatroom for every answer, and updates db with the chatroom id. - Takes in Submission struct """ def create_rooms( submission = %Submission{ @@ -38,7 +37,6 @@ defmodule Cadet.Chat.Room do @doc """ Creates a chatroom for every answer, and updates db with the chatroom id. - Takes in Submission, Answer and User struct """ def create_rooms( %Submission{ @@ -82,7 +80,15 @@ defmodule Cadet.Chat.Room do {:ok, %HTTPoison.Response{body: body, status_code: 201}} -> Poison.decode(body) - {:ok, _} -> + {:ok, %HTTPoison.Response{body: body, status_code: status_code}} -> + response_body = Poison.decode!(body) + + Logger.error( + "Room creation failed: #{response_body["error"]}, #{response_body["error_description"]} (status code #{ + status_code + }) [user_id: #{student_id}, assessment_id: #{assessment_id}, question_id: #{question_id}]" + ) + {:error, nil} {:error, %HTTPoison.Error{reason: error}} -> diff --git a/lib/cadet/chat/token.ex b/lib/cadet/chat/token.ex index ef0a07adc..0939ecae3 100644 --- a/lib/cadet/chat/token.ex +++ b/lib/cadet/chat/token.ex @@ -10,6 +10,7 @@ defmodule Cadet.Chat.Token do @key_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:key_id) @key_secret :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:key_secret) @token_ttl 86_400 + @admin_user_id "admin" @doc """ Generates user token for connection to ChatKit's ChatManager. @@ -25,7 +26,7 @@ defmodule Cadet.Chat.Token do Returns {:ok, token} """ def get_superuser_token do - get_token("admin", true) + get_token(@admin_user_id, true) end defp get_token(user_id, su \\ false) when is_binary(user_id) do From e709d0ce2571e6aa4ea1e76dc9970fbfb1043b38 Mon Sep 17 00:00:00 2001 From: flxffy Date: Sat, 6 Jul 2019 20:24:52 +0800 Subject: [PATCH 39/44] Tests for room creation --- lib/cadet/assessments/assessments.ex | 2 +- lib/cadet/chat/room.ex | 4 +- lib/cadet_web/controllers/chat_controller.ex | 4 +- test/cadet/chat/room_test.exs | 214 ++++++++++++++++++ .../vcr_cassettes/chatkit/create/room#1.json | 30 +++ .../vcr_cassettes/chatkit/create/room#2.json | 29 +++ .../vcr_cassettes/chatkit/create/room#3.json | 29 +++ 7 files changed, 306 insertions(+), 6 deletions(-) create mode 100644 test/cadet/chat/room_test.exs create mode 100644 test/fixtures/vcr_cassettes/chatkit/create/room#1.json create mode 100644 test/fixtures/vcr_cassettes/chatkit/create/room#2.json create mode 100644 test/fixtures/vcr_cassettes/chatkit/create/room#3.json diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index dc96d5f13..7a2f5bc98 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -355,7 +355,7 @@ defmodule Cadet.Assessments do {:ok, answer} <- insert_or_update_answer(submission, question, raw_answer) do update_submission_status(submission, question.assessment) - if answer.comment == "" or answer.comment == nil do + if answer.comment == nil do Room.create_rooms(submission, answer, user) end diff --git a/lib/cadet/chat/room.ex b/lib/cadet/chat/room.ex index de4695967..87751e8e3 100644 --- a/lib/cadet/chat/room.ex +++ b/lib/cadet/chat/room.ex @@ -29,9 +29,7 @@ defmodule Cadet.Chat.Room do Answer |> where(submission_id: ^submission_id) |> Repo.all() - |> Enum.filter(fn answer -> - answer.comment == "" or answer.comment == nil - end) + |> Enum.filter(fn answer -> answer.comment == nil end) |> Enum.each(fn answer -> create_rooms(submission, answer, user) end) end diff --git a/lib/cadet_web/controllers/chat_controller.ex b/lib/cadet_web/controllers/chat_controller.ex index 2b21e83a7..5e6318391 100644 --- a/lib/cadet_web/controllers/chat_controller.ex +++ b/lib/cadet_web/controllers/chat_controller.ex @@ -7,11 +7,11 @@ defmodule CadetWeb.ChatController do use CadetWeb, :controller use PhoenixSwagger - import Cadet.Chat.Token + alias Cadet.Chat.Token def index(conn, _) do user = conn.assigns.current_user - {:ok, token, ttl} = get_user_token(user) + {:ok, token, ttl} = Token.get_user_token(user) render( conn, diff --git a/test/cadet/chat/room_test.exs b/test/cadet/chat/room_test.exs new file mode 100644 index 000000000..65c3cbf8c --- /dev/null +++ b/test/cadet/chat/room_test.exs @@ -0,0 +1,214 @@ +defmodule Cadet.Chat.RoomTest do + @moduledoc """ + All tests in this module use pre-recorded HTTP responses saved by ExVCR. + this allows testing without the use of actual external Chatkit API calls. + """ + + use Cadet.DataCase + use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney + + import Cadet.Factory + import Ecto.Query + import ExUnit.CaptureLog + + alias Cadet.Assessments.Submission + alias Cadet.Chat.Room + alias Cadet.Repo + + setup do + student = insert(:user, %{role: :student}) + assessment = insert(:assessment) + submission = insert(:submission, %{student: student, assessment: assessment}) + question = insert(:question, %{assessment: assessment}) + + answer_no_comment = + insert(:answer, %{ + submission_id: submission.id, + question_id: question.id, + comment: nil + }) + + {:ok, + %{ + student: student, + answer_no_comment: answer_no_comment, + assessment: assessment, + submission: submission + }} + end + + describe "create a room on chatkit if answer does not have a comment" do + test "success", %{ + answer_no_comment: answer, + assessment: assessment, + submission: submission, + student: student + } do + use_cassette "chatkit/create/room#1" do + Room.create_rooms(submission, answer, student) + + answer_db = + Submission + |> where(assessment_id: ^assessment.id) + |> where(student_id: ^student.id) + |> join(:inner, [s], a in assoc(s, :answers)) + |> select([_, a], a) + |> Repo.one() + + assert answer_db.comment == "19420137" + end + + use_cassette "chatkit/create/room#1" do + Room.create_rooms(submission) + + answer_db = + Submission + |> where(assessment_id: ^assessment.id) + |> where(student_id: ^student.id) + |> join(:inner, [s], a in assoc(s, :answers)) + |> select([_, a], a) + |> Repo.one() + + assert answer_db.comment == "19420137" + end + end + + test "user does not exist", %{ + answer_no_comment: answer, + assessment: assessment, + submission: submission, + student: student + } do + use_cassette "chatkit/create/room#2" do + error = "services/chatkit/bad_request/users_not_found" + error_description = "1 out of 2 users (including the room creator) could not be found" + status_code = 400 + + assert capture_log(fn -> + Room.create_rooms(submission, answer, student) + + answer_db = + Submission + |> where(assessment_id: ^assessment.id) + |> where(student_id: ^student.id) + |> join(:inner, [s], a in assoc(s, :answers)) + |> select([_, a], a) + |> Repo.one() + + assert answer_db == answer + end) =~ + "Room creation failed: #{error}, #{error_description} (status code #{status_code}) " <> + "[user_id: #{student.id}, assessment_id: #{assessment.id}, question_id: #{ + answer.question_id + }]" + end + + use_cassette "chatkit/create/room#2" do + error = "services/chatkit/bad_request/users_not_found" + error_description = "1 out of 2 users (including the room creator) could not be found" + status_code = 400 + + assert capture_log(fn -> + Room.create_rooms(submission) + + answer_db = + Submission + |> where(assessment_id: ^assessment.id) + |> where(student_id: ^student.id) + |> join(:inner, [s], a in assoc(s, :answers)) + |> select([_, a], a) + |> Repo.one() + + assert answer_db == answer + end) =~ + "Room creation failed: #{error}, #{error_description} (status code #{status_code}) " <> + "[user_id: #{student.id}, assessment_id: #{assessment.id}, question_id: #{ + answer.question_id + }]" + end + end + + test "user does not have permission", %{ + answer_no_comment: answer, + assessment: assessment, + submission: submission, + student: student + } do + use_cassette "chatkit/create/room#3" do + error = "services/chatkit_authorizer/authorization/missing_permission" + error_description = "User does not have access to requested resource" + status_code = 401 + + assert capture_log(fn -> + Room.create_rooms(submission, answer, student) + + answer_db = + Submission + |> where(assessment_id: ^assessment.id) + |> where(student_id: ^student.id) + |> join(:inner, [s], a in assoc(s, :answers)) + |> select([_, a], a) + |> Repo.one() + + assert answer_db == answer + end) =~ + "Room creation failed: #{error}, #{error_description} (status code #{status_code}) " <> + "[user_id: #{student.id}, assessment_id: #{assessment.id}, question_id: #{ + answer.question_id + }]" + end + + use_cassette "chatkit/create/room#3" do + error = "services/chatkit_authorizer/authorization/missing_permission" + error_description = "User does not have access to requested resource" + status_code = 401 + + assert capture_log(fn -> + Room.create_rooms(submission, answer, student) + + answer_db = + Submission + |> where(assessment_id: ^assessment.id) + |> where(student_id: ^student.id) + |> join(:inner, [s], a in assoc(s, :answers)) + |> select([_, a], a) + |> Repo.one() + + assert answer_db == answer + end) =~ + "Room creation failed: #{error}, #{error_description} (status code #{status_code}) " <> + "[user_id: #{student.id}, assessment_id: #{assessment.id}, question_id: #{ + answer.question_id + }]" + end + end + end + + describe "do not create a rom on chatkit if answer has a comment" do + test "success", %{ + student: student + } do + assessment = insert(:assessment) + submission = insert(:submission, %{student: student, assessment: assessment}) + question = insert(:question, %{assessment: assessment}) + + answer_with_comment = + insert(:answer, %{ + submission_id: submission.id, + question_id: question.id + }) + + Room.create_rooms(submission, answer_with_comment, student) + + answer_db = + Submission + |> where(assessment_id: ^assessment.id) + |> where(student_id: ^student.id) + |> join(:inner, [s], a in assoc(s, :answers)) + |> select([_, a], a) + |> Repo.one() + + assert answer_db.comment == answer_with_comment.comment + end + end +end diff --git a/test/fixtures/vcr_cassettes/chatkit/create/room#1.json b/test/fixtures/vcr_cassettes/chatkit/create/room#1.json new file mode 100644 index 000000000..7e1942aea --- /dev/null +++ b/test/fixtures/vcr_cassettes/chatkit/create/room#1.json @@ -0,0 +1,30 @@ +[ + { + "request": { + "body": "{\"user_ids\":[\"1\",\"2\",\"3\"],\"private\":true,\"name\":\"room_name\"}", + "headers": { + "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjI0OTAwNDcsImlhdCI6MTU2MjQwMzY0NywiaW5zdGFuY2UiOiI0MmNmNTkyZS1lNjhhLTQwMDktYTEzNS01YWM4Nzg0NjE2Y2UiLCJpc3MiOiJhcGlfa2V5cy8zM2Q5ODljNy03MzQ5LTQ5NmUtODkyNy1jNzg4YTc1YjZjMjIiLCJzdSI6dHJ1ZSwic3ViIjoiYWRtaW4ifQ.PXSeNVZkPx3v1S3Vr15JK00RcmoJ4mUtNwCC9oHDJUU" + }, + "method": "post", + "options": [], + "request_body": "", + "url": "https://us1.pusherplatform.io/services/chatkit/v4/42cf592e-e68a-4009-a135-5ac8784616ce/rooms" + }, + "response": { + "binary": false, + "body": "{\"created_at\":\"2019-07-06T09:00:47Z\",\"created_by_id\":\"admin\",\"id\":\"19420137\",\"member_user_ids\":[\"admin\",\"1\",\"2\",\"3\"],\"name\":\"room_name\",\"private\":true,\"updated_at\":\"2019-07-06T09:00:47Z\"}", + "headers": { + "access-control-expose-headers": "Server, Access-Control-Expose-Headers, X-Request-Id, Date, X-Envoy-Upstream-Service-Time, Access-Control-Max-Age", + "access-control-max-age": "86400", + "content-type": "application/json", + "date": "Sat, 06 Jul 2019 09:00:47 GMT", + "server": "istio-envoy", + "x-envoy-upstream-service-time": "44", + "x-request-id": "f61cc0eb-2261-4817-a8a6-1637c5202a96", + "content-length": "187" + }, + "status_code": 201, + "type": "ok" + } + } +] \ No newline at end of file diff --git a/test/fixtures/vcr_cassettes/chatkit/create/room#2.json b/test/fixtures/vcr_cassettes/chatkit/create/room#2.json new file mode 100644 index 000000000..971581ade --- /dev/null +++ b/test/fixtures/vcr_cassettes/chatkit/create/room#2.json @@ -0,0 +1,29 @@ +[ + { + "request": { + "body": "{\"user_ids\":[\"42186\"],\"private\":true,\"name\":\"E0002401_34044_Q67988\"}", + "headers": { + "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjI0OTQ5ODAsImlhdCI6MTU2MjQwODU4MCwiaW5zdGFuY2UiOiI0MmNmNTkyZS1lNjhhLTQwMDktYTEzNS01YWM4Nzg0NjE2Y2UiLCJpc3MiOiJhcGlfa2V5cy8zM2Q5ODljNy03MzQ5LTQ5NmUtODkyNy1jNzg4YTc1YjZjMjIiLCJzdSI6dHJ1ZSwic3ViIjoiYWRtaW4ifQ.Jtzzs8QwfzlTsp7fe8I5xPNrHq73ThF_tfGVaMcgpJo" + }, + "method": "post", + "options": [], + "request_body": "", + "url": "https://us1.pusherplatform.io/services/chatkit/v4/42cf592e-e68a-4009-a135-5ac8784616ce/rooms" + }, + "response": { + "binary": false, + "body": "{\"error\":\"services/chatkit/bad_request/users_not_found\",\"error_description\":\"1 out of 2 users (including the room creator) could not be found\",\"error_uri\":\"https://docs.pusher.com/errors/services/chatkit/bad_request/users_not_found\"}", + "headers": { + "access-control-expose-headers": "Access-Control-Max-Age, Date, X-Envoy-Upstream-Service-Time, Server, Access-Control-Expose-Headers", + "access-control-max-age": "86400", + "content-type": "application/json", + "date": "Sat, 06 Jul 2019 10:23:00 GMT", + "server": "istio-envoy", + "x-envoy-upstream-service-time": "11", + "content-length": "233" + }, + "status_code": 400, + "type": "ok" + } + } +] \ No newline at end of file diff --git a/test/fixtures/vcr_cassettes/chatkit/create/room#3.json b/test/fixtures/vcr_cassettes/chatkit/create/room#3.json new file mode 100644 index 000000000..db084e473 --- /dev/null +++ b/test/fixtures/vcr_cassettes/chatkit/create/room#3.json @@ -0,0 +1,29 @@ +[ + { + "request": { + "body": "{\"user_ids\":[\"56695\"],\"private\":true,\"name\":\"E0002401_45753_Q91364\"}", + "headers": { + "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjI0OTgyNDMsImlhdCI6MTU2MjQxMTg0MywiaW5zdGFuY2UiOiI0MmNmNTkyZS1lNjhhLTQwMDktYTEzNS01YWM4Nzg0NjE2Y2UiLCJpc3MiOiJhcGlfa2V5cy8zM2Q5ODljNy03MzQ5LTQ5NmUtODkyNy1jNzg4YTc1YjZjMjIiLCJzdSI6ZmFsc2UsInN1YiI6Im5vdCBhZG1pbiJ9.EkC5NVMhVLHxqCGVqRSmtTZFjodzsVEkTgRxntZ9L6Y" + }, + "method": "post", + "options": [], + "request_body": "", + "url": "https://us1.pusherplatform.io/services/chatkit/v4/42cf592e-e68a-4009-a135-5ac8784616ce/rooms" + }, + "response": { + "binary": false, + "body": "{\"error\":\"services/chatkit_authorizer/authorization/missing_permission\",\"error_description\":\"User does not have access to requested resource\",\"error_uri\":\"https://docs.pusher.com/errors/services/chatkit_authorizer/authorization/missing_permission\"}", + "headers": { + "access-control-expose-headers": "Access-Control-Max-Age", + "access-control-max-age": "86400", + "content-type": "application/json", + "content-length": "248", + "date": "Sat, 06 Jul 2019 11:17:23 GMT", + "x-envoy-upstream-service-time": "13", + "server": "istio-envoy" + }, + "status_code": 401, + "type": "ok" + } + } +] \ No newline at end of file From d5c1fdfbecb466202d1412d4081143b01666600e Mon Sep 17 00:00:00 2001 From: flxffy Date: Sat, 6 Jul 2019 22:33:40 +0800 Subject: [PATCH 40/44] Changed db query in ChatkitRoom --- lib/cadet/assessments/answer.ex | 1 + lib/mix/tasks/chatkit_room.ex | 12 +++++++++--- test/cadet/chat/room_test.exs | 1 - 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/cadet/assessments/answer.ex b/lib/cadet/assessments/answer.ex index 549b55664..cfb7e85bc 100644 --- a/lib/cadet/assessments/answer.ex +++ b/lib/cadet/assessments/answer.ex @@ -63,6 +63,7 @@ defmodule Cadet.Assessments.Answer do |> validate_xp_grade_adjustment_total() end + # TODO: refactor this with some sort of validation. @spec comment_changeset(%__MODULE__{} | Ecto.Changeset.t(), map()) :: Ecto.Changeset.t() def comment_changeset(answer, params) do cast(answer, params, ~w(comment)a) diff --git a/lib/mix/tasks/chatkit_room.ex b/lib/mix/tasks/chatkit_room.ex index 4fd70fb8d..a17920c65 100644 --- a/lib/mix/tasks/chatkit_room.ex +++ b/lib/mix/tasks/chatkit_room.ex @@ -18,11 +18,17 @@ defmodule Mix.Tasks.Cadet.ChatkitRoom do def run(_args) do ensure_started(Repo, []) - HTTPoison.start() Submission - |> where(status: ^:submitted) + |> join(:left, [s], a in assoc(s, :answers)) + |> join(:inner, [s], u in assoc(s, :student)) + |> preload([_, a, u], answers: a, student: u) + |> where([_, a], is_nil(a.comment)) |> Repo.all() - |> Enum.each(fn submission -> Room.create_rooms(submission) end) + |> Enum.each(fn submission -> + Enum.each(submission.answers, fn answer -> + Room.create_rooms(submission, answer, submission.student) + end) + end) end end diff --git a/test/cadet/chat/room_test.exs b/test/cadet/chat/room_test.exs index 65c3cbf8c..162d05fdd 100644 --- a/test/cadet/chat/room_test.exs +++ b/test/cadet/chat/room_test.exs @@ -7,7 +7,6 @@ defmodule Cadet.Chat.RoomTest do use Cadet.DataCase use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney - import Cadet.Factory import Ecto.Query import ExUnit.CaptureLog From aa75e3f4d240cdf34b34fb5b9997a3c8bb6b64c2 Mon Sep 17 00:00:00 2001 From: flxffy Date: Sat, 6 Jul 2019 22:50:11 +0800 Subject: [PATCH 41/44] Minor fix --- lib/cadet/chat/room.ex | 24 ++------------ lib/mix/tasks/chatkit_room.ex | 2 +- test/cadet/chat/room_test.exs | 62 ----------------------------------- 3 files changed, 4 insertions(+), 84 deletions(-) diff --git a/lib/cadet/chat/room.ex b/lib/cadet/chat/room.ex index 87751e8e3..2612283b2 100644 --- a/lib/cadet/chat/room.ex +++ b/lib/cadet/chat/room.ex @@ -15,24 +15,6 @@ defmodule Cadet.Chat.Room do @instance_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) - @doc """ - Creates a chatroom for every answer, and updates db with the chatroom id. - """ - def create_rooms( - submission = %Submission{ - id: submission_id, - student_id: student_id - } - ) do - user = User |> where(id: ^student_id) |> Repo.one() - - Answer - |> where(submission_id: ^submission_id) - |> Repo.all() - |> Enum.filter(fn answer -> answer.comment == nil end) - |> Enum.each(fn answer -> create_rooms(submission, answer, user) end) - end - @doc """ Creates a chatroom for every answer, and updates db with the chatroom id. """ @@ -82,9 +64,9 @@ defmodule Cadet.Chat.Room do response_body = Poison.decode!(body) Logger.error( - "Room creation failed: #{response_body["error"]}, #{response_body["error_description"]} (status code #{ - status_code - }) [user_id: #{student_id}, assessment_id: #{assessment_id}, question_id: #{question_id}]" + "Room creation failed: #{response_body["error"]}, " <> + "#{response_body["error_description"]} (status code #{status_code}) " <> + "[user_id: #{student_id}, assessment_id: #{assessment_id}, question_id: #{question_id}]" ) {:error, nil} diff --git a/lib/mix/tasks/chatkit_room.ex b/lib/mix/tasks/chatkit_room.ex index a17920c65..e9507cc46 100644 --- a/lib/mix/tasks/chatkit_room.ex +++ b/lib/mix/tasks/chatkit_room.ex @@ -20,7 +20,7 @@ defmodule Mix.Tasks.Cadet.ChatkitRoom do ensure_started(Repo, []) Submission - |> join(:left, [s], a in assoc(s, :answers)) + |> join(:inner, [s], a in assoc(s, :answers)) |> join(:inner, [s], u in assoc(s, :student)) |> preload([_, a, u], answers: a, student: u) |> where([_, a], is_nil(a.comment)) diff --git a/test/cadet/chat/room_test.exs b/test/cadet/chat/room_test.exs index 162d05fdd..283368792 100644 --- a/test/cadet/chat/room_test.exs +++ b/test/cadet/chat/room_test.exs @@ -56,20 +56,6 @@ defmodule Cadet.Chat.RoomTest do assert answer_db.comment == "19420137" end - - use_cassette "chatkit/create/room#1" do - Room.create_rooms(submission) - - answer_db = - Submission - |> where(assessment_id: ^assessment.id) - |> where(student_id: ^student.id) - |> join(:inner, [s], a in assoc(s, :answers)) - |> select([_, a], a) - |> Repo.one() - - assert answer_db.comment == "19420137" - end end test "user does not exist", %{ @@ -101,30 +87,6 @@ defmodule Cadet.Chat.RoomTest do answer.question_id }]" end - - use_cassette "chatkit/create/room#2" do - error = "services/chatkit/bad_request/users_not_found" - error_description = "1 out of 2 users (including the room creator) could not be found" - status_code = 400 - - assert capture_log(fn -> - Room.create_rooms(submission) - - answer_db = - Submission - |> where(assessment_id: ^assessment.id) - |> where(student_id: ^student.id) - |> join(:inner, [s], a in assoc(s, :answers)) - |> select([_, a], a) - |> Repo.one() - - assert answer_db == answer - end) =~ - "Room creation failed: #{error}, #{error_description} (status code #{status_code}) " <> - "[user_id: #{student.id}, assessment_id: #{assessment.id}, question_id: #{ - answer.question_id - }]" - end end test "user does not have permission", %{ @@ -156,30 +118,6 @@ defmodule Cadet.Chat.RoomTest do answer.question_id }]" end - - use_cassette "chatkit/create/room#3" do - error = "services/chatkit_authorizer/authorization/missing_permission" - error_description = "User does not have access to requested resource" - status_code = 401 - - assert capture_log(fn -> - Room.create_rooms(submission, answer, student) - - answer_db = - Submission - |> where(assessment_id: ^assessment.id) - |> where(student_id: ^student.id) - |> join(:inner, [s], a in assoc(s, :answers)) - |> select([_, a], a) - |> Repo.one() - - assert answer_db == answer - end) =~ - "Room creation failed: #{error}, #{error_description} (status code #{status_code}) " <> - "[user_id: #{student.id}, assessment_id: #{assessment.id}, question_id: #{ - answer.question_id - }]" - end end end From 21de4ea2e2c019ceda580536c2be307ba410943d Mon Sep 17 00:00:00 2001 From: flxffy Date: Sun, 7 Jul 2019 00:12:07 +0800 Subject: [PATCH 42/44] Minor fix to cassettes --- lib/cadet/chat/room.ex | 11 ++++++++--- test/cadet/chat/room_test.exs | 8 ++++---- .../vcr_cassettes/chatkit/{create => }/room#1.json | 10 ++++------ .../vcr_cassettes/chatkit/{create => }/room#2.json | 10 ++++------ .../vcr_cassettes/chatkit/{create => }/room#3.json | 10 ++++------ 5 files changed, 24 insertions(+), 25 deletions(-) rename test/fixtures/vcr_cassettes/chatkit/{create => }/room#1.json (66%) rename test/fixtures/vcr_cassettes/chatkit/{create => }/room#2.json (65%) rename test/fixtures/vcr_cassettes/chatkit/{create => }/room#3.json (63%) diff --git a/lib/cadet/chat/room.ex b/lib/cadet/chat/room.ex index 2612283b2..e611db6a2 100644 --- a/lib/cadet/chat/room.ex +++ b/lib/cadet/chat/room.ex @@ -13,7 +13,11 @@ defmodule Cadet.Chat.Room do alias Cadet.Accounts.User alias Cadet.Chat.Token - @instance_id :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) + @instance_id (if(Mix.env() != :test) do + :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) + else + "instance_id" + end) @doc """ Creates a chatroom for every answer, and updates db with the chatroom id. @@ -22,10 +26,11 @@ defmodule Cadet.Chat.Room do %Submission{ assessment_id: assessment_id }, - answer = %Answer{question_id: question_id}, + answer = %Answer{question_id: question_id, comment: comment}, user ) do - with {:ok, %{"id" => room_id}} <- create_room(assessment_id, question_id, user) do + with true <- comment == nil, + {:ok, %{"id" => room_id}} <- create_room(assessment_id, question_id, user) do answer |> Answer.comment_changeset(%{ comment: room_id diff --git a/test/cadet/chat/room_test.exs b/test/cadet/chat/room_test.exs index 283368792..6cfa0cfa8 100644 --- a/test/cadet/chat/room_test.exs +++ b/test/cadet/chat/room_test.exs @@ -43,7 +43,7 @@ defmodule Cadet.Chat.RoomTest do submission: submission, student: student } do - use_cassette "chatkit/create/room#1" do + use_cassette "chatkit/room#1" do Room.create_rooms(submission, answer, student) answer_db = @@ -64,7 +64,7 @@ defmodule Cadet.Chat.RoomTest do submission: submission, student: student } do - use_cassette "chatkit/create/room#2" do + use_cassette "chatkit/room#2" do error = "services/chatkit/bad_request/users_not_found" error_description = "1 out of 2 users (including the room creator) could not be found" status_code = 400 @@ -95,7 +95,7 @@ defmodule Cadet.Chat.RoomTest do submission: submission, student: student } do - use_cassette "chatkit/create/room#3" do + use_cassette "chatkit/room#3" do error = "services/chatkit_authorizer/authorization/missing_permission" error_description = "User does not have access to requested resource" status_code = 401 @@ -121,7 +121,7 @@ defmodule Cadet.Chat.RoomTest do end end - describe "do not create a rom on chatkit if answer has a comment" do + describe "do not create a room on chatkit if answer has a comment" do test "success", %{ student: student } do diff --git a/test/fixtures/vcr_cassettes/chatkit/create/room#1.json b/test/fixtures/vcr_cassettes/chatkit/room#1.json similarity index 66% rename from test/fixtures/vcr_cassettes/chatkit/create/room#1.json rename to test/fixtures/vcr_cassettes/chatkit/room#1.json index 7e1942aea..0e967a450 100644 --- a/test/fixtures/vcr_cassettes/chatkit/create/room#1.json +++ b/test/fixtures/vcr_cassettes/chatkit/room#1.json @@ -1,14 +1,12 @@ [ { "request": { - "body": "{\"user_ids\":[\"1\",\"2\",\"3\"],\"private\":true,\"name\":\"room_name\"}", - "headers": { - "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjI0OTAwNDcsImlhdCI6MTU2MjQwMzY0NywiaW5zdGFuY2UiOiI0MmNmNTkyZS1lNjhhLTQwMDktYTEzNS01YWM4Nzg0NjE2Y2UiLCJpc3MiOiJhcGlfa2V5cy8zM2Q5ODljNy03MzQ5LTQ5NmUtODkyNy1jNzg4YTc1YjZjMjIiLCJzdSI6dHJ1ZSwic3ViIjoiYWRtaW4ifQ.PXSeNVZkPx3v1S3Vr15JK00RcmoJ4mUtNwCC9oHDJUU" - }, + "body": "", + "headers": {}, "method": "post", "options": [], "request_body": "", - "url": "https://us1.pusherplatform.io/services/chatkit/v4/42cf592e-e68a-4009-a135-5ac8784616ce/rooms" + "url": "https://us1.pusherplatform.io/services/chatkit/v4/instance_id/rooms" }, "response": { "binary": false, @@ -27,4 +25,4 @@ "type": "ok" } } -] \ No newline at end of file +] diff --git a/test/fixtures/vcr_cassettes/chatkit/create/room#2.json b/test/fixtures/vcr_cassettes/chatkit/room#2.json similarity index 65% rename from test/fixtures/vcr_cassettes/chatkit/create/room#2.json rename to test/fixtures/vcr_cassettes/chatkit/room#2.json index 971581ade..e0b9be443 100644 --- a/test/fixtures/vcr_cassettes/chatkit/create/room#2.json +++ b/test/fixtures/vcr_cassettes/chatkit/room#2.json @@ -1,14 +1,12 @@ [ { "request": { - "body": "{\"user_ids\":[\"42186\"],\"private\":true,\"name\":\"E0002401_34044_Q67988\"}", - "headers": { - "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjI0OTQ5ODAsImlhdCI6MTU2MjQwODU4MCwiaW5zdGFuY2UiOiI0MmNmNTkyZS1lNjhhLTQwMDktYTEzNS01YWM4Nzg0NjE2Y2UiLCJpc3MiOiJhcGlfa2V5cy8zM2Q5ODljNy03MzQ5LTQ5NmUtODkyNy1jNzg4YTc1YjZjMjIiLCJzdSI6dHJ1ZSwic3ViIjoiYWRtaW4ifQ.Jtzzs8QwfzlTsp7fe8I5xPNrHq73ThF_tfGVaMcgpJo" - }, + "body": "", + "headers": {}, "method": "post", "options": [], "request_body": "", - "url": "https://us1.pusherplatform.io/services/chatkit/v4/42cf592e-e68a-4009-a135-5ac8784616ce/rooms" + "url": "https://us1.pusherplatform.io/services/chatkit/v4/instance_id/rooms" }, "response": { "binary": false, @@ -26,4 +24,4 @@ "type": "ok" } } -] \ No newline at end of file +] diff --git a/test/fixtures/vcr_cassettes/chatkit/create/room#3.json b/test/fixtures/vcr_cassettes/chatkit/room#3.json similarity index 63% rename from test/fixtures/vcr_cassettes/chatkit/create/room#3.json rename to test/fixtures/vcr_cassettes/chatkit/room#3.json index db084e473..b5a304d9b 100644 --- a/test/fixtures/vcr_cassettes/chatkit/create/room#3.json +++ b/test/fixtures/vcr_cassettes/chatkit/room#3.json @@ -1,14 +1,12 @@ [ { "request": { - "body": "{\"user_ids\":[\"56695\"],\"private\":true,\"name\":\"E0002401_45753_Q91364\"}", - "headers": { - "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjI0OTgyNDMsImlhdCI6MTU2MjQxMTg0MywiaW5zdGFuY2UiOiI0MmNmNTkyZS1lNjhhLTQwMDktYTEzNS01YWM4Nzg0NjE2Y2UiLCJpc3MiOiJhcGlfa2V5cy8zM2Q5ODljNy03MzQ5LTQ5NmUtODkyNy1jNzg4YTc1YjZjMjIiLCJzdSI6ZmFsc2UsInN1YiI6Im5vdCBhZG1pbiJ9.EkC5NVMhVLHxqCGVqRSmtTZFjodzsVEkTgRxntZ9L6Y" - }, + "body": "", + "headers": {}, "method": "post", "options": [], "request_body": "", - "url": "https://us1.pusherplatform.io/services/chatkit/v4/42cf592e-e68a-4009-a135-5ac8784616ce/rooms" + "url": "https://us1.pusherplatform.io/services/chatkit/v4/instance_id/rooms" }, "response": { "binary": false, @@ -26,4 +24,4 @@ "type": "ok" } } -] \ No newline at end of file +] From 05b830dd7cdf2209e4919c231cb133d8db70d351 Mon Sep 17 00:00:00 2001 From: flxffy Date: Sun, 7 Jul 2019 01:02:16 +0800 Subject: [PATCH 43/44] Fixed tests --- test/cadet_web/controllers/answer_controller_test.exs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/cadet_web/controllers/answer_controller_test.exs b/test/cadet_web/controllers/answer_controller_test.exs index 9280ea590..31cc09612 100644 --- a/test/cadet_web/controllers/answer_controller_test.exs +++ b/test/cadet_web/controllers/answer_controller_test.exs @@ -2,6 +2,7 @@ defmodule CadetWeb.AnswerControllerTest do use CadetWeb.ConnCase import Ecto.Query + import Mock alias Cadet.Assessments.{Answer, Submission} alias Cadet.Repo @@ -24,6 +25,12 @@ defmodule CadetWeb.AnswerControllerTest do } end + setup_with_mocks([ + {Cadet.Chat.Room, [], [create_rooms: fn _submission, _answer, _student -> nil end]} + ]) do + :ok + end + describe "POST /assessments/question/{questionId}/submit/, Unauthenticated" do test "is disallowed", %{conn: conn, mcq_question: question} do conn = post(conn, build_url(question.id), %{answer: 5}) From 842e534ad4a38af58d73d3bcd8ea71ea0fa851f6 Mon Sep 17 00:00:00 2001 From: flxffy Date: Sun, 7 Jul 2019 01:09:07 +0800 Subject: [PATCH 44/44] Minor fix --- lib/cadet/assessments/answer.ex | 2 +- lib/cadet/chat/room.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cadet/assessments/answer.ex b/lib/cadet/assessments/answer.ex index cfb7e85bc..902077291 100644 --- a/lib/cadet/assessments/answer.ex +++ b/lib/cadet/assessments/answer.ex @@ -63,7 +63,7 @@ defmodule Cadet.Assessments.Answer do |> validate_xp_grade_adjustment_total() end - # TODO: refactor this with some sort of validation. + # TODO: add some validation @spec comment_changeset(%__MODULE__{} | Ecto.Changeset.t(), map()) :: Ecto.Changeset.t() def comment_changeset(answer, params) do cast(answer, params, ~w(comment)a) diff --git a/lib/cadet/chat/room.ex b/lib/cadet/chat/room.ex index e611db6a2..e118ace35 100644 --- a/lib/cadet/chat/room.ex +++ b/lib/cadet/chat/room.ex @@ -13,7 +13,7 @@ defmodule Cadet.Chat.Room do alias Cadet.Accounts.User alias Cadet.Chat.Token - @instance_id (if(Mix.env() != :test) do + @instance_id (if Mix.env() != :test do :cadet |> Application.fetch_env!(:chat) |> Keyword.get(:instance_id) else "instance_id"