Skip to content

Commit 5d8a76d

Browse files
alexhuang091Rita-C
andauthoredApr 14, 2021
Release 1.5.3 (#116)
* correct totalRecords count since it could be -1 (when firewhennotzero not set) * correctly handle unsubscribe myannotation with token (#99) correctly handle unsubscribe myannotation with token * Updated email template, set pageSize=300 for records query * updated email template * explicitly set page size so we can get all records in one call * release 1.5.2 (#104) * 1.5.3-SNAPSHOT * create user when addMyAnnotation is called by user not exists (#107) * create user when addMyAnnotation is called by user not exists * changed add/delete myannotaion to subscribe/unsubscribe myannotaion. (#109) * run a myannotation query after it's added (#112) * need to run a myannotation query after it's added * print error witg log.error() instead of println * if user subscribed to 'My Annotation' and changes frequency, we need to trigger a query for 'my annotation' (#114) * update 'my annotation' QueryResult instead of delete + add when frequency changes * release 1.5.3 Co-authored-by: Rita <rita.chen@csiro.au>
1 parent 324ab8b commit 5d8a76d

File tree

10 files changed

+100
-37
lines changed

10 files changed

+100
-37
lines changed
 

‎build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ plugins {
2828
id "com.gorylenko.gradle-git-properties" version "1.4.17"
2929
}
3030

31-
version "1.5.2"
31+
version "1.5.3"
3232
group "au.org.ala"
3333

3434
apply plugin:"eclipse"

‎grails-app/controllers/au/org/ala/alerts/NotificationController.groovy

+7-8
Original file line numberDiff line numberDiff line change
@@ -43,24 +43,24 @@ class NotificationController {
4343
}
4444
}
4545

46-
def addMyAnnotation = {
46+
def subscribeMyAnnotation = {
4747
def user = getUser()
4848
try {
49-
notificationService.addMyAnnotation(user)
49+
notificationService.subscribeMyAnnotation(user)
5050
render ([success: true] as JSON)
5151
} catch (ignored) {
52-
response.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR, "failed to add 'my annotation' alert for user " + user?.getUserId())
52+
response.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR, "failed to subscribe to 'my annotation' alert for user " + user?.getUserId())
5353
}
5454

5555
}
5656

57-
def deleteMyAnnotation = {
57+
def unsubscribeMyAnnotation = {
5858
def user = getUser()
5959
try {
60-
notificationService.deleteMyAnnotation(user)
60+
notificationService.unsubscribeMyAnnotation(user)
6161
render ([success: true] as JSON)
6262
} catch (ignored) {
63-
response.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR, "failed to delete 'my annotation' alert for user " + user?.getUserId())
63+
response.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR, "failed to unsubscribe 'my annotation' alert for user " + user?.getUserId())
6464
}
6565
}
6666

@@ -96,8 +96,7 @@ class NotificationController {
9696
def changeFrequency = {
9797
def user = userService.getUser()
9898
log.debug("Changing frequency to: " + params.frequency + " for user ${user}")
99-
user.frequency = Frequency.findByName(params.frequency)
100-
user.save(flush: true)
99+
notificationService.updateFrequency(user, params.frequency)
101100
return null
102101
}
103102

‎grails-app/controllers/au/org/ala/alerts/UnsubscribeController.groovy

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class UnsubscribeController {
4747
// for my annotation, we also need to delete the query and query result
4848
String myAnnotationQueryPath = queryService.constructMyAnnotationQueryPath(userAndNotifications.user.userId)
4949
if (userAndNotifications.notifications.any { it.query.queryPath == myAnnotationQueryPath }) {
50-
notificationService.deleteMyAnnotation(userAndNotifications.user)
50+
notificationService.unsubscribeMyAnnotation(userAndNotifications.user)
5151
}
5252

5353
render view: "unsubscribed"

‎grails-app/controllers/au/org/ala/alerts/WebserviceController.groovy

+11-11
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ class WebserviceController {
373373
response.status = HttpStatus.SC_BAD_REQUEST
374374
response.sendError(HttpStatus.SC_BAD_REQUEST, "userId is a required parameter")
375375
} else {
376-
def user = User.findByUserId(params.userId)
376+
def user = userService.getUserById(params.userId)
377377
if (!user) {
378378
Map userDetails = ["userId": params.userId, "email": params.email, "userDisplayName": params.firstName + " " + params.lastName]
379379
user = userService.getUser(userDetails)
@@ -401,41 +401,41 @@ class WebserviceController {
401401
def getUserAlertsWS() {
402402
User user = userService.getUserById(params.userId)
403403
if (user == null) {
404-
response.status = 404
404+
response.status = HttpStatus.SC_NOT_FOUND
405405
render ([error : "can't find a user with userId " + params.userId] as JSON)
406406
} else {
407407
render (userService.getUserAlertsConfig(user) as JSON)
408408
}
409409
}
410410

411411
@RequireApiKey
412-
def addMyAnnotationAlertWS() {
413-
User user = userService.getUserById(params.userId)
412+
def subscribeMyAnnotationWS() {
413+
User user = userService.getUser((String)params.userId)
414414
if (user == null) {
415-
response.status = 404
415+
response.status = HttpStatus.SC_NOT_FOUND
416416
render ([error : "can't find a user with userId " + params.userId] as JSON)
417417
} else {
418418
try {
419-
notificationService.addMyAnnotation(user)
419+
notificationService.subscribeMyAnnotation(user)
420420
render([success: true] as JSON)
421421
} catch (ignored) {
422-
render "failed to add my annotation for user " + params.userId, contentType: 'text/plain', status: 500
422+
render text: "failed to subscribe to my annotation for user " + params.userId, contentType: 'text/plain', status: 500
423423
}
424424
}
425425
}
426426

427427
@RequireApiKey
428-
def deleteMyAnnotationAlertWS() {
428+
def unsubscribeMyAnnotationWS() {
429429
User user = userService.getUserById(params.userId)
430430
if (user == null) {
431-
response.status = 404
431+
response.status = HttpStatus.SC_NOT_FOUND
432432
render ([error : "can't find a user with userId " + params.userId] as JSON)
433433
} else {
434434
try {
435-
notificationService.deleteMyAnnotation(user)
435+
notificationService.unsubscribeMyAnnotation(user)
436436
render([success: true] as JSON)
437437
} catch (ignored) {
438-
render "failed to delete my annotation for user " + params.userId, contentType: 'text/plain', status: 500
438+
render text: "failed to unsubscribe my annotation for user " + params.userId, contentType: 'text/plain', status: 500
439439
}
440440
}
441441
}

‎grails-app/init/au/org/ala/alerts/BootStrap.groovy

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ class BootStrap {
1414
// if my annotation feature turned on, add url mapping to handle add/remove alert requests
1515
if (grailsApplication.config.getProperty('myannotation.enabled', Boolean, false)) {
1616
grailsUrlMappingsHolder.addMappings({
17-
"/admin/user/addMyAnnotation/"(controller: 'notification', action: 'addMyAnnotation')
18-
"/admin/user/deleteMyAnnotation/"(controller: 'notification', action: 'deleteMyAnnotation')
17+
"/admin/user/subscribeMyAnnotation/"(controller: 'notification', action: 'subscribeMyAnnotation')
18+
"/admin/user/unsubscribeMyAnnotation/"(controller: 'notification', action: 'unsubscribeMyAnnotation')
1919

20-
"/api/alerts/user/$userId/addMyAnnotationAlert"(controller: 'webservice', action: [POST: 'addMyAnnotationAlertWS'])
21-
"/api/alerts/user/$userId/deleteMyAnnotationAlert"(controller: 'webservice', action: [POST: 'deleteMyAnnotationAlertWS'])
20+
"/api/alerts/user/$userId/subscribeMyAnnotation"(controller: 'webservice', action: [POST: 'subscribeMyAnnotationWS'])
21+
"/api/alerts/user/$userId/unsubscribeMyAnnotation"(controller: 'webservice', action: [POST: 'unsubscribeMyAnnotationWS'])
2222
})
2323
}
2424

‎grails-app/services/au/org/ala/alerts/NotificationService.groovy

+43-5
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class NotificationService {
3232
* @param frequency
3333
* @return
3434
*/
35-
boolean checkStatus(Query query, Frequency frequency) {
35+
boolean checkStatus(Query query, Frequency frequency, boolean flushResult = false) {
3636

3737
QueryResult qr = getQueryResult(query, frequency)
3838

@@ -68,7 +68,7 @@ class NotificationService {
6868
qr.lastChanged = new Date()
6969
}
7070

71-
qr.save(true)
71+
qr.save(validate: true, flush: flushResult)
7272
qr.hasChanged
7373
} catch (Exception e){
7474
log.error("[QUERY " + query.id + "] There was a problem checking the URL: " + urlString, e)
@@ -581,12 +581,19 @@ class NotificationService {
581581
}
582582
}
583583

584-
def addMyAnnotation(User user) {
584+
def subscribeMyAnnotation(User user) {
585585
Query myAnnotationQuery = queryService.createMyAnnotationQuery(user?.userId)
586-
queryService.createQueryForUserIfNotExists(myAnnotationQuery, user, false)
586+
boolean newQueryCreated = queryService.createQueryForUserIfNotExists(myAnnotationQuery, user, false)
587+
// trigger a check for this query to generate query result
588+
// user could call multiple subscribeMyAnnotation, only the first one will create a new query so it's
589+
// triggered only once.
590+
if (newQueryCreated) {
591+
Query savedQuery = Query.findByBaseUrlAndQueryPath(myAnnotationQuery.baseUrl, myAnnotationQuery.queryPath)
592+
checkStatus(savedQuery, user.frequency, true)
593+
}
587594
}
588595

589-
def deleteMyAnnotation(User user) {
596+
def unsubscribeMyAnnotation(User user) {
590597
String myAnnotationQueryPath = queryService.constructMyAnnotationQueryPath(user?.userId)
591598
Query retrievedQuery = Query.findByQueryPath(myAnnotationQueryPath)
592599

@@ -607,4 +614,35 @@ class NotificationService {
607614
retrievedQuery.delete(flush: true)
608615
}
609616
}
617+
618+
// update user to new frequency
619+
// there are some special work if user is subscribed to 'My Annotation' alert
620+
def updateFrequency(User user, String newFrequency) {
621+
def oldFrequency = user.frequency
622+
user.frequency = Frequency.findByName(newFrequency)
623+
624+
String myAnnotationQueryPath = queryService.constructMyAnnotationQueryPath(user?.userId)
625+
Query query = Query.findByQueryPath(myAnnotationQueryPath)
626+
627+
// my annotation generates alert(diff) by comparing QueryResult at 2 time points.
628+
// first QueryResult will be inserted when user subscribes to my annotation
629+
// every time user changes the frequency, we also need to create a new QueryResult
630+
// here we update the frequency of existing QueryResult instead of delete old + create new for below reason
631+
// suppose
632+
// 1. we have an hourly QueryResult
633+
// 2. some changes happened
634+
// 3. user changes frequency to daily
635+
// 4. if now we create a daily QueryResult which reflects current verifications status (at time position 4)
636+
// and is used to do diff, then changes in 2 will be lost. So we directly update existing hourly QueryResult to be daily
637+
// so next time scheduled daily task runs, it compares with status at time 1 so changes at time 2 will be captured
638+
if (query) {
639+
QueryResult qr = QueryResult.findByQueryAndFrequency(query, oldFrequency)
640+
if (qr) {
641+
qr.frequency = user.frequency
642+
qr.save(flush: true)
643+
}
644+
}
645+
646+
user.save(flush: true)
647+
}
610648
}

‎grails-app/services/au/org/ala/alerts/QueryService.groovy

+8-3
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,15 @@ class QueryService {
8888
toBeRemoved.size()
8989
}
9090

91-
def createQueryForUserIfNotExists(Query newQuery, User user, boolean setPropertyPath = true){
91+
// return true if a new query is created, otherwise return false
92+
boolean createQueryForUserIfNotExists(Query newQuery, User user, boolean setPropertyPath = true) {
93+
boolean newQueryCreated = false
9294
//find the query
9395
Query retrievedQuery = Query.findByBaseUrlAndQueryPath(newQuery.baseUrl, newQuery.queryPath)
9496
if (retrievedQuery == null) {
9597
try {
9698
newQuery = newQuery.save(true)
99+
newQueryCreated = true
97100
if (setPropertyPath) {
98101
new PropertyPath([name: "totalRecords", jsonPath: "totalRecords", query: newQuery, fireWhenNotZero: true]).save(true)
99102
new PropertyPath([name: "last_loaded_record", jsonPath: "occurrences[0].rowKey", query: newQuery]).save(true)
@@ -112,10 +115,12 @@ class QueryService {
112115
Notification n = new Notification([query: newQuery, user: user])
113116
n.save(true)
114117

115-
if(n.hasErrors()){
116-
n.errors.allErrors.each { e -> println(e)}
118+
if (n.hasErrors()) {
119+
n.errors.allErrors.each { e -> log.error(e) }
117120
}
118121
}
122+
123+
newQueryCreated
119124
}
120125

121126
/**

‎grails-app/services/au/org/ala/alerts/UserService.groovy

+21
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,27 @@ class UserService {
151151
user
152152
}
153153

154+
// get user via userId, if not found in database create one
155+
User getUser(String userId) {
156+
if (!userId) {
157+
return null
158+
}
159+
160+
// try to find in User database
161+
User user = User.findByUserId(userId)
162+
// if not in database try to create it
163+
if (user == null) {
164+
UserDetails userDetails = authService.getUserForUserId(userId)
165+
if (userDetails?.userId && userDetails?.email) {
166+
log.debug "User is not in user table - creating new record for " + userDetails
167+
user = new User([email: userDetails.email, userId: userDetails.userId, locked: userDetails.locked, frequency: Frequency.findByName("weekly")])
168+
user.save(flush:true, failOnError: true)
169+
}
170+
}
171+
172+
user
173+
}
174+
154175
User getUserById(userId) {
155176
User.findByUserId(userId)
156177
}

‎grails-app/views/notification/myAlerts.gsp

+3-3
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@
143143
var deleteMyAlertUrl = 'deleteMyAlert/';
144144
var deleteMyAlertWRUrl ='deleteMyAlertWR/';
145145

146-
var addMyAnnotationUrl = 'addMyAnnotation/'
147-
var deleteMyAnnotationUrl = 'deleteMyAnnotation/'
146+
var subscribeMyAnnotationUrl = 'subscribeMyAnnotation/'
147+
var unsubscribeMyAnnotationUrl = 'unsubscribeMyAnnotation/'
148148

149149
$(document).ready( function(){
150150

@@ -177,7 +177,7 @@
177177

178178
var url = '';
179179
if ($(this).attr('data-type') === 'myannotation') {
180-
url = (state ? addMyAnnotationUrl : deleteMyAnnotationUrl) + '?userId=${userId}';
180+
url = (state ? subscribeMyAnnotationUrl : unsubscribeMyAnnotationUrl) + '?userId=${userId}';
181181
} else {
182182
url = (state ? addMyAlertUrl : deleteMyAlertUrl) + $(this).attr('id') + '?userId=${userId}';
183183
}

‎src/test/groovy/au/org/ala/alerts/UnsubscribeControllerSpec.groovy

+1-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ class UnsubscribeControllerSpec extends Specification {
315315

316316
then:
317317
log.info "token = ${params.token}"
318-
1 * controller.notificationService.deleteMyAnnotation(user)
318+
1 * controller.notificationService.unsubscribeMyAnnotation(user)
319319
response.status == HttpStatus.SC_OK
320320
}
321321

0 commit comments

Comments
 (0)