RESTful API for managing sites

Jens Pelzetter 2020-06-29 21:16:40 +02:00
parent e7e5afab5c
commit f7571263da
9 changed files with 623 additions and 174 deletions

View File

@ -20,6 +20,7 @@ import org.libreccm.api.DefaultResponseHeaders;
import org.libreccm.api.PreflightRequestFilter; import org.libreccm.api.PreflightRequestFilter;
import org.libreccm.api.admin.categorization.CategoriesApi; import org.libreccm.api.admin.categorization.CategoriesApi;
import org.libreccm.api.admin.categorization.DomainsApi; import org.libreccm.api.admin.categorization.DomainsApi;
import org.libreccm.api.admin.sites.SitesApi;
import org.libreccm.configuration.Configuration; import org.libreccm.configuration.Configuration;
/** /**
@ -49,6 +50,9 @@ public class AdminApi extends Application {
classes.add(RolesApi.class); classes.add(RolesApi.class);
classes.add(UsersApi.class); classes.add(UsersApi.class);
// Sites API
classes.add(SitesApi.class);
// System Information API // System Information API
classes.add(SystemInformationApi.class); classes.add(SystemInformationApi.class);

View File

@ -52,7 +52,7 @@ public class CategoryData {
private long categoryOrder; private long categoryOrder;
public CategoryData() { public CategoryData() {
// Nothing super();
} }
public CategoryData(final Category fromCategory) { public CategoryData(final Category fromCategory) {

View File

@ -44,7 +44,6 @@ import org.libreccm.security.Role;
import org.libreccm.security.RoleManager; import org.libreccm.security.RoleManager;
import org.libreccm.security.RoleRepository; import org.libreccm.security.RoleRepository;
import java.net.URI;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.ws.rs.WebApplicationException; import javax.ws.rs.WebApplicationException;

View File

@ -0,0 +1,334 @@
/*
* Copyright (C) 2020 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.libreccm.api.admin.sites;
import org.libreccm.api.Identifier;
import org.libreccm.api.IdentifierParser;
import org.libreccm.api.admin.sites.dto.SiteDto;
import org.libreccm.api.admin.web.dto.CcmApplicationId;
import org.libreccm.core.CoreConstants;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.RequiresPrivilege;
import org.libreccm.sites.Site;
import org.libreccm.sites.SiteAwareApplication;
import org.libreccm.sites.SiteManager;
import org.libreccm.sites.SiteRepository;
import org.libreccm.web.ApplicationRepository;
import org.libreccm.web.CcmApplication;
import java.util.List;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@Path("/sites")
public class SitesApi {
@Context
private UriInfo uriInfo;
@Inject
private ApplicationRepository applicationRepository;
@Inject
private IdentifierParser identifierParser;
@Inject
private SiteManager siteManager;
@Inject
private SiteRepository siteRepository;
@GET
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public List<SiteDto> getSites() {
return siteRepository
.findAll()
.stream()
.map(SiteDto::new)
.collect(Collectors.toList());
}
@GET
@Path("/{siteIdentifier}")
@Produces(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public SiteDto getSite(
@PathParam("siteIdentifier") final String siteIdentifier
) {
return new SiteDto(findSite(siteIdentifier));
}
@POST
@Path("/")
@Consumes(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public Response addSite(final SiteDto siteDto) {
final Site site = new Site();
site.setDefaultSite(siteDto.isDefaultSite());
site.setDefaultTheme(siteDto.getDefaultTheme());
site.setDomainOfSite(siteDto.getDomainOfSite());
siteRepository.save(site);
return Response
.created(
uriInfo
.getBaseUriBuilder()
.path(site.getDomainOfSite())
.build()
).build();
}
@PUT
@Path("/{siteIdentifier}")
@Consumes(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public Response updateSite(
@PathParam("siteIdentifier") final String siteIdentifier,
final SiteDto siteDto
) {
final Site site = findSite(siteIdentifier);
site.setDefaultSite(siteDto.isDefaultSite());
site.setDefaultTheme(siteDto.getDefaultTheme());
site.setDomainOfSite(siteDto.getDomainOfSite());
siteRepository.save(site);
return Response.ok(
String.format(
"Site %s updated successfully.",
siteIdentifier
)
).build();
}
@DELETE
@Path("/{siteIdentifier}")
@Produces(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public Response deleteSite(
@PathParam("siteIdentifier") final String siteIdentifier
) {
final Site site = findSite(siteIdentifier);
siteRepository.delete(site);
return Response.ok(
String.format("Site %s successfully deleted", siteIdentifier)
).build();
}
@GET
@Path("/{siteIdentifier}/applications")
@Produces(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public List<CcmApplicationId> getApplications(
@PathParam("siteIdentifier") final String siteIdentifier
) {
final Site site = findSite(siteIdentifier);
return site
.getApplications()
.stream()
.map(CcmApplicationId::new)
.collect(Collectors.toList());
}
@PUT
@Path("/{siteIdentifier}/applications/{applicationIdentifier}")
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public Response addApplication(
@PathParam("siteIdentifier") final String siteIdentifier,
@PathParam("applicationIdentifier") final String applicationIdentifier
) {
final Site site = findSite(siteIdentifier);
final SiteAwareApplication application = findApplication(
applicationIdentifier
);
siteManager.addApplicationToSite(application, site);
return Response.ok(
String.format(
"Application %s successfully added to Site %s.",
applicationIdentifier,
siteIdentifier
)
).build();
}
@DELETE
@Path("/{siteIdentifier}/applications/{applicationIdentifier}")
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public Response removeApplication(
@PathParam("siteIdentifier") final String siteIdentifier,
@PathParam("applicationIdentifier") final String applicationIdentifier
) {
final Site site = findSite(siteIdentifier);
final SiteAwareApplication application = findApplication(
applicationIdentifier
);
siteManager.removeApplicationFromSite(application, site);
return Response.ok(
String.format(
"Application %s successfully removed from Site %s.",
applicationIdentifier,
siteIdentifier
)
).build();
}
private Site findSite(final String siteIdentifier) {
final Identifier identifier = identifierParser.parseIdentifier(
siteIdentifier
);
switch (identifier.getType()) {
case ID:
return siteRepository.findById(
Long.parseLong(identifier.getIdentifier())
).orElseThrow(
() -> new NotFoundException(
String.format(
"No Site with ID %s found.",
identifier.getIdentifier()
)
)
);
case UUID:
return siteRepository
.findByUuid(identifier.getIdentifier())
.orElseThrow(
() -> new NotFoundException(
String.format(
"No Site with UUID %s found",
identifier.getIdentifier()
)
)
);
default:
return siteRepository
.findByDomain(identifier.getIdentifier())
.orElseThrow(
() -> new NotFoundException(
String.format(
"No Site for domain %s found.",
identifier.getIdentifier()
)
)
);
}
}
private SiteAwareApplication findApplication(final String appIdentifier) {
final Identifier identifier = identifierParser.parseIdentifier(
appIdentifier
);
final CcmApplication application;
switch (identifier.getType()) {
case ID:
application = applicationRepository
.findById(Long.parseLong(identifier.getIdentifier()))
.orElseThrow(
() -> new NotFoundException(
String.format(
"No CcmApplication with ID %s found.",
identifier.getIdentifier()
)
)
);
break;
case UUID:
application = applicationRepository
.findByUuid(identifier.getIdentifier())
.orElseThrow(
() -> new NotFoundException(
String.format(
"No CcmApplication with UUID %s found.",
identifier.getIdentifier()
)
)
);
break;
default:
application = applicationRepository
.retrieveApplicationForPath(identifier.getIdentifier())
.orElseThrow(
() -> new NotFoundException(
String.format(
"No CcmApplication for path %s found.",
identifier.getIdentifier()
)
)
);
break;
}
if (application instanceof SiteAwareApplication) {
return (SiteAwareApplication) application;
} else {
throw new BadRequestException(
String.format(
"Site %s is not a site aware application.",
identifier.getIdentifier()
)
);
}
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2020 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.libreccm.api.admin.sites.dto;
import org.libreccm.sites.Site;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class SiteDto {
private long siteId;
private String uuid;
private String domainOfSite;
private boolean defaultSite;
private String defaultTheme;
public SiteDto() {
super();
}
public SiteDto(final Site fromSite) {
siteId = fromSite.getObjectId();
uuid = fromSite.getUuid();
domainOfSite = fromSite.getDomainOfSite();
defaultSite = fromSite.isDefaultSite();
defaultTheme = fromSite.getDefaultTheme();
}
public long getSiteId() {
return siteId;
}
public void setSiteId(long siteId) {
this.siteId = siteId;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getDomainOfSite() {
return domainOfSite;
}
public void setDomainOfSite(String domainOfSite) {
this.domainOfSite = domainOfSite;
}
public boolean isDefaultSite() {
return defaultSite;
}
public void setDefaultSite(boolean defaultSite) {
this.defaultSite = defaultSite;
}
public String getDefaultTheme() {
return defaultTheme;
}
public void setDefaultTheme(String defaultTheme) {
this.defaultTheme = defaultTheme;
}
}

View File

@ -49,15 +49,17 @@ import javax.persistence.Table;
query = "SELECT s FROM Site s " query = "SELECT s FROM Site s "
+ "WHERE s.domainOfSite = :domain " + "WHERE s.domainOfSite = :domain "
+ "ORDER BY s.domainOfSite" + "ORDER BY s.domainOfSite"
) ),
,
@NamedQuery( @NamedQuery(
name = "Site.findDefaultSite", name = "Site.findDefaultSite",
query = "SELECT s FROM Site s " query = "SELECT s FROM Site s "
+ "WHERE s.defaultSite = true " + "WHERE s.defaultSite = true "
+ "ORDER BY s.domainOfSite" + "ORDER BY s.domainOfSite"
) ),
, @NamedQuery(
name = "Site.findByUuid",
query = "SELECT s FROM Site s WHERE s.uuid = :uuid"
),
@NamedQuery( @NamedQuery(
name = "Site.hasSiteForDomain", name = "Site.hasSiteForDomain",
query = "SELECT (CASE WHEN COUNT(s) > 0 THEN true ELSE false END) " query = "SELECT (CASE WHEN COUNT(s) > 0 THEN true ELSE false END) "

View File

@ -96,8 +96,9 @@ public class SiteAwareApplication extends CcmApplication {
} else if (site != null && other.getSite() == null) { } else if (site != null && other.getSite() == null) {
return false; return false;
} else { } else {
if (!Objects.equals(site.getDomainOfSite(), if (!Objects.equals(
other.getSite().getDomainOfSite())) { site.getDomainOfSite(), other.getSite().getDomainOfSite()
)) {
return false; return false;
} }
if (site.isDefaultSite() != other.getSite().isDefaultSite()) { if (site.isDefaultSite() != other.getSite().isDefaultSite()) {
@ -121,16 +122,20 @@ public class SiteAwareApplication extends CcmApplication {
public String toString(final String data) { public String toString(final String data) {
if (site == null) { if (site == null) {
return super.toString(String.format(", site = null%d", data)); return super.toString(String.format(", site = null%s", data));
} else { } else {
return super.toString(String.format(", site = { " return super.toString(
String.format(
", site = { "
+ "domainOfSite = \"%s\", " + "domainOfSite = \"%s\", "
+ "isDefaultSite = %b," + "isDefaultSite = %b,"
+ "defaultTheme = \"%s\" }%s", + "defaultTheme = \"%s\" }%s",
site.getDomainOfSite(), site.getDomainOfSite(),
site.isDefaultSite(), site.isDefaultSite(),
site.getDefaultTheme(), site.getDefaultTheme(),
data)); data
)
);
} }
} }

View File

@ -41,6 +41,20 @@ public class SiteRepository extends AbstractEntityRepository<Long, Site> {
private static final long serialVersionUID = 3120528987720524155L; private static final long serialVersionUID = 3120528987720524155L;
@Transactional(Transactional.TxType.REQUIRED)
public Optional<Site> findByUuid(final String uuid) {
try {
return Optional.of(
getEntityManager()
.createNamedQuery("Site.findByUuid", Site.class)
.setParameter("uuid", uuid)
.getSingleResult()
);
} catch (NoResultException ex) {
return Optional.empty();
}
}
/** /**
* Retrieve the {@link Site} for a specific domain. * Retrieve the {@link Site} for a specific domain.
* *

View File

@ -69,7 +69,7 @@
}, },
"/api/admin/categories/{domainIdentifier}/{path}" : { "/api/admin/categories/{domainIdentifier}/{path}" : {
"get" : { "get" : {
"operationId" : "getCategory_2", "operationId" : "getCategory_1",
"parameters" : [ { "parameters" : [ {
"name" : "domainIdentifier", "name" : "domainIdentifier",
"in" : "path", "in" : "path",
@ -162,7 +162,7 @@
}, },
"/api/admin/categories/ID-{categoryId}" : { "/api/admin/categories/ID-{categoryId}" : {
"get" : { "get" : {
"operationId" : "getCategory_1", "operationId" : "getCategory",
"parameters" : [ { "parameters" : [ {
"name" : "categoryId", "name" : "categoryId",
"in" : "path", "in" : "path",
@ -237,7 +237,7 @@
}, },
"/api/admin/categories/{domainIdentifier}/{path}/subcategories" : { "/api/admin/categories/{domainIdentifier}/{path}/subcategories" : {
"get" : { "get" : {
"operationId" : "getSubCategories_2", "operationId" : "getSubCategories",
"parameters" : [ { "parameters" : [ {
"name" : "domainIdentifier", "name" : "domainIdentifier",
"in" : "path", "in" : "path",
@ -631,7 +631,7 @@
}, },
"/api/admin/categories/UUID-{categoryId}" : { "/api/admin/categories/UUID-{categoryId}" : {
"get" : { "get" : {
"operationId" : "getCategory", "operationId" : "getCategory_2",
"parameters" : [ { "parameters" : [ {
"name" : "categoryId", "name" : "categoryId",
"in" : "path", "in" : "path",
@ -656,7 +656,7 @@
}, },
"/api/admin/categories/UUID-{categoryUid}/subcategories" : { "/api/admin/categories/UUID-{categoryUid}/subcategories" : {
"get" : { "get" : {
"operationId" : "getSubCategories", "operationId" : "getSubCategories_1",
"parameters" : [ { "parameters" : [ {
"name" : "categoryUuid", "name" : "categoryUuid",
"in" : "path", "in" : "path",
@ -697,7 +697,7 @@
}, },
"/api/admin/categories/ID-{categoryId}/subcategories" : { "/api/admin/categories/ID-{categoryId}/subcategories" : {
"get" : { "get" : {
"operationId" : "getSubCategories_1", "operationId" : "getSubCategories_2",
"parameters" : [ { "parameters" : [ {
"name" : "categoryId", "name" : "categoryId",
"in" : "path", "in" : "path",
@ -1299,47 +1299,6 @@
} }
} }
}, },
"/api/admin/groups/{groupIdentifier}/members" : {
"get" : {
"operationId" : "getMembers",
"parameters" : [ {
"name" : "groupIdentifier",
"in" : "path",
"required" : true,
"schema" : {
"type" : "string"
}
}, {
"name" : "limit",
"in" : "query",
"schema" : {
"type" : "integer",
"format" : "int32",
"default" : 20
}
}, {
"name" : "offset",
"in" : "query",
"schema" : {
"type" : "integer",
"format" : "int32",
"default" : 0
}
} ],
"responses" : {
"default" : {
"description" : "default response",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/ListViewGroupUserMembership"
}
}
}
}
}
}
},
"/api/admin/groups/{groupIdentifier}/roles" : { "/api/admin/groups/{groupIdentifier}/roles" : {
"get" : { "get" : {
"operationId" : "getRoleMemberships", "operationId" : "getRoleMemberships",
@ -1422,6 +1381,47 @@
} }
} }
}, },
"/api/admin/groups/{groupIdentifier}/members" : {
"get" : {
"operationId" : "getMembers",
"parameters" : [ {
"name" : "groupIdentifier",
"in" : "path",
"required" : true,
"schema" : {
"type" : "string"
}
}, {
"name" : "limit",
"in" : "query",
"schema" : {
"type" : "integer",
"format" : "int32",
"default" : 20
}
}, {
"name" : "offset",
"in" : "query",
"schema" : {
"type" : "integer",
"format" : "int32",
"default" : 0
}
} ],
"responses" : {
"default" : {
"description" : "default response",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/ListViewGroupUserMembership"
}
}
}
}
}
}
},
"/api/admin/roles/{roleIdentifier}" : { "/api/admin/roles/{roleIdentifier}" : {
"get" : { "get" : {
"operationId" : "getRole", "operationId" : "getRole",
@ -1548,6 +1548,60 @@
} }
} }
}, },
"/api/admin/roles" : {
"get" : {
"operationId" : "getRoles",
"parameters" : [ {
"name" : "limit",
"in" : "query",
"schema" : {
"type" : "integer",
"format" : "int32",
"default" : 20
}
}, {
"name" : "offset",
"in" : "query",
"schema" : {
"type" : "integer",
"format" : "int32",
"default" : 0
}
} ],
"responses" : {
"default" : {
"description" : "default response",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/ListViewRoleData"
}
}
}
}
}
},
"post" : {
"operationId" : "addRole",
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/RoleData"
}
}
}
},
"responses" : {
"default" : {
"description" : "default response",
"content" : {
"*/*" : { }
}
}
}
}
},
"/api/admin/roles/{roleIdentifier}/permissions" : { "/api/admin/roles/{roleIdentifier}/permissions" : {
"get" : { "get" : {
"operationId" : "getPermissions", "operationId" : "getPermissions",
@ -1617,6 +1671,34 @@
} }
} }
}, },
"/api/admin/roles/{roleIdentifier}/permissions/{permissionIdentifier}" : {
"delete" : {
"operationId" : "removePermission",
"parameters" : [ {
"name" : "roleIdentifier",
"in" : "path",
"required" : true,
"schema" : {
"type" : "string"
}
}, {
"name" : "permissionIdentifier",
"in" : "path",
"required" : true,
"schema" : {
"type" : "string"
}
} ],
"responses" : {
"default" : {
"description" : "default response",
"content" : {
"*/*" : { }
}
}
}
}
},
"/api/admin/roles/{roleIdentifier}/members" : { "/api/admin/roles/{roleIdentifier}/members" : {
"get" : { "get" : {
"operationId" : "getMembers_1", "operationId" : "getMembers_1",
@ -1658,88 +1740,6 @@
} }
} }
}, },
"/api/admin/roles" : {
"get" : {
"operationId" : "getRoles",
"parameters" : [ {
"name" : "limit",
"in" : "query",
"schema" : {
"type" : "integer",
"format" : "int32",
"default" : 20
}
}, {
"name" : "offset",
"in" : "query",
"schema" : {
"type" : "integer",
"format" : "int32",
"default" : 0
}
} ],
"responses" : {
"default" : {
"description" : "default response",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/ListViewRoleData"
}
}
}
}
}
},
"post" : {
"operationId" : "addRole",
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/RoleData"
}
}
}
},
"responses" : {
"default" : {
"description" : "default response",
"content" : {
"*/*" : { }
}
}
}
}
},
"/api/admin/roles/{roleIdentifier}/permissions/{permissionIdentifier}" : {
"delete" : {
"operationId" : "removePermission",
"parameters" : [ {
"name" : "roleIdentifier",
"in" : "path",
"required" : true,
"schema" : {
"type" : "string"
}
}, {
"name" : "permissionIdentifier",
"in" : "path",
"required" : true,
"schema" : {
"type" : "string"
}
} ],
"responses" : {
"default" : {
"description" : "default response",
"content" : {
"*/*" : { }
}
}
}
}
},
"/api/admin/users" : { "/api/admin/users" : {
"get" : { "get" : {
"operationId" : "getUsers", "operationId" : "getUsers",
@ -2521,13 +2521,13 @@
} }
} }
}, },
"ListViewGroupUserMembership" : { "ListViewGroupData" : {
"type" : "object", "type" : "object",
"properties" : { "properties" : {
"list" : { "list" : {
"type" : "array", "type" : "array",
"items" : { "items" : {
"$ref" : "#/components/schemas/GroupUserMembership" "$ref" : "#/components/schemas/GroupData"
} }
}, },
"count" : { "count" : {
@ -2544,13 +2544,13 @@
} }
} }
}, },
"ListViewGroupData" : { "ListViewGroupUserMembership" : {
"type" : "object", "type" : "object",
"properties" : { "properties" : {
"list" : { "list" : {
"type" : "array", "type" : "array",
"items" : { "items" : {
"$ref" : "#/components/schemas/GroupData" "$ref" : "#/components/schemas/GroupUserMembership"
} }
}, },
"count" : { "count" : {
@ -2625,6 +2625,29 @@
} }
} }
}, },
"ListViewRoleData" : {
"type" : "object",
"properties" : {
"list" : {
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/RoleData"
}
},
"count" : {
"type" : "integer",
"format" : "int64"
},
"limit" : {
"type" : "integer",
"format" : "int64"
},
"offset" : {
"type" : "integer",
"format" : "int64"
}
}
},
"ListViewRolePermission" : { "ListViewRolePermission" : {
"type" : "object", "type" : "object",
"properties" : { "properties" : {
@ -2686,29 +2709,6 @@
} }
} }
}, },
"ListViewRoleData" : {
"type" : "object",
"properties" : {
"list" : {
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/RoleData"
}
},
"count" : {
"type" : "integer",
"format" : "int64"
},
"limit" : {
"type" : "integer",
"format" : "int64"
},
"offset" : {
"type" : "integer",
"format" : "int64"
}
}
},
"EmailAddress" : { "EmailAddress" : {
"required" : [ "address" ], "required" : [ "address" ],
"type" : "object", "type" : "object",