Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 54 additions & 6 deletions CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

package com.cedarpolicy.model.schema;

import java.util.Optional;

import com.cedarpolicy.loader.LibraryLoader;
import com.cedarpolicy.model.exception.InternalException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Optional;

/** Represents a schema. */
public final class Schema {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
Expand Down Expand Up @@ -122,21 +124,67 @@ public static Schema parse(JsonOrCedar type, String str) throws InternalExceptio

}

/**
* Converts a schema to Cedar format
*
* @return String representing the schema in Cedar format
* @throws InternalException If conversion from JSON to Cedar format fails
* @throws IllegalStateException If schema content is missing
* @throws NullPointerException
*/
public String toCedarFormat() throws InternalException, IllegalStateException, NullPointerException {
if (type == JsonOrCedar.Cedar && schemaText.isPresent()) {
return schemaText.get();
} else if (type == JsonOrCedar.Json && schemaJson.isPresent()) {
return jsonToCedarJni(schemaJson.get().toString());
} else {
throw new IllegalStateException("No schema found");
}
}

/**
* Converts a Cedar format schema to JSON format
*
* @return JsonNode representing the schema in JSON format
* @throws InternalException If conversion from Cedar to JSON format fails
* @throws IllegalStateException If schema content is missing
* @throws JsonMappingException If invalid JSON
* @throws JsonProcessingException If invalid JSON
* @throws NullPointerException
*/
public JsonNode toJsonFormat()
throws InternalException, JsonMappingException, JsonProcessingException, NullPointerException,
IllegalStateException {
if (type == JsonOrCedar.Json && schemaJson.isPresent()) {
return schemaJson.get();
} else if (type == JsonOrCedar.Cedar && schemaText.isPresent()) {
return OBJECT_MAPPER.readTree(cedarToJsonJni(schemaText.get()));
} else {
throw new IllegalStateException("No schema found");
}
}

/** Specifies the schema format used. */
public enum JsonOrCedar {
/**
* Cedar JSON schema format. See <a href="https://docs.cedarpolicy.com/schema/json-schema.html">
* https://docs.cedarpolicy.com/schema/json-schema.html</a>
* Cedar JSON schema format. See
* <a href="https://docs.cedarpolicy.com/schema/json-schema.html">
* https://docs.cedarpolicy.com/schema/json-schema.html</a>
*/
Json,
/**
* Cedar schema format. See <a href="https://docs.cedarpolicy.com/schema/human-readable-schema.html">
* https://docs.cedarpolicy.com/schema/human-readable-schema.html</a>
* Cedar schema format. See
* <a href="https://docs.cedarpolicy.com/schema/human-readable-schema.html">
* https://docs.cedarpolicy.com/schema/human-readable-schema.html</a>
*/
Cedar
}

private static native String parseJsonSchemaJni(String schemaJson) throws InternalException, NullPointerException;

private static native String parseCedarSchemaJni(String schemaText) throws InternalException, NullPointerException;

private static native String jsonToCedarJni(String json) throws InternalException, NullPointerException;

private static native String cedarToJsonJni(String cedar) throws InternalException, NullPointerException;
}
136 changes: 132 additions & 4 deletions CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,21 @@

package com.cedarpolicy;

import com.cedarpolicy.model.schema.Schema;
import com.cedarpolicy.model.schema.Schema.JsonOrCedar;

import org.junit.jupiter.api.Test;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import com.cedarpolicy.model.exception.InternalException;
import com.cedarpolicy.model.schema.Schema;
import com.cedarpolicy.model.schema.Schema.JsonOrCedar;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class SchemaTests {
@Test
Expand Down Expand Up @@ -107,4 +115,124 @@ public void parseCedarSchema() {
Schema.parse(JsonOrCedar.Cedar, "namspace Foo::Bar;");
});
}

@Nested
@DisplayName("toCedarFormat Tests")
class ToCedarFormatTests {

@Test
@DisplayName("Should return the same Cedar schema text")
void testFromCedar() throws InternalException {
String cedarSchema = "entity User;";
Schema cedarSchemaObj = new Schema(cedarSchema);
String result = cedarSchemaObj.toCedarFormat();
assertNotNull(result, "Result should not be null");
assertEquals(cedarSchema, result, "Should return the original Cedar schema");
}

@Test
@DisplayName("Should convert JSON schema to Cedar format")
void testFromJson() throws InternalException {
String jsonSchema = """
{
"": {
"entityTypes": {
"User": {}
},
"actions": {}
}
}
""";
Schema jsonSchemaObj = Schema.parse(JsonOrCedar.Json, jsonSchema);
String result = jsonSchemaObj.toCedarFormat();

assertNotNull(result, "Result should not be null");
String expectedCedar = "entity User;";
assertEquals(expectedCedar, result.trim(), "Converted Cedar should match expected format");
}

@Test
@DisplayName("Should throw IllegalStateException for empty schema")
void testEmptySchema() {
Schema emptySchema = new Schema(JsonOrCedar.Cedar, Optional.empty(), Optional.empty());
Exception exception = assertThrows(IllegalStateException.class, emptySchema::toCedarFormat);
assertEquals("No schema found", exception.getMessage());
}

@Test
@DisplayName("Should throw exception for malformed JSON schema")
void testMalformedSchema() {
String malformedJson = """
{
"": {
"entityMalformedTypes": {
"User": {}
},
"actions": {}
}
}
""";
Schema malformedSchema = new Schema(JsonOrCedar.Json, Optional.of(malformedJson), Optional.empty());
assertNotNull(malformedSchema.schemaJson);
assertThrows(InternalException.class, malformedSchema::toCedarFormat);
}
}

@Nested
@DisplayName("toJsonFormat Tests")
class ToJsonFormatTests {

@Test
@DisplayName("Should convert Cedar schema to JSON format")
void testFromCedar() throws Exception {
String cedarSchema = "entity User;";
Schema cedarSchemaObj = new Schema(cedarSchema);
JsonNode result = cedarSchemaObj.toJsonFormat();

String expectedJson = "{\"\":{\"entityTypes\":{\"User\":{}},\"actions\":{}}}";
JsonNode expectedNode = new ObjectMapper().readTree(expectedJson);

assertNotNull(result, "Result should not be null");
assertEquals(expectedNode, result, "JSON should match expected structure");
}

@Test
@DisplayName("Should return the same JSON schema object")
void testFromJson() throws Exception {
String jsonSchema = """
{
"": {
"entityTypes": {
"User": {}
},
"actions": {}
}
}
""";
Schema jsonSchemaObj = Schema.parse(JsonOrCedar.Json, jsonSchema);
JsonNode result = jsonSchemaObj.toJsonFormat();

ObjectMapper mapper = new ObjectMapper();
JsonNode expectedNode = mapper.readTree(jsonSchema);

assertNotNull(result, "Result should not be null");
assertEquals(expectedNode, result, "JSON should match the original schema");
}

@Test
@DisplayName("Should throw IllegalStateException for empty schema")
void testEmptySchema() {
Schema emptySchema = new Schema(JsonOrCedar.Cedar, Optional.empty(), Optional.empty());
Exception exception = assertThrows(IllegalStateException.class, emptySchema::toJsonFormat);
assertEquals("No schema found", exception.getMessage());
}

@Test
@DisplayName("Should throw exception for malformed Cedar schema")
void testMalformedSchema() {
String malformedCedar = "entty User";
Schema malformedSchema = new Schema(JsonOrCedar.Cedar, Optional.empty(), Optional.of(malformedCedar));
assertThrows(InternalException.class, malformedSchema::toJsonFormat);
}
}
}
Loading