Skip to content

Commit

Permalink
MPL-75: Implement schema filter on team ownership
Browse files Browse the repository at this point in the history
  • Loading branch information
lampajr authored and johnaohara committed Apr 24, 2024
1 parent d93c4e4 commit dd9790c
Show file tree
Hide file tree
Showing 10 changed files with 266 additions and 111 deletions.
6 changes: 6 additions & 0 deletions docs/site/content/en/openapi/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,12 @@ paths:
schema:
$ref: '#/components/schemas/SortDirection'
example: Ascending
- name: roles
in: query
description: "__my, __all or a comma delimited list of roles"
schema:
type: string
example: __my
responses:
"200":
description: OK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,11 @@ public interface SchemaService {
@Parameter(name = "limit", description = "limit the number of results", example = "20"),
@Parameter(name = "page", description = "filter by page number of a paginated list of Schemas", example = "2"),
@Parameter(name = "sort", description = "Field name to sort results", example = "name"),
@Parameter(name = "direction", description = "Sort direction", example ="Ascending")
@Parameter(name = "direction", description = "Sort direction", example ="Ascending"),
@Parameter(name = "roles", description = "__my, __all or a comma delimited list of roles", example = "__my"),
})
SchemaQueryResult list(@QueryParam("limit") Integer limit,
SchemaQueryResult list(@QueryParam("roles") String roles,
@QueryParam("limit") Integer limit,
@QueryParam("page") Integer page,
@QueryParam("sort") String sort,
@QueryParam("direction") SortDirection direction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,9 @@ class TestQueryResult {
@JsonProperty(required = true)
public long count;

// required for automatic parsing in testing
public TestQueryResult() {}

public TestQueryResult(List<Test> tests, long count) {
this.tests = tests;
this.count = count;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public final class Roles {
public static final String HORREUM_SYSTEM = "horreum.system";
public static final String HORREUM_MESSAGEBUS = "horreum.messagebus";

private static final String MY_ROLES = "__my";
private static final String ALL_ROLES = "__all";
public static final String MY_ROLES = "__my";
public static final String ALL_ROLES = "__all";

private Roles() {}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
package io.hyperfoil.tools.horreum.svc;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.networknt.schema.AbsoluteIri;
import com.networknt.schema.JsonMetaSchema;
import com.networknt.schema.JsonSchemaFactory;
import com.networknt.schema.SchemaLocation;
import com.networknt.schema.resource.InputStreamSource;
import com.networknt.schema.resource.SchemaLoader;
import io.hyperfoil.tools.horreum.api.SortDirection;
import io.hyperfoil.tools.horreum.api.data.Access;
import io.hyperfoil.tools.horreum.api.data.Dataset;
import io.hyperfoil.tools.horreum.api.data.Extractor;
import io.hyperfoil.tools.horreum.api.data.Label;
import io.hyperfoil.tools.horreum.api.data.Schema;
import io.hyperfoil.tools.horreum.api.data.SchemaExport;
import io.hyperfoil.tools.horreum.api.data.Transformer;
import io.hyperfoil.tools.horreum.api.services.SchemaService;
import io.hyperfoil.tools.horreum.bus.AsyncEventChannels;
import io.hyperfoil.tools.horreum.bus.BlockingTaskDispatcher;
import io.hyperfoil.tools.horreum.entity.ValidationErrorDAO;
import io.hyperfoil.tools.horreum.entity.data.DatasetDAO;
import io.hyperfoil.tools.horreum.entity.data.LabelDAO;
import io.hyperfoil.tools.horreum.entity.data.RunDAO;
Expand All @@ -25,44 +33,23 @@
import io.hyperfoil.tools.horreum.mapper.LabelMapper;
import io.hyperfoil.tools.horreum.mapper.SchemaMapper;
import io.hyperfoil.tools.horreum.mapper.TransformerMapper;
import io.hyperfoil.tools.horreum.api.services.SchemaService;
import io.hyperfoil.tools.horreum.api.SortDirection;
import io.hyperfoil.tools.horreum.bus.BlockingTaskDispatcher;
import io.hyperfoil.tools.horreum.entity.ValidationErrorDAO;
import io.hyperfoil.tools.horreum.mapper.ValidationErrorMapper;
import io.hyperfoil.tools.horreum.server.WithRoles;
import io.hyperfoil.tools.horreum.server.WithToken;
import io.quarkus.hibernate.orm.panache.PanacheQuery;
import io.quarkus.narayana.jta.runtime.TransactionConfiguration;
import io.quarkus.panache.common.Page;
import io.quarkus.panache.common.Sort;
import io.quarkus.security.identity.SecurityIdentity;

import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.persistence.Query;
import jakarta.transaction.TransactionManager;
import jakarta.persistence.Tuple;
import jakarta.transaction.TransactionManager;
import jakarta.transaction.Transactional;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import jakarta.ws.rs.DefaultValue;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
Expand All @@ -72,11 +59,13 @@
import org.hibernate.type.StandardBasicTypes;
import org.jboss.logging.Logger;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;

import com.networknt.schema.JsonSchemaFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class SchemaServiceImpl implements SchemaService {
private static final Logger log = Logger.getLogger(SchemaServiceImpl.class);
Expand Down Expand Up @@ -212,19 +201,37 @@ private void verifyNewSchema(Schema schemaDTO) {
@PermitAll
@WithRoles
@Override
public SchemaQueryResult list(Integer limit, Integer page, String sort,
public SchemaQueryResult list(String roles, Integer limit, Integer page, String sort,
@DefaultValue("Ascending") SortDirection direction) {
PanacheQuery<SchemaDAO> query;
Set<String> actualRoles = null;
if (Roles.hasRolesParam(roles)) {
if (roles.equals(Roles.MY_ROLES)) {
if (!identity.isAnonymous()) {
actualRoles = identity.getRoles();
}
} else {
actualRoles = new HashSet<>(Arrays.asList(roles.split(";")));
}
}

if (sort == null || sort.isEmpty()) {
sort = "name";
}
Sort.Direction sortDirection = direction == null ? null : Sort.Direction.valueOf(direction.name());
if (limit != null && page != null) {
List<SchemaDAO> schemas = SchemaDAO.findAll(Sort.by(sort).direction(sortDirection)).page(Page.of(page, limit)).list();
return new SchemaQueryResult( schemas.stream().map(SchemaMapper::from).toList(), SchemaDAO.count());
Sort sortOpts = Sort.by(sort).direction(sortDirection);

if (actualRoles == null) {
query = SchemaDAO.findAll(sortOpts);
} else {
List<SchemaDAO> schemas = SchemaDAO.listAll(Sort.by(sort).direction(sortDirection));
return new SchemaQueryResult( schemas.stream().map(SchemaMapper::from).toList(), SchemaDAO.count());
query = SchemaDAO.find("owner IN ?1", sortOpts, actualRoles);
}

if (limit != null && page != null) {
query.page(Page.of(page, limit));
}

return new SchemaQueryResult( query.list().stream().map(SchemaMapper::from).toList(), SchemaDAO.count());
}

@WithRoles
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
package io.hyperfoil.tools.horreum.svc;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.hyperfoil.tools.horreum.api.SortDirection;
import io.hyperfoil.tools.horreum.api.data.*;
import io.hyperfoil.tools.horreum.api.data.datastore.DatastoreType;
import io.hyperfoil.tools.horreum.api.data.Access;
import io.hyperfoil.tools.horreum.api.data.ExportedLabelValues;
import io.hyperfoil.tools.horreum.api.data.Fingerprints;
import io.hyperfoil.tools.horreum.api.data.Test;
import io.hyperfoil.tools.horreum.api.data.TestExport;
import io.hyperfoil.tools.horreum.api.data.TestToken;
import io.hyperfoil.tools.horreum.api.data.datastore.DatastoreType;
import io.hyperfoil.tools.horreum.api.services.TestService;
import io.hyperfoil.tools.horreum.bus.AsyncEventChannels;
import io.hyperfoil.tools.horreum.entity.alerting.WatchDAO;
import io.hyperfoil.tools.horreum.entity.backend.DatastoreConfigDAO;
import io.hyperfoil.tools.horreum.entity.data.*;
import io.hyperfoil.tools.horreum.entity.data.DatasetDAO;
import io.hyperfoil.tools.horreum.entity.data.RunDAO;
import io.hyperfoil.tools.horreum.entity.data.TestDAO;
import io.hyperfoil.tools.horreum.entity.data.TestTokenDAO;
import io.hyperfoil.tools.horreum.entity.data.TransformerDAO;
import io.hyperfoil.tools.horreum.entity.data.ViewDAO;
import io.hyperfoil.tools.horreum.hibernate.JsonBinaryType;
import io.hyperfoil.tools.horreum.mapper.DatasourceMapper;
import io.hyperfoil.tools.horreum.mapper.TestMapper;
import io.hyperfoil.tools.horreum.mapper.TestTokenMapper;
import io.hyperfoil.tools.horreum.api.services.TestService;
import io.hyperfoil.tools.horreum.server.EncryptionManager;
import io.hyperfoil.tools.horreum.server.WithRoles;
import io.hyperfoil.tools.horreum.server.WithToken;
Expand All @@ -23,24 +34,19 @@
import io.quarkus.panache.common.Sort;
import io.quarkus.security.UnauthorizedException;
import io.quarkus.security.identity.SecurityIdentity;

import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.persistence.*;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.Query;
import jakarta.persistence.Tuple;
import jakarta.transaction.TransactionManager;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response;

import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.hibernate.Hibernate;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
Expand All @@ -49,8 +55,11 @@
import org.hibernate.type.StandardBasicTypes;
import org.jboss.logging.Logger;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@ApplicationScoped
Expand Down Expand Up @@ -281,7 +290,7 @@ public TestQueryResult list(String roles, Integer limit, Integer page, String so
PanacheQuery<TestDAO> query;
Set<String> actualRoles = null;
if (Roles.hasRolesParam(roles)) {
if (roles.equals("__my")) {
if (roles.equals(Roles.MY_ROLES)) {
if (!identity.isAnonymous()) {
actualRoles = identity.getRoles();
}
Expand All @@ -300,7 +309,7 @@ public TestQueryResult list(String roles, Integer limit, Integer page, String so
if (limit != null && page != null) {
query.page(Page.of(page, limit));
}
return new TestQueryResult( query.list().stream().map(TestMapper::from).collect(Collectors.toList()), TestDAO.count() ) ;
return new TestQueryResult(query.list().stream().map(TestMapper::from).toList(), TestDAO.count());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ public static Test createExampleTest(String testName) {
return createExampleTest(testName, null);
}

public static Test createExampleTest(String testName, Integer datastoreID) {
public static Test createExampleTest(String testName, Integer datastoreID) {
Test test = new Test();
test.name = testName;
test.description = "Bar";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,25 +111,65 @@ void testCreateSchemaWithInvalidId() {


@org.junit.jupiter.api.Test
void testListSchemasWithDifferentOrderings() {
void testListSchemas() {
// create some schemas
createSchema("Ghi", "urn:schema:ghi");
createSchema("Abc", "urn:schema:abc");
createSchema("Def", "urn:schema:def");
createSchema("Ghi", "urn:schema:1");
createSchema("Abc", "urn:schema:2");
createSchema("Def", "urn:schema:3");

// by default order by name and with ascending direction
SchemaService.SchemaQueryResult res = listSchemas(null, null, null, null);
// by default order by name and with descending direction
SchemaService.SchemaQueryResult res = listSchemas(null, null, null, null, null, null);
assertEquals(3, res.schemas.size());
assertEquals(3, res.count);
assertEquals("Ghi", res.schemas.get(0).name);

// order by uri with descending direction
res = listSchemas(null, null, null, null, "uri", null);
assertEquals(3, res.schemas.size());
assertEquals(3, res.count);
assertEquals("Def", res.schemas.get(0).name);

// order by uri with ascending direction
res = listSchemas(null, null, null, null, "uri", SortDirection.Ascending);
assertEquals(3, res.schemas.size());
assertEquals(3, res.count);
assertEquals("Ghi", res.schemas.get(0).name);

// order by name with ascending direction
res = listSchemas(null, null, null, null, "name", SortDirection.Ascending);
assertEquals(3, res.schemas.size());
assertEquals(3, res.count);
assertEquals("Abc", res.schemas.get(0).name);

// limit the list to 2 results
res = listSchemas(2, 0, null, null);
res = listSchemas(null, null, 2, 0, null, null);
assertEquals(2, res.schemas.size());
// total number of records
assertEquals(3, res.count);
}

@org.junit.jupiter.api.Test
void testListSchemasWithDifferentRoles() {
// create some schemas
createSchema("Ghi", "urn:schema:ghi");
createSchema("Abc", "urn:schema:abc");
createSchema("Def", "urn:schema:def");
createSchema("jkl", "urn:schema:jkl");

// by default order by name and with ascending direction
SchemaService.SchemaQueryResult res = listSchemas(null, null, null, null, null, null);
assertEquals(4, res.schemas.size());
assertEquals(4, res.count);

res = listSchemas(getAdminToken(), Roles.MY_ROLES, null, null, null, null);
assertEquals(0, res.schemas.size());
assertEquals(4, res.count);

res = listSchemas(getAdminToken(), Roles.ALL_ROLES, null, null, null, null);
assertEquals(4, res.schemas.size());
assertEquals(4, res.count);
}

@org.junit.jupiter.api.Test
void testDropTokenFromSchema() {
// create some schemas
Expand Down Expand Up @@ -709,11 +749,15 @@ void testDeleteSchemaAfterRun() {
}

// utility to get list of schemas
private SchemaService.SchemaQueryResult listSchemas(Integer limit, Integer page, String sort, SortDirection direction) {
private SchemaService.SchemaQueryResult listSchemas(String token, String roles, Integer limit, Integer page, String sort, SortDirection direction) {
StringBuilder query = new StringBuilder("/api/schema/");
if (limit != null || page != null || sort != null || direction != null) {
if (roles != null || limit != null || page != null || sort != null || direction != null) {
query.append("?");

if (roles != null) {
query.append("roles=").append(roles).append("&");
}

if (limit != null) {
query.append("limit=").append(limit).append("&");
}
Expand All @@ -730,7 +774,10 @@ private SchemaService.SchemaQueryResult listSchemas(Integer limit, Integer page,
query.append("direction=").append(direction);
}
}
return jsonRequest().get(query.toString())
return jsonRequest()
.auth()
.oauth2(token == null ? getTesterToken() : token)
.get(query.toString())
.then()
.statusCode(200)
.extract()
Expand Down
Loading

0 comments on commit dd9790c

Please sign in to comment.