1 /*
2  *  Make.org Core API
3  *  Copyright (C) 2018 Make.org
4  *
5  * This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU Affero General Public License as
7  *  published by the Free Software Foundation, either version 3 of the
8  *  License, or (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Affero General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Affero General Public License
16  *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  *
18  */
19 
20 package org.make.api.user.social
21 
22 import java.nio.charset.Charset
23 
24 import akka.http.scaladsl.model._
25 import akka.http.scaladsl.{Http, HttpExt}
26 import grizzled.slf4j.Logging
27 import io.circe.parser._
28 import org.make.api.technical.ActorSystemComponent
29 import org.make.api.user.social.models.facebook.{UserInfo => FacebookUserInfo}
30 import org.make.core.{ValidationError, ValidationFailedError}
31 
32 import scala.concurrent.ExecutionContext.Implicits.global
33 import scala.concurrent.Future
34 import scala.concurrent.duration.DurationInt
35 
36 trait FacebookApiComponent {
37   def facebookApi: FacebookApi
38 }
39 
40 trait FacebookApi {
41   def getUserInfo(accessToken: String): Future[FacebookUserInfo]
42 }
43 
44 trait DefaultFacebookApiComponent extends FacebookApiComponent {
45   self: ActorSystemComponent =>
46 
47   override lazy val facebookApi: FacebookApi = new DefaultFacebookApi
48 
49   class DefaultFacebookApi extends FacebookApi with Logging {
50     private val http: HttpExt = Http()
51 
52     def getUserInfo(accessToken: String): Future[FacebookUserInfo] = {
53       val url =
54         s"https://graph.facebook.com/v12.0/me?access_token=$accessToken&fields=email,first_name"
55 
56       http
57         .singleRequest(HttpRequest(method = HttpMethods.GET, uri = url))
58         .flatMap(strictToString(_))
59         .flatMap { entity =>
60           parse(entity).flatMap(_.as[FacebookUserInfo]) match {
61             case Right(userInfo) if userInfo.email.isEmpty =>
62               Future.failed(ValidationFailedError(Seq(ValidationError("email", "mandatory", Some("not valid")))))
63             case Right(userInfo) => Future.successful(userInfo)
64             case Left(e)         => Future.failed(e)
65           }
66         }
67     }
68 
69     private def strictToString(response: HttpResponse, expectedCode: StatusCode = StatusCodes.OK): Future[String] = {
70       logger.debug(s"Server answered $response")
71       response match {
72         case HttpResponse(`expectedCode`, _, entity, _) =>
73           val result = entity
74             .toStrict(2.second)
75             .map(_.data.decodeString(Charset.forName("UTF-8")))
76           result
77         case HttpResponse(code, _, entity, _) =>
78           entity.toStrict(2.second).flatMap { entity =>
79             val response = entity.data.decodeString(Charset.forName("UTF-8"))
80             Future.failed(SocialProviderException(s"Got unexpected response code: $code, with body: $response"))
81           }
82       }
83     }
84 
85   }
86 }
Line Stmt Id Pos Tree Symbol Tests Code
50 33035 1689 - 1689 Select org.make.api.technical.ActorSystemComponent.actorSystem DefaultFacebookApiComponent.this.actorSystem
50 45383 1685 - 1691 ApplyToImplicitArgs akka.http.scaladsl.Http.apply akka.http.scaladsl.Http.apply()(DefaultFacebookApiComponent.this.actorSystem)
57 42278 1933 - 1948 Select akka.http.scaladsl.model.HttpMethods.GET akka.http.scaladsl.model.HttpMethods.GET
57 34394 1956 - 1959 ApplyImplicitView akka.http.scaladsl.model.Uri.apply model.this.Uri.apply(url)
57 39588 1912 - 1912 Select akka.http.scaladsl.model.HttpRequest.apply$default$4 akka.http.scaladsl.model.HttpRequest.apply$default$4
57 47510 1912 - 1960 Apply akka.http.scaladsl.model.HttpRequest.apply akka.http.scaladsl.model.HttpRequest.apply(akka.http.scaladsl.model.HttpMethods.GET, model.this.Uri.apply(url), akka.http.scaladsl.model.HttpRequest.apply$default$3, akka.http.scaladsl.model.HttpRequest.apply$default$4, akka.http.scaladsl.model.HttpRequest.apply$default$5)
57 31755 1912 - 1912 Select akka.http.scaladsl.model.HttpRequest.apply$default$5 akka.http.scaladsl.model.HttpRequest.apply$default$5
57 32778 1898 - 1898 Select akka.http.scaladsl.HttpExt.singleRequest$default$3 DefaultFacebookApi.this.http.singleRequest$default$3
57 47165 1912 - 1912 Select akka.http.scaladsl.model.HttpRequest.apply$default$3 akka.http.scaladsl.model.HttpRequest.apply$default$3
57 45589 1898 - 1898 Select akka.http.scaladsl.HttpExt.singleRequest$default$4 DefaultFacebookApi.this.http.singleRequest$default$4
57 40388 1898 - 1898 Select akka.http.scaladsl.HttpExt.singleRequest$default$2 DefaultFacebookApi.this.http.singleRequest$default$2
58 38318 1979 - 1979 Select org.make.api.user.social.DefaultFacebookApiComponent.DefaultFacebookApi.strictToString$default$2 DefaultFacebookApi.this.strictToString$default$2
58 33894 1979 - 1996 Apply org.make.api.user.social.DefaultFacebookApiComponent.DefaultFacebookApi.strictToString DefaultFacebookApi.this.strictToString(x$1, DefaultFacebookApi.this.strictToString$default$2)
58 47210 1978 - 1978 Select scala.concurrent.ExecutionContext.Implicits.global scala.concurrent.ExecutionContext.Implicits.global
59 32564 2015 - 2015 Select scala.concurrent.ExecutionContext.Implicits.global scala.concurrent.ExecutionContext.Implicits.global
59 45372 1884 - 2405 ApplyToImplicitArgs scala.concurrent.Future.flatMap DefaultFacebookApi.this.http.singleRequest(akka.http.scaladsl.model.HttpRequest.apply(akka.http.scaladsl.model.HttpMethods.GET, model.this.Uri.apply(url), akka.http.scaladsl.model.HttpRequest.apply$default$3, akka.http.scaladsl.model.HttpRequest.apply$default$4, akka.http.scaladsl.model.HttpRequest.apply$default$5), DefaultFacebookApi.this.http.singleRequest$default$2, DefaultFacebookApi.this.http.singleRequest$default$3, DefaultFacebookApi.this.http.singleRequest$default$4).flatMap[String](((x$1: akka.http.scaladsl.model.HttpResponse) => DefaultFacebookApi.this.strictToString(x$1, DefaultFacebookApi.this.strictToString$default$2)))(scala.concurrent.ExecutionContext.Implicits.global).flatMap[org.make.api.user.social.models.facebook.UserInfo](((entity: String) => io.circe.parser.`package`.parse(entity).flatMap[io.circe.Error, org.make.api.user.social.models.facebook.UserInfo](((x$2: io.circe.Json) => x$2.as[org.make.api.user.social.models.facebook.UserInfo](facebook.this.UserInfo.decoder))) match { case (value: org.make.api.user.social.models.facebook.UserInfo): scala.util.Right[io.circe.Error,org.make.api.user.social.models.facebook.UserInfo]((userInfo @ _)) if userInfo.email.isEmpty => scala.concurrent.Future.failed[Nothing](org.make.core.ValidationFailedError.apply(scala.`package`.Seq.apply[org.make.core.ValidationError](org.make.core.ValidationError.apply("email", "mandatory", scala.Some.apply[String]("not valid"))))) case (value: org.make.api.user.social.models.facebook.UserInfo): scala.util.Right[io.circe.Error,org.make.api.user.social.models.facebook.UserInfo]((userInfo @ _)) => scala.concurrent.Future.successful[org.make.api.user.social.models.facebook.UserInfo](userInfo) case (value: io.circe.Error): scala.util.Left[io.circe.Error,org.make.api.user.social.models.facebook.UserInfo]((e @ _)) => scala.concurrent.Future.failed[Nothing](e) }))(scala.concurrent.ExecutionContext.Implicits.global)
60 39338 2063 - 2063 Select org.make.api.user.social.models.facebook.UserInfo.decoder facebook.this.UserInfo.decoder
60 31175 2059 - 2081 ApplyToImplicitArgs io.circe.Json.as x$2.as[org.make.api.user.social.models.facebook.UserInfo](facebook.this.UserInfo.decoder)
60 48573 2037 - 2082 Apply scala.util.Either.flatMap io.circe.parser.`package`.parse(entity).flatMap[io.circe.Error, org.make.api.user.social.models.facebook.UserInfo](((x$2: io.circe.Json) => x$2.as[org.make.api.user.social.models.facebook.UserInfo](facebook.this.UserInfo.decoder)))
61 40426 2127 - 2149 Select scala.Option.isEmpty userInfo.email.isEmpty
62 46955 2203 - 2264 Apply scala.collection.SeqFactory.Delegate.apply scala.`package`.Seq.apply[org.make.core.ValidationError](org.make.core.ValidationError.apply("email", "mandatory", scala.Some.apply[String]("not valid")))
62 30925 2167 - 2266 Apply scala.concurrent.Future.failed scala.concurrent.Future.failed[Nothing](org.make.core.ValidationFailedError.apply(scala.`package`.Seq.apply[org.make.core.ValidationError](org.make.core.ValidationError.apply("email", "mandatory", scala.Some.apply[String]("not valid")))))
62 33933 2207 - 2263 Apply org.make.core.ValidationError.apply org.make.core.ValidationError.apply("email", "mandatory", scala.Some.apply[String]("not valid"))
62 37745 2245 - 2262 Apply scala.Some.apply scala.Some.apply[String]("not valid")
62 45335 2232 - 2243 Literal <nosymbol> "mandatory"
62 32819 2223 - 2230 Literal <nosymbol> "email"
62 38833 2181 - 2265 Apply org.make.core.ValidationFailedError.apply org.make.core.ValidationFailedError.apply(scala.`package`.Seq.apply[org.make.core.ValidationError](org.make.core.ValidationError.apply("email", "mandatory", scala.Some.apply[String]("not valid"))))
63 48024 2303 - 2330 Apply scala.concurrent.Future.successful scala.concurrent.Future.successful[org.make.api.user.social.models.facebook.UserInfo](userInfo)
64 40466 2367 - 2383 Apply scala.concurrent.Future.failed scala.concurrent.Future.failed[Nothing](e)
70 37502 2537 - 2579 Apply grizzled.slf4j.Logger.debug DefaultFacebookApi.this.logger.debug(("Server answered ".+(response): String))
74 30967 2713 - 2713 ApplyToImplicitArgs akka.stream.Materializer.matFromSystem stream.this.Materializer.matFromSystem(DefaultFacebookApiComponent.this.actorSystem)
74 34388 2714 - 2715 Literal <nosymbol> 2
74 38589 2713 - 2713 Select org.make.api.technical.ActorSystemComponent.actorSystem DefaultFacebookApiComponent.this.actorSystem
74 46994 2714 - 2722 Select scala.concurrent.duration.DurationConversions.second scala.concurrent.duration.`package`.DurationInt(2).second
75 45127 2685 - 2787 ApplyToImplicitArgs scala.concurrent.Future.map entity.toStrict(scala.concurrent.duration.`package`.DurationInt(2).second)(stream.this.Materializer.matFromSystem(DefaultFacebookApiComponent.this.actorSystem)).map[String](((x$3: akka.http.scaladsl.model.HttpEntity.Strict) => x$3.data.decodeString(java.nio.charset.Charset.forName("UTF-8"))))(scala.concurrent.ExecutionContext.Implicits.global)
75 43781 2761 - 2785 Apply java.nio.charset.Charset.forName java.nio.charset.Charset.forName("UTF-8")
75 32066 2740 - 2740 Select scala.concurrent.ExecutionContext.Implicits.global scala.concurrent.ExecutionContext.Implicits.global
75 40938 2741 - 2786 Apply akka.util.ByteString.decodeString x$3.data.decodeString(java.nio.charset.Charset.forName("UTF-8"))
78 45158 2898 - 2898 Select scala.concurrent.ExecutionContext.Implicits.global scala.concurrent.ExecutionContext.Implicits.global
78 38344 2864 - 3112 ApplyToImplicitArgs scala.concurrent.Future.flatMap entity.toStrict(scala.concurrent.duration.`package`.DurationInt(2).second)(stream.this.Materializer.matFromSystem(DefaultFacebookApiComponent.this.actorSystem)).flatMap[Nothing](((entity: akka.http.scaladsl.model.HttpEntity.Strict) => { val response: String = entity.data.decodeString(java.nio.charset.Charset.forName("UTF-8")); scala.concurrent.Future.failed[Nothing](SocialProviderException.apply(("Got unexpected response code: ".+(code).+(", with body: ").+(response): String))) }))(scala.concurrent.ExecutionContext.Implicits.global)
78 47470 2879 - 2879 Select org.make.api.technical.ActorSystemComponent.actorSystem DefaultFacebookApiComponent.this.actorSystem
78 38624 2879 - 2879 ApplyToImplicitArgs akka.stream.Materializer.matFromSystem stream.this.Materializer.matFromSystem(DefaultFacebookApiComponent.this.actorSystem)
78 34431 2880 - 2888 Select scala.concurrent.duration.DurationConversions.second scala.concurrent.duration.`package`.DurationInt(2).second
78 37538 2880 - 2881 Literal <nosymbol> 2
79 30712 2962 - 2986 Apply java.nio.charset.Charset.forName java.nio.charset.Charset.forName("UTF-8")
79 44842 2937 - 2987 Apply akka.util.ByteString.decodeString entity.data.decodeString(java.nio.charset.Charset.forName("UTF-8"))
80 32524 3000 - 3100 Apply scala.concurrent.Future.failed scala.concurrent.Future.failed[Nothing](SocialProviderException.apply(("Got unexpected response code: ".+(code).+(", with body: ").+(response): String)))
80 39668 3014 - 3099 Apply org.make.api.user.social.SocialProviderException.apply SocialProviderException.apply(("Got unexpected response code: ".+(code).+(", with body: ").+(response): String))