Skip to content

Commit

Permalink
Merge pull request #69 from CaptainFact/staging
Browse files Browse the repository at this point in the history
Release 0.9.1
  • Loading branch information
Betree authored Dec 20, 2018
2 parents 18b78d7 + 621f160 commit 7a6a14b
Show file tree
Hide file tree
Showing 56 changed files with 526 additions and 509 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ until_fail.sh
# Secrets
apps/*/priv/secrets/*
!apps/*/priv/secrets/.keep
**/*.secret.exs

# Elixir LS
.elixir_ls
26 changes: 2 additions & 24 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
# ----- Config -----

stages:
- name: test
- name: release

env:
CF_API_REST_IMAGE: captainfact/rest-api:$TRAVIS_BRANCH
CF_API_GRAPHQL_IMAGE: captainfact/graphql-api:$TRAVIS_BRANCH
CF_API_ATOM_FEED: captainfact/atom-feed:$TRAVIS_BRANCH
CF_API_OPENGRAPH_IMAGE: captainfact/opengraph:$TRAVIS_BRANCH
CF_API_JOBS_IMAGE: captainfact/jobs:$TRAVIS_BRANCH

# ---- Jobs ----

jobs:
Expand All @@ -24,32 +15,19 @@ jobs:
env:
- MIX_ENV=test
before_script:
- mix format --check-formatted
- mix local.hex --force
- mix local.rebar --force
- mix deps.get
- mix ecto.create
- mix ecto.migrate
script:
- mix coveralls.travis --umbrella
- mix format --check-formatted

- stage: release
if: branch IN (master, staging) AND type != pull_request
language: generic
sudo: required
services: [docker]
script:
- set -e
- # ---- Build ----
- docker build --build-arg APP=cf_rest_api -t ${CF_API_REST_IMAGE} .
- docker build --build-arg APP=cf_graphql -t ${CF_API_GRAPHQL_IMAGE} .
- docker build --build-arg APP=cf_atom_feed -t ${CF_API_ATOM_FEED} .
- docker build --build-arg APP=cf_opengraph -t ${CF_API_OPENGRAPH_IMAGE} .
- docker build --build-arg APP=cf_jobs -t ${CF_API_JOBS_IMAGE} .
- # ---- Push release ----
- docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD";
- docker push $CF_API_REST_IMAGE
- docker push $CF_API_GRAPHQL_IMAGE
- docker push $CF_API_ATOM_FEED
- docker push $CF_API_OPENGRAPH_IMAGE
- docker push $CF_API_JOBS_IMAGE
- ./rel/release.sh $TRAVIS_BRANCH
53 changes: 20 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,26 @@
<hr/>
<br/>

## Install & Run
# Install & Run

### Start DB
## Prerequisites

You need to install Elixir. We recommand using [asdf](https://github.com/asdf-vm/asdf#setup).
Check their documentation on how to install it, then run `asdf install` from
root `captain-fact-api` folder.

## Start DB

Create / launch a postgres instance on your local machine. If you have
docker installed, you can use the pre-seed postgres docker image:

`docker run -d --name cf_dev_db -p 5432:5432 captainfact/dev-db:latest`

### Start API services

- Without Docker (recommended if you want to make changes in the API)
## Start API

- `mix deps.get`
- `mix ecto.migrate`
- `iex -S mix`

- With Docker
- Download project's dependencies with `./dev/get_dependencies.sh`
- Migrate your database with `./dev/db_migrate.sh`
- Start server with `./dev/start_server.sh`
- `mix deps.get` --> Get dependencies
- `mix ecto.migrate` --> Migrate DB
- `iex -S mix` --> Start project

Following services will be started:

Expand All @@ -46,16 +45,16 @@ Following services will be started:
- [localhost:4003](https://localhost:4003) - GraphQL API (https)
- [localhost:4004](http://localhost:4004) - Atom feed

You can run tests with `./dev/test.sh`. You can filter which tests to run by
running something like `./dev/test.sh test/your_test_subpath`.
Check `./dev/test.sh` script comments for details.
## Other useful commands

## Project architecture
- `mix test` --> Run tests
- `mix test.watch` --> Run tests watcher
- `mix format` --> Format code
- `mix ecto.gen.migration [migration_name]` --> Generate migration

Elixir offers very nice ways to separate concerns and work with microservices.
This application is organized as an [umbrella project](https://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-apps.html) which allows us to divide CaptainFact API into small apps.
# Project architecture

### File structure
This application is organized as an [umbrella project](https://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-apps.html) which allows us to divide CaptainFact API into small apps.

```
.
Expand All @@ -82,21 +81,9 @@ This application is organized as an [umbrella project](https://elixir-lang.org/g
│   └── config.exs => Releases configuration.
```

## Styling

Code should follow [Elixy Style Guide](https://github.com/christopheradams/elixir_style_guide)
and [Credo style guide](https://github.com/rrrene/elixir-style-guide)
as much as possible.

Avoid lines longer than 80 characters, **never** go beyond 110 characters.

## Linked projects
# Linked projects

- [Community discussions and documentation](https://github.com/CaptainFact/captain-fact/)
- [Frontend](https://github.com/CaptainFact/captain-fact-frontend)
- [Extension](https://github.com/CaptainFact/captain-fact-extension)
- [Overlay injector](https://github.com/CaptainFact/captain-fact-overlay-injector)

# Feature requests

[![Feature Requests](http://feathub.com/CaptainFact/captain-fact?format=svg)](http://feathub.com/CaptainFact/captain-fact)
3 changes: 3 additions & 0 deletions apps/cf/config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ config :rollbax,

# Mails
config :cf, CF.Mailer, adapter: Bamboo.LocalAdapter

# Import local secrets if any - use wildcard to ignore errors
import_config "*dev.secret.exs"
6 changes: 3 additions & 3 deletions apps/cf/config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ config :cf, CF.Mailer, adapter: Bamboo.TestAdapter
# Reduce the number of round for encryption during tests
config :bcrypt_elixir, :log_rounds, 4

# Captions mock for testing
config :cf,
captions_fetcher: CF.Videos.CaptionsFetcherTest
# Behaviours mock for testing
config :cf, captions_fetcher: CF.Videos.CaptionsFetcherTest
config :cf, use_test_video_metadata_fetcher: true

# Configure Rollbar (errors reporting)
config :rollbax,
Expand Down
16 changes: 16 additions & 0 deletions apps/cf/lib/accounts/accounts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,27 @@ defmodule CF.Accounts do

# Do create user
user_params
|> prepare_user_params_from_third_party()
|> create_account_from_params(provider_params, allow_empty_username)
|> after_create(invitation)
end
end

# Special formating for third-party provided user params
defp prepare_user_params_from_third_party(params) do
# Truncate name to avoid crashing when registering with a too-long name
cond do
Map.has_key?(params, :name) ->
Map.update(params, :name, nil, &String.slice(&1, 0..19))

Map.has_key?(params, "name") ->
Map.update(params, "name", nil, &String.slice(&1, 0..19))

true ->
params
end
end

defp create_account_from_params(user_params, provider_params, allow_empty_username) do
case Map.get(user_params, "username") || Map.get(user_params, :username) do
username when allow_empty_username and (is_nil(username) or username == "") ->
Expand Down
2 changes: 1 addition & 1 deletion apps/cf/lib/actions/action_creator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ defmodule CF.Actions.ActionCreator do
user_id,
:video,
:update,
video_id: video.video_id,
video_id: video.id,
changes: changes
)
end
Expand Down
3 changes: 1 addition & 2 deletions apps/cf/lib/videos/captions_fetcher.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@ defmodule CF.Videos.CaptionsFetcher do
Fetch captions for videos.
"""

@callback fetch(String.t(), String.t()) ::
{:ok, DB.Schema.VideoCaption.t()} | {:error, binary()}
@callback fetch(DB.Schema.Video.t()) :: {:ok, DB.Schema.VideoCaption.t()} | {:error, binary()}
end
2 changes: 1 addition & 1 deletion apps/cf/lib/videos/captions_fetcher_test.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule CF.Videos.CaptionsFetcherTest do
@behaviour CF.Videos.CaptionsFetcher

@impl true
def fetch(_provider_id, _locale) do
def fetch(_video) do
captions = %DB.Schema.VideoCaption{
content: "__TEST-CONTENT__",
format: "xml"
Expand Down
2 changes: 1 addition & 1 deletion apps/cf/lib/videos/captions_fetcher_youtube.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule CF.Videos.CaptionsFetcherYoutube do
@behaviour CF.Videos.CaptionsFetcher

@impl true
def fetch(youtube_id, locale) do
def fetch(%{youtube_id: youtube_id, locale: locale}) do
with {:ok, content} <- fetch_captions_content(youtube_id, locale) do
captions = %DB.Schema.VideoCaption{
content: content,
Expand Down
76 changes: 8 additions & 68 deletions apps/cf/lib/videos/metadata_fetcher.ex
Original file line number Diff line number Diff line change
@@ -1,76 +1,16 @@
defmodule CF.Videos.MetadataFetcher do
@moduledoc """
Methods to fetch metadata (title, language) from videos
Fetch metadata for video.
"""

require Logger

alias Kaur.Result
alias GoogleApi.YouTube.V3.Connection, as: YouTubeConnection
alias GoogleApi.YouTube.V3.Api.Videos, as: YouTubeVideos
alias GoogleApi.YouTube.V3.Model.Video, as: YouTubeVideo
alias GoogleApi.YouTube.V3.Model.VideoListResponse, as: YouTubeVideoList

alias DB.Schema.Video
@type video_metadata :: %{
title: String.t(),
language: String.t(),
url: String.t()
}

@doc """
Fetch metadata from video. Returns an object containing :title, :url and :language
Usage:
iex> fetch_video_metadata("https://www.youtube.com/watch?v=OhWRT3PhMJs")
iex> fetch_video_metadata({"youtube", "OhWRT3PhMJs"})
Takes an URL, fetch the metadata and return them
"""
def fetch_video_metadata(nil),
do: {:error, "Invalid URL"}

if Application.get_env(:db, :env) == :test do
def fetch_video_metadata(url = "__TEST__/" <> _id) do
{:ok, %{title: "__TEST-TITLE__", url: url}}
end
end

def fetch_video_metadata(url) when is_binary(url),
do: fetch_video_metadata(Video.parse_url(url))

def fetch_video_metadata({"youtube", provider_id}) do
case Application.get_env(:cf, :youtube_api_key) do
nil ->
Logger.warn("No YouTube API key provided. Falling back to HTML fetcher")
fetch_video_metadata_html("youtube", provider_id)

api_key ->
fetch_video_metadata_api("youtube", provider_id, api_key)
end
end

defp fetch_video_metadata_api("youtube", provider_id, api_key) do
YouTubeConnection.new()
|> YouTubeVideos.youtube_videos_list("snippet", id: provider_id, key: api_key)
|> Result.map_error(fn e -> "YouTube API Error: #{inspect(e)}" end)
|> Result.keep_if(&(!Enum.empty?(&1.items)), "Video doesn't exist")
|> Result.map(fn %YouTubeVideoList{items: [video = %YouTubeVideo{} | _]} ->
%{
title: video.snippet.title,
language: video.snippet.defaultLanguage || video.snippet.defaultAudioLanguage,
url: Video.build_url(%{provider: "youtube", provider_id: provider_id})
}
end)
end

defp fetch_video_metadata_html(provider, id) do
url = Video.build_url(%{provider: provider, provider_id: id})

case HTTPoison.get(url) do
{:ok, %HTTPoison.Response{body: body}} ->
meta = Floki.attribute(body, "meta[property='og:title']", "content")

case meta do
[] -> {:error, "Page does not contains an OpenGraph title attribute"}
[title] -> {:ok, %{title: HtmlEntities.decode(title), url: url}}
end

{_, _} ->
{:error, "Remote URL didn't respond correctly"}
end
end
@callback fetch_video_metadata(String.t()) :: {:ok, video_metadata} | {:error, binary()}
end
25 changes: 25 additions & 0 deletions apps/cf/lib/videos/metadata_fetcher_opengraph.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule CF.Videos.MetadataFetcher.Opengraph do
@moduledoc """
Methods to fetch metadata (title, language) from videos
"""

@behaviour CF.Videos.MetadataFetcher

@doc """
Fetch metadata from video using OpenGraph tags.
"""
def fetch_video_metadata(url) do
case HTTPoison.get(url) do
{:ok, %HTTPoison.Response{body: body}} ->
meta = Floki.attribute(body, "meta[property='og:title']", "content")

case meta do
[] -> {:error, "Page does not contains an OpenGraph title attribute"}
[title] -> {:ok, %{title: HtmlEntities.decode(title), url: url}}
end

{_, _} ->
{:error, "Remote URL didn't respond correctly"}
end
end
end
18 changes: 18 additions & 0 deletions apps/cf/lib/videos/metadata_fetcher_test.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
defmodule CF.Videos.MetadataFetcher.Test do
@moduledoc """
Methods to fetch metadata (title, language) from videos
"""

@behaviour CF.Videos.MetadataFetcher

@doc """
Fetch metadata from video using OpenGraph tags.
"""
def fetch_video_metadata(url) do
{:ok,
%{
title: "__TEST-TITLE__",
url: url
}}
end
end
Loading

0 comments on commit 7a6a14b

Please sign in to comment.