Skip to content

Commit

Permalink
Removed user-role endpoint, added /projects #823
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisala committed Oct 19, 2023
1 parent 9722e23 commit 99ae7c5
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 78 deletions.
43 changes: 26 additions & 17 deletions grails-app/controllers/au/org/ala/ecodata/ParatooController.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,17 @@ import javax.ws.rs.Path
class ParatooController {

static responseFormats = ['json']
static allowedMethods =
[userProjects:'GET',
protocolReadCheck:'GET',
protocolWriteCheck:'GET',
validateToken: 'POST',
mintCollectionId: 'POST',
submitCollection: 'POST',
collectionIdStatus: 'GET'
static allowedMethods = [
userProjects:'GET',
protocolReadCheck:'GET',
protocolWriteCheck:'GET',
validateToken: 'POST',
mintCollectionId: 'POST',
submitCollection: 'POST',
collectionIdStatus: 'GET',
addPlotSelection: ['POST', 'PUT'],
updatePlotSelection: ['POST', 'PUT'],
updateProjectSites: ['PUT']
]

ParatooService paratooService
Expand All @@ -98,15 +101,6 @@ class ParatooController {
respond projects:paratooService.userProjects(userService.currentUserDetails.userId)
}

@GET
@Path("/user-role")
@Operation(summary = "Returns the roles the user has on each project",
responses = [@ApiResponse(responseCode = "200", description = "Project roles assigned to the user", content = @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = Project.class)))),
@ApiResponse(responseCode = "403", description = "Forbidden"), @ApiResponse(responseCode = "404", description = "Not found")], tags = ["Org Interface"])
def userRoles() {
respond projects:paratooService.userProjects(userService.currentUserDetails.userId)
}

@SkipApiKeyCheck
@POST
@Path("/validate-token")
Expand Down Expand Up @@ -304,6 +298,21 @@ class ParatooController {
]
}

@PUT
@Path("/projects")
def updateProjectSites(String id) {
String userId = userService.currentUserDetails.userId
List projects = paratooService.userProjects(userId)
ParatooProject project = projects?.find{it.id == id}
if (!project) {
error(HttpStatus.SC_FORBIDDEN, "Project not available")
return
}
Map data = request.JSON

paratooService.updateProjectSites(userId, project, data)
}

def options() {
respond([statusCode:HttpStatus.SC_NO_CONTENT])
}
Expand Down
10 changes: 10 additions & 0 deletions grails-app/controllers/au/org/ala/ecodata/UrlMappings.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,16 @@ class UrlMappings {
action = [POST: 'addPlotSelection', OPTIONS:'options', PUT: 'updatePlotSelection']
}

"/ws/paratoo/user-role" {
controller = 'paratoo'
action = [GET: 'userRoles', OPTIONS: 'options']
}

"/ws/paratoo/projects" {
controller = 'paratoo'
action = [POST: 'updateProjectSites', PUT: 'updateProjectSites', OPTIONS:'options']
}

"/"(redirect:[controller:"documentation"])
"500"(view:'/error')
}
Expand Down
58 changes: 54 additions & 4 deletions grails-app/services/au/org/ala/ecodata/ParatooService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ class ParatooService {
List<UserPermission> permissions = UserPermission.findAllByUserIdAndEntityTypeAndStatusNotEqual(userId, Project.class.name, Status.DELETED)


// If the permission has been set as a favourite then delegate to the Hub permission
// so that "readOnly" access for a hub is supported, and we don't return projects that a user
// has only marked as starred without having hub level permissions
// If the permission has been set as a favourite then delegate to the Hub permission.
Map projectAccessLevels = [:]
permissions?.each { UserPermission permission ->
String projectId = permission.entityId
Expand All @@ -111,7 +109,9 @@ class ParatooService {

Program program = Program.findByProgramId(project.programId)
Map config = program.getInheritedConfig()
config?.get(PROGRAM_CONFIG_PARATOO_ITEM) && projectAccessLevels[project.projectId]
// The Monitor/Paratoo app is "write only" (i.e. there is no view mode for the data), so we don't support
// the read only role
config?.get(PROGRAM_CONFIG_PARATOO_ITEM) && projectAccessLevels[project.projectId] && projectAccessLevels[project.projectId] != AccessLevel.readOnly
}

List paratooProjects = projects.collect { Project project ->
Expand Down Expand Up @@ -326,6 +326,7 @@ class ParatooService {
accessLevel: accessLevel,
project:project,
projectArea: projectAreaGeoJson,
projectAreaSite: projectArea,
plots: plotSelections]
new ParatooProject(attributes)

Expand Down Expand Up @@ -416,6 +417,55 @@ class ParatooService {
site
}

Map updateProjectSites(ParatooProject project, Map siteData) {
if (siteData.plot_selections) {
linkProjectToSites(project, siteData.plot_selections)
}
if (siteData.project_area_type && siteData.project_area_coordinates) {
updateProjectArea(project, siteData.project_area_type, siteData.project_area_coordinates)
}
}


private Map linkProjectToSites(ParatooProject project, List siteExternalIds) {
List errors = []
List<Site> sites = Site.findAllByExternalIdInList(siteExternalIds)
sites.each { Site site ->
site.projects = site.projects ?: []
site.projects << project.id
site.save()
if (site.hasErrors()) {
errors << site.errors
}
}
[success:!errors, error:errors]
}

private Map updateProjectArea(ParatooProject project, String type, List coordinates) {
Map geometry = [
type:type,
coordinates: coordinates.collect{[it.lng, it.lat]}
]
Site projectArea = project.projectAreaSite
if (projectArea) {
projectArea.extent.geometry.type = geometry.type
projectArea.extent.geometry.coordinates = geometry.coordinates
siteService.update(projectArea.extent, projectArea.siteId)
}
else {

Map site = [
name:'Monitor project area',
type:Site.TYPE_PROJECT_AREA,
extent: [
source:'drawn',
geometry:geometry
],
projects: [project.id]
]
siteService.create(site)
}
}

// Protocol = 2 (vegetation mapping survey).
// endpoint /api/vegetation-mapping-surveys is useless
Expand Down
1 change: 1 addition & 0 deletions grails-app/views/paratoo/_paratooProject.gson
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ json {
protocols tmpl.paratooProtocol('protocol', project.protocols)
project_area project.projectArea ? tmpl.projectArea(projectArea:project.projectArea) : null
plot_selections tmpl.plot('plot', project.plots)
role project.paratooRole
}
7 changes: 0 additions & 7 deletions grails-app/views/paratoo/_projectRole.gson

This file was deleted.

8 changes: 0 additions & 8 deletions grails-app/views/paratoo/userRoles.gson

This file was deleted.

34 changes: 15 additions & 19 deletions src/main/groovy/au/org/ala/ecodata/paratoo/ParatooProject.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@ import au.org.ala.ecodata.Site
/** DTO for a response to the paratoo app */
class ParatooProject {

static String READ_ONLY = 'authenticated'
static String EDITABLE = 'project_admin'
static String EDITOR = 'authenticated'
static String ADMIN = 'project_admin'
static String PUBLIC = 'public'

String id
String name
AccessLevel accessLevel
Project project
List<ActivityForm> protocols
Map projectArea = null
Site projectAreaSite = null
List<Site> plots = null

List<Map> getDataSets() {
Expand All @@ -32,27 +34,21 @@ class ParatooProject {
project.getMonitoringProtocolCategories()
}

String getParatooAccessLevelType() {
String paratooAccessLevel = READ_ONLY
String getParatooRole() {
String paratooRole
switch (accessLevel) {
case AccessLevel.editor:
case AccessLevel.admin:
case AccessLevel.caseManager:
paratooAccessLevel = EDITABLE
break;
}
paratooAccessLevel
}

String getParatooAccessLevelName() {
String paratooAccessLevelName = 'Authenticated'
switch (accessLevel) {
paratooRole = ADMIN
break
case AccessLevel.projectParticipant:
case AccessLevel.editor:
case AccessLevel.admin:
case AccessLevel.caseManager:
paratooAccessLevelName = 'Project Admin'
break;
paratooRole = EDITOR
break
default:
paratooRole = PUBLIC
}
paratooAccessLevelName
paratooRole
}

}
27 changes: 8 additions & 19 deletions src/test/groovy/au/org/ala/ecodata/ParatooJsonViewSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,23 @@ class ParatooJsonViewSpec extends Specification implements JsonViewTest {
project_area:null,
plot_selections:[
[uuid:'s1', plot_name:"Site 1"]
]
],
role:"project_admin"
],[
id:"p2", name:"Project 2", protocols:[], plot_selections:[], project_area:[type:"Polygon", coordinates: DUMMY_POLYGON]
id:"p2", name:"Project 2", protocols:[], plot_selections:[],
project_area:[type:"Polygon", coordinates: DUMMY_POLYGON],
role:"authenticated"
],[
id:"p3", name:"Project 3", protocols:[
[id:1, identifier: "guid-1", name: "Protocol 1", version: 1, module: 'module-1']
], project_area:null, plot_selections:[]
], project_area:null, plot_selections:[], role:'authenticated'
]
]]

when: "The results of /paratoo/user-projects is rendered"
List projects = buildProjectsForRendering(projectSpec)
projects[1].accessLevel = AccessLevel.editor
projects[2].accessLevel = AccessLevel.projectParticipant
def result = render(view: "/paratoo/userProjects", model:[projects:projects])

then:"The json is correct"
Expand All @@ -42,22 +47,6 @@ class ParatooJsonViewSpec extends Specification implements JsonViewTest {

}

def "The /user-role response is rendered correctly"() {

when:
int[][] projectSpec = [[3, 1, 0], [0, 0, 1], [1, 0, 0]] as int[][]
List projects = buildProjectsForRendering(projectSpec)
projects[2].accessLevel = AccessLevel.readOnly

def result = render(view: "/paratoo/userRoles", model:[projects:projects])

then:"The json is correct"
result.json[0] == [(projects[0].id):[name:"Project Admin", type:'project_admin']]
result.json[1] == [(projects[1].id):[name:"Project Admin", type:'project_admin']]
result.json[2] == [(projects[2].id):[name:"Authenticated", type:'authenticated']]

}

private List<ParatooProject> buildProjectsForRendering(int[][] projectSpec) {

List projects = []
Expand Down
25 changes: 21 additions & 4 deletions src/test/groovy/au/org/ala/ecodata/ParatooServiceSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ class ParatooServiceSpec extends MongoSpec implements ServiceUnitTest<ParatooSer
then:
projects.size() == 0

when: "The user has the MERIT read only role"
UserPermission meritReadOnly = new UserPermission(userId:userId, entityId:'merit', entityType: 'au.org.ala.ecodata.Hub', accessLevel:AccessLevel.readOnly)
meritReadOnly.save(flush:true, failOnError:true)
when: "The user has the MERIT grant manager role"
UserPermission meritGrantManager = new UserPermission(userId:userId, entityId:'merit', entityType: 'au.org.ala.ecodata.Hub', accessLevel:AccessLevel.caseManager)
meritGrantManager.save(flush:true, failOnError:true)
projects = service.userProjects(userId)

then:
Expand Down Expand Up @@ -194,6 +194,21 @@ class ParatooServiceSpec extends MongoSpec implements ServiceUnitTest<ParatooSer
1 * siteService.create(expected)
}

void "The service can link a site to a project"() {
setup:
String projectId = 'p1'
ParatooProject project = new ParatooProject(id:projectId, project:new Project(projectId:projectId))
Map data = [plot_selections:['s2']]


when:
service.updateProjectSites(project, data)
Site s2 = Site.findBySiteId('s2')

then:
s2.projects.indexOf(projectId) >= 0
}

private void setupData() {
Hub hub = new Hub(hubId:"merit", urlPath:"merit")
hub.save(failOnError:true, flush:true)
Expand All @@ -210,7 +225,9 @@ class ParatooServiceSpec extends MongoSpec implements ServiceUnitTest<ParatooSer
userPermission.save(failOnError:true, flush:true)

Site projectArea = new Site(siteId:'s1', name:'Site 1', type:Site.TYPE_PROJECT_AREA, extent: [geometry:DUMMY_POLYGON])
Site plot = new Site(siteId:'s2', name:"Site 2", type:Site.TYPE_SURVEY_AREA, extent: [geometry:DUMMY_POLYGON])
projectArea.save(failOnError:true, flush:true)
Site plot = new Site(siteId:'s2', name:"Site 2", type:Site.TYPE_SURVEY_AREA, extent: [geometry:DUMMY_POLYGON], projects:['p1'])
plot.save(failOnError:true, flush:true)
siteService.sitesForProject('p1') >> [projectArea, plot]

Program program = new Program(programId: "prog1", name:"A program", config:[(ParatooService.PROGRAM_CONFIG_PARATOO_ITEM):true])
Expand Down

0 comments on commit 99ae7c5

Please sign in to comment.