Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Use gcal functions + Deploy gcal.fly.dev #31 #35

Merged
merged 11 commits into from
May 16, 2023
Merged
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ npm-debug.log

.env
cache_test.dets
# Mac Noise
.DS_Store
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,7 @@ CMD ["/app/bin/server"]
# Appended by flyctl
ENV ECTO_IPV6 true
ENV ERL_AFLAGS "-proto_dist inet6_tcp"

# Appended by flyctl
ENV ECTO_IPV6 true
ENV ERL_AFLAGS "-proto_dist inet6_tcp"
3 changes: 3 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ config :elixir_auth_google,
client_id: "631770888008-6n0oruvsm16kbkqg6u76p5cv5kfkcekt.apps.googleusercontent.com",
client_secret: "MHxv6-RGF5nheXnxh1b0LNDq",
httpoison_mock: true

config :gcal,
httpoison_mock: true
6 changes: 3 additions & 3 deletions fly.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# fly.toml file generated for dwyl-calendar on 2023-04-14T12:34:13+01:00
# fly.toml file generated for gcal on 2023-05-15T14:53:32+01:00

app = "dwyl-calendar"
app = "gcal"
kill_signal = "SIGTERM"
kill_timeout = 5
processes = []
Expand All @@ -9,7 +9,7 @@ processes = []
release_command = "/app/bin/migrate"

[env]
PHX_HOST = "dwyl-calendar.fly.dev"
PHX_HOST = "gcal.fly.dev"
PORT = "8080"

[experimental]
Expand Down
81 changes: 0 additions & 81 deletions lib/cal/http.ex

This file was deleted.

2 changes: 0 additions & 2 deletions lib/cal_web/controllers/google_auth_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ defmodule CalWeb.GoogleAuthController do
"""
def index(conn, %{"code" => code}) do
{:ok, token} = ElixirAuthGoogle.get_token(code, conn)
# {:ok, profile} = ElixirAuthGoogle.get_user_profile(token.access_token)

conn
# |> put_flash(:person_email, profile.email)
|> put_flash(:token, token)
|> redirect(to: ~p"/app")
end
Expand Down
2 changes: 1 addition & 1 deletion lib/cal_web/controllers/page_html/home.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<div class="px-4 py-10 sm:px-6 sm:py-28 lg:px-8 xl:px-28 xl:py-32">
<div class="mx-auto max-w-xl lg:mx-0">
<p class="text-[2rem] mt-4 font-semibold leading-10 tracking-tighter text-zinc-900 ">
DWYL Calendar
Google Calendar Demo
</p>
<p class="mt-4 text-base leading-7 text-zinc-600">
A demo project where you can visualize and create events that are stored in your Google Calendar.
Expand Down
2 changes: 2 additions & 0 deletions lib/cal_web/controllers/page_html/privacy.html.heex
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Comming Soon!
see: https://github.com/dwyl/calendar/issues/37
162 changes: 29 additions & 133 deletions lib/cal_web/live/app_live.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
defmodule CalWeb.AppLive do
use CalWeb, :live_view
import CalWeb.AppHTML
import Cal.HTTPoison

def mount(params, session, socket) do
case connected?(socket) do
Expand All @@ -13,15 +12,14 @@ defmodule CalWeb.AppLive do
def connected_mount(_params, _session, socket) do
# Getting information about the timezone of the client
hoursFromUTC = Map.get(get_connect_params(socket), "hoursFromUTC", "+0000")

timezone = Timex.Timezone.name_of(hoursFromUTC)

# We fetch the access token to make the requests.
# If none is found, we redirect the user to the home page.
case get_token(socket) do
{:ok, token} ->
case get_access_token(socket) do
{:ok, access_token} ->
{:ok, primary_calendar} = Gcal.get_calendar_details(access_token, "primary")
# Get event list and update socket
{primary_calendar, event_list} = get_event_list(token, Timex.now(timezone))
{:ok, event_list} = Gcal.get_event_list(access_token, Timex.now(timezone))
{:ok, assign(socket, event_list: event_list.items, calendar: primary_calendar)}

_ ->
Expand All @@ -38,14 +36,14 @@ defmodule CalWeb.AppLive do
socket
) do
# Get token from socket and primary calendar
{:ok, token} = get_token(socket)
{:ok, access_token} = get_access_token(socket)

# Parse new date
datetime =
Timex.parse!("#{year}-#{month}-#{day} #{hoursFromUTC}", "{YYYY}-{M}-{D} {Z}")
|> Timex.to_datetime()

{_primary_calendar, new_event_list} = get_event_list(token, datetime)
{:ok, new_event_list} = Gcal.get_event_list(access_token, datetime)

# Update socket assigns
{:noreply, assign(socket, event_list: new_event_list.items)}
Expand All @@ -54,145 +52,43 @@ defmodule CalWeb.AppLive do
# Handler to create an event.
# Receives the title of the event, the date, the start and stop timestamps and a boolean stating if it's an "all-day" event or not.
# It also receives the `hoursFromUTC` timezone in military hours (e.g. +0300) to properly set the timezone of the event when it's created.
def handle_event(
"create-event",
%{
"title" => title,
"date" => date,
"start" => start,
"stop" => stop,
"all_day" => all_day,
"hoursFromUTC" => hoursFromUTC
},
socket
) do
@doc """
`event_details` typically contains the following fields:
%{
"all_day" => false,
"date" => "2023-05-15",
"hoursFromUTC" => "+0100",
"start" => "16:00",
"stop" => "18:00",
"title" => "My Awesome Event"
}
"""
def handle_event("create-event", event_details, socket) do
event_details = Useful.atomize_map_keys(event_details)
# Get token and primary calendar
{:ok, token} = get_token(socket)
{:ok, access_token} = get_access_token(socket)

# Post new event
{:ok, _response} =
create_event(token, %{
"title" => title,
"date" => date,
"start" => start,
"stop" => stop,
"all_day" => all_day,
"hoursFromUTC" => hoursFromUTC
})
{:ok, _event} = Gcal.create_event(access_token, event_details)

# Parse new date to datetime and fetch events to refresh
datetime =
Timex.parse!(date, "{YYYY}-{M}-{D}")
|> Timex.to_datetime(Timex.Timezone.name_of(hoursFromUTC))
Timex.parse!(event_details.date, "{YYYY}-{M}-{D}")
|> Timex.to_datetime(Timex.Timezone.name_of(event_details.hoursFromUTC))

{_primary_calendar, new_event_list} = get_event_list(token, datetime)
{:ok, new_event_list} = Gcal.get_event_list(access_token, datetime)

{:noreply, assign(socket, event_list: new_event_list.items)}
end

# Gets the event list of primary calendar.
# We pass on the `token` and the `datetime` of the day to fetch the events.
defp get_event_list(token, datetime) do
headers = [Authorization: "Bearer #{token.access_token}", "Content-Type": "application/json"]

# Get primary calendar
{:ok, primary_calendar} =
httpoison().get("https://www.googleapis.com/calendar/v3/calendars/primary", headers)
|> parse_body_response()

# Get events of primary calendar
params = %{
singleEvents: true,
timeMin: datetime |> Timex.beginning_of_day() |> Timex.format!("{RFC3339}"),
timeMax: datetime |> Timex.end_of_day() |> Timex.format!("{RFC3339}")
}

{:ok, event_list} =
httpoison().get(
"https://www.googleapis.com/calendar/v3/calendars/#{primary_calendar.id}/events",
headers,
params: params
)
|> parse_body_response()

{primary_calendar, event_list}
end

# Create new event to the primary calendar.
defp create_event(token, %{
"title" => title,
"date" => date,
"start" => start,
"stop" => stop,
"all_day" => all_day,
"hoursFromUTC" => hoursFromUTC
}) do
headers = [Authorization: "Bearer #{token.access_token}", "Content-Type": "application/json"]

# Get primary calendar
{:ok, primary_calendar} =
httpoison().get("https://www.googleapis.com/calendar/v3/calendars/primary", headers)
|> parse_body_response()

# Setting `start` and `stop` according to the `all-day` boolean,
# If `all-day` is set to true, we should return the date instead of the datetime,
# as per https://developers.google.com/calendar/api/v3/reference/events/insert.
start =
case all_day do
true ->
%{date: date}

false ->
%{
dateTime:
Timex.parse!("#{date} #{start} #{hoursFromUTC}", "{YYYY}-{0M}-{D} {h24}:{m} {Z}")
|> Timex.format!("{RFC3339}")
}
end

stop =
case all_day do
true ->
%{date: date}

false ->
%{
dateTime:
Timex.parse!("#{date} #{stop} #{hoursFromUTC}", "{YYYY}-{0M}-{D} {h24}:{m} {Z}")
|> Timex.format!("{RFC3339}")
}
end

# Post new event
body = Jason.encode!(%{summary: title, start: start, end: stop})

httpoison().post(
"https://www.googleapis.com/calendar/v3/calendars/#{primary_calendar.id}/events",
body,
headers
)
end

# Get token from the flash session
defp get_token(socket) do
defp get_access_token(socket) do
case Map.get(socket.assigns.flash, "token") do
nil -> {:error, nil}
token -> {:ok, token}
end
end
nil ->
{:error, nil}

# Parse JSON body response
defp parse_body_response({:ok, response}) do
body = Map.get(response, :body)
# make keys of map atoms for easier access in templates
if body == nil do
{:error, :no_body}
else
{:ok, str_key_map} = Jason.decode(body)
atom_key_map = for {key, val} <- str_key_map, into: %{}, do: {String.to_atom(key), val}
{:ok, atom_key_map}
token ->
{:ok, token.access_token}
end

# https://stackoverflow.com/questions/31990134
end
end
6 changes: 3 additions & 3 deletions lib/cal_web/live/app_live.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<div class="ml-5 pb-2 pr-2 pt-2">
<div class="sm:flex sm:justify-between sm:gap-4">
<h3 class="text-lg font-bold text-gray-900 sm:text-xl">
<span class="mr-3"><%= Map.get(event, "summary") %></span>
<span class="mr-3"><%= Map.get(event, :summary) %></span>
<span class="rounded-full border border-indigo-500 px-3 py-1 text-xs text-indigo-500">
<span class="font-bold"><%= render_start_end_times(event) %></span>
</span>
Expand All @@ -30,8 +30,8 @@
<p class="w-full text-sm text-gray-500">
<span>Organized by: </span>
<span class="font-bold">
<%= Map.get(event, "organizer") |> Map.get("displayName") ||
Map.get(event, "organizer") |> Map.get("email") %>
<%= Map.get(event, :organizer) |> Map.get(:displayName) ||
Map.get(event, :organizer) |> Map.get(:email) %>
</span>
</p>
</div>
Expand Down
Loading
Loading