Skip to content

Conversation

@ning-y
Copy link
Member

@ning-y ning-y commented Jun 27, 2018

Features

  • Cadet.Public.Updater is started by passing the command line flag --updater
    • Using mix, run elixir --erl "--updater" -S mix phx.server. (or iex)
    • Using the compiled binary, run bin/cadet start --updater
  • Cadet.Public.Updater is a GenServer that runs as a worker in the Cadet.Application supervision tree
  • Cadet.Public.Updater will periodically do work defined in handle_info/2(:work, ...).
  • Cadet.Public.Updater schedules itself recursively by a private function schedule_work/0, which calls handle_info/2(:work, ...), which calls schedule_work/0, and so on
  • The interval at which this happens is defined in the config file (config/config.exs)

@coveralls
Copy link

coveralls commented Jun 28, 2018

Pull Request Test Coverage Report for Build 640

  • 26 of 26 (100.0%) changed or added relevant lines in 2 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.2%) to 96.995%

Totals Coverage Status
Change from base Build 628: 0.2%
Covered Lines: 355
Relevant Lines: 366

💛 - Coveralls

@indocomsoft
Copy link
Member

I think you should rebase this on master first to reduce the number of changes @ningyuansg

@ning-y
Copy link
Member Author

ning-y commented Jun 29, 2018

@indocomsoft Rebased, but you'd probably want to look at #76 first, since this PR uses one commit from that branch (90497ee).

Copy link
Member

@indocomsoft indocomsoft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job on using GenServer! Just some refactors here and there

url =
@api_url
|> URI.merge(method)
|> (&"#{&1}?#{URI.encode_query(queries)}").()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should update this part from #76

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed with rebase to #76.

config :cadet, ecto_repos: [Cadet.Repo]
config :cadet,
ecto_repos: [Cadet.Repo],
updater: [interval: 5 * 60 * 1000]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be better:

alias Timex.Duration

5 |> Duration.from_minutes() |> Duration.to_milliseconds()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't easily use Timex in the config, since config is evaluated before dependencies are compiled. I don't think it's worth the trouble to implement a work around just to be able to use this pipe. Added a comment indicating that the value for interval is in milliseconds in fd9de8e.

WARNING: The GenServer crashes if the API key is invalid, or not provided.
"""
def start_link() do
GenServer.start_link(__MODULE__, %{})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If init/1 ignores its argument, then maybe change %{} to nil?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored with 9131740.

`schedule_work/0` -> `handle_info/2` -> ...
"""
def init(_) do
token = get_token()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can refactor out get_api_params/0

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored with 9131740.

{:error, :bad_request} ->
# the token has probably expired---get a new one
Logger.info("Updater failed fetching announcements. Refreshing token...")
token = get_token()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor to get_api_params/0

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored with 9131740.

assert {:ok, pid} = Updater.start_link()
assert Process.alive?(pid) == true
assert GenServer.stop(pid) == :ok
assert Process.alive?(pid) == false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead do refute Process.alive?(pid)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with a2ae3d9.


test "GenServer init/1 callback" do
use_cassette "updater/init#1", custom: true do
assert {:ok, %{token: token, course_id: course_id}} = Updater.init(%{})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change %{} to nil if it is ignored

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with a2ae3d9.

assert {:ok, %{token: token, course_id: course_id}} = Updater.init(%{})
assert String.length(token) == 480
assert String.length(course_id) == 36
assert course_id != "00000000-0000-0000-0000-000000000000"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use refute

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with a2ae3d9.

use_cassette "updater/handle_info#1", custom: true do
token = Updater.get_token()
course_id = Updater.get_course_id(token)
api_params = %{token: token, course_id: course_id}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once get_api_params/0 is refactored, use that instead here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with a2ae3d9.

use_cassette "updater/handle_info#2", custom: true do
token = Updater.get_token()
course_id = Updater.get_course_id(token)
api_params = %{token: "bad_token", course_id: course_id}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_api_params/0

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with a2ae3d9.

@indocomsoft indocomsoft added this to the Sprint 4 milestone Jun 29, 2018
@source-academy source-academy deleted a comment from coveralls Jun 30, 2018
@ning-y ning-y force-pushed the updater-state branch 2 times, most recently from a2ae3d9 to 5147d95 Compare July 2, 2018 03:37
@ning-y
Copy link
Member Author

ning-y commented Jul 2, 2018

Adding tests for new get_api_params/0. Please wait on merge.


e: Done with 8a56a35.

@ning-y
Copy link
Member Author

ning-y commented Jul 2, 2018

Test coverage fell, seems updater/handle_info#2 (Send :work to GenServer, Invalid token) is not working as expected. Please wait on merge.


e: Fixed with 6196dc9.

@ning-y ning-y force-pushed the updater-state branch 2 times, most recently from de4b70d to 3f878c2 Compare July 2, 2018 04:20
@ning-y
Copy link
Member Author

ning-y commented Jul 2, 2018

@indocomsoft We need to hold off on this merge until I implement some sort of flag that decides if the GenServer should be run. The issue is there are at least two ec2 instances running this application simultaneously because of the AWS auto-scaling group. As a result, there would be at least twice the expected number of calls to IVLE due to this GenServer.

This is also a problem when we have to implement automated grading later on. Only one 'main' instance of this application should be the one checking the deadline and sending out requests to the AWS lambda auto grader. Otherwise, we end up duplication calls.


e: Done with 9c3a157.

@ning-y
Copy link
Member Author

ning-y commented Jul 3, 2018

@indocomsoft Rebased and adding command line options to start the updater.

@indocomsoft
Copy link
Member

Might wanna add notice on README to warn people that changing .env requires a re-compilation with mix clean because module attribute is only evaluated during compile time

Copy link
Member

@indocomsoft indocomsoft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few small changes

"""
def api_call(method, queries) do
case HTTPoison.get(api_url(method, queries)) do
{:ok, %{body: body, status_code: 200}} when body != ~s("") ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing

{:ok, %{body: body, status_code: 200}} when body != ~s("") ->
{:ok, %{body: ~s(""), status_code: 200}} ->

Maybe you could consider doing

{:ok, %{body: ~s(""), status_code: 200}} ->
{:ok, %{body: body, status_code: 200}} ->

Since case pattern matching is evaluated sequentially

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the current match order, since it follows an 'if ok, else error' structure, rather than an 'if error, elif ok, else error'.


# To supply command line args to phx.server, you must use the elixir/iex bin
# $ elixir --erl "--updater" -S mix phx.server
# $ iex --erl "--updater" -S mix phx.server
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be cadet.server me thinks

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's aliased to phx.server at mix.exs

{:ok, modules} = api_fetch("Modules", AuthToken: token, CourseID: "CS1101S")
{:ok, modules} = api_call("Modules", AuthToken: token, CourseID: "CS1101S")

cs1101s =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if the
cs1101s = modules["Results"] |> Enum.find part should be refactored to a separate method. Currently there are only 2 occurrences of them, so let's keep them separate for now following the rule of 3

@ning-y
Copy link
Member Author

ning-y commented Jul 4, 2018

@indocomsoft There's already instructions about recompilation in the current README.

If you've compiled the application before setting a valid value, you must force a recompilation with mix clean && mix.

@indocomsoft indocomsoft merged commit 6c70e87 into master Jul 4, 2018
@indocomsoft indocomsoft deleted the updater-state branch July 4, 2018 11:46
indocomsoft pushed a commit that referenced this pull request Aug 17, 2018
* Add Updater.get_course_id/1

* Add test for Updater.get_course_id/1

* Add tests for IVLE.api_fetch/2

* Make Updater implement GenServer

* Fix invalid token failing silently (announcements)

* Add Updater to the supervisor tree

* Rename IVLE's api_fetch/2 -> api_call/2

* Update documentation

* Add test for new guarded IVLE.api_call/2

* Add test for Updater.get_announcements#1

* Add tests for Updater.init/1

* Add tests for Updater.handle_info/2

* Fix missing cassette in updater_test

* Refactor a line with pipe operators

* Improve test coverage for Updater

* Fix private function being public

* Bump test coverage for Updater to 100%

* Document interval being milliseconds with comments

* Refactor updater

* Remove redundant case block

* Replace Enum.filter with Enum.find

* Refactor tests

* Add test for get_api_params/0

* Fix reduced test coverage

* Add command line flag to toggle updater
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants