Skip to content

Commit

Permalink
TRELLO-2203: add social blade client
Browse files Browse the repository at this point in the history
  • Loading branch information
ssedoudbgouv committed Feb 17, 2024
1 parent 7b72f9b commit eda9ee7
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 18 deletions.
3 changes: 2 additions & 1 deletion app/config/ApplicationConfiguration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ case class ApplicationConfiguration(
amazonBucketName: String,
crypter: JcaCrypterSettings,
signer: JcaSignerSettings,
cookie: CookieAuthenticatorSettings
cookie: CookieAuthenticatorSettings,
socialBlade: SocialBladeClientConfiguration
)
7 changes: 7 additions & 0 deletions app/config/SocialBladeClientConfiguration.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package config

case class SocialBladeClientConfiguration(
url: String,
clientId: String,
token: String
)
5 changes: 1 addition & 4 deletions app/controllers/SocialNetworkController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ class SocialNetworkController(
def get(name: String, socialNetwork: String) = Action.async { _ =>
SocialNetworkSlug
.withNameInsensitiveOption(socialNetwork)
.traverse(socialNetworkSlug =>
influencerOrchestrator
.get(name, socialNetworkSlug)
)
.traverse(socialNetworkSlug => influencerOrchestrator.exist(name, socialNetworkSlug))
.map(_.getOrElse(false))
.map(result => Ok(Json.toJson(result)))

Expand Down
5 changes: 3 additions & 2 deletions app/loader/SignalConsoApplicationLoader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import config._
import models.report.ArborescenceNode
import orchestrators._
import orchestrators.socialmedia.InfluencerOrchestrator
import orchestrators.socialmedia.SocialBladeClient
import org.flywaydb.core.Flyway
import play.api._
import play.api.db.slick.DbName
Expand Down Expand Up @@ -385,8 +386,8 @@ class SignalConsoComponents(
companiesVisibilityOrchestrator,
messagesApi
)

val influencerOrchestrator = new InfluencerOrchestrator(influencerRepository)
val socialBladeClient = new SocialBladeClient(applicationConfiguration.socialBlade)
val influencerOrchestrator = new InfluencerOrchestrator(influencerRepository, socialBladeClient)

val reportsExtractActor: typed.ActorRef[ReportsExtractActor.ReportsExtractCommand] =
actorSystem.spawn(
Expand Down
22 changes: 19 additions & 3 deletions app/orchestrators/socialmedia/InfluencerOrchestrator.scala
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
package orchestrators.socialmedia

import models.report.SocialNetworkSlug
import models.report.socialnetwork.CertifiedInfluencer
import repositories.influencer.InfluencerRepositoryInterface

import java.util.UUID
import scala.concurrent.ExecutionContext
import scala.concurrent.Future

class InfluencerOrchestrator(
influencerRepository: InfluencerRepositoryInterface
influencerRepository: InfluencerRepositoryInterface,
socialBladeClient: SocialBladeClient
)(implicit
val executionContext: ExecutionContext
) {

def get(name: String, socialNetwork: SocialNetworkSlug): Future[Boolean] =
influencerRepository.get(name, socialNetwork).map(_.nonEmpty)
def exist(name: String, socialNetwork: SocialNetworkSlug): Future[Boolean] =
influencerRepository.get(name, socialNetwork).flatMap { signalConsoCertifiedInfluencers =>
if (signalConsoCertifiedInfluencers.nonEmpty) {
Future.successful(true)
} else {
socialBladeClient.checkSocialNetworkUsername(socialNetwork, name).flatMap { existsOnSocialBlade =>
if (existsOnSocialBlade) {
influencerRepository.create(CertifiedInfluencer(UUID.randomUUID(), socialNetwork, name)).map(_ => true)
} else {
Future.successful(false)
}
}
}
}

}
82 changes: 82 additions & 0 deletions app/orchestrators/socialmedia/SocialBladeClient.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package orchestrators.socialmedia

import config.SocialBladeClientConfiguration
import models.report.SocialNetworkSlug
import play.api.Logger
import play.api.libs.json.JsError
import play.api.libs.json.JsSuccess
import play.api.libs.json.Json
import sttp.client3.asynchttpclient.future.AsyncHttpClientFutureBackend
import sttp.client3.Identity
import sttp.client3.RequestT
import sttp.client3.Response
import sttp.client3.UriContext
import sttp.client3.asString
import sttp.client3.basicRequest
import utils.Logs.RichLogger

import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.util.Try

class SocialBladeClient(config: SocialBladeClientConfiguration)(implicit ec: ExecutionContext) {

val logger = Logger(this.getClass)
val backend = AsyncHttpClientFutureBackend()

def checkSocialNetworkUsername(
platform: SocialNetworkSlug,
username: String
): Future[Boolean] = {

val request: RequestT[Identity, Either[String, String], Any] = basicRequest
.get(uri"${config.url}/b/${platform.entryName.toLowerCase}/statistics")
.header("query", username.toLowerCase)
.header("clientid", config.clientId)
.header("token", config.token)
.header("history", "default")
.response(asString)

request.send(backend).map {
case Response(Right(body), statusCode, _, _, _, _) if statusCode.isSuccess =>
handleSuccessResponse(body, username)

case Response(Right(body), statusCode, _, _, _, _) =>
logger.errorWithTitle(
"socialblade_client_error",
s"Unexpected status code $statusCode calling Social blade : $body"
)
// Act as the username does not exist in social blade
false

case Response(Left(error), statusCode, _, _, _, _) =>
logger.errorWithTitle("socialblade_client_error", s"Error $statusCode calling Social blade : $error")
// Act as the username does not exist in social blade
false
}

}

private def handleSuccessResponse(body: String, username: String): Boolean =
Try(Json.parse(body))
.map { jsonBody =>
jsonBody.validate[SocialBladeResponse] match {
case JsSuccess(response, _) =>
response.data.id.username.equalsIgnoreCase(username)
case JsError(errors) =>
logger.errorWithTitle(
"socialblade_client_error",
s"Cannot parse json to SocialBladeResponse: ${jsonBody.toString}, errors : ${errors}"
)
// Act as the username does not exist in social blade
false
}
}
.recover { case exception: Exception =>
logger.errorWithTitle("socialblade_client_error", "Cannot parse SocialBladeResponse to json", exception)
// Act as the username does not exist in social blade
false
}
.getOrElse(false)

}
18 changes: 18 additions & 0 deletions app/orchestrators/socialmedia/SocialBladeResponse.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package orchestrators.socialmedia

import play.api.libs.json.Json
import play.api.libs.json.OFormat

case class SocialBladeStatus(success: Boolean, status: Int)

case class Id(username: String)

case class Data(id: Id)
case class SocialBladeResponse(status: SocialBladeStatus, data: Data)

object SocialBladeResponse {
implicit val SocialBladeStatusFormat: OFormat[SocialBladeStatus] = Json.format[SocialBladeStatus]
implicit val IdFormat: OFormat[Id] = Json.format[Id]
implicit val DataFormat: OFormat[Data] = Json.format[Data]
implicit val SocialBladeResponseFormat: OFormat[SocialBladeResponse] = Json.format[SocialBladeResponse]
}
3 changes: 2 additions & 1 deletion conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ include "common/auth.conf"
include "common/app.conf"
include "common/thread-pool.conf"
include "common/siret-extractor.conf"
include "common/gs1.conf"
include "common/gs1.conf"
include "common/socialblade.conf"
8 changes: 8 additions & 0 deletions conf/common/socialblade.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
social-blade {
url: "https://matrix.sbapis.com"
url: ${?SOCIALBLADE_URL}
client-id: ""
client-id: ${?SOCIALBLADE-CLIENT_ID}
token: ""
token: ${?SOCIALBLADE-TOKEN}
}
7 changes: 0 additions & 7 deletions test.http

This file was deleted.

3 changes: 3 additions & 0 deletions zip.http
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ GET localhost:9000/api/reports/files?reportId=d894fe80-935b-45f8-bfab-ea1c8933f5

###


GET localhost:9000/api/certified-influencer?name=orange&socialNetwork=Facebook

0 comments on commit eda9ee7

Please sign in to comment.