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 31928 1685 - 1691 ApplyToImplicitArgs akka.http.scaladsl.Http.apply akka.http.scaladsl.Http.apply()(DefaultFacebookApiComponent.this.actorSystem)
50 40837 1689 - 1689 Select org.make.api.technical.ActorSystemComponent.actorSystem DefaultFacebookApiComponent.this.actorSystem
57 32992 1898 - 1898 Select akka.http.scaladsl.HttpExt.singleRequest$default$4 DefaultFacebookApi.this.http.singleRequest$default$4
57 47690 1898 - 1898 Select akka.http.scaladsl.HttpExt.singleRequest$default$2 DefaultFacebookApi.this.http.singleRequest$default$2
57 34343 1912 - 1912 Select akka.http.scaladsl.model.HttpRequest.apply$default$3 akka.http.scaladsl.model.HttpRequest.apply$default$3
57 41170 1956 - 1959 ApplyImplicitView akka.http.scaladsl.model.Uri.apply model.this.Uri.apply(url)
57 50048 1933 - 1948 Select akka.http.scaladsl.model.HttpMethods.GET akka.http.scaladsl.model.HttpMethods.GET
57 40879 1898 - 1898 Select akka.http.scaladsl.HttpExt.singleRequest$default$3 DefaultFacebookApi.this.http.singleRequest$default$3
57 39237 1912 - 1912 Select akka.http.scaladsl.model.HttpRequest.apply$default$5 akka.http.scaladsl.model.HttpRequest.apply$default$5
57 35696 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 47353 1912 - 1912 Select akka.http.scaladsl.model.HttpRequest.apply$default$4 akka.http.scaladsl.model.HttpRequest.apply$default$4
58 49479 1979 - 1979 Select org.make.api.user.social.DefaultFacebookApiComponent.DefaultFacebookApi.strictToString$default$2 DefaultFacebookApi.this.strictToString$default$2
58 41206 1979 - 1996 Apply org.make.api.user.social.DefaultFacebookApiComponent.DefaultFacebookApi.strictToString DefaultFacebookApi.this.strictToString(x$1, DefaultFacebookApi.this.strictToString$default$2)
58 34105 1978 - 1978 Select scala.concurrent.ExecutionContext.Implicits.global scala.concurrent.ExecutionContext.Implicits.global
59 40661 2015 - 2015 Select scala.concurrent.ExecutionContext.Implicits.global scala.concurrent.ExecutionContext.Implicits.global
59 32236 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 35127 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)))
60 38997 2059 - 2081 ApplyToImplicitArgs io.circe.Json.as x$2.as[org.make.api.user.social.models.facebook.UserInfo](facebook.this.UserInfo.decoder)
60 47393 2063 - 2063 Select org.make.api.user.social.models.facebook.UserInfo.decoder facebook.this.UserInfo.decoder
61 48495 2127 - 2149 Select scala.Option.isEmpty userInfo.email.isEmpty
62 41668 2207 - 2263 Apply org.make.core.ValidationError.apply org.make.core.ValidationError.apply("email", "mandatory", scala.Some.apply[String]("not valid"))
62 34140 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 39032 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 32483 2232 - 2243 Literal <nosymbol> "mandatory"
62 45524 2245 - 2262 Apply scala.Some.apply scala.Some.apply[String]("not valid")
62 47139 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"))))
62 40622 2223 - 2230 Literal <nosymbol> "email"
63 35170 2303 - 2330 Apply scala.concurrent.Future.successful scala.concurrent.Future.successful[org.make.api.user.social.models.facebook.UserInfo](userInfo)
64 47646 2367 - 2383 Apply scala.concurrent.Future.failed scala.concurrent.Future.failed[Nothing](e)
70 45560 2537 - 2579 Apply grizzled.slf4j.Logger.debug DefaultFacebookApi.this.logger.debug(("Server answered ".+(response): String))
74 33282 2714 - 2722 Select scala.concurrent.duration.DurationConversions.second scala.concurrent.duration.`package`.DurationInt(2).second
74 38790 2713 - 2713 ApplyToImplicitArgs akka.stream.Materializer.matFromSystem stream.this.Materializer.matFromSystem(DefaultFacebookApiComponent.this.actorSystem)
74 46637 2713 - 2713 Select org.make.api.technical.ActorSystemComponent.actorSystem DefaultFacebookApiComponent.this.actorSystem
74 41167 2714 - 2715 Literal <nosymbol> 2
75 47687 2741 - 2786 Apply akka.util.ByteString.decodeString x$3.data.decodeString(java.nio.charset.Charset.forName("UTF-8"))
75 31181 2761 - 2785 Apply java.nio.charset.Charset.forName java.nio.charset.Charset.forName("UTF-8")
75 32275 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 39823 2740 - 2740 Select scala.concurrent.ExecutionContext.Implicits.global scala.concurrent.ExecutionContext.Implicits.global
78 45349 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 33030 2898 - 2898 Select scala.concurrent.ExecutionContext.Implicits.global scala.concurrent.ExecutionContext.Implicits.global
78 33326 2879 - 2879 Select org.make.api.technical.ActorSystemComponent.actorSystem DefaultFacebookApiComponent.this.actorSystem
78 47092 2879 - 2879 ApplyToImplicitArgs akka.stream.Materializer.matFromSystem stream.this.Materializer.matFromSystem(DefaultFacebookApiComponent.this.actorSystem)
78 45596 2880 - 2881 Literal <nosymbol> 2
78 42214 2880 - 2888 Select scala.concurrent.duration.DurationConversions.second scala.concurrent.duration.`package`.DurationInt(2).second
79 38823 2962 - 2986 Apply java.nio.charset.Charset.forName java.nio.charset.Charset.forName("UTF-8")
79 30667 2937 - 2987 Apply akka.util.ByteString.decodeString entity.data.decodeString(java.nio.charset.Charset.forName("UTF-8"))
80 40617 3000 - 3100 Apply scala.concurrent.Future.failed scala.concurrent.Future.failed[Nothing](SocialProviderException.apply(("Got unexpected response code: ".+(code).+(", with body: ").+(response): String)))
80 48738 3014 - 3099 Apply org.make.api.user.social.SocialProviderException.apply SocialProviderException.apply(("Got unexpected response code: ".+(code).+(", with body: ").+(response): String))