@@ -16,22 +16,25 @@ import java.time.Duration
1616import java.time.Instant
1717import kotlin.streams.asSequence
1818
19- class OuraRequestGenerator @JvmOverloads
19+ class OuraRequestGenerator
20+ @JvmOverloads
2021constructor (
2122 private val userRepository: UserRepository ,
2223 private val defaultQueryRange: Duration = Duration .ofDays(15 ),
2324 private val ouraOffsetManager: OuraOffsetManager ,
2425 public val routes: List <Route > = OuraRouteFactory .getRoutes(userRepository),
2526) : RequestGenerator {
26-
2727 private val userNextRequest: MutableMap <String , Instant > = mutableMapOf ()
2828
2929 public var nextRequestTime: Instant = Instant .MIN
3030
3131 private val shouldBackoff: Boolean
3232 get() = Instant .now() < nextRequestTime
3333
34- override fun requests (user : User , max : Int ): Sequence <RestRequest > {
34+ override fun requests (
35+ user : User ,
36+ max : Int ,
37+ ): Sequence <RestRequest > {
3538 return if (user.ready()) {
3639 routes.asSequence()
3740 .flatMap { route ->
@@ -43,7 +46,10 @@ constructor(
4346 }
4447 }
4548
46- override fun requests (route : Route , max : Int ): Sequence <RestRequest > {
49+ override fun requests (
50+ route : Route ,
51+ max : Int ,
52+ ): Sequence <RestRequest > {
4753 return userRepository
4854 .stream()
4955 .flatMap { user ->
@@ -56,25 +62,33 @@ constructor(
5662 .takeWhile { ! shouldBackoff }
5763 }
5864
59- override fun requests (route : Route , user : User , max : Int ): Sequence <RestRequest > {
65+ override fun requests (
66+ route : Route ,
67+ user : User ,
68+ max : Int ,
69+ ): Sequence <RestRequest > {
6070 return if (user.ready()) {
6171 return generateRequests(route, user).takeWhile { ! shouldBackoff }
6272 } else {
6373 emptySequence()
6474 }
6575 }
6676
67- fun generateRequests (route : Route , user : User ): Sequence <RestRequest > {
77+ fun generateRequests (
78+ route : Route ,
79+ user : User ,
80+ ): Sequence <RestRequest > {
6881 val offset = ouraOffsetManager.getOffset(route, user)
6982 val startDate = user.startDate
70- val startOffset: Instant = if (offset == null ) {
71- logger.info(" No offsets found for $user , using the start date." )
72- startDate
73- } else {
74- logger.info(" Offsets found in persistence: " + offset.offset.toString())
75- logger.info(offset.offset.coerceAtLeast(startDate).toString())
76- offset.offset.coerceAtLeast(startDate)
77- }
83+ val startOffset: Instant =
84+ if (offset == null ) {
85+ logger.info(" No offsets found for $user , using the start date." )
86+ startDate
87+ } else {
88+ val offsetTime = offset.offset
89+ logger.info(" Offsets found in persistence: " + offsetTime.toString())
90+ offsetTime.coerceAtLeast(startDate)
91+ }
7892 val endDate = if (user.endDate >= Instant .now()) Instant .now() else user.endDate
7993 if (Duration .between(startOffset, endDate).toDays() <= ONE_DAY ) {
8094 logger.info(" Interval between dates is too short. Backing off.." )
@@ -85,25 +99,31 @@ constructor(
8599 return route.generateRequests(user, startOffset, endTime, USER_MAX_REQUESTS )
86100 }
87101
88- fun handleResponse (req : RestRequest , response : Response ): OuraResult <List <TopicData >> {
102+ fun handleResponse (
103+ req : RestRequest ,
104+ response : Response ,
105+ ): OuraResult <List <TopicData >> {
89106 if (response.isSuccessful) {
90107 return OuraResult .Success <List <TopicData >>(requestSuccessful(req, response))
91108 } else {
92109 try {
93110 OuraResult .Error (requestFailed(req, response))
94- } catch (e: TooManyRequestsException ) {} finally {
111+ } catch (e: TooManyRequestsException ) {
112+ } finally {
95113 return OuraResult .Success (listOf<TopicData >())
96114 }
97115 }
98116 }
99117
100- override fun requestSuccessful (request : RestRequest , response : Response ): List <TopicData > {
118+ override fun requestSuccessful (
119+ request : RestRequest ,
120+ response : Response ,
121+ ): List <TopicData > {
101122 logger.debug(" Request successful: {}.." , request.request)
102123 val body: ResponseBody ? = response.body
103124 val data = body?.bytes()!!
104- val records = request.route.converters.flatMap {
105- it.convert(request, response.headers, data)
106- }
125+ val records =
126+ request.route.converters.flatMap { it.convert(request, response.headers, data) }
107127 val offset = records.maxByOrNull { it -> it.offset }?.offset
108128 if (offset != null ) {
109129 logger.info(" Writing ${records.size} records to offsets..." )
@@ -112,7 +132,16 @@ constructor(
112132 request.user,
113133 Instant .ofEpochSecond(offset).plus(Duration .ofMillis(500 )),
114134 )
115- userNextRequest[request.user.versionedId] = Instant .now().plus(SUCCESS_BACK_OFF_TIME )
135+ val nextRequestTime = userNextRequest[request.user.versionedId]
136+ userNextRequest[request.user.versionedId] =
137+ nextRequestTime?.let {
138+ if (nextRequestTime > Instant .now()) {
139+ nextRequestTime
140+ } else {
141+ Instant .now().plus(SUCCESS_BACK_OFF_TIME )
142+ }
143+ }
144+ ? : Instant .now().plus(SUCCESS_BACK_OFF_TIME )
116145 } else {
117146 if (request.startDate.plus(TIME_AFTER_REQUEST ).isBefore(Instant .now())) {
118147 ouraOffsetManager.updateOffsets(
@@ -125,7 +154,10 @@ constructor(
125154 return records
126155 }
127156
128- override fun requestFailed (request : RestRequest , response : Response ): OuraError {
157+ override fun requestFailed (
158+ request : RestRequest ,
159+ response : Response ,
160+ ): OuraError {
129161 return when (response.code) {
130162 429 -> {
131163 logger.info(" Too many requests, rate limit reached. Backing off..." )
@@ -134,10 +166,13 @@ constructor(
134166 }
135167 403 -> {
136168 logger.warn(
137- " User ${request.user} has expired." +
138- " Please renew the subscription..." ,
169+ " User ${request.user} has expired." + " Please renew the subscription..." ,
139170 )
140- userNextRequest[request.user.versionedId] = Instant .now().plus(USER_BACK_OFF_TIME )
171+ userNextRequest[request.user.versionedId] =
172+ Instant .now()
173+ .plus(
174+ USER_BACK_OFF_TIME ,
175+ )
141176 OuraAccessForbiddenError (
142177 " Oura subscription has expired or API data not available.." ,
143178 IOException (" Unauthorized" ),
@@ -147,9 +182,14 @@ constructor(
147182 401 -> {
148183 logger.warn(
149184 " User ${request.user} access token is" +
150- " expired, malformed, or revoked. " + response.body?.string(),
185+ " expired, malformed, or revoked. " +
186+ response.body?.string(),
151187 )
152- userNextRequest[request.user.versionedId] = Instant .now().plus(USER_BACK_OFF_TIME )
188+ userNextRequest[request.user.versionedId] =
189+ Instant .now()
190+ .plus(
191+ USER_BACK_OFF_TIME ,
192+ )
153193 OuraUnauthorizedAccessError (
154194 " Access token expired or revoked.." ,
155195 IOException (" Unauthorized" ),
@@ -175,7 +215,11 @@ constructor(
175215 }
176216 404 -> {
177217 logger.warn(" Not found.." )
178- OuraNotFoundError (response.body!! .string(), IOException (" Data not found" ), " 404" )
218+ OuraNotFoundError (
219+ response.body!! .string(),
220+ IOException (" Data not found" ),
221+ " 404" ,
222+ )
179223 }
180224 else -> {
181225 logger.warn(" Request Failed: {}, {}" , request, response)
0 commit comments