Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

[RJA-1469] [feature] - Project additional methods and links + refactoring #792

Merged
merged 4 commits into from

3 participants

@rsvato
Collaborator

No description provided.

@ddurnev ddurnev commented on the diff
...a/com/griddynamics/genesis/rest/GroupController.scala
((13 lines not shown))
@RequestMapping(value = Array("{id}"), method = Array(RequestMethod.GET))
@ResponseBody
- def get(@PathVariable(value = "id") id: Int) =
- groupService.get(id).getOrElse(throw new ResourceNotFoundException("Group [id = %d] was not found".format(id)))
+ def get(@PathVariable(value = "id") id: Int, request: HttpServletRequest) = {
+ val group = find(id)
+ implicit val req = request
+ wrap(group).withLinksToSelf(GET, PUT, DELETE).filtered()
+ }
+
+ private def find(id: Int) = groupService.get(id).getOrElse(throw new ResourceNotFoundException("Group [id = %d] was not found".format(id)))
@ddurnev
ddurnev added a note

Why not use scala 2.10 string interpolation?

@rsvato Collaborator
rsvato added a note

Guess it :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@bugzmanov bugzmanov merged commit 53357b0 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 311 additions and 114 deletions.
  1. +25 −0 api/src/main/scala/com/griddynamics/genesis/api/Model.scala
  2. +16 −0 frontend/src/main/java/com/griddynamics/genesis/rest/annotations/AddSelfLinks.java
  3. +6 −0 frontend/src/main/resources/WEB-INF/spring/security-config.xml
  4. +1 −0  frontend/src/main/scala/com/griddynamics/genesis/rest/ConfigurationController.scala
  5. +2 −2 frontend/src/main/scala/com/griddynamics/genesis/rest/DatabagController.scala
  6. +5 −5 frontend/src/main/scala/com/griddynamics/genesis/rest/EnvironmentsController.scala
  7. +25 −6 frontend/src/main/scala/com/griddynamics/genesis/rest/GroupController.scala
  8. +6 −5 frontend/src/main/scala/com/griddynamics/genesis/rest/PluginsController.scala
  9. +33 −10 frontend/src/main/scala/com/griddynamics/genesis/rest/ProjectDatabagController.scala
  10. +36 −11 frontend/src/main/scala/com/griddynamics/genesis/rest/ProjectsController.scala
  11. +24 −4 frontend/src/main/scala/com/griddynamics/genesis/rest/RemoteAgentController.scala
  12. +3 −3 frontend/src/main/scala/com/griddynamics/genesis/rest/RootController.scala
  13. +22 −9 frontend/src/main/scala/com/griddynamics/genesis/rest/ServersController.scala
  14. +8 −11 frontend/src/main/scala/com/griddynamics/genesis/rest/SettingsController.scala
  15. +24 −5 frontend/src/main/scala/com/griddynamics/genesis/rest/UsersController.scala
  16. +6 −5 frontend/src/main/scala/com/griddynamics/genesis/rest/links/{Wrappers.scala → CollectionWrapper.scala}
  17. +8 −29 frontend/src/main/scala/com/griddynamics/genesis/rest/links/{Link.scala → Links.scala}
  18. +3 −3 frontend/src/main/scala/com/griddynamics/genesis/spring/JsonMessageConverter.scala
  19. +31 −2 frontend/src/main/scala/com/griddynamics/genesis/spring/aop/LinksToAspect.scala
  20. +1 −1  frontend/src/main/scala/com/griddynamics/genesis/spring/security/LinkSecurityBean.scala
  21. +2 −2 ...end/src/main/scala/com/griddynamics/genesis/spring/security/acls/ScalaMethodSecurityExpressionHandler.scala
  22. +9 −1 ui/src/main/resources/genesis/app/modules/project_properties/servers.js
  23. +2 −0  ui/src/main/resources/genesis/app/modules/settings/agents.coffee
  24. +3 −0  ui/src/main/resources/genesis/app/modules/settings/groups.coffee
  25. +10 −0 ui/src/main/resources/genesis/app/modules/settings/users.coffee
View
25 api/src/main/scala/com/griddynamics/genesis/api/Model.scala
@@ -303,3 +303,28 @@ object AgentStatus extends Enumeration {
}
case class JobStats(runningJobs: Int, totalJobs: Int)
+
+case class Link(href: String, rel: String, `type`: Option[String], methods: Array[String] = Array()) {
+ def disassemble: Array[Link] = methods.map(m => new Link(href, rel, `type`, Array(m)))
+ def remove(method: String) = new Link(href, rel, `type`, methods.filter(_ != method))
+}
+
+object Links {
+ def merge(links: Array[Link]) = {
+ val groupedByHref = links.groupBy(_.href)
+
+ def assembleLinkList(linksList: Array[Link], href: String): Link = {
+ val firstLink = linksList(0)
+ val methods = linksList.map(_.methods).flatten
+ Link(href, firstLink.rel, firstLink.`type`, methods)
+ }
+
+ groupedByHref.map {
+ case (href, linksList) => assembleLinkList(linksList, href)
+ }
+ }
+}
+
+case class SystemSettings(links: Array[Link])
+
+case class ApplicationRole(name: String)
View
16 frontend/src/main/java/com/griddynamics/genesis/rest/annotations/AddSelfLinks.java
@@ -0,0 +1,16 @@
+package com.griddynamics.genesis.rest.annotations;
+
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(value = {ElementType.METHOD})
+public @interface AddSelfLinks {
+ RequestMethod[] methods() default {RequestMethod.GET};
+ Class<?> modelClass();
+
+}
View
6 frontend/src/main/resources/WEB-INF/spring/security-config.xml
@@ -88,6 +88,9 @@
hasPermission( #urlId('/rest/projects/{id}/.*', #url), 'com.griddynamics.genesis.api.Project', 'read') or
hasPermission( #urlId('/rest/projects/{id}/.*', #url), 'com.griddynamics.genesis.api.Project', 'administration')"/>
+ <s:intercept-url pattern="/rest/projects/*/settings" method="GET" access="hasRole('ROLE_GENESIS_ADMIN') or hasRole('ROLE_GENESIS_READONLY') or
+ hasPermission( #urlId('/rest/projects/{id}/.*', #url), 'com.griddynamics.genesis.api.Project', 'administration')" />
+
<s:intercept-url pattern="/rest/projects/*/envs" method="POST" access="
not(@environmentSecurity.restrictionsEnabled()) or
hasRole('ROLE_GENESIS_ADMIN') or
@@ -103,6 +106,9 @@
hasPermission( #urlId('/rest/projects/{id}/.*', #url), 'com.griddynamics.genesis.api.Project', 'administration')"/>
+ <s:intercept-url pattern="/rest/projects/*/roles" method="GET" access="hasRole('ROLE_GENESIS_ADMIN') or hasRole('ROLE_GENESIS_READONLY') or
+ hasPermission( #urlId('/rest/projects/{id}/.*', #url), 'com.griddynamics.genesis.api.Project', 'administration')" />
+
<s:intercept-url pattern="/rest/projects/*/roles/**" method="GET" access="hasRole('ROLE_GENESIS_ADMIN') or hasRole('ROLE_GENESIS_READONLY') or
hasPermission( #urlId('/rest/projects/{id}/.*', #url), 'com.griddynamics.genesis.api.Project', 'administration')" />
<s:intercept-url pattern="/rest/projects/*/roles/**" access="hasRole('ROLE_GENESIS_ADMIN') or
View
1  frontend/src/main/scala/com/griddynamics/genesis/rest/ConfigurationController.scala
@@ -49,6 +49,7 @@ class ConfigurationController extends RestApiExceptionsHandler{
@Autowired var userService: UserService = _
@Autowired var groupService: GroupService = _
+
@RequestMapping(value = Array(""), method = Array(RequestMethod.GET))
@ResponseBody
@PostFilter("not(@environmentSecurity.restrictionsEnabled()) " +
View
4 frontend/src/main/scala/com/griddynamics/genesis/rest/DatabagController.scala
@@ -25,7 +25,7 @@ package com.griddynamics.genesis.rest
import annotations.LinkTarget
import links._
import HrefBuilder._
-import Wrappers._
+import CollectionWrapper._
import links.WebPath
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation._
@@ -80,7 +80,7 @@ class DatabagController extends RestApiExceptionsHandler {
wrapCollection(
service.list.map(databag => {
val wrappedItem: ItemWrapper[DataBag] = wrap(databag)
- wrappedItem.withLinks(Link(top / databag.id.get.toString, LinkTarget.SELF, classOf[DataBag], GET)).filtered()
+ wrappedItem.withLinks(LinkBuilder(top / databag.id.get.toString, LinkTarget.SELF, classOf[DataBag], GET, PUT, DELETE)).filtered()
})
).withLinksToSelf(classOf[DataBag], GET, POST).filtered()
}
View
10 frontend/src/main/scala/com/griddynamics/genesis/rest/EnvironmentsController.scala
@@ -28,7 +28,7 @@ import links._
import HrefBuilder._
import links.ItemWrapper
import links.WebPath
-import Wrappers._
+import CollectionWrapper._
import org.springframework.stereotype.Controller
import scala.Array
import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
@@ -177,8 +177,8 @@ class EnvironmentsController extends RestApiExceptionsHandler {
implicit val req: HttpServletRequest = request
val top = WebPath(request)
wrapper.withLinksToSelf(GET, PUT, DELETE).withLinks(
- Link(top / "history", COLLECTION, classOf[WorkflowHistory], GET),
- Link(top / "actions", COLLECTION, POST) // TODO: GET not present now. User can only post there.
+ LinkBuilder(top / "history", COLLECTION, classOf[WorkflowHistory], GET),
+ LinkBuilder(top / "actions", COLLECTION, POST) // TODO: GET not present now. User can only post there.
).filtered()
}
@@ -213,11 +213,11 @@ class EnvironmentsController extends RestApiExceptionsHandler {
}
implicit val req: HttpServletRequest = request
val wrapped = environments.map(environment =>
- wrap(environment).withLinks(Link(HrefBuilder.withPathParam(request, environment.id),
+ wrap(environment).withLinks(LinkBuilder(HrefBuilder.withPathParam(request, environment.id),
SELF, classOf[Environment], GET))
)
- wrapCollection(wrapped).withLinks(Link(request,
+ wrapCollection(wrapped).withLinks(LinkBuilder(request,
SELF, classOf[Environment], POST)).filtered()
}
View
31 frontend/src/main/scala/com/griddynamics/genesis/rest/GroupController.scala
@@ -23,21 +23,28 @@
package com.griddynamics.genesis.rest
+import annotations.LinkTarget
+import links.CollectionWrapper._
+import links.{WebPath, LinkBuilder}
+import links.HrefBuilder._
import org.springframework.stereotype.Controller
import org.springframework.beans.factory.annotation.Autowired
import com.griddynamics.genesis.groups.GroupService
import javax.servlet.http.HttpServletRequest
import GenesisRestController._
import org.springframework.web.bind.annotation._
+import org.springframework.web.bind.annotation.RequestMethod._
import com.griddynamics.genesis.api.{ExtendedResult, UserGroup}
import com.griddynamics.genesis.spring.ApplicationContextAware
import javax.validation.Valid
+import com.griddynamics.genesis.spring.security.LinkSecurityBean
@Controller
@RequestMapping(Array("/rest/groups"))
class GroupController extends RestApiExceptionsHandler with ApplicationContextAware {
@Autowired var groupService: GroupService = _
+ @Autowired implicit var linkSecurity: LinkSecurityBean = _
@RequestMapping(method = Array(RequestMethod.GET), params = Array("available"))
@ResponseBody
@@ -45,12 +52,24 @@ class GroupController extends RestApiExceptionsHandler with ApplicationContextAw
@RequestMapping(method = Array(RequestMethod.GET))
@ResponseBody
- def list() = groupService.list
+ def list(request: HttpServletRequest) = {
+ implicit val req = request
+ val top = WebPath(request)
+ def wrapGroup(group: UserGroup) = {
+ wrap(group).withLinks(LinkBuilder(top / group.id.get.toString, LinkTarget.SELF, classOf[UserGroup], GET, PUT, DELETE)).filtered()
+ }
+ wrapCollection(groupService.list.map(wrapGroup(_))).withLinksToSelf(classOf[UserGroup], GET, POST).filtered()
+ }
@RequestMapping(value = Array("{id}"), method = Array(RequestMethod.GET))
@ResponseBody
- def get(@PathVariable(value = "id") id: Int) =
- groupService.get(id).getOrElse(throw new ResourceNotFoundException("Group [id = %d] was not found".format(id)))
+ def get(@PathVariable(value = "id") id: Int, request: HttpServletRequest) = {
+ val group = find(id)
+ implicit val req = request
+ wrap(group).withLinksToSelf(GET, PUT, DELETE).filtered()
+ }
+
+ private def find(id: Int) = groupService.get(id).getOrElse(throw new ResourceNotFoundException("Group [id = %d] was not found".format(id)))
@ddurnev
ddurnev added a note

Why not use scala 2.10 string interpolation?

@rsvato Collaborator
rsvato added a note

Guess it :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@RequestMapping(method = Array(RequestMethod.GET), params = Array("tag"))
@ResponseBody
@@ -65,7 +84,7 @@ class GroupController extends RestApiExceptionsHandler with ApplicationContextAw
@RequestMapping(value = Array("{id}"), method = Array(RequestMethod.PUT))
@ResponseBody
def update(@PathVariable(value = "id") id: Int, @Valid @RequestBody request: UserGroup) = {
- val group = get(id).copy(
+ val group = find(id).copy(
name = request.name,
description = request.description,
mailingList = request.mailingList,
@@ -76,7 +95,7 @@ class GroupController extends RestApiExceptionsHandler with ApplicationContextAw
@RequestMapping(value = Array("{id}"), method = Array(RequestMethod.DELETE))
@ResponseBody
- def delete(@PathVariable(value = "id") id: Int) = groupService.delete(get(id))
+ def delete(@PathVariable(value = "id") id: Int) = groupService.delete(find(id))
@RequestMapping(value = Array("{id}/users"), method = Array(RequestMethod.GET))
@ResponseBody
@@ -104,5 +123,5 @@ class GroupController extends RestApiExceptionsHandler with ApplicationContextAw
}
}
- def withGroup(id: Int)(block: UserGroup => ExtendedResult[_]) = block(get(id))
+ def withGroup(id: Int)(block: UserGroup => ExtendedResult[_]) = block(find(id))
}
View
11 frontend/src/main/scala/com/griddynamics/genesis/rest/PluginsController.scala
@@ -25,7 +25,7 @@ package com.griddynamics.genesis.rest
import annotations.LinkTarget._
import links._
import HrefBuilder._
-import Wrappers._
+import CollectionWrapper._
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation._
import org.springframework.web.bind.annotation.RequestMethod._
@@ -48,11 +48,12 @@ class PluginsController extends RestApiExceptionsHandler {
@RequestMapping(method = Array(RequestMethod.GET))
@ResponseBody
- def listPlugins(request: HttpServletRequest): Wrappers[ItemWrapper[Plugin]] = {
+ def listPlugins(request: HttpServletRequest): CollectionWrapper[ItemWrapper[Plugin]] = {
+ implicit val req = request
wrapCollection(repository.listPlugins.toList.map(plugin => {
val top = WebPath(request)
- wrap(plugin).withLinks(Link(top / plugin.id, SELF, classOf[Plugin], GET)).filtered()
- })).withLinks(Link(request, SELF, GET)).filtered()
+ wrap(plugin).withLinks(LinkBuilder(top / plugin.id, SELF, classOf[Plugin], GET, PUT)).filtered()
+ })).withLinksToSelf(classOf[Plugin], GET).filtered()
}
@RequestMapping(value = Array("{pluginId}"), method = Array(RequestMethod.GET))
@@ -60,7 +61,7 @@ class PluginsController extends RestApiExceptionsHandler {
def getPluginDescription(@PathVariable("pluginId") pluginId: String, request: HttpServletRequest): ItemWrapper[PluginDetails] ={
val plugin = repository.getPlugin(pluginId).getOrElse(throw new ResourceNotFoundException("Plugin [id = " + pluginId + "] was not found"))
wrap(plugin.copy(configuration = hidePasswords(plugin.configuration))).
- withLinks(Link(request, SELF, classOf[PluginDetails], GET, PUT)).filtered()
+ withLinks(LinkBuilder(request, SELF, classOf[PluginDetails], GET, PUT)).filtered()
}
@RequestMapping(value = Array("{pluginId}"), method = Array(RequestMethod.PUT))
View
43 frontend/src/main/scala/com/griddynamics/genesis/rest/ProjectDatabagController.scala
@@ -22,22 +22,39 @@
*/
package com.griddynamics.genesis.rest
+import annotations.{LinkTarget, AddSelfLinks}
+import links.CollectionWrapper._
+import links.HrefBuilder._
+import links.{WebPath, LinkBuilder, ItemWrapper, CollectionWrapper}
import org.springframework.stereotype.Controller
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation._
+import org.springframework.web.bind.annotation.RequestMethod._
import com.griddynamics.genesis.service.DataBagService
-import com.griddynamics.genesis.api.DataBag
import javax.validation.Valid
+import javax.servlet.http.HttpServletRequest
+import com.griddynamics.genesis.spring.security.LinkSecurityBean
+import com.griddynamics.genesis.api.DataBag
+import scala.Some
@Controller
@RequestMapping(value = Array("/rest/projects/{projectId}/databags"))
class ProjectDatabagController extends RestApiExceptionsHandler {
@Autowired
var databagService: DataBagService = _
+ @Autowired implicit var linkSecurity: LinkSecurityBean = _
@RequestMapping(method = Array(RequestMethod.GET))
@ResponseBody
- def listForProject(@PathVariable("projectId") projectId: Int) = databagService.listForProject(projectId)
+ @AddSelfLinks(methods = Array(GET, POST), modelClass = classOf[DataBag])
+ def listForProject(@PathVariable("projectId") projectId: Int, request: HttpServletRequest) : CollectionWrapper[ItemWrapper[DataBag]] = {
+ def wrapDatabag(databag: DataBag) : ItemWrapper[DataBag] = {
+ val top = WebPath(request)
+ val wrappedItem: ItemWrapper[DataBag] = databag
+ wrappedItem.withLinks(LinkBuilder(top / databag.id.get.toString, LinkTarget.SELF, classOf[DataBag], GET, PUT, DELETE)).filtered()
+ }
+ databagService.listForProject(projectId).map(wrapDatabag(_))
+ }
@RequestMapping(value = Array("{databagId}"), method = Array(RequestMethod.PUT))
@ResponseBody
@@ -49,14 +66,20 @@ class ProjectDatabagController extends RestApiExceptionsHandler {
@RequestMapping(value = Array("{databagId}"), method = Array(RequestMethod.GET))
@ResponseBody
- def find(@PathVariable("projectId") projectId: Int, @PathVariable("databagId") databagId: Int) = {
- val bag = for {
- databag <- databagService.get(databagId)
- project <- databag.projectId
- if project == projectId
- } yield databag
+ @AddSelfLinks(methods = Array(GET, PUT, DELETE), modelClass = classOf[DataBag])
+ def find(@PathVariable("projectId") projectId: Int, @PathVariable("databagId") databagId: Int, request: HttpServletRequest) : ItemWrapper[DataBag] = {
+ get(databagId, projectId)
+ }
+
+
+ def get(databagId: Int, projectId: Int): DataBag = {
+ val bag = for {
+ databag <- databagService.get(databagId)
+ project <- databag.projectId
+ if project == projectId
+ } yield databag
- bag.getOrElse(throw new ResourceNotFoundException("Couldn't find databag"))
+ bag.getOrElse(throw new ResourceNotFoundException("Couldn't find databag"))
}
@RequestMapping(method = Array(RequestMethod.POST))
@@ -68,7 +91,7 @@ class ProjectDatabagController extends RestApiExceptionsHandler {
@RequestMapping(value = Array("{databagId}"), method = Array(RequestMethod.DELETE))
@ResponseBody
def delete(@PathVariable("projectId") projectId: Int, @PathVariable("databagId") databagId: Int) = {
- val bag = find(projectId, databagId)
+ val bag = get(databagId, projectId)
databagService.delete(bag)
}
}
View
47 frontend/src/main/scala/com/griddynamics/genesis/rest/ProjectsController.scala
@@ -6,7 +6,7 @@ import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
import com.griddynamics.genesis.rest.GenesisRestController._
import links._
import HrefBuilder._
-import Wrappers._
+import CollectionWrapper._
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation._
import com.griddynamics.genesis.service.impl.ProjectService
@@ -69,7 +69,7 @@ class ProjectsController extends RestApiExceptionsHandler {
@ResponseBody
@LinksTo(value = Array(new LinkTo(methods = Array(RequestMethod.POST), clazz = classOf[Project], controller = classOf[ProjectsController])))
def listProjects(@RequestParam(value = "sorting", required = false, defaultValue = "name") sorting: Ordering,
- request: HttpServletRequest): Wrappers[ItemWrapper[_]] = {
+ request: HttpServletRequest): CollectionWrapper[ItemWrapper[_]] = {
val projects = if (request.isUserInRole(GenesisRole.SystemAdmin.toString) || request.isUserInRole(GenesisRole.ReadonlySystemAdmin.toString)) {
projectService.orderedList(sorting)
} else {
@@ -77,11 +77,41 @@ class ProjectsController extends RestApiExceptionsHandler {
val ids = authorityService.getAllowedProjectIds(request.getUserPrincipal.getName, authorities)
projectService.getProjects(ids, Option(sorting))
}
- projects.map(project => wrap(project).withLinks(Link(WebPath(request) / project.id.get.toString,
+ projects.map(project => wrap(project).withLinks(LinkBuilder(WebPath(request) / project.id.get.toString,
SELF, classOf[Environment], GET)).filtered())
}
+ @RequestMapping(value = Array("{projectId}/settings"), method = Array(GET))
+ @ResponseBody
+ def projectSettings(@PathVariable("projectId") projectId: Int, request: HttpServletRequest): SystemSettings = {
+ val top = WebPath(absolutePath(s"/rest/projects/${projectId}")(request))
+ val links = Array(
+ LinkBuilder(top / "envs", COLLECTION, classOf[Environment], GET, POST),
+ LinkBuilder(top / "databags", COLLECTION, classOf[DataBag], GET),
+ LinkBuilder(top / "configs", COLLECTION, classOf[Configuration], GET),
+ LinkBuilder(top / "credentials", COLLECTION, classOf[Credentials], GET),
+ LinkBuilder(top / "server-arrays", COLLECTION, classOf[ServerArray], GET),
+ LinkBuilder(top / "template" / "repository", COLLECTION, classOf[TemplateRepo], GET),
+ LinkBuilder(top / "roles", COLLECTION, classOf[ApplicationRole], GET)
+ )
+ SystemSettings(linkSecurity.filter(links).toArray)
+ }
+
+ @RequestMapping(value = Array("{projectId}/roles"), method = Array(GET))
+ @ResponseBody
+ def projectRoles(@PathVariable("projectId") projectId: Int, request: HttpServletRequest): CollectionWrapper[ItemWrapper[ApplicationRole]] = {
+ implicit val req = request
+ val rolesToShow = List(GenesisRole.ProjectAdmin, GenesisRole.ProjectUser).map(role => ApplicationRole(role.toString))
+ def wrapRole(role: ApplicationRole): ItemWrapper[ApplicationRole] = {
+ wrap(role).withLinks(
+ LinkBuilder(WebPath(request) / role.name, SELF, classOf[ApplicationRole], GET, PUT)
+ ).filtered()
+ }
+ wrapCollection(rolesToShow.map(role => wrapRole(role))).withLinksToSelf(classOf[ApplicationRole], GET).filtered()
+ }
+
+
@RequestMapping(method = Array(RequestMethod.POST))
@ResponseBody
def createProject(@Valid @RequestBody attr: ProjectAttributes) = {
@@ -109,14 +139,9 @@ class ProjectsController extends RestApiExceptionsHandler {
def findProject(@PathVariable("projectId") projectId: Int, request: HttpServletRequest): ItemWrapper[Project] = {
val top = WebPath(request)
wrap(getProject(projectId)).withLinks(
- Link(top, SELF, classOf[Project], GET, PUT, DELETE),
- Link(top / "envs", COLLECTION, classOf[Environment], GET, POST),
- Link(top / "databags", COLLECTION, classOf[DataBag], GET),
- Link(top / "configs", COLLECTION, classOf[Configuration], GET),
- Link(top / "credentials", COLLECTION, classOf[Credentials], GET),
- Link(top / "server-arrays", COLLECTION, classOf[ServerArray], GET),
- Link(top / "templates" / "repository", COLLECTION, classOf[TemplateRepo], GET),
- Link(top / "roles", COLLECTION, GET) //TODO: fake link, but seems to be ok for now
+ LinkBuilder(top, SELF, classOf[Project], GET, PUT, DELETE),
+ LinkBuilder(top / "envs", COLLECTION, classOf[Environment], GET, POST),
+ LinkBuilder(top / "settings", COLLECTION, classOf[SystemSettings], GET)
).filtered()
}
View
28 frontend/src/main/scala/com/griddynamics/genesis/rest/RemoteAgentController.scala
@@ -23,36 +23,54 @@
package com.griddynamics.genesis.rest
+import annotations.LinkTarget
+import links.CollectionWrapper._
+import links.{WebPath, LinkBuilder}
+import links.HrefBuilder._
import org.springframework.web.bind.annotation._
+import org.springframework.web.bind.annotation.RequestMethod._
import org.springframework.beans.factory.annotation.Autowired
import com.griddynamics.genesis.service.RemoteAgentsService
import javax.validation.Valid
import org.springframework.stereotype.Controller
import com.griddynamics.genesis.api.RemoteAgent
+import javax.servlet.http.HttpServletRequest
+import com.griddynamics.genesis.spring.security.LinkSecurityBean
@Controller
@RequestMapping(value = Array("/rest/agents"))
class RemoteAgentController extends RestApiExceptionsHandler {
@Autowired
var service: RemoteAgentsService = _
+ @Autowired implicit var linkSecurity: LinkSecurityBean = _
@RequestMapping(method = Array(RequestMethod.GET))
@ResponseBody
- def list() = service.list
+ def list(request: HttpServletRequest) = {
+ implicit val req = request
+ val top = WebPath(request)
+ def wrapAgent(agent: RemoteAgent) = {
+ wrap(agent).withLinks(LinkBuilder(top / agent.id.get.toString, LinkTarget.SELF, classOf[RemoteAgent], GET, PUT, DELETE)).filtered()
+ }
+ wrapCollection(service.list.map(wrapAgent(_))).withLinksToSelf(classOf[RemoteAgent], GET, POST).filtered()
+ }
@ResponseBody @RequestMapping(value = Array("{id}"), method = Array(RequestMethod.GET))
- def get(@PathVariable(value = "id") key: Int) = service.get(key).getOrElse(throw new ResourceNotFoundException("Couldn't find agent"))
+ def get(@PathVariable(value = "id") key: Int, request: HttpServletRequest) = {
+ implicit val req = request
+ wrap(find(key)).withLinksToSelf(GET, PUT, DELETE).filtered()
+ }
@ResponseBody @RequestMapping(value = Array("{id}"), method = Array(RequestMethod.PUT))
def update(@PathVariable(value = "id") key: Int, @Valid @RequestBody agent: RemoteAgent) = {
- val existing: RemoteAgent = get(key)
+ val existing: RemoteAgent = find(key)
service.update(agent)
}
@ResponseBody @RequestMapping(value = Array("{id}"), method = Array(RequestMethod.DELETE))
def delete(@PathVariable(value = "id") key: Int) = {
- val agent: RemoteAgent = get(key)
+ val agent: RemoteAgent = find(key)
service.delete(agent)
}
@@ -60,4 +78,6 @@ class RemoteAgentController extends RestApiExceptionsHandler {
def create(@Valid @RequestBody agent: RemoteAgent) = {
service.create(agent)
}
+
+ private def find(key: Int) = service.get(key).getOrElse(throw new ResourceNotFoundException("Couldn't find agent"))
}
View
6 frontend/src/main/scala/com/griddynamics/genesis/rest/RootController.scala
@@ -1,11 +1,11 @@
package com.griddynamics.genesis.rest
import annotations.LinkTarget
-import com.griddynamics.genesis.rest.links.{HrefBuilder, ControllerClassAggregator, Link}
+import links.{LinkBuilder, HrefBuilder, ControllerClassAggregator}
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.{ResponseBody, RequestMethod, RequestMapping}
import javax.servlet.http.HttpServletRequest
-import com.griddynamics.genesis.api.Project
+import com.griddynamics.genesis.api.{SystemSettings, Link, Project}
import org.springframework.beans.factory.annotation.Autowired
import javax.servlet.{ServletContext, ServletConfig}
import com.griddynamics.genesis.users.GenesisRole
@@ -30,7 +30,7 @@ class RootController {
val links: Seq[Link] =
collectLinks(classOf[ProjectsController], classOf[Project], LinkTarget.COLLECTION) ++
(if (isAdminOrReadOnly) collectLinks(classOf[SettingsController], classOf[SystemSettings], LinkTarget.COLLECTION) else Seq()) ++
- (if (!isLogoutDisabled) Seq(Link(HrefBuilder.absolutePath("logout"), LinkTarget.LOGOUT, RequestMethod.GET)) else Seq())
+ (if (!isLogoutDisabled) Seq(LinkBuilder(HrefBuilder.absolutePath("logout"), LinkTarget.LOGOUT, RequestMethod.GET)) else Seq())
Map(
"user" -> GenesisRestController.getCurrentUser,
View
31 frontend/src/main/scala/com/griddynamics/genesis/rest/ServersController.scala
@@ -22,18 +22,21 @@
*/
package com.griddynamics.genesis.rest
+import annotations.{AddSelfLinks, LinkTarget}
+import links.CollectionWrapper._
+import links.{CollectionWrapper, ItemWrapper, WebPath, LinkBuilder}
+import links.HrefBuilder._
+import org.springframework.web.bind.annotation.RequestMethod._
import org.springframework.web.bind.annotation._
import org.springframework.stereotype.Controller
-import scala.Array
import javax.servlet.http.HttpServletRequest
import com.griddynamics.genesis.service.{CredentialsStoreService, ServersLoanService, ServersService}
import com.griddynamics.genesis.rest.GenesisRestController._
import com.griddynamics.genesis.api
import api.{ServerArray, ServerDescription, ExtendedResult}
-import scala.Some
-import com.griddynamics.genesis.service.impl.ProjectService
import org.springframework.beans.factory.annotation.Autowired
import javax.validation.Valid
+import com.griddynamics.genesis.spring.security.LinkSecurityBean
@Controller
@RequestMapping(Array("/rest/projects/{projectId}/server-arrays"))
@@ -42,6 +45,7 @@ class ServersController extends RestApiExceptionsHandler {
@Autowired var service: ServersService = _
@Autowired var loanService: ServersLoanService = _
@Autowired var credService: CredentialsStoreService =_
+ @Autowired implicit var linkSecurity: LinkSecurityBean = _
@RequestMapping(value = Array(""), method = Array(RequestMethod.POST))
@ResponseBody
@@ -51,26 +55,35 @@ class ServersController extends RestApiExceptionsHandler {
@RequestMapping(value = Array(""), method = Array(RequestMethod.GET))
@ResponseBody
- def list(@PathVariable("projectId") projectId: Int, request: HttpServletRequest) = {
- service.list(projectId)
+ @AddSelfLinks(methods = Array(GET, POST), modelClass = classOf[ServerArray])
+ def list(@PathVariable("projectId") projectId: Int, request: HttpServletRequest) : CollectionWrapper[ItemWrapper[ServerArray]] = {
+ implicit val req = request
+ def wrapServer(server: ServerArray) = {
+ val top = WebPath(request)
+ wrap(server).withLinks(LinkBuilder(top / server.id.get.toString, LinkTarget.SELF, server.getClass, PUT, DELETE, GET)).filtered()
+ }
+ wrapCollection(service.list(projectId).map(wrapServer(_)))
}
@RequestMapping(value = Array("{id}"), method = Array(RequestMethod.GET))
@ResponseBody
- def get(@PathVariable("projectId") projectId: Int, @PathVariable("id") id: Int) = {
- service.get(projectId, id).getOrElse(throw new ResourceNotFoundException("Server array wasn't found in project"))
+ @AddSelfLinks(methods = Array(GET, PUT, DELETE), modelClass = classOf[ServerArray])
+ def get(@PathVariable("projectId") projectId: Int, @PathVariable("id") id: Int, request: HttpServletRequest) : ItemWrapper[ServerArray] = {
+ find(projectId, id)
}
+ private def find(projectId: Int, id: Int) = service.get(projectId, id).getOrElse(throw new ResourceNotFoundException("Server array wasn't found in project"))
+
@RequestMapping(value = Array("{id}"), method = Array(RequestMethod.DELETE))
@ResponseBody
def delete(@PathVariable("projectId") projectId: Int, @PathVariable("id") id: Int, request: HttpServletRequest) = {
- service.delete(get(projectId, id))
+ service.delete(find(projectId, id))
}
@RequestMapping(value = Array("{id}"), method = Array(RequestMethod.PUT))
@ResponseBody
def updateServerArray(@PathVariable("projectId") projectId: Int, @PathVariable("id") id: Int, @Valid @RequestBody request: ServerArray) = {
- val array = get(projectId, id).copy(
+ val array = find(projectId, id).copy(
name = request.name,
description = request.description
)
View
19 frontend/src/main/scala/com/griddynamics/genesis/rest/SettingsController.scala
@@ -24,7 +24,7 @@
package com.griddynamics.genesis.rest
import annotations.LinkTarget._
-import links.WebPath
+import links.{LinkBuilder, WebPath, HrefBuilder}
import org.springframework.web.bind.annotation._
import org.springframework.web.bind.annotation.RequestMethod._
import org.springframework.stereotype.Controller
@@ -34,18 +34,15 @@ import com.griddynamics.genesis.rest.GenesisRestController.{extractParamsMap, pa
import javax.servlet.http.HttpServletRequest
import com.griddynamics.genesis.api._
import org.springframework.beans.factory.annotation.Autowired
-import links.{WebPath, HrefBuilder, Link}
import HrefBuilder._
import com.griddynamics.genesis.spring.security.LinkSecurityBean
import com.griddynamics.genesis.api.ConfigProperty
import com.griddynamics.genesis.api.Failure
import scala.Some
-import com.griddynamics.genesis.rest.SystemSettings
import com.griddynamics.genesis.api.Success
import com.griddynamics.genesis.users.UserService
import com.griddynamics.genesis.groups.GroupService
-case class SystemSettings(links: Array[Link]) //TODO: move to api after link will be moved
@Controller
@RequestMapping(value = Array("/rest/settings"))
@@ -72,17 +69,17 @@ class SettingsController extends RestApiExceptionsHandler {
implicit val req: HttpServletRequest = request
val path: WebPath = WebPath(absolutePath("/rest"))
var result = List (
- Link(path / "settings", COLLECTION, classOf[ConfigProperty], GET),
- Link(path / "databags", COLLECTION, classOf[DataBag], GET),
- Link(path / "roles", COLLECTION, GET, POST),
- Link(path / "agents", COLLECTION, classOf[RemoteAgent], GET),
- Link(path / "plugins", COLLECTION, classOf[Plugin], GET)
+ LinkBuilder(path / "settings", COLLECTION, classOf[ConfigProperty], GET),
+ LinkBuilder(path / "databags", COLLECTION, classOf[DataBag], GET),
+ LinkBuilder(path / "roles", COLLECTION, GET, POST),
+ LinkBuilder(path / "agents", COLLECTION, classOf[RemoteAgent], GET),
+ LinkBuilder(path / "plugins", COLLECTION, classOf[Plugin], GET)
)
if (! userService.isReadOnly) {
- result = Link(path / "users", COLLECTION, classOf[User], GET) :: result
+ result = LinkBuilder(path / "users", COLLECTION, classOf[User], GET) :: result
}
if (! groupService.isReadOnly) {
- result = Link(path / "groups", COLLECTION, classOf[UserGroup], GET) :: result
+ result = LinkBuilder(path / "groups", COLLECTION, classOf[UserGroup], GET) :: result
}
result.toArray
}
View
29 frontend/src/main/scala/com/griddynamics/genesis/rest/UsersController.scala
@@ -23,13 +23,20 @@
package com.griddynamics.genesis.rest
+import annotations.LinkTarget
+import links.CollectionWrapper._
+import links.{WebPath, LinkBuilder}
+import links.HrefBuilder._
import org.springframework.stereotype.Controller
import org.springframework.beans.factory.annotation.Autowired
import com.griddynamics.genesis.users.UserService
import com.griddynamics.genesis.groups.GroupService
import org.springframework.web.bind.annotation._
+import org.springframework.web.bind.annotation.RequestMethod._
import com.griddynamics.genesis.api.User
import javax.validation.Valid
+import com.griddynamics.genesis.spring.security.LinkSecurityBean
+import javax.servlet.http.HttpServletRequest
@Controller
@RequestMapping(Array("/rest/users"))
@@ -37,6 +44,7 @@ class UsersController extends RestApiExceptionsHandler {
@Autowired var userService: UserService = _
@Autowired var groupService: GroupService = _
+ @Autowired implicit var linkSecurity: LinkSecurityBean = _
@RequestMapping(method = Array(RequestMethod.GET), params = Array("available"))
@ResponseBody
@@ -44,7 +52,14 @@ class UsersController extends RestApiExceptionsHandler {
@RequestMapping(method = Array(RequestMethod.GET))
@ResponseBody
- def list() = userService.list
+ def list(request: HttpServletRequest) = {
+ implicit val req = request
+ val top = WebPath(request)
+ def wrapUser(user: User) = {
+ wrap(user).withLinks(LinkBuilder(top / user.username, LinkTarget.SELF, classOf[User], GET, PUT, DELETE)).filtered()
+ }
+ wrapCollection(userService.list.map(wrapUser(_))).withLinksToSelf(classOf[User], GET, POST).filtered()
+ }
@RequestMapping(method = Array(RequestMethod.GET), params = Array("tag"))
@ResponseBody
@@ -75,10 +90,14 @@ class UsersController extends RestApiExceptionsHandler {
@RequestMapping(value = Array("{username:.+}"), method=Array(RequestMethod.GET))
@ResponseBody
- def get(@PathVariable(value = "username") username: String) = {
- userService.findByUsername(username).getOrElse(throw new ResourceNotFoundException("User[username = " + username + "] was not found"))
+ def get(@PathVariable(value = "username") username: String, request: HttpServletRequest) = {
+ implicit val req = request
+ val user = find(username)
+ wrap(user).withLinksToSelf(GET, PUT, DELETE).filtered()
}
+ private def find(username: String) = userService.findByUsername(username).getOrElse(throw new ResourceNotFoundException("User[username = " + username + "] was not found"))
+
@RequestMapping(method = Array(RequestMethod.POST))
@ResponseBody
def create(@RequestBody @Valid request: User) = userService.create(request)
@@ -86,7 +105,7 @@ class UsersController extends RestApiExceptionsHandler {
@RequestMapping(value = Array("{username:.+}"), method = Array(RequestMethod.PUT))
@ResponseBody
def update(@PathVariable("username") username: String, @RequestBody @Valid request: User) = {
- val user = get(username).copy(
+ val user = find(username).copy(
email = request.email,
firstName = request.firstName,
lastName = request.lastName,
@@ -99,7 +118,7 @@ class UsersController extends RestApiExceptionsHandler {
@RequestMapping(value = Array("{username:.+}"), method = Array(RequestMethod.DELETE))
@ResponseBody
- def delete(@PathVariable(value="username") username: String) = userService.delete(get(username))
+ def delete(@PathVariable(value="username") username: String) = userService.delete(find(username))
@RequestMapping(value = Array("{userName}/groups"), method = Array(RequestMethod.GET))
@ResponseBody
View
11 ...om/griddynamics/genesis/rest/links/Wrappers.scala → ...namics/genesis/rest/links/CollectionWrapper.scala
@@ -5,6 +5,7 @@ import com.griddynamics.genesis.spring.security.LinkSecurityBean
import HrefBuilder.duplicate
import com.griddynamics.genesis.rest.annotations.LinkTarget
import org.springframework.web.bind.annotation.RequestMethod
+import com.griddynamics.genesis.api.Link
trait WithLinks {
@@ -13,12 +14,12 @@ trait WithLinks {
def filtered()(implicit security: LinkSecurityBean): WithLinks
}
-case class Wrappers[T](items: Iterable[T], links: Iterable[Link]) extends WithLinks {
+case class CollectionWrapper[T](items: Iterable[T], links: Iterable[Link]) extends WithLinks {
override def add(coll: Iterable[Link]) = copy(items = items, links = coll)
def withItems(newItems: Iterable[T]) = copy(items = newItems, links = links)
def withLinks(link: Link, rest: Link*) = copy(items = items, links = links ++ (link :: rest.toList))
def withLinksToSelf(clazz: Class[_], method: RequestMethod, methods: RequestMethod*)(implicit request: HttpServletRequest) =
- copy(items = items, links = Link(request, LinkTarget.SELF, clazz, (method :: methods.toList).toArray : _*) :: Nil)
+ copy(items = items, links = LinkBuilder(request, LinkTarget.SELF, clazz, (method :: methods.toList).toArray : _*) :: Nil)
override def filtered()(implicit security: LinkSecurityBean) = copy(items = items, links = security.filter(links.toArray))
}
@@ -27,12 +28,12 @@ case class ItemWrapper[T](item: T, links: Iterable[Link]) extends WithLinks {
implicit def withLinks(link: Link, rest: Link*) = copy(item = item, links = links ++ (link :: rest.toList))
override def filtered()(implicit security: LinkSecurityBean) = copy(item = item, links = security.filter(links.toArray))
def withLinksToSelf(method: RequestMethod, methods: RequestMethod*)(implicit request: HttpServletRequest) =
- copy(item = item, links = Link(request, LinkTarget.SELF, item.getClass, (method :: methods.toList).toArray : _*) :: Nil)
+ copy(item = item, links = LinkBuilder(request, LinkTarget.SELF, item.getClass, (method :: methods.toList).toArray : _*) :: Nil)
}
-object Wrappers {
- implicit def wrapCollection[T](coll: Iterable[T]) = new Wrappers[T](coll, List())
+object CollectionWrapper {
+ implicit def wrapCollection[T](coll: Iterable[T]) = new CollectionWrapper[T](coll, List())
implicit def wrap[T](item: T) = ItemWrapper[T](item, List())
}
View
37 ...la/com/griddynamics/genesis/rest/links/Link.scala → ...a/com/griddynamics/genesis/rest/links/Links.scala
@@ -6,38 +6,18 @@ import org.springframework.web.bind.annotation.{RequestMethod, RequestMapping}
import org.springframework.web.util.UriComponentsBuilder
import com.griddynamics.genesis.http.TunnelFilter
import com.griddynamics.genesis.rest.annotations.LinkTarget
-
-case class Link(href: String, rel: String, `type`: Option[String], methods: Array[String] = Array()) {
- def disassemble: Array[Link] = methods.map(m => new Link(href, rel, `type`, Array(m)))
- def remove(method: String) = new Link(href, rel, `type`, methods.filter(_ != method))
-}
-
-object Links {
- def merge(links: Array[Link]) = {
- val groupedByHref = links.groupBy(_.href)
-
- def assembleLinkList(linksList: Array[Link], href: String): Link = {
- val firstLink = linksList(0)
- val methods = linksList.map(_.methods).flatten
- Link(href, firstLink.rel, firstLink.`type`, methods)
- }
-
- groupedByHref.map {
- case (href, linksList) => assembleLinkList(linksList, href)
- }
- }
-}
+import com.griddynamics.genesis.api.Link
case class WebPath(start: String, elements: List[String] = List()) {
def / (path: String) = WebPath(start, elements ++ (path :: Nil))
override def toString = (start :: elements).mkString("/")
}
-object Link {
+object LinkBuilder {
def apply(href: String, rel: LinkTarget, methods: RequestMethod*) =
- new Link(href, rel.toRel, None, (methods.toList).map(_.toString.toLowerCase).toArray)
+ Link(href, rel.toRel, None, (methods.toList).map(_.toString.toLowerCase).toArray)
def apply(href: String, rel: LinkTarget, modelClazz: Class[_], methods: RequestMethod*) =
- new Link(href, rel.toRel, modelClazz, (methods.toList).map(_.toString.toLowerCase).toArray)
+ Link(href, rel.toRel, modelClazz, (methods.toList).map(_.toString.toLowerCase).toArray)
implicit def toContentType(modelClazz: Class[_]): Some[String] = {
Some(s"application/vnd.griddynamics.genesis.${modelClazz.getSimpleName}+json")
}
@@ -67,7 +47,6 @@ object HrefBuilder {
}
object ControllerClassAggregator {
- import Link._
def apply(controllerClazz: Class[_], modelClazz: Class[_], rel: LinkTarget, methods: Array[RequestMethod] = Array())(implicit request: HttpServletRequest): Array[Link] = {
val builder = HrefBuilder.uriBuilder
@@ -78,15 +57,15 @@ object ControllerClassAggregator {
ann.method()
else
methods
- Link(link, rel.toRel, modelClazz, getMethods(method))
+ LinkBuilder(link, rel, modelClazz, getMethods(method): _*)
})
}).getOrElse(Array())
}
- private[links] def getMethods(methods: Array[RequestMethod]) : Array[String] =
+ private[links] def getMethods(methods: Array[RequestMethod]) : Array[RequestMethod] =
if (methods.isEmpty)
- Array(RequestMethod.GET.toString.toLowerCase)
+ Array(RequestMethod.GET)
else
- methods.map(_.toString.toLowerCase)
+ methods
}
View
6 frontend/src/main/scala/com/griddynamics/genesis/spring/JsonMessageConverter.scala
@@ -31,7 +31,7 @@ import org.springframework.http.{HttpStatus, HttpOutputMessage, HttpInputMessage
import com.griddynamics.genesis.api.{ExtendedResult, Success, Failure}
import com.griddynamics.genesis.rest.GenesisRestController.{DEFAULT_CHARSET => DC}
import java.io.StringWriter
-import com.griddynamics.genesis.rest.links.{Wrappers, ItemWrapper}
+import com.griddynamics.genesis.rest.links.{CollectionWrapper, ItemWrapper}
class JsonMessageConverter
extends HttpMessageConverter[AnyRef]{
@@ -62,7 +62,7 @@ class JsonMessageConverter
JField("items", JArray(wrapped.toList map decomposeItemWrapper))
}
- def serializeCollectionWrapper(res: Wrappers[_]): String = {
+ def serializeCollectionWrapper(res: CollectionWrapper[_]): String = {
if (res.items.isEmpty) {
Serialization.write(res)
} else {
@@ -86,7 +86,7 @@ class JsonMessageConverter
val message: String = t match {
case b: ExtendedResult[_] => serializeExtendedResult(b)
case wrapped: ItemWrapper[_] => serializeItemWrapper(wrapped)
- case collectionWrapper: Wrappers[_] => serializeCollectionWrapper(collectionWrapper)
+ case collectionWrapper: CollectionWrapper[_] => serializeCollectionWrapper(collectionWrapper)
case _ => Serialization.write(t)
}
val messageBytes: Array[Byte] = message.getBytes(DEFAULT_CHARSET.name())
View
33 frontend/src/main/scala/com/griddynamics/genesis/spring/aop/LinksToAspect.scala
@@ -2,14 +2,16 @@ package com.griddynamics.genesis.spring.aop
import org.aspectj.lang.annotation._
import org.aspectj.lang.ProceedingJoinPoint
-import com.griddynamics.genesis.rest.annotations.LinksTo
-import com.griddynamics.genesis.rest.links.{ControllerClassAggregator, Link, WithLinks}
+import com.griddynamics.genesis.rest.annotations.{LinkTarget, AddSelfLinks, LinksTo}
+import com.griddynamics.genesis.rest.links.{LinkBuilder, WebPath, ControllerClassAggregator, WithLinks}
+import com.griddynamics.genesis.rest.links.HrefBuilder._
import javax.servlet.http.HttpServletRequest
import com.griddynamics.genesis.util.Logging
import org.springframework.security.access.{SecurityMetadataSource, AccessDecisionManager}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import com.griddynamics.genesis.spring.security.LinkSecurityBean
+import com.griddynamics.genesis.api.Link
@Aspect
class LinksToAspect extends Logging {
@@ -25,6 +27,9 @@ class LinksToAspect extends Logging {
@Pointcut(value="@annotation(com.griddynamics.genesis.rest.annotations.LinksTo)")
def linksTo() {}
+ @Pointcut(value="@annotation(com.griddynamics.genesis.rest.annotations.AddSelfLinks)")
+ def addSelfLinks() {}
+
@Around(value = "linksTo() && @annotation(ann)")
def postProcessMessage(joinPoint: ProceedingJoinPoint, ann: LinksTo) = {
val proceed = joinPoint.proceed()
@@ -43,6 +48,30 @@ class LinksToAspect extends Logging {
proceed
}
}
+
+ @Around(value = "addSelfLinks() && @annotation(ann)")
+ def appendLinks(joinPoint: ProceedingJoinPoint, ann: AddSelfLinks) = {
+ val proceed = joinPoint.proceed()
+ if (proceed.isInstanceOf[WithLinks]) {
+ LinksToAspect.getRequest(joinPoint).map(
+ implicit request => {
+ def wrapper = proceed.asInstanceOf[WithLinks]
+ val top = WebPath(request)
+ val link = Array(LinkBuilder(top, LinkTarget.SELF, ann.modelClass(), ann.methods() : _*))
+ wrapper.add(linkSecurity.filter(link))
+ }
+ ).getOrElse({
+ log.warn("Cannot find request in arguments of method annotated with %s", ann.getClass)
+ })
+ } else {
+ val actualClass = if (proceed != null)
+ proceed.getClass
+ else
+ Void.TYPE
+ log.warn("Unsupported result for method annotated with %s. Expected one of WithLinks actually: %s", ann.getClass, actualClass)
+ proceed
+ }
+ }
}
object LinksToAspect {
View
2  frontend/src/main/scala/com/griddynamics/genesis/spring/security/LinkSecurityBean.scala
@@ -7,10 +7,10 @@ import javax.annotation.PostConstruct
import org.springframework.security.web.FilterInvocation
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
import com.griddynamics.genesis.util.Logging
-import com.griddynamics.genesis.rest.links.{Links, Link}
import java.net.URL
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.authentication.InsufficientAuthenticationException
+import com.griddynamics.genesis.api.{Links, Link}
class LinkSecurityBean extends Logging {
View
4 .../src/main/scala/com/griddynamics/genesis/spring/security/acls/ScalaMethodSecurityExpressionHandler.scala
@@ -26,7 +26,7 @@ import org.springframework.security.access.expression.method.DefaultMethodSecuri
import org.springframework.expression.{EvaluationContext, Expression}
import org.springframework.security.access.expression.{SecurityExpressionRoot, ExpressionUtils}
import org.springframework.security.access.PermissionCacheOptimizer
-import com.griddynamics.genesis.rest.links.{ItemWrapper, Wrappers}
+import com.griddynamics.genesis.rest.links.{ItemWrapper, CollectionWrapper}
class ScalaMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
@@ -35,7 +35,7 @@ class ScalaMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressi
override def filter(filterTarget: Any, filterExpression: Expression, ctx: EvaluationContext) = {
filterTarget match {
case seq: Iterable[AnyRef] => filterScalaCollection(seq, filterExpression, ctx)
- case wrapper: Wrappers[AnyRef] => wrapper.withItems(filterScalaCollection(wrapper.items, filterExpression, ctx))
+ case wrapper: CollectionWrapper[AnyRef] => wrapper.withItems(filterScalaCollection(wrapper.items, filterExpression, ctx))
case _ => super.filter(filterTarget, filterExpression, ctx)
}
}
View
10 ui/src/main/resources/genesis/app/modules/project_properties/servers.js
@@ -29,7 +29,15 @@ function(genesis, status, validation, Backbone, $) {
this.projectId = options.projectId;
},
- url: function() { return "rest/projects/" + this.projectId + "/server-arrays"; }
+ url: function() { return "rest/projects/" + this.projectId + "/server-arrays"; },
+
+ parse: function(json) {
+ if (json.items) {
+ return json.items;
+ } else {
+ return json;
+ }
+ }
});
Servers.ServerModel = Backbone.Model.extend({
View
2  ui/src/main/resources/genesis/app/modules/settings/agents.coffee
@@ -18,6 +18,8 @@ define [
class Agents.Collection extends Backbone.Collection
model: Agents.Model
url: URL
+ parse: (json) ->
+ json.items
class Agents.Views.Main extends Backbone.View
events:
View
3  ui/src/main/resources/genesis/app/modules/settings/groups.coffee
@@ -10,6 +10,9 @@ define ["genesis", "backbone", "cs!modules/settings/users", "modules/status", "m
url: ->
URL + "/" + @groupId + "/users"
+ parse: (json) ->
+ json.items
+
EmptyGroup = new Groups.Model(
name: ""
description: ""
View
10 ui/src/main/resources/genesis/app/modules/settings/users.coffee
@@ -8,9 +8,19 @@ define ["genesis", "backbone", "modules/status", "modules/validation", "services
class Users.Collections.People extends Backbone.Collection
url: URL
model: Users.Model
+ parse: (json) ->
+ if json.items?
+ json.items
+ else
+ json
class Users.Collections.Groups extends Backbone.Collection
url: "rest/groups"
+ parse: (json) ->
+ if json.items?
+ json.items
+ else
+ json
EMPTY_USER = new Users.Model(
username: ""
Something went wrong with that request. Please try again.