Skip to content

Commit e245653

Browse files
authored
Merge pull request #54 from Hub-of-all-Things/dev
Fitbit Static Update fix
2 parents 74eaa59 + d35f2a9 commit e245653

File tree

3 files changed

+103
-9
lines changed

3 files changed

+103
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (C) 2017 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 Andrius Aucinas <andrius.aucinas@hatdex.org>
22+
* 7 / 2018
23+
*/
24+
25+
package org.hatdex.hat.api.controllers
26+
27+
import com.mohiva.play.silhouette.api.Silhouette
28+
import javax.inject.Inject
29+
import org.hatdex.hat.api.json.ApplicationJsonProtocol
30+
import org.hatdex.hat.api.models.applications.HatApplication
31+
import org.hatdex.hat.api.models.{ ApplicationManage, ErrorMessage, Owner }
32+
import org.hatdex.hat.api.service.RemoteExecutionContext
33+
import org.hatdex.hat.api.service.applications.ApplicationsService
34+
import org.hatdex.hat.authentication.{ ContainsApplicationRole, HatApiAuthEnvironment, HatApiController, WithRole }
35+
import play.api.Logger
36+
import play.api.http.HttpEntity
37+
import play.api.libs.json.Json
38+
import play.api.libs.ws.WSClient
39+
import play.api.mvc.{ Action, AnyContent, ControllerComponents }
40+
41+
import scala.concurrent.Future
42+
43+
class ApplicationRequestProxy @Inject() (
44+
components: ControllerComponents,
45+
silhouette: Silhouette[HatApiAuthEnvironment],
46+
wsClient: WSClient)(
47+
implicit
48+
val ec: RemoteExecutionContext,
49+
applicationsService: ApplicationsService)
50+
extends HatApiController(components, silhouette) with ApplicationJsonProtocol {
51+
52+
import org.hatdex.hat.api.json.HatJsonFormats.errorMessage
53+
54+
val logger = Logger(this.getClass)
55+
56+
def proxyRequest(id: String, path: String, method: String = "GET"): Action[AnyContent] = SecuredAction(ContainsApplicationRole(Owner(), ApplicationManage(id)) || WithRole(Owner())).async { implicit request =>
57+
logger.info(s"Proxy $method request for $id to $path with parameters: ${request.queryString}")
58+
applicationsService.applicationStatus(id).flatMap { maybeStatus
59+
maybeStatus map {
60+
case HatApplication(app, _, true, _, _, _)
61+
62+
applicationsService.applicationToken(request.identity, app)
63+
.flatMap { token
64+
val baseRequest = wsClient.url(s"${app.kind.url}/$path")
65+
.withHttpHeaders("x-auth-token" token.accessToken)
66+
.addQueryStringParameters(request.queryString.map(p (p._1, p._2.head)).toSeq: _*)
67+
.withMethod(method)
68+
69+
request.body.asJson.fold(baseRequest)(b baseRequest.withBody(b))
70+
.stream()
71+
.map(r new Status(r.status).sendEntity(HttpEntity.Strict(r.bodyAsBytes, Some("application/json"))))
72+
}
73+
74+
case _ Future.successful(BadRequest(Json.toJson(ErrorMessage(
75+
"Application not active",
76+
s"Application $id does not appear to be activated by the user"))))
77+
} getOrElse {
78+
Future.successful(NotFound(Json.toJson(ErrorMessage(
79+
"Application not Found",
80+
s"Application $id does not appear to be a valid application registered with the DEX"))))
81+
}
82+
}
83+
}
84+
}

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

+11-9
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,8 @@ class FitbitProfileMapper extends DataEndpointMapper with FeedItemComparator {
478478
def dataQueries(fromDate: Option[DateTime], untilDate: Option[DateTime]): Seq[PropertyQuery] = {
479479
Seq(PropertyQuery(
480480
List(
481-
EndpointQuery("fitbit/profile", None, dateFilter(fromDate, untilDate).map(f Seq(EndpointQueryFilter("updated_time", None, f))), None)),
482-
Some("updated_time"), Some("descending"), None))
481+
EndpointQuery("fitbit/profile", None, dateFilter(fromDate, untilDate).map(f Seq(EndpointQueryFilter("dateCreated", None, f))), None)),
482+
Some("dateCreated"), Some("descending"), None))
483483
}
484484

485485
def mapDataRecord(recordId: UUID, content: JsValue, tailRecordId: Option[UUID] = None, tailContent: Option[JsValue] = None): Try[DataFeedItem] = {
@@ -512,14 +512,16 @@ class FitbitProfileMapper extends DataEndpointMapper with FeedItemComparator {
512512
if (tailContent.isEmpty)
513513
Seq()
514514
else {
515+
// Note: we are comparing in descending order. Reverse content <-> tailContent if ascending
515516
Seq(
516-
compareString(content, tailContent.get, "fullName", "Name"),
517-
compareString(content, tailContent.get, "displayName", "Display Name"),
518-
compareString(content, tailContent.get, "gender", "Gender"),
519-
compareInt(content, tailContent.get, "age", "Age"),
520-
compareInt(content, tailContent.get, "height", "Height"),
521-
compareFloat(content, tailContent.get, "weight", "Weight"),
522-
compareString(content, tailContent.get, "country", "Country"))
517+
compareString(tailContent.get, content, "fullName", "Name"),
518+
compareString(tailContent.get, content, "displayName", "Display Name"),
519+
compareString(tailContent.get, content, "gender", "Gender"),
520+
compareInt(tailContent.get, content, "age", "Age"),
521+
compareInt(tailContent.get, content, "height", "Height"),
522+
compareFloat(tailContent.get, content, "weight", "Weight"),
523+
compareString(tailContent.get, content, "country", "Country"),
524+
compareString(tailContent.get, content, "timezone", "Time Zone"))
523525
}
524526
}
525527
}

hat/conf/v26.routes

+8
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ GET /applications/:application/setup
5353
GET /applications/:application/disable org.hatdex.hat.api.controllers.Applications.applicationDisable(application)
5454
GET /applications/:application/access-token org.hatdex.hat.api.controllers.Applications.applicationToken(application)
5555

56+
GET /applications/:application/proxy/*path org.hatdex.hat.api.controllers.ApplicationRequestProxy.proxyRequest(application, path, method: String = "GET")
57+
POST /applications/:application/proxy/*path org.hatdex.hat.api.controllers.ApplicationRequestProxy.proxyRequest(application, path, method: String = "POST")
58+
PUT /applications/:application/proxy/*path org.hatdex.hat.api.controllers.ApplicationRequestProxy.proxyRequest(application, path, method: String = "PUT")
59+
PATCH /applications/:application/proxy/*path org.hatdex.hat.api.controllers.ApplicationRequestProxy.proxyRequest(application, path, method: String = "PATCH")
60+
HEAD /applications/:application/proxy/*path org.hatdex.hat.api.controllers.ApplicationRequestProxy.proxyRequest(application, path, method: String = "HEAD")
61+
DELETE /applications/:application/proxy/*path org.hatdex.hat.api.controllers.ApplicationRequestProxy.proxyRequest(application, path, method: String = "DELETE")
62+
OPTIONS /applications/:application/proxy/*path org.hatdex.hat.api.controllers.ApplicationRequestProxy.proxyRequest(application, path, method: String = "OPTIONS")
63+
5664
# Smart HAT Engine (SHE) routes
5765

5866
GET /she/function org.hatdex.hat.she.controllers.FunctionManager.functionList()

0 commit comments

Comments
 (0)