From 02ac1344303e1be2860464cc9f72a92deefe96f1 Mon Sep 17 00:00:00 2001 From: temi Date: Thu, 12 Sep 2024 16:47:55 +1000 Subject: [PATCH] #1007 - gets list of collection ids of a user - fixed issue with externalIds not saving on Activity entity - added script to update external ids on saved activities --- .../org/ala/ecodata/ParatooController.groovy | 18 ++++ .../au/org/ala/ecodata/UrlMappings.groovy | 6 ++ .../domain/au/org/ala/ecodata/Activity.groovy | 6 +- .../au/org/ala/ecodata/ParatooService.groovy | 17 ++++ .../5.0/updateMissingActivityExternalIds.js | 26 ++++++ scripts/utils/audit.js | 88 +++++++++++++++++++ .../org/ala/ecodata/ParatooServiceSpec.groovy | 46 ++++++++++ 7 files changed, 202 insertions(+), 5 deletions(-) create mode 100644 scripts/releases/5.0/updateMissingActivityExternalIds.js create mode 100644 scripts/utils/audit.js diff --git a/grails-app/controllers/au/org/ala/ecodata/ParatooController.groovy b/grails-app/controllers/au/org/ala/ecodata/ParatooController.groovy index 577908d99..c0dd5d7bb 100644 --- a/grails-app/controllers/au/org/ala/ecodata/ParatooController.groovy +++ b/grails-app/controllers/au/org/ala/ecodata/ParatooController.groovy @@ -119,6 +119,24 @@ class ParatooController { respond projects: paratooService.userProjects(userService.currentUserDetails.userId) } + @GET + @SecurityRequirements([@SecurityRequirement(name = "jwt"), @SecurityRequirement(name = "openIdConnect"), @SecurityRequirement(name = "oauth")]) + @Path("/get-all-collections") + @Operation( + method = "GET", + summary = "Gets the list of all org minted uuids submitted by an authenticated user.", + description = "Gets the list of all org minted uuids submitted by an authenticated user.", + responses = [ + @ApiResponse(responseCode = "200", description = "Returns the list of all org minted uuids submitted by the user.", content = @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = Map.class)))), + @ApiResponse(responseCode = "403", description = "Forbidden"), + @ApiResponse(responseCode = "404", description = "Not found") + ], + tags = "Org Interface" + ) + def userCollections() { + respond collections: paratooService.userCollections(userService.currentUserDetails.userId) + } + @SkipApiKeyCheck @POST @Path("/validate-token") diff --git a/grails-app/controllers/au/org/ala/ecodata/UrlMappings.groovy b/grails-app/controllers/au/org/ala/ecodata/UrlMappings.groovy index c759f7a07..4e5d91d5d 100644 --- a/grails-app/controllers/au/org/ala/ecodata/UrlMappings.groovy +++ b/grails-app/controllers/au/org/ala/ecodata/UrlMappings.groovy @@ -237,6 +237,12 @@ class UrlMappings { action = 'hasWriteAccess' } + "/ws/paratoo/get-all-collections" { + controller = 'paratoo' + action = [GET:'userCollections', OPTIONS:'options'] + } + + "/ws/paratoo/validate-token" { controller = 'paratoo' action = [POST:'validateToken', OPTIONS:'options'] diff --git a/grails-app/domain/au/org/ala/ecodata/Activity.groovy b/grails-app/domain/au/org/ala/ecodata/Activity.groovy index f79fc8124..4a982afac 100644 --- a/grails-app/domain/au/org/ala/ecodata/Activity.groovy +++ b/grails-app/domain/au/org/ala/ecodata/Activity.groovy @@ -1,12 +1,7 @@ package au.org.ala.ecodata import au.org.ala.ecodata.graphql.mappers.ActivityGraphQLMapper -import grails.util.Holders -import graphql.schema.DataFetcher -import graphql.schema.DataFetchingEnvironment import org.bson.types.ObjectId -import org.grails.gorm.graphql.entity.dsl.GraphQLMapping - /** * Currently this holds both activities and assessments. */ @@ -44,6 +39,7 @@ class Activity { } static hasMany = [externalIds:ExternalId] + static embedded = ['externalIds'] ObjectId id String activityId diff --git a/grails-app/services/au/org/ala/ecodata/ParatooService.groovy b/grails-app/services/au/org/ala/ecodata/ParatooService.groovy index 73088d92a..febfb3aff 100644 --- a/grails-app/services/au/org/ala/ecodata/ParatooService.groovy +++ b/grails-app/services/au/org/ala/ecodata/ParatooService.groovy @@ -100,6 +100,23 @@ class ParatooService { projects.findAll { it.protocols } } + List userCollections (String userId) { + Set collectionIds = new HashSet() + List activities = Activity.createCriteria().list { + eq('userId', userId) + ne('status', Status.DELETED) + eq('externalIds.idType', ExternalId.IdType.MONITOR_MINTED_COLLECTION_ID) + } + + activities?.each { Activity activity -> + ExternalId externalId = activity.externalIds?.find { it.idType == ExternalId.IdType.MONITOR_MINTED_COLLECTION_ID } + if (externalId?.externalId) + collectionIds.add(externalId.externalId) + } + + collectionIds.toList() + } + private List findProjectProtocols(ParatooProject project) { log.debug "Finding protocols for ${project.id} ${project.name}" List protocols = [] diff --git a/scripts/releases/5.0/updateMissingActivityExternalIds.js b/scripts/releases/5.0/updateMissingActivityExternalIds.js new file mode 100644 index 000000000..94b1f37da --- /dev/null +++ b/scripts/releases/5.0/updateMissingActivityExternalIds.js @@ -0,0 +1,26 @@ +load('../../utils/audit.js'); +let adminUserId = 'system'; +let count = 0; +db.activity.find({"description" : "Activity submitted by monitor"}).forEach(function(activity) { + if (!activity.externalIds || activity.externalIds.length == 0) { + var project = db.project.findOne({projectId: activity.projectId}); + if (project && project.custom && project.custom.dataSets) { + var dataset = project.custom.dataSets.find(function(dataset) { + return dataset.activityId == activity.activityId; + }); + + if (dataset) { + activity.externalIds = [{idType: "MONITOR_MINTED_COLLECTION_ID", externalId: dataset.dataSetId}]; + db.activity.replaceOne({activityId: activity.activityId}, activity); + audit(activity, activity.activityId, 'au.org.ala.ecodata.Activity', adminUserId); + print(`Updated activity: + ${activity.activityId} + with dataSetId: ${dataset.dataSetId}`); + count ++; + } + else { + print(`Activity: ${activity.activityId} does not have a dataset`); + } + } + } +}); + +print(`Updated ${count} activities`); \ No newline at end of file diff --git a/scripts/utils/audit.js b/scripts/utils/audit.js new file mode 100644 index 000000000..ce98d7003 --- /dev/null +++ b/scripts/utils/audit.js @@ -0,0 +1,88 @@ +/** Inserts a document into the auditMessage collection */ +function audit(entity, entityId, type, userId, projectId, eventType) { + var auditMessage = { + date: ISODate(), + entity: entity, + eventType: eventType || 'Update', + entityType: type, + entityId: entityId, + userId: userId + }; + if (entity.projectId || projectId) { + auditMessage.projectId = (entity.projectId || projectId); + } + db.auditMessage.insertOne(auditMessage); +} + +/** Updates the name and dates for a supplied report */ +function updateReportDetails(reportId, name, fromDate, toDate, userId, submissionDate, description) { + var now = ISODate(); + var report = db.report.findOne({reportId:reportId}); + report.fromDate = fromDate; + report.toDate = toDate; + report.lastUpdated = now; + report.name = name; + report.description = description || name; + report.submissionDate = submissionDate || toDate; + + db.report.replaceOne({_id: report._id}, report); + audit(report, report.reportId, 'au.org.ala.ecodata.Report', userId); + + updateActivity(report, userId); +} + +/** Updates an activity to match the changes made to a supplied report and audits the change */ +function updateActivity(report, userId) { + var activity = db.activity.findOne({activityId:report.activityId}); + activity.plannedEndDate = report.toDate; + activity.endDate = report.toDate; + activity.plannedStartDate = report.fromDate; + activity.startDate = report.fromDate; + activity.description = report.name; + + activity.lastUpdated = report.lastUpdated; + + db.activity.replaceOne({_id:activity._id}, activity); + audit(activity, activity.activityId, 'au.org.ala.ecodata.Activity', userId); +} + +function addProjectPermission(userId, projectId, accessLevel, adminUserId) { + addPermission(userId, projectId, accessLevel, 'au.org.ala.ecodata.Project', adminUserId); +} + +function addPermission(userId, entityId, accessLevel, entityType, adminUserId) { + var userPermission = { + userId:userId, + entityType:entityType, + entityId:entityId, + accessLevel:accessLevel + }; + if (db.userPermission.findOne({userId:userId, entityId:entityId})) { + print("Not adding permission for user: "+userId+" to entity: "+entityId+", permission already exists"); + } + else { + print("Adding permission for user: "+userId+" to entity: "+entityId+" of type: "+entityType); + db.userPermission.insert(userPermission); + var id = db.userPermission.findOne({userId:userId, entityId:entityId})._id; + audit(userPermission, id, 'au.org.ala.ecodata.UserPermission', adminUserId, entityId); + } + +} + + +function restoreRemovedPermissions(userId, removalDate, adminUserId) { + let messages = db.auditMessage.find( + {'entity.userId':userId, + userId:'', + entityType:'au.org.ala.ecodata.UserPermission', + date:{$gt:ISODate(removalDate)}, + eventType:"Delete" + }); + while (messages.hasNext()) { + let message = messages.next(); + let permission = message.entity; + + addProjectPermission(userId, permission.entityId, permission.accessLevel, permission.entityType, adminUserId); + + } +} \ No newline at end of file diff --git a/src/test/groovy/au/org/ala/ecodata/ParatooServiceSpec.groovy b/src/test/groovy/au/org/ala/ecodata/ParatooServiceSpec.groovy index 699e2b47b..eecefa9dc 100644 --- a/src/test/groovy/au/org/ala/ecodata/ParatooServiceSpec.groovy +++ b/src/test/groovy/au/org/ala/ecodata/ParatooServiceSpec.groovy @@ -84,6 +84,7 @@ class ParatooServiceSpec extends MongoSpec implements ServiceUnitTest