Skip to content

Commit 2afb593

Browse files
authored
Merge pull request #74 from Hub-of-all-Things/dev
Release v2.6.6
2 parents fdb28dc + b47ed0f commit 2afb593

16 files changed

+489
-14
lines changed

hat/app/org/hatdex/hat/api/controllers/SystemStatus.scala

+33-6
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,19 @@ import org.hatdex.hat.api.service.{ SystemStatusService, UsersService }
3333
import org.hatdex.hat.authentication.{ ContainsApplicationRole, HatApiAuthEnvironment, HatApiController, WithRole }
3434
import org.hatdex.hat.resourceManagement._
3535
import org.ocpsoft.prettytime.PrettyTime
36-
import play.api.Logger
37-
import play.api.cache.Cached
36+
import play.api.{ Configuration, Logger }
37+
import play.api.cache.{ AsyncCacheApi, Cached }
3838
import play.api.libs.json._
3939
import play.api.mvc._
4040

41-
import scala.concurrent.{ ExecutionContext }
41+
import scala.concurrent.{ ExecutionContext, Future }
42+
import scala.util.{ Failure, Success }
4243

4344
class SystemStatus @Inject() (
4445
components: ControllerComponents,
4546
cached: Cached,
47+
cache: AsyncCacheApi,
48+
configuration: Configuration,
4649
silhouette: Silhouette[HatApiAuthEnvironment],
4750
systemStatusService: SystemStatusService,
4851
usersService: UsersService,
@@ -51,6 +54,10 @@ class SystemStatus @Inject() (
5154
val ec: ExecutionContext,
5255
val applicationsService: ApplicationsService) extends HatApiController(components, silhouette) with HatJsonFormats {
5356

57+
private val dbStorageAllowance: Long = configuration.get[Long]("resourceManagement.hatDBStorageAllowance")
58+
private val fileStorageAllowance: Long = configuration.get[Long]("resourceManagement.hatFileStorageAllowance")
59+
private val hatSharedSecret: String = configuration.get[String]("resourceManagement.hatSharedSecret")
60+
5461
private val logger = Logger(this.getClass)
5562

5663
private val indefiniteSuccessCaching = cached
@@ -68,9 +75,6 @@ class SystemStatus @Inject() (
6875

6976
def status(): Action[AnyContent] =
7077
SecuredAction(WithRole(Owner(), Platform()) || ContainsApplicationRole(Owner(), Platform())).async { implicit request =>
71-
val dbStorageAllowance = Math.pow(1000, 3).toLong
72-
val fileStorageAllowance = Math.pow(1000, 3).toLong
73-
7478
val eventualStatus = for {
7579
dbsize <- systemStatusService.tableSizeTotal
7680
fileSize <- systemStatusService.fileStorageTotal
@@ -100,5 +104,28 @@ class SystemStatus @Inject() (
100104
Ok(Json.toJson(stats))
101105
}
102106
}
107+
108+
def destroyCache: Action[AnyContent] = UserAwareAction.async { implicit request =>
109+
request.headers.get("X-Auth-Token") match {
110+
case Some(authToken) if authToken == hatSharedSecret =>
111+
val response = Ok(Json.toJson(SuccessResponse("beforeDestroy DONE")))
112+
113+
val hatAddress = request.dynamicEnvironment.domain
114+
val clearApps = cache.remove(s"apps:$hatAddress")
115+
val clearConfiguration = cache.remove(s"configuration:$hatAddress")
116+
val clearServer = cache.remove(s"server:$hatAddress")
117+
118+
// We don't care if the cache is successfully cleared. We just go ahead and return successful.
119+
Future.sequence(Seq(clearApps, clearConfiguration, clearServer)).onComplete {
120+
case Success(_) => logger.info(s"BEFORE DESTROY: $hatAddress DONE")
121+
case Failure(exception) => logger.error(s"BEFORE DESTROY: Could not clear cache of $hatAddress. Reason: ${exception.getMessage}")
122+
}
123+
Future.successful(response)
124+
125+
case Some(_) => Future.successful(Forbidden(Json.toJson(ErrorMessage("Invalid token", s"Not a valid token provided"))))
126+
127+
case None => Future.successful(Forbidden(Json.toJson(ErrorMessage("Credentials missing", s"Credentials required"))))
128+
}
129+
}
103130
}
104131

hat/app/org/hatdex/hat/phata/assets/js/main.bundle.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Binary file not shown.
Binary file not shown.
Binary file not shown.

hat/app/org/hatdex/hat/phata/assets/webfonts/outlined/Icon

Whitespace-only changes.

hat/app/org/hatdex/hat/phata/views/mails/emailHatClaim.scala.html

+1-2
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright (C) 2019 HAT Data Exchange Ltd
3+
* SPDX-License-Identifier: AGPL-3.0
4+
*
5+
* This file is part of the Hub of All Things project (HAT).
6+
*
7+
* HAT is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU Affero General Public License
9+
* as published by the Free Software Foundation, version 3 of
10+
* the License.
11+
*
12+
* HAT is distributed in the hope that it will be useful, but
13+
* WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
15+
* the GNU Affero General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General
18+
* Public License along with this program. If not, see
19+
* <http://www.gnu.org/licenses/>.
20+
*
21+
* Written by Marios Tsekis <marios.tsekis@hatdex.org>
22+
* 2 / 2019
23+
*/
24+
package org.hatdex.hat.she.controllers
25+
26+
import com.mohiva.play.silhouette.api.Silhouette
27+
import javax.inject.Inject
28+
import org.hatdex.hat.api.json.RichDataJsonFormats
29+
import org.hatdex.hat.api.models.Owner
30+
import org.hatdex.hat.api.service.applications.ApplicationsService
31+
import org.hatdex.hat.authentication.{ ContainsApplicationRole, HatApiAuthEnvironment, HatApiController, WithRole }
32+
import org.hatdex.hat.she.models.FunctionConfigurationJsonProtocol
33+
import org.hatdex.hat.she.service.StaticDataGeneratorService
34+
import play.api.libs.json.Json
35+
import play.api.mvc._
36+
37+
import scala.concurrent.ExecutionContext
38+
39+
class StaticDataGenerator @Inject() (
40+
components: ControllerComponents,
41+
silhouette: Silhouette[HatApiAuthEnvironment],
42+
feedGeneratorService: StaticDataGeneratorService)(
43+
implicit
44+
val ec: ExecutionContext,
45+
applicationsService: ApplicationsService)
46+
extends HatApiController(components, silhouette)
47+
with RichDataJsonFormats
48+
with FunctionConfigurationJsonProtocol {
49+
50+
def getStaticData(endpoint: String): Action[AnyContent] = {
51+
SecuredAction(WithRole(Owner()) || ContainsApplicationRole(Owner())).async { implicit request
52+
feedGeneratorService.getStaticData(endpoint)
53+
.map(items Ok(Json.toJson(items)))
54+
}
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (C) 2019 HAT Data Exchange Ltd
3+
* SPDX-License-Identifier: AGPL-3.0
4+
*
5+
* This file is part of the Hub of All Things project (HAT).
6+
*
7+
* HAT is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU Affero General Public License
9+
* as published by the Free Software Foundation, version 3 of
10+
* the License.
11+
*
12+
* HAT is distributed in the hope that it will be useful, but
13+
* WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
15+
* the GNU Affero General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General
18+
* Public License along with this program. If not, see
19+
* <http://www.gnu.org/licenses/>.
20+
*
21+
* Written by Marios Tsekis <marios.tsekis@hatdex.org>
22+
* 2 / 2019
23+
*/
24+
package org.hatdex.hat.she.models
25+
26+
import play.api.libs.json.{ JsValue, Json, Writes }
27+
28+
case class StaticDataValues(name: String, values: Map[String, JsValue]) {
29+
30+
}
31+
32+
object StaticDataValues {
33+
implicit val facebookLocationReads: Writes[StaticDataValues] = Json.writes[StaticDataValues]
34+
}

hat/app/org/hatdex/hat/she/service/DataEndpointMapper.scala

+59-3
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ class InsightsMapper extends DataEndpointMapper {
227227
"facebook/feed" "Posts composed",
228228
"notables/feed" "Notes taken",
229229
"spotify/feed" "Songs listened to",
230+
"instagram/feed" "Photos uploaded",
231+
"fitbit/weight" -> "Fitbit weight records",
232+
"fitbit/sleep" -> "Fitbit sleep records",
233+
"fitbit/activity" -> "Fitbit activities logged",
230234
"calendar/google/events" "Calendar events recorded",
231235
"monzo/transactions" "Transactions performed",
232236
"she/insights/emotions" -> "Posts analysed for Sentiments",
@@ -239,6 +243,10 @@ class InsightsMapper extends DataEndpointMapper {
239243
"facebook/feed" "facebook",
240244
"notables/feed" "notables",
241245
"spotify/feed" "spotify",
246+
"instagram/feed" "instagram",
247+
"fitbit/weight" -> "fitbit-weight",
248+
"fitbit/sleep" -> "fitbit-sleep",
249+
"fitbit/activity" -> "fitbit-activity",
242250
"calendar/google/events" "google",
243251
"monzo/transactions" "monzo",
244252
"she/insights/emotions" -> "sentiment",
@@ -622,8 +630,8 @@ class FacebookProfileMapper extends DataEndpointMapper with FeedItemComparator {
622630
def dataQueries(fromDate: Option[DateTime], untilDate: Option[DateTime]): Seq[PropertyQuery] = {
623631
Seq(PropertyQuery(
624632
List(
625-
EndpointQuery("facebook/profile", None, dateFilter(fromDate, untilDate).map(f Seq(EndpointQueryFilter("updated_time", None, f))), None)),
626-
Some("updated_time"), Some("descending"), None))
633+
EndpointQuery("facebook/profile", None, dateFilter(fromDate, untilDate).map(f Seq(EndpointQueryFilter("hat_updated_time", None, f))), None)),
634+
Some("hat_updated_time"), Some("descending"), None))
627635
}
628636

629637
def mapDataRecord(recordId: UUID, content: JsValue, tailRecordId: Option[UUID] = None, tailContent: Option[JsValue] = None): Try[DataFeedItem] = {
@@ -640,7 +648,7 @@ class FacebookProfileMapper extends DataEndpointMapper with FeedItemComparator {
640648
Some(contentText), None, None, None))
641649
}
642650
} yield {
643-
DataFeedItem("facebook", (tailContent.getOrElse(content) \ "updated_time").as[DateTime], Seq("profile"),
651+
DataFeedItem("facebook", (tailContent.getOrElse(content) \ "hat_updated_time").as[DateTime], Seq("profile"),
644652
Some(title), Some(itemContent), None)
645653
}
646654
}
@@ -760,6 +768,54 @@ class FacebookFeedMapper extends DataEndpointMapper {
760768
}
761769
}
762770

771+
class FacebookPagesLikesMapper extends DataEndpointMapper {
772+
def dataQueries(fromDate: Option[DateTime], untilDate: Option[DateTime]): Seq[PropertyQuery] = {
773+
Seq(PropertyQuery(
774+
List(
775+
EndpointQuery("facebook/likes/pages", None, dateFilter(fromDate, untilDate).map(f Seq(EndpointQueryFilter("created_time", None, f))), None)),
776+
Some("created_time"), Some("descending"), None))
777+
}
778+
779+
def mapDataRecord(recordId: UUID, content: JsValue, tailRecordId: Option[UUID] = None, tailContent: Option[JsValue] = None): Try[DataFeedItem] = {
780+
for {
781+
name <- Try((content \ "name").as[String])
782+
title <- Try(DataFeedItemTitle(s"You liked $name", None, None))
783+
784+
itemContent Try(DataFeedItemContent(
785+
Some(
786+
s"""Page Name - ${name}
787+
|
788+
|Location - ${(content \ "location" \ "city").asOpt[String].getOrElse("")}
789+
|Website - ${(content \ "website").asOpt[String].getOrElse("")}""".stripMargin.trim), None, None, None))
790+
date Try((content \ "created_time").as[DateTime])
791+
tags Try(Seq("page", name))
792+
} yield {
793+
794+
val locationGeo = Try(LocationGeo(
795+
(content \ "location" \ "longitude").as[Double],
796+
(content \ "location" \ "latitude").as[Double])).toOption
797+
798+
val locationAddress = Try(LocationAddress(
799+
(content \ "location" \ "country").asOpt[String],
800+
(content \ "location" \ "city").asOpt[String],
801+
(content \ "name").asOpt[String],
802+
(content \ "location" \ "street").asOpt[String],
803+
(content \ "location" \ "zip").asOpt[String])).toOption
804+
805+
val maybeLocation = if (locationAddress.contains(LocationAddress(None, None, None, None, None))) {
806+
None
807+
}
808+
else {
809+
locationAddress
810+
}
811+
812+
val location = locationGeo.orElse(maybeLocation).map(_ DataFeedItemLocation(locationGeo, maybeLocation, None))
813+
814+
DataFeedItem("facebook", date, tags, Some(title), Some(itemContent), location)
815+
}
816+
}
817+
}
818+
763819
class NotablesFeedMapper extends DataEndpointMapper {
764820
def dataQueries(fromDate: Option[DateTime], untilDate: Option[DateTime]): Seq[PropertyQuery] = {
765821
Seq(PropertyQuery(

hat/app/org/hatdex/hat/she/service/FeedGeneratorService.scala

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class FeedGeneratorService @Inject() ()(
5454
private val dataMappers: Seq[(String, DataEndpointMapper)] = Seq(
5555
"facebook/profile" -> new FacebookProfileMapper(),
5656
"facebook/feed" new FacebookFeedMapper(),
57+
"facebook/likes/pages" new FacebookPagesLikesMapper(),
5758
"facebook/events" new FacebookEventMapper(),
5859
"twitter/profile" new TwitterProfileMapper(),
5960
"twitter/tweets" new TwitterFeedMapper(),

0 commit comments

Comments
 (0)