Skip to content

Commit

Permalink
Merge pull request #1541 from betagouv/master
Browse files Browse the repository at this point in the history
MEP
  • Loading branch information
ssedoudbgouv authored Feb 12, 2024
2 parents 88ad9dd + cdcaae5 commit 6f1e850
Show file tree
Hide file tree
Showing 15 changed files with 236 additions and 57 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ conf/local.application.conf
/.bsp
javaOptions.sbt
*.sc
!/test.http
!/zip.http
!/generatesql.sh
!/app/orchestrators/socialmedia/SocialBladeClient.scala
17 changes: 16 additions & 1 deletion app/controllers/ConstantController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package controllers
import authentication.Authenticator
import models.User
import models.report.ReportCategory
import models.report.ReportCategoryStatus
import play.api.Logger
import play.api.libs.json.Json
import play.api.mvc.ControllerComponents
Expand All @@ -21,7 +22,21 @@ class ConstantController(authenticator: Authenticator[User], controllerComponent
}

def getCategories = Action.async {
Future(Ok(Json.toJson(ReportCategory.values.filterNot(_.legacy))))
Future(Ok(Json.toJson(ReportCategory.values.filter(_.status != ReportCategoryStatus.Legacy))))
}

def getCategoriesByStatus() = Action.async {
val legacy = ReportCategory.values.filter(_.status == ReportCategoryStatus.Legacy)
val closed = ReportCategory.values.filter(_.status == ReportCategoryStatus.Closed)
val inactive = ReportCategory.values.filter(_.status == ReportCategoryStatus.Inactive)
val active = ReportCategory.values.filter(_.status == ReportCategoryStatus.Active)
val json = Json.obj(
"active" -> active,
"inactive" -> inactive,
"legacy" -> legacy,
"closed" -> closed
)
Future(Ok(json))
}

}
35 changes: 35 additions & 0 deletions app/controllers/SocialNetworkController.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package controllers

import authentication.Authenticator
import cats.implicits.toTraverseOps
import models.User
import models.report.SocialNetworkSlug
import orchestrators.socialmedia.InfluencerOrchestrator
import play.api.Logger
import play.api.libs.json.Json
import play.api.mvc.ControllerComponents

import scala.concurrent.ExecutionContext

class SocialNetworkController(
influencerOrchestrator: InfluencerOrchestrator,
authenticator: Authenticator[User],
controllerComponents: ControllerComponents
)(implicit
val ec: ExecutionContext
) extends BaseController(authenticator, controllerComponents) {
val logger: Logger = Logger(this.getClass)

def get(name: String, socialNetwork: String) = Action.async { _ =>
SocialNetworkSlug
.withNameInsensitiveOption(socialNetwork)
.traverse(socialNetworkSlug =>
influencerOrchestrator
.get(name, socialNetworkSlug)
)
.map(_.getOrElse(false))
.map(result => Ok(Json.toJson(result)))

}

}
10 changes: 5 additions & 5 deletions app/controllers/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ package object controllers {
implicit val IdentificationStatusQueryStringBindable: QueryStringBindable[IdentificationStatus] =
QueryStringBindable.bindableString
.transform[IdentificationStatus](
identificationStatus => IdentificationStatus.withName(identificationStatus),
identificationStatus => IdentificationStatus.withNameInsensitive(identificationStatus),
identificationStatus => identificationStatus.entryName
)

Expand Down Expand Up @@ -73,28 +73,28 @@ package object controllers {
implicit val ReportResponseTypeQueryStringBindable: QueryStringBindable[ReportResponseType] =
QueryStringBindable.bindableString
.transform[ReportResponseType](
reportResponseType => ReportResponseType.withName(reportResponseType),
reportResponseType => ReportResponseType.withNameInsensitive(reportResponseType),
reportResponseType => reportResponseType.entryName
)

implicit val ReportFileOriginQueryStringBindable: QueryStringBindable[ReportFileOrigin] =
QueryStringBindable.bindableString
.transform[ReportFileOrigin](
reportFileOrigin => ReportFileOrigin.withName(reportFileOrigin),
reportFileOrigin => ReportFileOrigin.withNameInsensitive(reportFileOrigin),
reportFileOrigin => reportFileOrigin.entryName
)

implicit val ReportAdminActionTypeQueryStringBindable: QueryStringBindable[ReportAdminActionType] =
QueryStringBindable.bindableString
.transform[ReportAdminActionType](
reportAdminActionType => ReportAdminActionType.withName(reportAdminActionType),
reportAdminActionType => ReportAdminActionType.withNameInsensitive(reportAdminActionType),
reportAdminActionType => reportAdminActionType.entryName
)

implicit val PublicStatQueryStringBindable: QueryStringBindable[PublicStat] =
QueryStringBindable.bindableString
.transform[PublicStat](
publicStat => PublicStat.withName(publicStat),
publicStat => PublicStat.withNameInsensitive(publicStat),
publicStat => publicStat.entryName
)

Expand Down
20 changes: 11 additions & 9 deletions app/loader/SignalConsoApplicationLoader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,13 @@ import actors._
import akka.actor.typed
import akka.actor.typed.scaladsl.adapter.ClassicActorSystemOps
import akka.util.Timeout
import authentication.APIKeyAuthenticator
import authentication.BCryptPasswordHasher
import authentication.CookieAuthenticator
import authentication.CredentialsProvider
import authentication.FingerprintGenerator
import authentication.JcaCrypter
import authentication.JcaSigner
import authentication.PasswordHasherRegistry
import authentication._
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import config._
import models.report.ArborescenceNode
import orchestrators._
import orchestrators.socialmedia.InfluencerOrchestrator
import org.flywaydb.core.Flyway
import play.api._
import play.api.db.slick.DbName
Expand All @@ -45,6 +39,7 @@ import repositories.authattempt.AuthAttemptRepository
import repositories.authattempt.AuthAttemptRepositoryInterface
import repositories.authtoken.AuthTokenRepository
import repositories.authtoken.AuthTokenRepositoryInterface
import repositories.barcode.BarcodeProductRepository
import repositories.blacklistedemails.BlacklistedEmailsRepository
import repositories.blacklistedemails.BlacklistedEmailsRepositoryInterface
import repositories.company.CompanyRepository
Expand All @@ -63,7 +58,8 @@ import repositories.emailvalidation.EmailValidationRepository
import repositories.emailvalidation.EmailValidationRepositoryInterface
import repositories.event.EventRepository
import repositories.event.EventRepositoryInterface
import repositories.barcode.BarcodeProductRepository
import repositories.influencer.InfluencerRepository
import repositories.influencer.InfluencerRepositoryInterface
import repositories.probe.ProbeRepository
import repositories.rating.RatingRepository
import repositories.rating.RatingRepositoryInterface
Expand Down Expand Up @@ -200,6 +196,7 @@ class SignalConsoComponents(

def eventRepository: EventRepositoryInterface = new EventRepository(dbConfig)
val ratingRepository: RatingRepositoryInterface = new RatingRepository(dbConfig)
val influencerRepository: InfluencerRepositoryInterface = new InfluencerRepository(dbConfig)
def reportRepository: ReportRepositoryInterface = new ReportRepository(dbConfig)
val reportMetadataRepository: ReportMetadataRepositoryInterface = new ReportMetadataRepository(dbConfig)
val reportNotificationBlockedRepository: ReportNotificationBlockedRepositoryInterface =
Expand Down Expand Up @@ -389,6 +386,8 @@ class SignalConsoComponents(
messagesApi
)

val influencerOrchestrator = new InfluencerOrchestrator(influencerRepository)

val reportsExtractActor: typed.ActorRef[ReportsExtractActor.ReportsExtractCommand] =
actorSystem.spawn(
ReportsExtractActor.create(
Expand Down Expand Up @@ -540,6 +539,8 @@ class SignalConsoComponents(
controllerComponents
)

val socialNetworkController =
new SocialNetworkController(influencerOrchestrator, cookieAuthenticator, controllerComponents)
val asyncFileController =
new AsyncFileController(asyncFileRepository, s3Service, cookieAuthenticator, controllerComponents)

Expand Down Expand Up @@ -741,6 +742,7 @@ class SignalConsoComponents(
adminController,
asyncFileController,
constantController,
socialNetworkController,
mobileAppController,
authController,
accountController,
Expand Down
59 changes: 37 additions & 22 deletions app/models/report/ReportCategory.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,46 @@ import controllers.error.AppError.MalformedValue
import enumeratum.EnumEntry
import enumeratum.PlayEnum

sealed abstract class ReportCategory(val label: String, val legacy: Boolean = false) extends EnumEntry
sealed trait ReportCategoryStatus
object ReportCategoryStatus {
// Modified / splitted / migrated, should never come back
case object Legacy extends ReportCategoryStatus
// Old category, not available and no investigation, but still 'valid'
case object Closed extends ReportCategoryStatus
// Category not available in the website, but an investigation could still happen (still searchable)
case object Inactive extends ReportCategoryStatus
case object Active extends ReportCategoryStatus
}

sealed abstract class ReportCategory(val label: String, val status: ReportCategoryStatus = ReportCategoryStatus.Active)
extends EnumEntry

object ReportCategory extends PlayEnum[ReportCategory] {

case object RetraitRappelSpecifique extends ReportCategory("Retrait-Rappel spécifique")
case object Coronavirus extends ReportCategory("COVID-19 (coronavirus)")
case object CafeRestaurant extends ReportCategory("Café / Restaurant")
case object AchatMagasinLegacy extends ReportCategory("Achat / Magasin", legacy = true)
case object AchatMagasinInternet extends ReportCategory("Achat (Magasin ou Internet)", legacy = true)
case object AchatMagasin extends ReportCategory("Achat en Magasin")
case object AchatInternet extends ReportCategory("Achat sur internet")
case object ServicesAuxParticuliers extends ReportCategory("Services aux particuliers")
case object TelEauGazElec extends ReportCategory("Téléphonie / Eau-Gaz-Electricité", legacy = true)
case object EauGazElectricite extends ReportCategory("Eau / Gaz / Electricité")
case object TelephonieFaiMedias extends ReportCategory("Téléphonie / Fournisseur d'accès internet / médias")
case object BanqueAssuranceMutuelle extends ReportCategory("Banque / Assurance / Mutuelle")
case object IntoxicationAlimentaire extends ReportCategory("Intoxication alimentaire")
case object ProduitsObjets extends ReportCategory("Produits / Objets", legacy = true)
case object Internet extends ReportCategory("Internet (hors achats)")
case object TravauxRenovations extends ReportCategory("Travaux / Rénovation")
case object VoyageLoisirs extends ReportCategory("Voyage / Loisirs")
case object Immobilier extends ReportCategory("Immobilier")
case object Sante extends ReportCategory("Secteur de la santé")
case object VoitureVehicule extends ReportCategory("Voiture / Véhicule", legacy = true)
case object Animaux extends ReportCategory("Animaux")
case object RetraitRappelSpecifique
extends ReportCategory("Retrait-Rappel spécifique", status = ReportCategoryStatus.Closed)
case object Coronavirus extends ReportCategory("COVID-19 (coronavirus)", status = ReportCategoryStatus.Closed)
case object CafeRestaurant extends ReportCategory("Café / Restaurant")
case object AchatMagasinLegacy extends ReportCategory("Achat / Magasin", status = ReportCategoryStatus.Legacy)
case object AchatMagasinInternet
extends ReportCategory("Achat (Magasin ou Internet)", status = ReportCategoryStatus.Legacy)
case object AchatMagasin extends ReportCategory("Achat en Magasin")
case object AchatInternet extends ReportCategory("Achat sur internet")
case object ServicesAuxParticuliers extends ReportCategory("Services aux particuliers")
case object TelEauGazElec
extends ReportCategory("Téléphonie / Eau-Gaz-Electricité", status = ReportCategoryStatus.Legacy)
case object EauGazElectricite extends ReportCategory("Eau / Gaz / Electricité")
case object TelephonieFaiMedias extends ReportCategory("Téléphonie / Fournisseur d'accès internet / médias")
case object BanqueAssuranceMutuelle extends ReportCategory("Banque / Assurance / Mutuelle")
case object IntoxicationAlimentaire extends ReportCategory("Intoxication alimentaire")
case object ProduitsObjets extends ReportCategory("Produits / Objets", status = ReportCategoryStatus.Legacy)
case object Internet extends ReportCategory("Internet (hors achats)")
case object TravauxRenovations extends ReportCategory("Travaux / Rénovation")
case object VoyageLoisirs extends ReportCategory("Voyage / Loisirs")
case object Immobilier extends ReportCategory("Immobilier")
case object Sante extends ReportCategory("Secteur de la santé")
case object VoitureVehicule extends ReportCategory("Voiture / Véhicule", status = ReportCategoryStatus.Legacy)
case object Animaux extends ReportCategory("Animaux")
case object DemarchesAdministratives extends ReportCategory("Démarches administratives")
case object VoitureVehiculeVelo extends ReportCategory("Voiture / Véhicule / Vélo")
case object DemarchageAbusif extends ReportCategory("Démarchage abusif")
Expand Down
13 changes: 13 additions & 0 deletions app/models/report/socialnetwork/CertifiedInfluencer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package models.report.socialnetwork

import models.report.SocialNetworkSlug
import play.api.libs.json.Json
import play.api.libs.json.OFormat

import java.util.UUID

case class CertifiedInfluencer(id: UUID, socialNetwork: SocialNetworkSlug, name: String)

object CertifiedInfluencer {
implicit val format: OFormat[CertifiedInfluencer] = Json.format[CertifiedInfluencer]
}
17 changes: 17 additions & 0 deletions app/orchestrators/socialmedia/InfluencerOrchestrator.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package orchestrators.socialmedia

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

import scala.concurrent.ExecutionContext
import scala.concurrent.Future

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

def get(name: String, socialNetwork: SocialNetworkSlug): Future[Boolean] =
influencerRepository.get(name, socialNetwork).map(_.nonEmpty)
}
31 changes: 31 additions & 0 deletions app/repositories/influencer/InfluencerRepository.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package repositories.influencer

import models.report.SocialNetworkSlug
import models.report.socialnetwork.CertifiedInfluencer
import play.api.Logger
import repositories.CRUDRepository
import repositories.PostgresProfile.api._
import slick.basic.DatabaseConfig
import slick.jdbc.JdbcProfile
import slick.lifted.TableQuery

import scala.concurrent.ExecutionContext
class InfluencerRepository(override val dbConfig: DatabaseConfig[JdbcProfile])(implicit
override val ec: ExecutionContext
) extends CRUDRepository[InfluencerTable, CertifiedInfluencer]
with InfluencerRepositoryInterface {

val logger: Logger = Logger(this.getClass)
val table: TableQuery[InfluencerTable] = InfluencerTable.table
import dbConfig._

override def get(name: String, socialNetwork: SocialNetworkSlug) = db.run(
table
.filter { result =>
result.name.toLowerCase === (name.toLowerCase: String)
}
.filter(_.socialNetwork === (socialNetwork: SocialNetworkSlug))
.result
)

}
11 changes: 11 additions & 0 deletions app/repositories/influencer/InfluencerRepositoryInterface.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package repositories.influencer

import models.report.SocialNetworkSlug
import models.report.socialnetwork.CertifiedInfluencer
import repositories.CRUDRepositoryInterface

import scala.concurrent.Future

trait InfluencerRepositoryInterface extends CRUDRepositoryInterface[CertifiedInfluencer] {
def get(name: String, socialNetwork: SocialNetworkSlug): Future[Seq[CertifiedInfluencer]]
}
20 changes: 20 additions & 0 deletions app/repositories/influencer/InfluencerTable.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package repositories.influencer

import models.report.SocialNetworkSlug
import models.report.socialnetwork.CertifiedInfluencer
import repositories.DatabaseTable
import repositories.PostgresProfile.api._
import slick.ast.TypedType

class InfluencerTable(tag: Tag)(implicit tt: TypedType[SocialNetworkSlug])
extends DatabaseTable[CertifiedInfluencer](tag, "influencers") {
def socialNetwork = column[SocialNetworkSlug]("social_network")

def name = column[String]("name")

override def * = (id, socialNetwork, name) <> ((CertifiedInfluencer.apply _).tupled, CertifiedInfluencer.unapply)
}

object InfluencerTable {
val table = TableQuery[InfluencerTable]
}
5 changes: 5 additions & 0 deletions conf/db/migration/default/V20__influencer_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS influencers (
id UUID NOT NULL PRIMARY KEY,
name VARCHAR NOT NULL,
social_network VARCHAR NOT NULL
);
2 changes: 2 additions & 0 deletions conf/routes
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ GET /api/async-files controll
# Constants API
GET /api/constants/countries controllers.ConstantController.getCountries()
GET /api/constants/categories controllers.ConstantController.getCategories()
GET /api/certified-influencer controllers.SocialNetworkController.get(name:String, socialNetwork: String)
GET /api/constants/categoriesByStatus controllers.ConstantController.getCategoriesByStatus()

# Mobile app specific API
GET /api/mobileapp/requirements controllers.MobileAppController.getRequirements()
Expand Down
Loading

0 comments on commit 6f1e850

Please sign in to comment.