From b51483a1d816550f47cf861829f80ed760dfde55 Mon Sep 17 00:00:00 2001 From: Adam Collins Date: Thu, 16 Nov 2023 13:46:03 +1000 Subject: [PATCH 01/13] bump 5.1.0-SNAPSHOT --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8794a08c..469bb7bf 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ buildscript { - version "5.0.0-SNAPSHOT" + version "5.1.0-SNAPSHOT" group "au.org.ala" } From e7f8f35530ac4582deec8b0261792118ca83e07b Mon Sep 17 00:00:00 2001 From: Adam Collins Date: Mon, 20 Nov 2023 12:48:33 +1000 Subject: [PATCH 02/13] openapi --- .../org/ala/collectory/DataController.groovy | 240 +++++++++++------- .../ala/collectory/LookupController.groovy | 10 +- 2 files changed, 153 insertions(+), 97 deletions(-) diff --git a/grails-app/controllers/au/org/ala/collectory/DataController.groovy b/grails-app/controllers/au/org/ala/collectory/DataController.groovy index cb2c0aac..e2c396a5 100644 --- a/grails-app/controllers/au/org/ala/collectory/DataController.groovy +++ b/grails-app/controllers/au/org/ala/collectory/DataController.groovy @@ -35,9 +35,9 @@ import static io.swagger.v3.oas.annotations.enums.ParameterIn.QUERY class DataController { def crudService, emlRenderService, collectoryAuthService, metadataService, providerGroupService, - activityLogService, asyncGbifRegistryService + activityLogService, asyncGbifRegistryService - def index = { } + def index = {} private def check(params) { def uid = params.uid @@ -54,11 +54,11 @@ class DataController { } } else { - if (params.entity){ + if (params.entity) { params.pg = providerGroupService._get(params.uid, params.entity) } - if (!params.pg){ + if (!params.pg) { // doesn't exist notFound "no entity with uid = ${uid}" return false @@ -87,14 +87,14 @@ class DataController { return false } // inject the user name into the session so it can be used by audit logging if changes are made - session.username = params?.json?.user?: collectoryAuthService.username() + session.username = params?.json?.user ?: collectoryAuthService.username() } return true } /******* Web Services Catalogue *******/ - def catalogue = { } + def catalogue = {} /***** CRUD RESTful services ********/ @@ -104,8 +104,8 @@ class DataController { final static String RFC1123_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z"; /** - * DateFormat to be used to format dates - */ + * DateFormat to be used to format dates + */ final static DateFormat rfc1123Format = new SimpleDateFormat(RFC1123_PATTERN) static { rfc1123Format.setTimeZone(TimeZone.getTimeZone("GMT")); @@ -135,10 +135,10 @@ class DataController { if (eTag) { addETagHeader eTag } - render(text:content, encoding:"UTF-8", contentType: "application/json") + render(text: content, encoding: "UTF-8", contentType: "application/json") } - def renderAsJson = {json, last, eTag -> + def renderAsJson = { json, last, eTag -> renderJson(json as JSON, last, eTag) } @@ -150,17 +150,17 @@ class DataController { response.addHeader 'content-location', grailsApplication.config.grails.serverURL + relativeUri } - def created = {clazz, uid -> + def created = { clazz, uid -> addLocation "/ws/${clazz}/${uid}" - render(status:201, text:'inserted entity') + render(status: 201, text: 'inserted entity') } - def badRequest = {text -> - render(status:400, text: text) + def badRequest = { text -> + render(status: 400, text: text) } def success = { text -> - render(status:200, text: text) + render(status: 200, text: text) } def notModified = { @@ -168,26 +168,26 @@ class DataController { } def notFound = { text -> - render(status:404, text: text) + render(status: 404, text: text) } def return404() { - render(status:404) + render(status: 404) } def notAllowed = { - response.addHeader 'allow','POST' - render(status:405, text: 'Only POST supported') + response.addHeader 'allow', 'POST' + render(status: 405, text: 'Only POST supported') } def unauthorised = { // using the 'forbidden' response code here as 401 causes the client to ask for a log in - render(status:403, text: 'You are not authorised to use this service') + render(status: 403, text: 'You are not authorised to use this service') } def noApiKey = { // using the 'forbidden' response code here as 401 causes the client to ask for a log in - render(status:400, text: 'This service requires API key') + render(status: 400, text: 'This service requires API key') } /** @@ -210,7 +210,7 @@ class DataController { switch (word?.size()) { case 0: return "" case 1: return word[0].toUpperCase() - default: return word[0].toUpperCase() + word [1..-1] + default: return word[0].toUpperCase() + word[1..-1] } } @@ -239,7 +239,7 @@ class DataController { in = PATH, description = "entity i.e. collection, institution, dataProvider, dataResource, tempDataResource, dataHub", schema = @Schema(implementation = String), - example ="collection", + example = "collection", required = true ), @Parameter( @@ -282,7 +282,7 @@ class DataController { ) @Path("/ws/{entity}/{uid}") @Produces("application/json") - def saveEntity () { + def saveEntity() { def ok = check(params) if (ok) { def pg = params.pg @@ -319,8 +319,8 @@ class DataController { /** * Define some variations on the level of detail returned for lists. */ - def brief = {[name: it.name, uri: it.buildUri(), uid: it.uid]} - def summary = {[name: it.name, uri: it.buildUri(), uid: it.uid, logo: it.buildLogoUrl()]} + def brief = { [name: it.name, uri: it.buildUri(), uid: it.uid] } + def summary = { [name: it.name, uri: it.buildUri(), uid: it.uid, logo: it.buildLogoUrl()] } def index() { def root = params.root @@ -339,13 +339,13 @@ class DataController { } def serveFile = { - def dirpath = "/" + params.directory + "/" + def dirpath = "/" + params.directory + "/" def uri = URLDecoder.decode(request.forwardURI, "UTF-8") def idx = uri.lastIndexOf(dirpath) + dirpath.length() def fullFileName = uri.substring(idx) def file = new File(grailsApplication.config.repository.location.images + File.separator + params.directory, fullFileName) - if(file.exists()){ - if(fullFileName.endsWith(".json")){ + if (file.exists()) { + if (fullFileName.endsWith(".json")) { response.setContentType("application/json") } file.withInputStream { response.outputStream << it } @@ -355,14 +355,14 @@ class DataController { } def fileDownload = { - def dirpath = "/" + params.directory + "/" + def dirpath = "/" + params.directory + "/" def idx = request.forwardURI.lastIndexOf(dirpath) + dirpath.length() def fullFileName = request.forwardURI.substring(idx) def file = new File(grailsApplication.config.uploadFilePath + File.separator + params.directory, fullFileName) - if (!file.exists()){ + if (!file.exists()) { file = new File(grailsApplication.config.uploadFilePath + File.separator + params.directory, URLDecoder.decode(fullFileName, "UTF-8")) } - if (file.exists()){ + if (file.exists()) { //set the content type response.setContentType("application/octet-stream") response.setHeader("Content-disposition", "attachment;filename=" + file.getName()) @@ -393,20 +393,27 @@ class DataController { description = "Get detailed information for a specific entity", parameters = [ @Parameter( - name = "entity", - in = PATH, - description = "entity type - e.g. datResource, dataProvider etc", - schema = @Schema(implementation = String), - example = "collection", - required = true + name = "entity", + in = PATH, + description = "entity type; collection, institution, dataProvider, dataResource, tempDataResource, dataHub", + schema = @Schema(implementation = String), + example = "collection", + required = true ), @Parameter( - name = "uid", - in = PATH, - description = "uid of an instance of entity", - schema = @Schema(implementation = String), - example = "co43", - required = true + name = "uid", + in = PATH, + description = "uid of an instance of entity", + schema = @Schema(implementation = String), + example = "co43", + required = true + ), + @Parameter( + name = "apikey", + in = HEADER, + description = "authorisation for dataResource connection details", + schema = @Schema(implementation = String), + required = false ) ], responses = [ @@ -416,7 +423,7 @@ class DataController { content = [ @Content( mediaType = "application/json", - array = @ArraySchema(schema = @Schema(implementation = Entity)) + schema = @Schema(oneOf = [Collection, Institution, DataProvider, DataResource, TempDataResource, DataHub]) ) ], headers = [ @@ -435,7 +442,7 @@ class DataController { * The functionality described above has been preserved to maintain backwards compatibility but should be removed in the future once the legacy API key access is deprecated */ - def getEntity () { + def getEntity() { check(params) if (params.entity == 'tempDataResource') { forward(controller: 'tempDataResource', action: 'getEntity') @@ -450,7 +457,7 @@ class DataController { if (clazz == 'DataResource') { // this auth check (JWT or API key) is a special case handling to support backwards compatibility(which used to check for API key). String requiredRoles = grailsApplication.config.ROLE_ADMIN - def authCheck = collectoryAuthService.isAuthorisedWsRequest(getParams(), request, response, requiredRoles,null) + def authCheck = collectoryAuthService.isAuthorisedWsRequest(getParams(), request, response, requiredRoles, null) entityInJson = crudService."read${clazz}"(params.pg, authCheck) } else { entityInJson = crudService."read${clazz}"(params.pg) @@ -480,6 +487,46 @@ class DataController { } } + @Operation( + method = "GET", + tags = "collection, institution, dataProvider, dataResource, tempDataResource, dataHub", + operationId = "listEntities", + summary = "list entities for a given type", + parameters = [ + @Parameter( + name = "entity", + in = PATH, + description = "entity type; collection, institution, dataProvider, dataResource, tempDataResource, dataHub", + schema = @Schema(implementation = String), + example = "collection", + required = true + ) + ], + responses = [ + @ApiResponse( + description = "List of entities", + responseCode = "200", + content = [ + @Content( + mediaType = "application/json", + array = @ArraySchema(schema = @Schema(implementation = Entity)) + ) + ], + headers = [ + @Header(name = 'Access-Control-Allow-Headers', description = "CORS header", schema = @Schema(type = "String")), + @Header(name = 'Access-Control-Allow-Methods', description = "CORS header", schema = @Schema(type = "String")), + @Header(name = 'Access-Control-Allow-Origin', description = "CORS header", schema = @Schema(type = "String")) + ] + ) + ], + security = [] + ) + @Path("/ws/{entity}") + @Produces("application/json") + def listEntity() { + // dummy method for openapi definition + } + @JsonIgnoreProperties('metaClass') class Entity { String name @@ -518,8 +565,7 @@ class DataController { def value = it[params.groupBy] if (groups[value]) { groups[value]++ - } - else { + } else { groups[value] = 1 } } @@ -531,6 +577,7 @@ class DataController { renderAsJson results, last, "" } + @Operation( method = "GET", tags = "gbif", @@ -557,7 +604,7 @@ class DataController { ) @Path("/ws/syncGBIF") @Produces("application/json") - def syncGBIF () { + def syncGBIF() { asyncGbifRegistryService.updateAllResources() .onComplete { log.info "Sync complete" @@ -668,18 +715,18 @@ class DataController { */ def delete = { if (grailsApplication.config.deletesForbidden) { - render(status:405, text:'delete is currently unavailable') + render(status: 405, text: 'delete is currently unavailable') return } // check role if (params.uid) { def pg = params.uid.startsWith('drt') ? - TempDataResource.findByUid(params.uid) : - providerGroupService._get(params.uid) + TempDataResource.findByUid(params.uid) : + providerGroupService._get(params.uid) if (pg) { def name = pg.name pg.delete() - def message = ['message':"deleted ${name}"] + def message = ['message': "deleted ${name}"] render message as JSON } else { def error = ['error': "no uid specified"] @@ -725,7 +772,7 @@ class DataController { in = QUERY, description = "restrict to associated object names that contain this value", schema = @Schema(implementation = String), - example ="Environment", + example = "Environment", required = false ) ], @@ -750,7 +797,7 @@ class DataController { ) @Path("/ws/eml/{id}") @Produces("text/xml") - def eml () { + def eml() { if (params.id) { def pg = providerGroupService._get(params.id) if (pg) { @@ -783,7 +830,6 @@ class DataController { } - def validate(xml) { try { def factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI) @@ -906,7 +952,7 @@ class DataController { ) @Path("/ws/contacts/{id}") @Produces("application/json") - def contacts () { + def contacts() { if (params.id) { def c = Contact.get(params.id) if (c) { @@ -914,9 +960,9 @@ class DataController { addVaryAcceptHeader() def cm = buildContactModel(c) withFormat { - json {render cm as JSON} - csv {render (contentType: 'text/csv', text: CONTACT_HEADER + mapToCsv(cm))} - xml {render (contentType: 'text/xml', text: objToXml(cm, 'contact'))} + json { render cm as JSON } + csv { render(contentType: 'text/csv', text: CONTACT_HEADER + mapToCsv(cm)) } + xml { render(contentType: 'text/xml', text: objToXml(cm, 'contact')) } } } else { badRequest ' no such id' @@ -925,25 +971,27 @@ class DataController { addContentLocation "/ws/contacts" addVaryAcceptHeader() withFormat { - json {render Contact.list().collect { buildContactModel(it) } as JSON} - csv {render (contentType: 'text/csv', - text: CONTACT_HEADER + Contact.list().collect { mapToCsv(buildContactModel(it)) }.join(''))} - xml {render (contentType: 'text/xml', text: objToXml(Contact.list().collect { buildContactModel(it) }, 'contacts'))} + json { render Contact.list().collect { buildContactModel(it) } as JSON } + csv { + render(contentType: 'text/csv', + text: CONTACT_HEADER + Contact.list().collect { mapToCsv(buildContactModel(it)) }.join('')) + } + xml { render(contentType: 'text/xml', text: objToXml(Contact.list().collect { buildContactModel(it) }, 'contacts')) } } } } def buildContactModel(contact) { return new LinkedHashMap( - [title: contact.title, firstName: contact.firstName, lastName: contact.lastName, email: contact.email, phone: contact.phone, - fax: contact.fax, mobile: contact.mobile, publish: contact.publish, dateCreated: contact.dateCreated, lastUpdated: contact.lastUpdated]) + [title: contact.title, firstName: contact.firstName, lastName: contact.lastName, email: contact.email, phone: contact.phone, + fax : contact.fax, mobile: contact.mobile, publish: contact.publish, dateCreated: contact.dateCreated, lastUpdated: contact.lastUpdated]) } def buildContactForModel(cf, urlContext) { return new LinkedHashMap( - [contact: buildContactModel(cf.contact), role: cf.role, primaryContact: cf.primaryContact, - editor: cf.administrator, notify: cf.notify, dateCreated: cf.dateCreated, lastUpdated: cf.dateLastModified, - uri: "${grailsApplication.config.grails.serverURL}/ws/${urlContext}/${cf.entityUid}/contacts/${cf.id}"]) + [contact: buildContactModel(cf.contact), role: cf.role, primaryContact: cf.primaryContact, + editor : cf.administrator, notify: cf.notify, dateCreated: cf.dateCreated, lastUpdated: cf.dateLastModified, + uri : "${grailsApplication.config.grails.serverURL}/ws/${urlContext}/${cf.entityUid}/contacts/${cf.id}"]) } /** @@ -1015,9 +1063,9 @@ class DataController { ) @Path("/ws/contacts/{id}") @Produces("application/json") - def updateContact () { + def updateContact() { def ok = check(params) - if (!ok){ + if (!ok) { return } def props = params.json @@ -1031,7 +1079,7 @@ class DataController { Contact.withTransaction { c.save(flush: true) } - c.errors.each { log.error(it.toString()) } + c.errors.each { log.error(it.toString()) } addContentLocation "/ws/contacts/${c.id}" def cm = buildContactModel(c) cm.id = c.id @@ -1074,7 +1122,7 @@ class DataController { } @Transactional - def deleteContact () { + def deleteContact() { if (params.id) { // update def c = Contact.get(params.id) @@ -1112,17 +1160,17 @@ class DataController { addContentLocation "/ws/${params.entity}/${params.pg.uid}/contacts" addVaryAcceptHeader() withFormat { - json {render contactList as JSON} + json { render contactList as JSON } csv { def out = new StringWriter() out << "name, role, primary contact, editor, notify, email, phone\n" contactList.each { - out << "\"${(it.contact.firstName + ' ' + it.contact.lastName).trim()}\",\"${it.role}\",${it.primaryContact},${it.editor},${it.notify},${it.contact.email?:""},${it.contact.phone?:""}\n" + out << "\"${(it.contact.firstName + ' ' + it.contact.lastName).trim()}\",\"${it.role}\",${it.primaryContact},${it.editor},${it.notify},${it.contact.email ?: ""},${it.contact.phone ?: ""}\n" } response.addHeader "Content-Type", "text/csv" render out.toString() } - xml {render (contentType: 'text/xml', text: objToXml(contactList, 'contactFors'))} + xml { render(contentType: 'text/xml', text: objToXml(contactList, 'contactFors')) } } } @@ -1156,10 +1204,10 @@ class DataController { xml { render(contentType: 'text/xml', text: objToXml(cm, 'contactFor')) } } } else { - forward(action:'contactsForEntity') + forward(action: 'contactsForEntity') } } else { - forward(action:'contactsForEntity') + forward(action: 'contactsForEntity') } } @@ -1172,7 +1220,7 @@ class DataController { */ def contactsForEntities = { def domain = grailsApplication.getClassForName("au.org.ala.collectory.${capitalise(params.entity)}") - def model = buildContactsModel(domain.list([sort:'name'])) + def model = buildContactsModel(domain.list([sort: 'name'])) addContentLocation "/ws/${params.entity}/contacts" addVaryAcceptHeader() withFormat { @@ -1180,10 +1228,10 @@ class DataController { render model as JSON } csv { - render (contentType: 'text/csv', + render(contentType: 'text/csv', text: SHORT_CONTACTS_HEADER + listToCsv(model)) } - xml {render (contentType: 'text/xml', text: objToXml(model, 'contacts'))} + xml { render(contentType: 'text/xml', text: objToXml(model, 'contacts')) } } } @@ -1210,8 +1258,7 @@ class DataController { def contact = Contact.get(params.id) if (!contact) { badRequest "contact ${params.id} does not exist" - } - else { + } else { def result = collectoryAuthService?.authorisedForUser(contact) renderAsJson result.sorted, result.latestMod, result.keys.toString().encodeAsMD5() } @@ -1240,7 +1287,7 @@ class DataController { */ def notification() { def ok = check(params) - if (!ok){ + if (!ok) { return } //println "notify" @@ -1352,9 +1399,9 @@ class DataController { ) @Path("/ws/{entity}/{uid}/contacts/{id}") @Produces("application/json") - def updateContactFor () { + def updateContactFor() { def ok = check(params) - if (!ok){ + if (!ok) { return } def props = params.json @@ -1368,7 +1415,7 @@ class DataController { Contact.withTransaction { c.save(flush: true) } - c.errors.each { log.error(it.toString()) } + c.errors.each { log.error(it.toString()) } success 'updated' } else { // create @@ -1385,14 +1432,14 @@ class DataController { } } - def deleteContactFor () { + def deleteContactFor() { def ok = check(params) - if (!ok){ + if (!ok) { return } def props = params.json props.userLastModified = session.username - log.error("body = " + props) + log.error("body = " + props) def c = Contact.get(params.id) def cf = ContactFor.findByContactAndEntityUid(c, params.pg.uid) if (cf) { @@ -1450,7 +1497,7 @@ class DataController { } } } - render(contentType: 'text/csv', text:csv) + render(contentType: 'text/csv', text: csv) } private String encodeHints(hints) { @@ -1470,12 +1517,13 @@ class DataController { def writer = new StringWriter() MarkupBuilder xml = new MarkupBuilder(writer) xml."${root}" { - toXml(obj,xml, (root[-1] == 's') ? root[0..-2] : 'item') + toXml(obj, xml, (root[-1] == 's') ? root[0..-2] : 'item') } return writer.toString() } /* called recursively to build xml */ + def toXml(obj, xml, listElement) { if (obj instanceof List) { obj.each { item -> @@ -1484,7 +1532,7 @@ class DataController { } else { obj.each { key, value -> if (value && value instanceof Map) { - xml."${key}" {toXml(value, xml, listElement)} + xml."${key}" { toXml(value, xml, listElement) } } else { xml."${key}"(value) } @@ -1507,8 +1555,8 @@ class DataController { */ def mapToCsv(map) { def out = new StringWriter() - def list = map.collect {key, value -> value} - list.eachWithIndex {it, idx -> + def list = map.collect { key, value -> value } + list.eachWithIndex { it, idx -> out << toCsvItem(it) if (idx == list.size() - 1) { out << '\n' diff --git a/grails-app/controllers/au/org/ala/collectory/LookupController.groovy b/grails-app/controllers/au/org/ala/collectory/LookupController.groovy index b0ed391f..e98289d8 100644 --- a/grails-app/controllers/au/org/ala/collectory/LookupController.groovy +++ b/grails-app/controllers/au/org/ala/collectory/LookupController.groovy @@ -300,6 +300,15 @@ class LookupController { tags = "citations", operationId = "getCitations", summary = "Get citations for a list of data resource UIDs.", + parameters = [ + @Parameter( + name = "Accept", + in = HEADER, + schema = @Schema(implementation = String), + example = "application/json or text/csv or text/plain", + required = false + ) + ], requestBody = @RequestBody( required = true, description = "A JSON array containing dataResource UIDs.", @@ -361,7 +370,6 @@ class LookupController { security = [] ) @Path("/ws/citations") - @Produces("application/json") def citations() { if (params.include) { params.uids = "[${params.include}]" From a1b700037361ec64fd8bdb181d133cd0b5f00f47 Mon Sep 17 00:00:00 2001 From: Adam Collins Date: Tue, 21 Nov 2023 15:23:53 +1000 Subject: [PATCH 03/13] cache DataHub.list --- .../collectory/CollectionController.groovy | 15 ++-- .../ala/collectory/DataHubController.groovy | 7 ++ .../ala/collectory/DataLinkController.groovy | 4 +- .../ala/collectory/LookupController.groovy | 40 +++++++++- .../collectory/ProviderGroupController.groovy | 6 -- .../collectory/ProviderMapController.groovy | 1 - .../ala/collectory/PublicController.groovy | 9 --- .../ala/collectory/ReportsController.groovy | 12 +-- .../ala/collectory/TempDataInterceptor.groovy | 1 - .../au/org/ala/collectory/Collection.groovy | 54 +------------- .../au/org/ala/collectory/DataResource.groovy | 49 +------------ .../au/org/ala/collectory/Institution.groovy | 40 ---------- .../ala/collectory/ActivityLogService.groovy | 6 +- .../ala/collectory/CollectionService.groovy | 69 ++++++++++++++++++ .../collectory/CollectoryAuthService.groovy | 10 ++- .../au/org/ala/collectory/CrudService.groovy | 25 +++---- .../org/ala/collectory/DataHubService.groovy | 27 +++++++ .../ala/collectory/DataLoaderService.groovy | 73 +------------------ .../ala/collectory/DataResourceService.groovy | 63 ++++++++++++++++ .../ala/collectory/InstitutionService.groovy | 57 +++++++++++++++ .../org/ala/collectory/MetadataService.groovy | 1 + .../collectory/ProviderGroupService.groovy | 1 - .../ala/collectory/CollectoryTagLib.groovy | 7 +- .../org/ala/collectory/Classification.groovy | 1 - .../org/ala/collectory/GBIFLoadSummary.groovy | 2 - .../org/ala/collectory/ProviderGroup.groovy | 11 --- 26 files changed, 304 insertions(+), 287 deletions(-) create mode 100644 grails-app/services/au/org/ala/collectory/CollectionService.groovy create mode 100644 grails-app/services/au/org/ala/collectory/DataHubService.groovy create mode 100644 grails-app/services/au/org/ala/collectory/DataResourceService.groovy create mode 100644 grails-app/services/au/org/ala/collectory/InstitutionService.groovy diff --git a/grails-app/controllers/au/org/ala/collectory/CollectionController.groovy b/grails-app/controllers/au/org/ala/collectory/CollectionController.groovy index 0f0f7d69..8c1fdd24 100644 --- a/grails-app/controllers/au/org/ala/collectory/CollectionController.groovy +++ b/grails-app/controllers/au/org/ala/collectory/CollectionController.groovy @@ -1,7 +1,6 @@ package au.org.ala.collectory import grails.converters.JSON -import grails.validation.Validateable import org.springframework.validation.annotation.Validated import java.text.NumberFormat @@ -15,6 +14,8 @@ class CollectionController extends ProviderGroupController { } def idGeneratorService + def collectionService + def institutionService def index = { redirect(action:"list") @@ -113,9 +114,8 @@ class CollectionController extends ProviderGroupController { */ def summary = { Collection collectionInstance = findCollection(params.id) - println ">> summary id = " + params.id if (collectionInstance) { - render collectionInstance.buildSummary() as JSON + render collectionService.buildSummary(collectionInstance.uid) as JSON } else { log.error "Unable to find collection for id = ${params.id}" def error = ["error":"unable to find collection for id = " + params.id] @@ -143,7 +143,12 @@ class CollectionController extends ProviderGroupController { } ProviderGroup pg = ProviderMap.findMatch(inst, coll) if (pg) { - render pg.buildSummary() as JSON + if (pg[0..1] == 'co') { + render collectionService.buildSummary(pg.uid) as JSON + } else { + // institution + render institutionService.buildSummary(pg.uid) as JSON + } } else { def error = ["error":"unable to find collection with inst code = ${inst} and coll code = ${coll}"] render error as JSON @@ -205,7 +210,7 @@ class CollectionController extends ProviderGroupController { } else { collection.collectionType = (params.collectionType as JSON).toString() } - + params.remove('collectionType') // special handling for keywords diff --git a/grails-app/controllers/au/org/ala/collectory/DataHubController.groovy b/grails-app/controllers/au/org/ala/collectory/DataHubController.groovy index 34bc3611..e268ba69 100644 --- a/grails-app/controllers/au/org/ala/collectory/DataHubController.groovy +++ b/grails-app/controllers/au/org/ala/collectory/DataHubController.groovy @@ -2,6 +2,8 @@ package au.org.ala.collectory class DataHubController extends ProviderGroupController { + def grailsCacheAdminService + DataHubController() { entityName = "DataHub" entityNameLower = "dataHub" @@ -22,6 +24,9 @@ class DataHubController extends ProviderGroupController { } def show = { + // After updating members this page is shown + grailsCacheAdminService.clearCache('dataHubCache') + def instance = get(params.id) if (!instance) { flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'dataHub.label', default: 'Data Hub'), params.id])}" @@ -36,6 +41,8 @@ class DataHubController extends ProviderGroupController { } def delete = { + grailsCacheAdminService.clearCache('dataHubCache') + def instance = get(params.id) if (instance) { if (isAdmin()) { diff --git a/grails-app/controllers/au/org/ala/collectory/DataLinkController.groovy b/grails-app/controllers/au/org/ala/collectory/DataLinkController.groovy index 1f47f041..5b992731 100644 --- a/grails-app/controllers/au/org/ala/collectory/DataLinkController.groovy +++ b/grails-app/controllers/au/org/ala/collectory/DataLinkController.groovy @@ -22,8 +22,6 @@ class DataLinkController { } def create = { - println "consumer = ${params.consumer}" - println "provider = ${params.provider}" def dataLinkInstance = new DataLink() dataLinkInstance.properties = params return [dataLinkInstance: dataLinkInstance, returnTo: params.returnTo] @@ -68,7 +66,7 @@ class DataLinkController { if (params.version) { def version = params.version.toLong() if (dataLinkInstance.version > version) { - + dataLinkInstance.errors.rejectValue("version", "default.optimistic.locking.failure", [message(code: 'dataLink.label', default: 'DataLink')] as Object[], "Another user has updated this DataLink while you were editing") render(view: "edit", model: [dataLinkInstance: dataLinkInstance, returnTo: params.returnTo]) return diff --git a/grails-app/controllers/au/org/ala/collectory/LookupController.groovy b/grails-app/controllers/au/org/ala/collectory/LookupController.groovy index e98289d8..8cabae4e 100644 --- a/grails-app/controllers/au/org/ala/collectory/LookupController.groovy +++ b/grails-app/controllers/au/org/ala/collectory/LookupController.groovy @@ -29,6 +29,9 @@ class LookupController { def idGeneratorService def providerGroupService + def dataResourceService + def collectionService + def institutionService static allowedMethods = [citation:['POST','GET']] @@ -97,7 +100,12 @@ class LookupController { } ProviderGroup pg = ProviderMap.findMatch(inst, coll) if (pg) { - render pg.buildSummary() as JSON + if (pg.uid[0..1] == 'co') { + render collectionService.buildSummary(pg.uid) as JSON + } else { + // institution + render institutionService.buildSummary(pg.uid) as JSON + } } else { def error = ["error":"unable to find collection with inst code = ${inst} and coll code = ${coll}"] render error as JSON @@ -136,7 +144,15 @@ class LookupController { // summary for single entity pg = providerGroupService._get(uid) if (pg) { - render pg.buildSummary() as JSON + if (pg.uid[0..1] == 'co') { + render collectionService.buildSummary(pg.uid) as JSON + } else if (pg[0..1] == 'in') { + render institutionService.buildSummary(pg.uid) as JSON + } else if (pg[0..1] == 'dr') { + render dataResourceService.buildSummary(pg.uid) as JSON + } else { + render pg.buildSummary() as JSON + } } else { log.error "Unable to find entity. id = ${params.id}" def error = ["error":"unable to find entity - id = ${params.id}"] @@ -146,7 +162,15 @@ class LookupController { // return summaries for all def list = [] domain.list().each { - list << it.buildSummary() + if (it.uid[0..1] == 'co') { + list << collectionService.buildSummary(it.uid) + } else if (pg[0..1] == 'in') { + list << institutionService.buildSummary(it.uid) + } else if (pg[0..1] == 'dr') { + list << dataResourceService.buildSummary(it.uid) + } else { + list << it.buildSummary() + } } render list as JSON } @@ -209,7 +233,15 @@ class LookupController { instance = findCollection(params.id) } if (instance) { - render instance.buildSummary() as JSON + if (instance.uid[0..1] == 'co') { + render collectionService.buildSummary(instance.uid) as JSON + } else if (pg[0..1] == 'in') { + render institutionService.buildSummary(instance.uid) as JSON + } else if (pg[0..1] == 'dr') { + render dataResourceService.buildSummary(instance.uid) as JSON + } else { + render instance.buildSummary() as JSON + } } else { // we would normally log this as a warning but it occurs too often //log.warning "Unable to find entity for id = ${params.id}" diff --git a/grails-app/controllers/au/org/ala/collectory/ProviderGroupController.groovy b/grails-app/controllers/au/org/ala/collectory/ProviderGroupController.groovy index 8d24d5f3..ad93f461 100644 --- a/grails-app/controllers/au/org/ala/collectory/ProviderGroupController.groovy +++ b/grails-app/controllers/au/org/ala/collectory/ProviderGroupController.groovy @@ -66,11 +66,9 @@ abstract class ProviderGroupController { if (params.version) { def version = params.version.toLong() if (pg.version > version) { - println "db version = ${pg.version} submitted version = ${version}" pg.errors.rejectValue("version", "default.optimistic.locking.failure", [message(code: "${pg.urlForm()}.label", default: pg.entityType())] as Object[], message(code: "provider.group.controller.02", default: "Another user has updated this") + " ${pg.entityType()} " + message(code: "provider.group.controller.03", default: "while you were editing. This page has been refreshed with the current values.")) - println "error added - rendering ${view}" response.setHeader("Content-type", "text/plain; charset=UTF-8") render(view: view, model: [command: pg]) } @@ -192,7 +190,6 @@ abstract class ProviderGroupController { if (c.hasErrors()) { c.errors.each { log.debug("Error saving new contact for ${user} - ${it}") - println "Error saving new contact for ${user} - ${it}" } } } @@ -200,7 +197,6 @@ abstract class ProviderGroupController { } def cancel = { - //println "Cancel - returnTo = ${params.returnTo}" if (params.returnTo) { redirect(uri: params.returnTo) } else { @@ -381,7 +377,6 @@ abstract class ProviderGroupController { def th = pg.taxonomyHints ? JSON.parse(pg.taxonomyHints) : [:] th.range = rangeList pg.taxonomyHints = th as JSON - println pg.taxonomyHints pg.userLastModified = collectoryAuthService?.username() if (!pg.hasErrors()) { @@ -708,7 +703,6 @@ abstract class ProviderGroupController { file.transferTo(f) activityLogService.log collectoryAuthService?.username(), collectoryAuthService?.userInRole(grailsApplication.config.ROLE_ADMIN), Action.UPLOAD_IMAGE, filename } else { - println "reject file of size ${file.size}" pg.errors.rejectValue('imageRef', 'image.too.big', message(code: "provider.group.controller.13", default: "The image you selected is too large. Images are limited to 200KB.")) render(view: "/shared/images", model: [command: pg, target: target]) return diff --git a/grails-app/controllers/au/org/ala/collectory/ProviderMapController.groovy b/grails-app/controllers/au/org/ala/collectory/ProviderMapController.groovy index 08328530..33164a8d 100644 --- a/grails-app/controllers/au/org/ala/collectory/ProviderMapController.groovy +++ b/grails-app/controllers/au/org/ala/collectory/ProviderMapController.groovy @@ -34,7 +34,6 @@ class ProviderMapController { def create = { def providerMapInstance = new ProviderMap() providerMapInstance.properties = params - println "createFor = ${params.createFor}" if (params.createFor) { def pg = Collection.findByUid(params.createFor) as Collection if (pg) { diff --git a/grails-app/controllers/au/org/ala/collectory/PublicController.groovy b/grails-app/controllers/au/org/ala/collectory/PublicController.groovy index 85f7f65b..b34b13b8 100644 --- a/grails-app/controllers/au/org/ala/collectory/PublicController.groovy +++ b/grails-app/controllers/au/org/ala/collectory/PublicController.groovy @@ -243,7 +243,6 @@ class PublicController { response.setHeader("Cache-Control","no-cache") response.addHeader("Cache-Control","no-store") def instance = providerGroupService._get(params.id) - //println ">>debug map key " + grailsApplication.config.google.maps.v2.key if (!instance) { log.error "Unable to find entity for id = ${params.id}" def error = ["error":"unable to find entity for id = " + params.id] @@ -251,17 +250,14 @@ class PublicController { } else { /* get decade breakdown */ def decadeUrl = grailsApplication.config.biocacheServicesUrl+ "/breakdown/collection/decades/${instance.generatePermalink()}.json"; - //println decadeUrl def conn = new URL(decadeUrl).openConnection() conn.setConnectTimeout 1500 def dataTable = null def json try { json = conn.content.text - //println "Response = " + json def decades = JSON.parse(json)?.decades dataTable = buildDecadeDataTable(decades) - //println "dataTable = " + dataTable } catch (SocketTimeoutException e) { log.warn "Timed out getting decade breakdown. URL= ${url}." def result = [error:"Timed out getting decade breakdown.", dataTable: null] @@ -303,7 +299,6 @@ class PublicController { def taxonUrl = grailsApplication.config.biocacheServicesUrl + "/breakdown/{entity}/{uid}?max=" + threshold taxonUrl = taxonUrl.replaceFirst(/\{uid\}/, params.id ?: '') taxonUrl = taxonUrl.replaceFirst(/\{entity\}/, wsEntityForBreakdown(params.id)) - //println "taxonUrl: " + taxonUrl def conn = new URL(taxonUrl).openConnection() def jsonResponse = null @@ -312,7 +307,6 @@ class PublicController { conn.setConnectTimeout(10000) conn.setReadTimeout(50000) jsonResponse = conn.content.text - //println "Response = " + json //sleep delay breakdown = JSON.parse(jsonResponse)?.breakdown ?: JSON.parse(jsonResponse) } catch (SocketTimeoutException e) { @@ -361,7 +355,6 @@ class PublicController { conn.setConnectTimeout 10000 conn.setReadTimeout 50000 json = conn.content.text - //println "Response = " + json def breakdown = JSON.parse(json)?.breakdown ?: JSON.parse(json) if (breakdown && breakdown.toString() != "null") { dataTable = buildTaxonChartDataTable(breakdown,params.rank,params.name) @@ -803,9 +796,7 @@ class PublicController { // must have at least one value to build a query if (instCodes || collCodes) { def instClause = instCodes ? buildSearchClause("inst", instCodes) : "" - //println instClause def collClause = collCodes ? buildSearchClause("coll", collCodes) : "" - //println collClause def url = grailsApplication.config.biocacheUiURL + "/searchForUID.JSON?pageSize=0" + instClause + collClause } else { return "" diff --git a/grails-app/controllers/au/org/ala/collectory/ReportsController.groovy b/grails-app/controllers/au/org/ala/collectory/ReportsController.groovy index 944e8413..44e00b76 100644 --- a/grails-app/controllers/au/org/ala/collectory/ReportsController.groovy +++ b/grails-app/controllers/au/org/ala/collectory/ReportsController.groovy @@ -5,7 +5,7 @@ import grails.converters.JSON class ReportsController { - def collectoryAuthService, activityLogService + def collectoryAuthService, activityLogService, collectionService, institutionService def index = { redirect action: list, params: params @@ -86,7 +86,9 @@ class ReportsController { } def codes = { - [codeSummaries: (ProviderMap.list().collect { it.collection.buildSummary() }).sort {it.name}] + [codeSummaries: (ProviderMap.list().collect { + collectionService.buildSummary(it.collection.uid) + }).sort {it.name}] } def duplicateContacts = { @@ -217,13 +219,13 @@ class ReportsController { def attributions = { def collAttributions = [] Collection.list([sort: 'name']).each { - ProviderGroupSummary pgs = it.buildSummary() + ProviderGroupSummary pgs = collectionService.buildSummary(it.uid) List attribs = it.getAttributionList() def ats = new Attributions(pgs, attribs) collAttributions << ats } def instAttributions = Institution.list([sort: 'name']).collect { - ProviderGroupSummary pgs = it.buildSummary() + ProviderGroupSummary pgs = institutionService.buildSummary(it.uid) List attribs = it.getAttributionList() new Attributions(pgs, attribs) } @@ -248,7 +250,7 @@ class ReportsController { } // compare to num digistised if (count == 0 || (count / it.numRecordsDigitised) < 0.7) { - mrs << [collection:it.buildSummary(), biocacheCount: count, claimed: it.numRecordsDigitised] + mrs << [collection: collectionService.buildSummary(it.uid), biocacheCount: count, claimed: it.numRecordsDigitised] } } } diff --git a/grails-app/controllers/au/org/ala/collectory/TempDataInterceptor.groovy b/grails-app/controllers/au/org/ala/collectory/TempDataInterceptor.groovy index 6d8be60a..0e704350 100644 --- a/grails-app/controllers/au/org/ala/collectory/TempDataInterceptor.groovy +++ b/grails-app/controllers/au/org/ala/collectory/TempDataInterceptor.groovy @@ -47,7 +47,6 @@ class TempDataInterceptor { params.json = request.JSON return true } catch (Exception e) { - println "exception caught ${e}" if (request.getContentLength() > 0) { badRequest 'cannot parse request body as JSON' return false diff --git a/grails-app/domain/au/org/ala/collectory/Collection.groovy b/grails-app/domain/au/org/ala/collectory/Collection.groovy index d42e8b66..ef4ca6e6 100644 --- a/grails-app/domain/au/org/ala/collectory/Collection.groovy +++ b/grails-app/domain/au/org/ala/collectory/Collection.groovy @@ -23,7 +23,7 @@ class Collection implements ProviderGroup, Serializable { static final String ENTITY_PREFIX = 'co' static auditable = [ignore: ['version','dateCreated','lastUpdated','userLastModified']] - + String collectionType // list of type of collection as JSON e.g ['live', 'preserved', 'tissue', 'DNA'] String active // tdwg developmentStatus int numRecords @@ -270,58 +270,6 @@ class Collection implements ProviderGroup, Serializable { return false } - /** - * Returns a list of all hubs this collection belongs to. - * - * @return list of DataHub - */ - List listHubMembership() { - DataHub.list().findAll {it.isCollectionMember(uid)} - } - - /** - * Returns a summary of the collection including: - * - id - * - name - * - acronym - * - lsid if available - * - institution (id,uid, name & logo url) if available - * - description - * - provider codes for matching with biocache records - * - * @return CollectionSummary - */ - CollectionSummary buildSummary() { - CollectionSummary cs = init(new CollectionSummary()) as CollectionSummary - if (institution) { - cs.institutionName = institution.name - cs.institutionId = institution.id - cs.institutionUid = institution.uid - if (institution.logoRef?.file) { - cs.institutionLogoUrl = au.org.ala.collectory.Utilities.buildInstitutionLogoUrl(institution.logoRef.file) - } - } - - cs.collectionId = cs.id - cs.collectionUid = cs.uid - cs.collectionName = cs.name - - cs.derivedInstCodes = getListOfInstitutionCodesForLookup() - cs.derivedCollCodes = getListOfCollectionCodesForLookup() - cs.hubMembership = listHubMembership().collect { [uid: it.uid, name: it.name] } - listProviders().each { - def pg = Collection.findByUid(it) - if (pg) { - if (it[0..1] == 'dp') { - cs.relatedDataProviders << [uid: pg.uid, name: pg.name] - } else { - cs.relatedDataResources << [uid: pg.uid, name: pg.name] - } - } - } - return cs - } - /** * Returns true if: * a) parent institution is a partner diff --git a/grails-app/domain/au/org/ala/collectory/DataResource.groovy b/grails-app/domain/au/org/ala/collectory/DataResource.groovy index 6d548b7b..2c4ad3fd 100644 --- a/grails-app/domain/au/org/ala/collectory/DataResource.groovy +++ b/grails-app/domain/au/org/ala/collectory/DataResource.groovy @@ -49,6 +49,8 @@ class DataResource implements ProviderGroup, Serializable { dataCollectionProtocolDoc type: "text" suitableFor type: "text" suitableForOtherDetail type: "text" + dataProvider fetch: 'join' + institution fetch: 'join' } String rights @@ -176,44 +178,6 @@ class DataResource implements ProviderGroup, Serializable { return false; } - /** - * Returns a summary of the data provider including: - * - id - * - name - * - acronym - * - lsid if available - * - description - * - data provider name, id and uid - * - * @return CollectionSummary - */ - DataResourceSummary buildSummary() { - DataResourceSummary drs = init(new DataResourceSummary()) as DataResourceSummary - drs.dataProvider = dataProvider?.name - drs.dataProviderId = dataProvider?.id - drs.dataProviderUid = dataProvider?.uid - drs.downloadLimit = downloadLimit - - drs.hubMembership = listHubMembership().collect { [uid: it.uid, name: it.name] } - def consumers = listConsumers() - consumers.each { - def pg = DataResource.findByUid(it) - if (pg) { - if (it[0..1] == 'co') { - drs.relatedCollections << [uid: pg.uid, name: pg.name] - } else { - drs.relatedInstitutions << [uid: pg.uid, name: pg.name] - } - } - } - // for backward compatibility - if (drs.relatedInstitutions) { - drs.institution = drs.relatedInstitutions[0].name - drs.institutionUid = drs.relatedInstitutions[0].uid - } - return drs - } - Boolean isVerified(){ if(defaultDarwinCoreValues){ @@ -265,15 +229,6 @@ class DataResource implements ProviderGroup, Serializable { save(flush:true) } - /** - * Returns a list of all hubs this resource belongs to. - * - * @return list of DataHub - */ - List listHubMembership() { - DataHub.list().findAll {it.isDataResourceMember(uid)} - } - /** * True if this resource uses a CC license. * @return diff --git a/grails-app/domain/au/org/ala/collectory/Institution.groovy b/grails-app/domain/au/org/ala/collectory/Institution.groovy index 55748fb6..22a4fa5d 100644 --- a/grails-app/domain/au/org/ala/collectory/Institution.groovy +++ b/grails-app/domain/au/org/ala/collectory/Institution.groovy @@ -64,37 +64,6 @@ class Institution implements ProviderGroup, Serializable { sort: 'name' } - /** - * Returns a summary of the institution including: - * - id - * - name - * - acronym - * - lsid if available - * - description - * - * @return InstitutionSummary - * @.history 2-8-2010 removed inst codes as these are now related only to collections (can be added back with a different mechanism if required) - */ - InstitutionSummary buildSummary() { - InstitutionSummary is = init(new InstitutionSummary()) as InstitutionSummary - is.institutionId = dbId() - is.institutionUid = uid - is.institutionName = name - is.collections = collections.collect { [it.uid, it.name] } - listProviders().each { - def pg = Institution.findByUid(it) - if (pg) { - if (it[0..1] == 'dp') { - is.relatedDataProviders << [uid: pg.uid, name: pg.name] - } else { - is.relatedDataResources << [uid: pg.uid, name: pg.name] - } - } - } - is.hubMembership = listHubMembership().collect { [uid: it.uid, name: it.name] } - return is - } - /** * Returns true if: * a) has membership of a collection network (hub) (assumed that all hubs are partners) @@ -147,15 +116,6 @@ class Institution implements ProviderGroup, Serializable { return result } - /** - * Returns a list of all hubs this collection belongs to. - * - * @return list of DataHub - */ - List listHubMembership() { - DataHub.list().findAll {it.isInstitutionMember(uid)} - } - /** * List of institutions that include this as a child institution. * diff --git a/grails-app/services/au/org/ala/collectory/ActivityLogService.groovy b/grails-app/services/au/org/ala/collectory/ActivityLogService.groovy index 145047a1..cfa2ad04 100644 --- a/grails-app/services/au/org/ala/collectory/ActivityLogService.groovy +++ b/grails-app/services/au/org/ala/collectory/ActivityLogService.groovy @@ -10,7 +10,6 @@ class ActivityLogService { def log(params) { def al = new ActivityLog(params) al.timestamp = new Date() - al.errors.each {println it} al.save(flush:true) } @@ -23,7 +22,6 @@ class ActivityLogService { //def a = Actions.valueOf(Actions.class, action) //def actionText = a ? a.toString() : action def al = new ActivityLog(timestamp: new Date(), user: user, admin: isAdmin, action: action.toString()) - al.errors.each {println it} al.save(flush:true) } @@ -36,9 +34,7 @@ class ActivityLogService { def log(String user, boolean isAdmin, Action action, String item) { def al = new ActivityLog(timestamp: new Date(), user: user, admin: isAdmin, action: action.toString() + " " + item) al.validate() - if (al.hasErrors()) { - al.errors.each {println it} - } + al.save(flush:true) } diff --git a/grails-app/services/au/org/ala/collectory/CollectionService.groovy b/grails-app/services/au/org/ala/collectory/CollectionService.groovy new file mode 100644 index 00000000..dcacf22a --- /dev/null +++ b/grails-app/services/au/org/ala/collectory/CollectionService.groovy @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2011 Atlas of Living Australia + * All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + */ +package au.org.ala.collectory + + +import grails.plugin.cache.Cacheable + +class CollectionService { + + static transactional = false + + def dataHubService + + /** + * Returns a summary of the collection including: + * - id + * - name + * - acronym + * - lsid if available + * - institution (id,uid, name & logo url) if available + * - description + * - provider codes for matching with biocache records + * + * @return CollectionSummary + */ + CollectionSummary buildSummary(Collection collection) { + CollectionSummary cs = collection.init(new CollectionSummary()) as CollectionSummary + if (collection.institution) { + cs.institutionName = collection.institution.name + cs.institutionId = collection.institution.id + cs.institutionUid = collection.institution.uid + if (collection.institution.logoRef?.file) { + cs.institutionLogoUrl = au.org.ala.collectory.Utilities.buildInstitutionLogoUrl(collection.institution.logoRef.file) + } + } + + cs.collectionId = cs.id + cs.collectionUid = cs.uid + cs.collectionName = cs.name + + cs.derivedInstCodes = collection.getListOfInstitutionCodesForLookup() + cs.derivedCollCodes = collection.getListOfCollectionCodesForLookup() + cs.hubMembership = dataHubService.listDataHubs().findAll {it.isCollectionMember(collection.uid)}.collect { [uid: it.uid, name: it.name] } + collection.listProviders().each { + def pg = Collection.findByUid(it) + if (pg) { + if (it[0..1] == 'dp') { + cs.relatedDataProviders << [uid: pg.uid, name: pg.name] + } else { + cs.relatedDataResources << [uid: pg.uid, name: pg.name] + } + } + } + return cs + } + +} diff --git a/grails-app/services/au/org/ala/collectory/CollectoryAuthService.groovy b/grails-app/services/au/org/ala/collectory/CollectoryAuthService.groovy index fa378b2f..15972fa3 100644 --- a/grails-app/services/au/org/ala/collectory/CollectoryAuthService.groovy +++ b/grails-app/services/au/org/ala/collectory/CollectoryAuthService.groovy @@ -255,10 +255,12 @@ class CollectoryAuthService{ Boolean authorised = false if(grailsApplication.config.security.apikey.checkEnabled.toBoolean() || grailsApplication.config.security.apikey.enabled.toBoolean()){ def apiKey = getApiKey(params, request) - Call checkApiKeyCall = apiKeyClient.checkApiKey(apiKey) - final Response checkApiKeyResponse = checkApiKeyCall.execute() - CheckApiKeyResult apiKeyCheck = checkApiKeyResponse.body(); - authorised = apiKeyCheck.isValid() + if (apiKey) { + Call checkApiKeyCall = apiKeyClient.checkApiKey(apiKey) + final Response checkApiKeyResponse = checkApiKeyCall.execute() + CheckApiKeyResult apiKeyCheck = checkApiKeyResponse.body(); + authorised = apiKeyCheck.isValid() + } } if(!authorised){ diff --git a/grails-app/services/au/org/ala/collectory/CrudService.groovy b/grails-app/services/au/org/ala/collectory/CrudService.groovy index 511b003f..27c631e4 100644 --- a/grails-app/services/au/org/ala/collectory/CrudService.groovy +++ b/grails-app/services/au/org/ala/collectory/CrudService.groovy @@ -17,6 +17,7 @@ class CrudService { def grailsApplication def idGeneratorService def providerGroupService + def dataHubService static baseStringProperties = ['guid','name','acronym','phone','email','state','pubShortDescription', 'pubDescription','techDescription','notes', 'isALAPartner','focus','attributions', @@ -157,7 +158,7 @@ class CrudService { } return dp } - + def updateDataProvider(dp, obj) { updateBaseProperties(dp, obj) updateDataProviderProperties(dp, obj) @@ -327,7 +328,7 @@ class CrudService { } use (OutputFormat) { networkMembership = p.networkMembership?.formatNetworkMembership() - hubMembership = p.listHubMembership()?.formatHubMembership() + hubMembership = dataHubService.listDataHubs()?.findAll {it.isDataResourceMember(p.uid)}?.formatHubMembership() taxonomyCoverageHints = JSONHelper.taxonomyHints(p.taxonomyHints) attributions = p.attributionList.formatAttributions() dateCreated = p.dateCreated @@ -355,9 +356,9 @@ class CrudService { riskAssessment = p.riskAssessment } contentTypes = p.contentTypes ? p.contentTypes.formatJSON() : [] - if (p.listConsumers()) { - linkedRecordConsumers = formatEntitiesFromUids(p.listConsumers()) - } +// if (p.listConsumers()) { +// linkedRecordConsumers = formatEntitiesFromUids(p.listConsumers()) +// } if (p.connectionParameters) { def connParams = p.connectionParameters.formatJSON() if (!apiKey){ @@ -371,7 +372,7 @@ class CrudService { if (p.defaultDarwinCoreValues) { defaultDarwinCoreValues = p.defaultDarwinCoreValues.formatJSON() } - hasMappedCollections = p.hasMappedCollections() +// hasMappedCollections = p.hasMappedCollections() status = p.status provenance = p.provenance harvestFrequency = p.harvestFrequency @@ -386,9 +387,9 @@ class CrudService { isShareableWithGBIF = p.isShareableWithGBIF verified = p.isVerified() gbifRegistryKey = p.gbifRegistryKey - if (p.externalIdentifiers) { - externalIdentifiers = p.externalIdentifiers.formatExternalIdentifiers() - } +// if (p.externalIdentifiers) { +// externalIdentifiers = p.externalIdentifiers.formatExternalIdentifiers() +// } doi = p.gbifDoi } } @@ -571,7 +572,7 @@ class CrudService { } use (OutputFormat) { networkMembership = p.networkMembership?.formatNetworkMembership() - hubMembership = p.listHubMembership()?.formatHubMembership() + hubMembership = dataHubService.listDataHubs()?.findAll {it.isInstitutionMember(p.uid)}?.formatHubMembership() attributions = p.attributionList.formatAttributions() dateCreated = p.dateCreated lastUpdated = p.lastUpdated @@ -662,7 +663,7 @@ class CrudService { } use (OutputFormat) { networkMembership = p.networkMembership?.formatNetworkMembership() - hubMembership = p.listHubMembership()?.formatHubMembership() + hubMembership = dataHubService.listDataHubs()?.findAll {it.isCollectionMember(p.uid)}?.formatHubMembership() taxonomyCoverageHints = JSONHelper.taxonomyHints(p.taxonomyHints) attributions = p.attributionList.formatAttributions() @@ -905,7 +906,6 @@ class CrudService { } // null objects are copies of JSONObject.NULL - set them to Java null [baseStringProperties,dataResourceStringProperties].flatten().each { - //println "checking base string property ${it} " + obj.has(it) ? "exists - " + obj."${it}" : "absent" if (obj.has(it) && obj."${it}".toString() == 'null') { obj."${it}" = null } @@ -989,7 +989,6 @@ class OutputFormat { static def formatAttributions(List list) { def result = [] list.each { - //println "attribution ${it.name} - ${it.url}" // lookup attribution result << [name: it.name, url: it.url] } diff --git a/grails-app/services/au/org/ala/collectory/DataHubService.groovy b/grails-app/services/au/org/ala/collectory/DataHubService.groovy new file mode 100644 index 00000000..e095a4fa --- /dev/null +++ b/grails-app/services/au/org/ala/collectory/DataHubService.groovy @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2011 Atlas of Living Australia + * All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + */ +package au.org.ala.collectory + +import grails.plugin.cache.Cacheable + +class DataHubService { + + static transactional = false + + @Cacheable('dataHubCache') + def listDataHubs() { + DataHub.list() + } +} diff --git a/grails-app/services/au/org/ala/collectory/DataLoaderService.groovy b/grails-app/services/au/org/ala/collectory/DataLoaderService.groovy index e0cd433e..e88b8d5e 100644 --- a/grails-app/services/au/org/ala/collectory/DataLoaderService.groovy +++ b/grails-app/services/au/org/ala/collectory/DataLoaderService.groovy @@ -44,10 +44,9 @@ class DataLoaderService { */ def params = [:] dataProvidercolumns.eachWithIndex {it, i -> - //println "i=${i} value=${nextLine[i]}" if (nextLine[i]) { // eliminate any \ used for line breaks - params[it] = nextLine[i] //.replaceAll("" + (92 as char)," ") + params[it] = nextLine[i] } } @@ -100,12 +99,7 @@ class DataLoaderService { def address = params.address params.remove('address') // don't apply un-parsed address directly to entity if (hasValue(address)) { - //println "---------------" - //println "${params.uid} address = ${address}" def addressParams = parseAddress(address) - /*addressParams.each {k,v -> - println "${k} = ${v}" - }*/ if (dp.address) { // dp.address.properties = addressParams doesn't work - readonly for some reason dp.address.street = addressParams.street @@ -155,10 +149,9 @@ class DataLoaderService { */ def params = [:] dataResourcecolumns.eachWithIndex {it, i -> - //println "i=${i} value=${nextLine[i]}" if (nextLine[i]) { // eliminate any \ used for line breaks - params[it] = nextLine[i] //.replaceAll("" + (92 as char)," ") + params[it] = nextLine[i] } } @@ -224,30 +217,7 @@ class DataLoaderService { contactFors = (contactFors)?:[] contactFors.add it contactMap.put(entityId, contactFors) - //println "added contact ${it.contact.id} to entity ${entityId}" } - /*imp.contact.each { - Contact ct = new Contact() - ct.id = it.id - ct.firstName = load(it.firstName) - ct.lastName = load(it.lastName) - ct.phone = load(it.phone) - ct.fax = load(it.fax) - ct.title = load(it.title) - ct.email = load(it.email) - ct.userLastModified = load(it.userLastModified) - ct.notes = load(it.notes) - ct.mobile = load(it.mobile) - // no point in these as Grails overrides them - ct.dateCreated = loadDate(it.dateCreated) - ct.lastUpdated = loadDate(it.dateLastModified) - - if (ct.hasErrors()) { - ct.errors.each {println it} - } else { - ct.save() - } - }*/ // clear institutions sql.execute("delete from provider_map_provider_code") sql.execute("delete from provider_map") @@ -739,7 +709,7 @@ class DataLoaderService { * save */ ProviderGroup provider - + // check whether it's really an institution if (recogniseInstitution(params.name)) { /* provider */ @@ -802,34 +772,6 @@ class DataLoaderService { provider.numRecords = buildSize(params) provider.userLastModified = "BCI loader" - /*/ create or assign infosource only if there is some data - String webServiceUri = params.webServiceUri - String webServiceType = params.webServiceProtocol - - if (webServiceUri) { - // see if there is an existing infosource that matches the access parameters - InfoSource is = InfoSource.list().find { - it.getWebServiceUri() == webServiceUri && - it.getWebServiceProtocol() == webServiceType - } - if (!is) { - // create a new one - is = new InfoSource(title: "created for " + provider.name) - is.setWebServiceUri webServiceUri - is.setWebServiceProtocol webServiceType - } - provider.infoSource = is - is.addToCollections(provider) - is.userLastModified = "BCI loader" - - is.save() - if (!is.validate()) { - is.errors.each { - println it - } - } - }*/ - } log.info ">> Loading ${provider?.name} as ${provider.groupType}" @@ -1133,8 +1075,6 @@ class DataLoaderService { def params = [:] // parse address eg Plant Pathology Branch, DPIand Fisheries, 80 meiers Rd, Indooroopilly, QLD 4069 address = trimTrailing(address) - //println "---------------" - //println "full address = " + address /* remove possible country at end */ ['australia','new zealand'].each { country -> @@ -1145,7 +1085,6 @@ class DataLoaderService { /* POSTCODE */ def postcode = address[address.length() - 4 .. address.length() - 1] - //println "possible postcode = " + postcode try { Integer.parseInt(postcode) address = address[0 .. address.length() - 5] @@ -1176,9 +1115,6 @@ class DataLoaderService { address = trimTrailing(address) def bits = address.tokenize(",") - /*println "<>"*/ if (bits.size > 1) { // if remainder contains a comma then assume the right-most bit is the city @@ -1211,9 +1147,6 @@ class DataLoaderService { if (postcode) {params.postcode = postcode} if (postBox) {params.postBox = postBox} - /*params.each {k,v -> - println "${k} = ${v}" - }*/ return params } diff --git a/grails-app/services/au/org/ala/collectory/DataResourceService.groovy b/grails-app/services/au/org/ala/collectory/DataResourceService.groovy new file mode 100644 index 00000000..04dcb2e0 --- /dev/null +++ b/grails-app/services/au/org/ala/collectory/DataResourceService.groovy @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2011 Atlas of Living Australia + * All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + */ +package au.org.ala.collectory + + +import grails.plugin.cache.Cacheable + +class DataResourceService { + + static transactional = false + + def dataHubService + + /** + * Returns a summary of the data provider including: + * - id + * - name + * - acronym + * - lsid if available + * - description + * - data provider name, id and uid + * + * @return CollectionSummary + */ + DataResourceSummary buildSummary(DataResource dataResource) { + DataResourceSummary drs = dataResource.init(new DataResourceSummary()) as DataResourceSummary + drs.dataProvider = dataResource.dataProvider?.name + drs.dataProviderId = dataResource.dataProvider?.id + drs.dataProviderUid = dataResource.dataProvider?.uid + drs.downloadLimit = dataResource.downloadLimit + + drs.hubMembership = dataHubService.listDataHubs().findAll {it.isDataResourceMember(dataResource.uid)}.collect { [uid: it.uid, name: it.name] } + def consumers = dataResource.listConsumers() + consumers.each { + def pg = DataResource.findByUid(it) + if (pg) { + if (it[0..1] == 'co') { + drs.relatedCollections << [uid: pg.uid, name: pg.name] + } else { + drs.relatedInstitutions << [uid: pg.uid, name: pg.name] + } + } + } + // for backward compatibility + if (drs.relatedInstitutions) { + drs.institution = drs.relatedInstitutions[0].name + drs.institutionUid = drs.relatedInstitutions[0].uid + } + return drs + } +} diff --git a/grails-app/services/au/org/ala/collectory/InstitutionService.groovy b/grails-app/services/au/org/ala/collectory/InstitutionService.groovy new file mode 100644 index 00000000..51aeaf41 --- /dev/null +++ b/grails-app/services/au/org/ala/collectory/InstitutionService.groovy @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 Atlas of Living Australia + * All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + */ +package au.org.ala.collectory + + +import grails.plugin.cache.Cacheable + +class InstitutionService { + + static transactional = false + + def dataHubService + + /** + * Returns a summary of the institution including: + * - id + * - name + * - acronym + * - lsid if available + * - description + * + * @return InstitutionSummary + * @.history 2-8-2010 removed inst codes as these are now related only to collections (can be added back with a different mechanism if required) + */ + InstitutionSummary buildSummary(Institution institution) { + InstitutionSummary is = institution.init(new InstitutionSummary()) as InstitutionSummary + is.institutionId = institution.dbId() + is.institutionUid = institution.uid + is.institutionName = institution.name + is.collections = institution.collections.collect { [it.uid, it.name] } + institution.listProviders().each { + def pg = Institution.findByUid(it) + if (pg) { + if (it[0..1] == 'dp') { + is.relatedDataProviders << [uid: pg.uid, name: pg.name] + } else { + is.relatedDataResources << [uid: pg.uid, name: pg.name] + } + } + } + is.hubMembership = dataHubService.listDataHubs().findAll {it.isInstitutionMember(institution.uid)}.collect { [uid: it.uid, name: it.name] } + return is + } + +} diff --git a/grails-app/services/au/org/ala/collectory/MetadataService.groovy b/grails-app/services/au/org/ala/collectory/MetadataService.groovy index f1e2fcc9..1e8b4366 100644 --- a/grails-app/services/au/org/ala/collectory/MetadataService.groovy +++ b/grails-app/services/au/org/ala/collectory/MetadataService.groovy @@ -15,6 +15,7 @@ package au.org.ala.collectory import grails.converters.JSON +import grails.plugin.cache.Cacheable class MetadataService { diff --git a/grails-app/services/au/org/ala/collectory/ProviderGroupService.groovy b/grails-app/services/au/org/ala/collectory/ProviderGroupService.groovy index 9e283e0d..f5e1a878 100644 --- a/grails-app/services/au/org/ala/collectory/ProviderGroupService.groovy +++ b/grails-app/services/au/org/ala/collectory/ProviderGroupService.groovy @@ -255,7 +255,6 @@ class ProviderGroupService { def th = pg.taxonomyHints ? JSON.parse(pg.taxonomyHints) : [:] th.range = rangeList pg.taxonomyHints = th as JSON - println pg.taxonomyHints pg.userLastModified = collectoryAuthService?.username() if (!pg.hasErrors() && pg.save(flush: true)) { diff --git a/grails-app/taglib/au/org/ala/collectory/CollectoryTagLib.groovy b/grails-app/taglib/au/org/ala/collectory/CollectoryTagLib.groovy index e93267bf..75536461 100644 --- a/grails-app/taglib/au/org/ala/collectory/CollectoryTagLib.groovy +++ b/grails-app/taglib/au/org/ala/collectory/CollectoryTagLib.groovy @@ -686,7 +686,7 @@ class CollectoryTagLib { if (!email) email = body().toString() int index = email.indexOf('@') - //println "index=${index}" + if (index > 0) { email = email.replaceAll("@", strEncodedAtSign) } @@ -1084,8 +1084,6 @@ class CollectoryTagLib { * Draw elements for taxa breakdown chart */ def taxonChart = { attrs -> - //println "taxonChart records link" - //println buildRecordsUrl(attrs.uid) out << "