Skip to content

Commit

Permalink
Merge pull request #19 from charvolant/master
Browse files Browse the repository at this point in the history
Release 1.2
  • Loading branch information
charvolant authored Oct 18, 2019
2 parents e182f22 + 991f99e commit 1815aa5
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 55 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ sudo: false
branches:
only:
- master
- develop
- hotfix

before_cache:
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,20 @@ var defaultOptions = {
All those can be overridden when creating the `GalleryWidget` instance.
### Configuration
The configuration options allow images to be marked as preferred by commmunicating with the
lists server, which maintains a persistent list of preferred images and the BIE index, whuich
records the current preferred image for a taxon.
The user needs to be logghed in and have the appropriate roles to prefer images.
Note this can get a bit tricky, since a successful prefer updates two services and there's
plenty of room for authentication errors.
| Config | Description
| ------ | -----------
| speciesList.baseURL | SpeciesList BaseURL (eg: https://lists.ala.org.au)
| speciesList.apiKey | API key for the species list web service
| bieService.baseUrl | Bie Index BaseURL (eg: http://bie.ala.org.au/ws)
| bieService.apiKey | API key for the BIE index
| speciesList.preferredSpeciesListDruid | Preferred Species List Druid (eg: dr4778)
| speciesList.preferredListName | Preferred Species List Name (eg: ALA Preferred Species Images)
| allowedImageEditingRoles | User roles for users to be able to nominate preferred image for species. (eg: ROLE_ADMIN,ROLE_USER)
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ buildscript {
}
}

version "1.1"
version "1.2"
group "au.org.ala.plugins.grails"

apply plugin:"eclipse"
Expand Down Expand Up @@ -60,7 +60,7 @@ dependencies {
}

//plugins
compile "org.grails.plugins:ala-auth:3.0.2"
compile group: "org.grails.plugins", name: "ala-auth", version: "3.1.2", changing: true
}

bootRun {
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
grailsVersion=3.3.9
grailsVersion=3.2.11
gormVersion=6.0.12.RELEASE
gradleWrapperVersion=3.4.1
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,16 @@ class ImageClientController {
}

def getPreferredSpeciesImageList() {
def list = speciesListWebService.getPreferredImageSpeciesList ()
if (!list) {
list = new ArrayList<String> ()
try {
def list = speciesListWebService.getPreferredImageSpeciesList()
if (!list) {
list = new ArrayList<String>()
}
render text: list as grails.converters.JSON, contentType: ContentType.APPLICATION_JSON
} catch (Exception ex) {
log.error("An error occurred while getting the preferred species image list", ex)
render text: "An error occurred while getting the preferred species image list.", status: HttpStatus.SC_INTERNAL_SERVER_ERROR
}
render text: list as grails.converters.JSON, contentType: ContentType.APPLICATION_JSON
}

def saveImageToSpeciesList() {
Expand All @@ -114,11 +119,13 @@ class ImageClientController {
render status: HttpStatus.SC_BAD_REQUEST, text: "You must be logged in"
} else {
if (params.id && params.scientificName) {
def cookies = request.getHeader('Cookie')
result = speciesListWebService.saveImageToSpeciesList(params.scientificName, params.id, cookies)
if (result.status == 200) {
result = bieWebService.updateBieIndex(result.data)
}
result = speciesListWebService.saveImageToSpeciesList(params.scientificName, params.family, params.id)
if (result.status == HttpStatus.SC_OK || result.status == HttpStatus.SC_CREATED || result.status == HttpStatus.SC_ACCEPTED) {
if (result.data.every { it?.guid != null })
result = bieWebService.updateBieIndex(result.data)
else
result = [status: HttpStatus.SC_CONFLICT, text: "Species list unable to match '${params.scientificName}'. The name/image has been stored in the list but is not available as a preferred image."]
}
} else {
result = [status: HttpStatus.SC_BAD_REQUEST, text: "Save image to species list failed. Missing parameter id or scientific name. This should not happen. Please refresh and try again."]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class BieWebService {
try {
HttpClient client = new HttpClient();
PostMethod post = new PostMethod(url);
post.setRequestHeader('Authorization', grailsApplication.config.bieApiKey)
post.setRequestHeader('Authorization', grailsApplication.config.bieService.apiKey)
StringRequestEntity requestEntity = new StringRequestEntity(jsonBody, "application/json", "utf-8")
post.setRequestEntity(requestEntity)
int status = client.executeMethod(post);
Expand Down
105 changes: 72 additions & 33 deletions grails-app/services/images/client/plugin/SpeciesListWebService.groovy
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package images.client.plugin

import grails.converters.JSON
import grails.web.JSONBuilder
import groovy.json.JsonSlurper
import org.apache.commons.httpclient.HttpClient
import org.apache.commons.httpclient.HttpStatus
import org.apache.commons.httpclient.methods.GetMethod
import org.apache.commons.httpclient.methods.PostMethod
import org.apache.commons.httpclient.methods.StringRequestEntity
import org.springframework.cache.annotation.Cacheable
import org.springframework.cache.annotation.CacheEvict
import org.springframework.cache.annotation.Cacheable
import org.springframework.web.context.request.RequestContextHolder

class SpeciesListWebService {

def grailsApplication
def authService

private String getServiceUrl() {
def url = grailsApplication.config.speciesList?.baseURL?:grailsApplication.config.speciesList?.baseUrl?:null
Expand All @@ -36,14 +39,18 @@ class SpeciesListWebService {
String druid = getSpeciesListDruid()
String url = getServiceUrl() + "ws/speciesListItemKvp/" + druid
log.info("Calling species list web service: " + getServiceUrl() + "ws/speciesListItemKvp/" + druid)
List speciesListKvps = getJSON(url)
List results = []
speciesListKvps.each {
def result = get(url, grailsApplication.config.speciesList.apiKey)
if (result.status != HttpStatus.SC_OK) {
throw new IOException(result.text)
}
result.data.each {
String imageId = ""
it.kvps?.each { kvp ->
if (kvp.key == "imageId") {
imageId = kvp.value?:""
}}
imageId = kvp.value ?: ""
}
}
if (imageId.trim() != "") {
results.push(["name": it.name, "imageId": imageId])
}
Expand All @@ -52,59 +59,91 @@ class SpeciesListWebService {
}

@CacheEvict(value="speciesListKvp", allEntries=true)
def saveImageToSpeciesList(def scientificName, def imageId, cookie) {
def saveImageToSpeciesList(def scientificName, def family, def imageId) {
String druid = getSpeciesListDruid ()
String listNameVal = getSpeciesListName ()
String url = getServiceUrl() + "ws/speciesList/" + druid
List kvpValues = [[key: "imageId", value: imageId]]
List kvpValues = [[key: 'imageId', value: imageId]]
if (family)
kvpValues << [key: 'family', value: family]
Map listMap = [
itemName: scientificName, kvpValues: kvpValues
]
def builder = new JSONBuilder()
def jsonBody = builder.build {
listName = listNameVal
listItems = [listMap]
replaceList = false
}
def response = doPostJSON(url, jsonBody, cookie)
def result = [status: response.status, text: response.message, data: response.data]
return result
Map body = [listName: listNameVal, listItems: [listMap], replaceList: false]
def response = post(url, body, grailsApplication.config.speciesList.apiKey)
return [status: response.status, text: response.text, data: response.data?.data]
}

def doPostJSON(String url, def jsonBody, def cookie) {
private post(String url, Object body, String apiKey) {
def response = [:]
try {
HttpClient client = new HttpClient();
PostMethod post = new PostMethod(url);
post.setRequestHeader('cookie',cookie)
StringRequestEntity requestEntity = new StringRequestEntity(jsonBody.toString(), "application/json", "utf-8")
post.setRequestHeader('Authorization', apiKey)
if (RequestContextHolder.getRequestAttributes() != null) {
def user = authService.userDetails()

if (user) {
post.setRequestHeader("X-ALA-userId", user.userId as String)
post.setRequestHeader("Cookie", "ALA-Auth=${URLEncoder.encode(user.email, "UTF-8")}")
}
}
String jsonBody = (body as JSON).toString()
StringRequestEntity requestEntity = new StringRequestEntity(jsonBody, "application/json", "utf-8")
post.setRequestEntity(requestEntity)
client.executeMethod(post);
int status = client.executeMethod(post);
String responseStr = post.getResponseBodyAsString();
response = JSON.parse(responseStr)
def data = null

if (status >= HttpStatus.SC_OK && status <= HttpStatus.SC_ACCEPTED) {
data = new JsonSlurper().parseText(responseStr)
}
response = [status: status, text: responseStr, data: data]
log.debug "${response.text} status: ${response.status}"
} catch (SocketTimeoutException e) {
String error = "Timed out calling web service. URL= ${url}."
String error = "Timed out calling web service. ${e.getMessage()} URL= ${url}. "
log.error error
response = [text: error, status: 500 ]
} catch (Exception e) {
String error = "Failed calling web service. ${e.getMessage()}. You may also want to check speciesList.baseURL config. ${url}."
String error = "Failed calling web service. ${e.getMessage()}. You may also want to check bieService.baseURL config. URL= ${url}."
log.error error
response = [text: error, status: 500]
}
return response
}

def getJSON(String url) {
private get(String url, String apiKey) {
def response = [:]
try {
def u = new URL(url);
def text = u.text
return new JsonSlurper().parseText(text)
} catch (Exception ex) {
log.error(url)
log.error(ex.message)
return null
HttpClient client = new HttpClient();
GetMethod get = new GetMethod(url);
get.setRequestHeader('Authorization', apiKey)
if (RequestContextHolder.getRequestAttributes() != null) {
def user = authService.userDetails()

if (user) {
get.setRequestHeader("X-ALA-userId", user.userId as String)
get.setRequestHeader("Cookie", "ALA-Auth=${URLEncoder.encode(user.email, "UTF-8")}")
}
}
int status = client.executeMethod(get);
String responseStr = get.getResponseBodyAsString();
def data = null

if (status == HttpStatus.SC_OK) {
data = new JsonSlurper().parseText(responseStr)
}
response = [status: status, text: responseStr, data: data]
log.debug "${response.text} status: ${response.status}"
} catch (SocketTimeoutException e) {
String error = "Timed out calling web service. ${e.getMessage()} URL= ${url}. "
log.error error
response = [text: error, status: 500 ]
} catch (Exception e) {
String error = "Failed calling web service. ${e.getMessage()}. You may also want to check speciesList.baseURL config. URL= ${url}."
log.error error
response = [text: error, status: 500]
}
return response
}

}
21 changes: 12 additions & 9 deletions grails-app/taglib/images/client/plugin/ImageClientTagLib.groovy
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
package images.client.plugin

import au.org.ala.web.AuthService
import grails.config.Config
import grails.core.support.GrailsConfigurationAware

/**
* Image Client Tag Lib to be used in the gsp related to image client plugin feature
*
*/
class ImageClientTagLib {

AuthService authService
class ImageClientTagLib implements GrailsConfigurationAware {
static namespace = 'imageClient'

/**
List<String> allowedRoles = []

@Override
void setConfiguration(Config config) {
def roleList = config.getProperty("allowedImageEditingRoles", "")
allowedRoles = roleList ? roleList.split(",").collect({ it.trim() }) : []
}
/**
*
* Outputs true if user is logged in and user role is in the allowable configured roles: allowedImageEditingRoles (Note that the IP must also match the authorised system IP for the user).
* Otherwise, outputs false.
*
*/
def checkAllowableEditRole = { attrs ->
def allowedRolesConfig = grailsApplication.config.get("allowedImageEditingRoles")
List<String> allowedRoles = allowedRolesConfig ? allowedRolesConfig.split(","):[]
def currentUserRoles = authService?.getUserId() ? authService.getUserForUserId(authService.getUserId())?.roles : []
boolean match = currentUserRoles.any {allowedRoles.contains(it)}
boolean match = allowedRoles.any { request.isUserInRole(it) }
out << match;
}

Expand Down

0 comments on commit 1815aa5

Please sign in to comment.