/* * Copyright 2010 Outerthought bvba * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.lilyproject.process.test; import java.util.UUID; import org.apache.http.HttpStatus; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.node.ArrayNode; import org.codehaus.jackson.node.JsonNodeFactory; import org.codehaus.jackson.node.ObjectNode; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; public class RecordRestTest extends AbstractRestTest { @Test public void testRecordBasics() throws Exception { makeBookSchema(); // Create a record using PUT and a user ID String body = json("{ type: 'b$book', fields: { 'b$title' : 'Faster Fishing' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); ResponseAndContent response = put("/record/USER.faster_fishing", body); assertStatus(HttpStatus.SC_CREATED, response); // Read the record response = get("/record/USER.faster_fishing"); assertStatus(HttpStatus.SC_OK, response); // Verify content JsonNode json = readJson(response); assertEquals(1L, json.get("fields").size()); assertNull(json.get("schema")); // schema info should not be included by default // Read the record with schema info response = get("/record/USER.faster_fishing?schema=true"); assertStatus(HttpStatus.SC_OK, response); // Verify content json = readJson(response); assertEquals(1L, json.get("fields").size()); assertNotNull(json.get("schema")); assertEquals(1L, json.get("schema").size()); // Read the record without namespace prefixes response = get("/record/USER.faster_fishing?nsprefixes=false"); assertStatus(HttpStatus.SC_OK, response); // Verify content json = readJson(response); assertEquals("Faster Fishing", json.get("fields").get("{org.lilyproject.resttest}title").getTextValue()); // Read the record as specific version response = get("/record/USER.faster_fishing/version/1"); assertStatus(HttpStatus.SC_OK, response); // Read a non-existing version response = get("/record/USER.faster_fishing/version/10"); assertStatus(HttpStatus.SC_NOT_FOUND, response); // Specify non-numeric version response = get("/record/USER.faster_fishing/version/abc"); assertStatus(HttpStatus.SC_NOT_FOUND, response); // Update the record body = json("{ action: 'update', record: { type: 'b$book', fields: { 'b$title' : 'Faster Fishing (new)' }, " + "namespaces : { 'org.lilyproject.resttest': 'b' } } }"); response = post("/record/USER.faster_fishing", body); assertStatus(HttpStatus.SC_OK, response); json = readJson(response); assertEquals(2L, json.get("version").getLongValue()); response = get("/record/USER.faster_fishing/version/2"); assertStatus(HttpStatus.SC_OK, response); // Update the record, specify the ID both in URI and json body = json("{ action: 'update', record: { id: 'USER.faster_fishing', type: 'b$book', " + "fields: { 'b$title' : 'Faster Fishing (new 2)' }, " + "namespaces : { 'org.lilyproject.resttest': 'b' } } }"); response = post("/record/USER.faster_fishing", body); assertStatus(HttpStatus.SC_OK, response); // Update the record, specify the ID both in URI and json but both different -- should be a bad request body = json("{ action: 'update', record: { id: 'USER.faster_fishing_smth', type: 'b$book', " + "fields: { 'b$title' : 'Faster Fishing (new 2)' }, " + "namespaces : { 'org.lilyproject.resttest': 'b' } } }"); response = post("/record/USER.faster_fishing", body); assertStatus(HttpStatus.SC_BAD_REQUEST, response); // Update a record which does not exist, should fail body = json("{ action: 'update', record: { type: 'b$book', fields: { 'b$title' : 'Faster Fishing (new 3)' }, " + "namespaces : { 'org.lilyproject.resttest': 'b' } } }"); response = post("/record/USER.non_existing_record", body); assertStatus(HttpStatus.SC_NOT_FOUND, response); // Delete the record response = delete("/record/USER.faster_fishing"); assertStatus(HttpStatus.SC_NO_CONTENT, response); // Verify deleted record is gone response = get("/record/USER.faster_fishing"); assertStatus(HttpStatus.SC_NOT_FOUND, response); // Test delete of non-existing record response = delete("/record/USER.faster_fishing"); assertStatus(HttpStatus.SC_NOT_FOUND, response); // Create a record using PUT and a client-specified UUID UUID uuid = UUID.randomUUID(); body = json("{ type: 'b$book', fields: { 'b$title' : 'Title 1' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = put("/record/UUID." + uuid.toString(), body); assertStatus(HttpStatus.SC_CREATED, response); response = get("/record/UUID." + uuid.toString()); assertStatus(HttpStatus.SC_OK, response); // Create a record using POST and a client-specified UUID uuid = UUID.randomUUID(); body = json("{ action: 'create', record: { id: 'UUID." + uuid.toString() + "', type: 'b$book', " + "fields: { 'b$title' : 'Title 1' }, namespaces : { 'org.lilyproject.resttest': 'b' } } }"); response = post("/record", body); assertStatus(HttpStatus.SC_CREATED, response); response = get("/record/UUID." + uuid.toString()); assertStatus(HttpStatus.SC_OK, response); // Update this record, specify the ID both in URI and json body = json("{ action: 'update', record: { id: 'UUID." + uuid.toString() + "', type: 'b$book', " + "fields: { 'b$title' : 'Title 1 (new)' }, " + "namespaces : { 'org.lilyproject.resttest': 'b' } } }"); response = post("/record/UUID." + uuid.toString(), body); assertStatus(HttpStatus.SC_OK, response); } /** * Test versioning of record types and the creation of a record that uses the non-latest version of a record type. */ @Test public void testVersionRecordType() throws Exception { // Create two field types String body = json("{action: 'create', fieldType: {name: 'n$vrt_field1', valueType: 'STRING', " + "scope: 'versioned', namespaces: { 'org.lilyproject.resttest': 'n' } } }"); ResponseAndContent response = post("/schema/fieldTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); body = json("{action: 'create', fieldType: {name: 'n$vrt_field2', valueType: 'STRING', " + "scope: 'versioned', namespaces: { 'org.lilyproject.resttest': 'n' } } }"); response = post("/schema/fieldTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); // Create a record type body = json("{action: 'create', recordType: {name: 'n$vrt', " + "fields: [ {name: 'n$vrt_field1'} ]," + "namespaces: { 'org.lilyproject.resttest': 'n' } } }"); response = post("/schema/recordTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); String rtIdUri = response.getLocationRef().toString(); body = json("{name: 'n$vrt', " + "fields: [ {name: 'n$vrt_field1'}, {name: 'n$vrt_field2'} ]," + "namespaces: { 'org.lilyproject.resttest': 'n' } }"); response = put("/schema/recordType/n$vrt?ns.n=org.lilyproject.resttest", body); assertStatus(HttpStatus.SC_OK, response); response = get("/schema/recordType/n$vrt?ns.n=org.lilyproject.resttest"); assertStatus(HttpStatus.SC_OK, response); JsonNode json = readJson(response); assertEquals(2L, json.get("version").getLongValue()); response = get("/schema/recordType/n$vrt/version/2?ns.n=org.lilyproject.resttest"); assertStatus(HttpStatus.SC_OK, response); json = readJson(response); assertEquals(2L, json.get("version").getLongValue()); response = getUri(rtIdUri + "/version/2"); assertStatus(HttpStatus.SC_OK, response); json = readJson(response); assertEquals(2L, json.get("version").getLongValue()); // Create record body = json("{ type: {name: 'n$vrt', version: 1}, " + "fields: { 'n$vrt_field1' : 'pink shoe laces' }, namespaces : { 'org.lilyproject.resttest': 'n' } }"); response = put("/record/USER.pink_shoe_laces", body); assertStatus(HttpStatus.SC_CREATED, response); json = readJson(response); assertEquals(1L, json.get("type").get("version").getLongValue()); // Update record body = json("{ type: {name: 'n$vrt', version: 1}, " + "fields: { 'n$vrt_field1' : 'pink shoe laces 2' }, namespaces : { 'org.lilyproject.resttest': 'n' } }"); response = put("/record/USER.pink_shoe_laces", body); assertStatus(HttpStatus.SC_OK, response); json = readJson(response); assertEquals(1L, json.get("type").get("version").getLongValue()); // Update without specifying version, should move to last version body = json("{ type: {name: 'n$vrt'}, " + "fields: { 'n$vrt_field1' : 'pink shoe laces 3' }, namespaces : { 'org.lilyproject.resttest': 'n' } }"); response = put("/record/USER.pink_shoe_laces", body); assertStatus(HttpStatus.SC_OK, response); json = readJson(response); assertEquals(2L, json.get("type").get("version").getLongValue()); } @Test public void testBlobs() throws Exception { // Create a blob field type String body = json("{action: 'create', fieldType: {name: 'b$blob1', valueType: 'BLOB', " + "scope: 'versioned', namespaces: { 'org.lilyproject.resttest': 'b' } } }"); ResponseAndContent response = post("/schema/fieldTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); // Create a record type holding the blob field body = json("{action: 'create', recordType: {name: 'b$blobRT', fields: [ {name: 'b$blob1'} ]," + "namespaces: { 'org.lilyproject.resttest': 'b' } } }"); response = post("/schema/recordTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); // Upload a blob String data = "Hello, blob world!"; response = postText("/blob", data); assertStatus(HttpStatus.SC_OK, response); JsonNode jsonNode = readJson(response); String blobValue = jsonNode.get("value").getTextValue(); assertEquals("text/plain", jsonNode.get("mediaType").getTextValue()); assertEquals(data.length(), jsonNode.get("size").getLongValue()); // Create a record with this blob ObjectNode recordNode = JsonNodeFactory.instance.objectNode(); recordNode.put("type", "b$blobRT"); ObjectNode fieldsNode = recordNode.putObject("fields"); ObjectNode blobNode = fieldsNode.putObject("b$blob1"); blobNode.put("size", data.length()); blobNode.put("mediaType", "text/plain"); blobNode.put("value", blobValue); blobNode.put("name", "helloworld.txt"); ObjectNode nsNode = recordNode.putObject("namespaces"); nsNode.put("org.lilyproject.resttest", "b"); response = put("/record/USER.blob1", recordNode.toString()); assertStatus(HttpStatus.SC_CREATED, response); // Read the record response = get("/record/USER.blob1"); assertStatus(HttpStatus.SC_OK, response); jsonNode = readJson(response); String prefix = jsonNode.get("namespaces").get("org.lilyproject.resttest").getValueAsText(); blobNode = (ObjectNode)jsonNode.get("fields").get(prefix + "$blob1"); assertEquals("text/plain", blobNode.get("mediaType").getValueAsText()); assertEquals(data.length(), blobNode.get("size").getLongValue()); assertEquals(blobValue, blobNode.get("value").getTextValue()); assertEquals("helloworld.txt", blobNode.get("name").getValueAsText()); // Read the blob response = get("/record/USER.blob1/field/b$blob1/data?ns.b=org.lilyproject.resttest"); assertStatus(HttpStatus.SC_OK, response); assertEquals(data, new String(response.getContent())); } @Test public void testMultiValueHierarchicalBlobs() throws Exception { // Create a blob field type String body = json("{action: 'create', fieldType: {name: 'b$blob2', valueType: 'LIST<PATH<BLOB>>', " + "scope: 'versioned', namespaces: { 'org.lilyproject.resttest': 'b' } } }"); ResponseAndContent response = post("/schema/fieldTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); // Create a record type holding the blob field body = json("{action: 'create', recordType: {name: 'b$blobRT2', fields: [ {name: 'b$blob2'} ]," + "namespaces: { 'org.lilyproject.resttest': 'b' } } }"); response = post("/schema/recordTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); // Upload some blobs ObjectNode blob1Value = (ObjectNode)readJson(postText("/blob", "My blob 1")); ObjectNode blob2Value = (ObjectNode)readJson(postText("/blob", "My blob 2")); ObjectNode blob3Value = (ObjectNode)readJson(postText("/blob", "My blob 3")); ObjectNode blob4Value = (ObjectNode)readJson(postText("/blob", "My blob 4")); // Create a record with these blobs ObjectNode recordNode = JsonNodeFactory.instance.objectNode(); recordNode.put("type", "b$blobRT2"); ObjectNode fieldsNode = recordNode.putObject("fields"); ArrayNode blobNode = fieldsNode.putArray("b$blob2"); ArrayNode hierarchicalBlob1 = blobNode.addArray(); hierarchicalBlob1.add(blob1Value); hierarchicalBlob1.add(blob2Value); ArrayNode hierarchicalBlob2 = blobNode.addArray(); hierarchicalBlob2.add(blob3Value); hierarchicalBlob2.add(blob4Value); ObjectNode nsNode = recordNode.putObject("namespaces"); nsNode.put("org.lilyproject.resttest", "b"); response = put("/record/USER.blob2", recordNode.toString()); assertStatus(HttpStatus.SC_CREATED, response); // Read the record response = get("/record/USER.blob2"); assertStatus(HttpStatus.SC_OK, response); JsonNode jsonNode = readJson(response); String prefix = jsonNode.get("namespaces").get("org.lilyproject.resttest").getValueAsText(); blobNode = (ArrayNode)jsonNode.get("fields").get(prefix + "$blob2"); assertEquals(2, blobNode.size()); // Read the blobs for (int mvIndex = 0; mvIndex < 2; mvIndex++) { for (int hIndex = 0; hIndex < 2; hIndex++) { response = get("/record/USER.blob2/field/b$blob2/data?ns.b=org.lilyproject.resttest" + "&indexes=" + mvIndex + "," + hIndex); assertStatus(HttpStatus.SC_OK, response); } } // Read a blob at non-existing indexes response = get("/record/USER.blob2/field/b$blob2/data?ns.b=org.lilyproject.resttest&mvIndex=5&hIndex=3"); assertStatus(HttpStatus.SC_NOT_FOUND, response); } @Test public void testVersionCollection() throws Exception { // Create some field types String body = json("{action: 'create', fieldType: {name: 'p$name', valueType: 'STRING', " + "scope: 'versioned', namespaces: { 'org.lilyproject.resttest': 'p' } } }"); ResponseAndContent response = post("/schema/fieldTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); body = json("{action: 'create', fieldType: {name: 'p$price', valueType: 'DOUBLE', " + "scope: 'versioned', namespaces: { 'org.lilyproject.resttest': 'p' } } }"); response = post("/schema/fieldTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); body = json("{action: 'create', fieldType: {name: 'p$colour', valueType: 'STRING', " + "scope: 'versioned', namespaces: { 'org.lilyproject.resttest': 'p' } } }"); response = post("/schema/fieldTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); // Create a record type body = json("{action: 'create', recordType: {name: 'p$product', fields: [ {name: 'p$name'}, " + "{name: 'p$price'}, {name: 'p$colour'} ], namespaces: { 'org.lilyproject.resttest': 'p' } } }"); response = post("/schema/recordTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); // Create a record with some versions body = json("{ type: 'p$product', fields: { 'p$name' : 'Product 1' }, namespaces : { 'org.lilyproject.resttest': 'p' } }"); response = put("/record/USER.product1", body); assertStatus(HttpStatus.SC_CREATED, response); body = json("{ fields: { 'p$name' : 'Product 1', 'p$price': 5.5 }, namespaces : { 'org.lilyproject.resttest': 'p' } }"); response = put("/record/USER.product1", body); assertStatus(HttpStatus.SC_OK, response); body = json("{ fields: { 'p$name' : 'Product 1', 'p$price': 5.5, 'p$colour': 'red' }, namespaces : { 'org.lilyproject.resttest': 'p' } }"); response = put("/record/USER.product1", body); assertStatus(HttpStatus.SC_OK, response); // Get list of versions response = get("/record/USER.product1/version"); assertStatus(HttpStatus.SC_OK, response); JsonNode json = readJson(response); assertEquals(3, json.get("results").size()); JsonNode results = json.get("results"); assertEquals(1, results.get(0).get("fields").size()); assertEquals(2, results.get(1).get("fields").size()); assertEquals(3, results.get(2).get("fields").size()); response = get("/record/USER.product1/version?max-results=1"); assertStatus(HttpStatus.SC_OK, response); json = readJson(response); assertEquals(1, json.get("results").size()); results = json.get("results"); assertEquals(1, results.get(0).get("version").getLongValue()); response = get("/record/USER.product1/version?start-index=2&max-results=1"); assertStatus(HttpStatus.SC_OK, response); json = readJson(response); assertEquals(1, json.get("results").size()); results = json.get("results"); assertEquals(2, results.get(0).get("version").getLongValue()); // Retrieve only one field response = get("/record/USER.product1/version?fields=n$name&ns.n=org.lilyproject.resttest"); assertStatus(HttpStatus.SC_OK, response); json = readJson(response); results = json.get("results"); assertEquals(1, results.get(0).get("fields").size()); assertEquals(1, results.get(1).get("fields").size()); assertEquals(1, results.get(2).get("fields").size()); } @Test public void testVariantCollection() throws Exception { makeBookSchema(); String body = json("{ type: 'b$book', fields: { 'b$title' : 'Hunting' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); ResponseAndContent response = put("/record/USER.hunting.lang=en", body); assertStatus(HttpStatus.SC_CREATED, response); body = json("{ type: 'b$book', fields: { 'b$title' : 'Jagen' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = put("/record/USER.hunting.lang=nl", body); assertStatus(HttpStatus.SC_CREATED, response); body = json("{ type: 'b$book', fields: { 'b$title' : 'La chasse' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = put("/record/USER.hunting.lang=fr", body); assertStatus(HttpStatus.SC_CREATED, response); response = get("/record/USER.hunting/variant"); assertStatus(HttpStatus.SC_OK, response); JsonNode jsonNode = readJson(response); ArrayNode resultsNode = (ArrayNode)jsonNode.get("results"); assertEquals(3, resultsNode.size()); // Keys are returned in storage order (though this is more of an implementation detail on which clients should not rely) assertEquals("USER.hunting.lang=en", resultsNode.get(0).get("id").getTextValue()); assertEquals("USER.hunting.lang=fr", resultsNode.get(1).get("id").getTextValue()); assertEquals("USER.hunting.lang=nl", resultsNode.get(2).get("id").getTextValue()); } @Test public void testRecordByVTag() throws Exception { makeBookSchema(); // Create 'active' vtag field String body = json("{action: 'create', fieldType: {name: 'v$active', valueType: 'LONG', " + "scope: 'non_versioned', namespaces: { 'org.lilyproject.vtag': 'v' } } }"); ResponseAndContent response = post("/schema/fieldTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); body = json("{ type: 'b$book', fields: { 'b$title' : 'Title version 1' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = put("/record/USER.vtagtest", body); assertStatus(HttpStatus.SC_CREATED, response); body = json("{ type: 'b$book', fields: { 'b$title' : 'Title version 2', 'v$active': 1 }, " + "namespaces : { 'org.lilyproject.resttest': 'b', 'org.lilyproject.vtag': 'v' } }"); response = put("/record/USER.vtagtest", body); assertStatus(HttpStatus.SC_OK, response); response = get("/record/USER.vtagtest/vtag/active"); assertStatus(HttpStatus.SC_OK, response); assertEquals(1L, readJson(response).get("version").getLongValue()); response = get("/record/USER.vtagtest/vtag/last"); assertStatus(HttpStatus.SC_OK, response); assertEquals(2L, readJson(response).get("version").getLongValue()); response = get("/record/USER.vtagtest/vtag/aNotExistingVTagName"); assertStatus(HttpStatus.SC_NOT_FOUND, response); } @Test public void testVersionedMutableScope() throws Exception { // Create a versioned field type String body = json("{action: 'create', fieldType: {name: 'b$subject', valueType: 'STRING', " + "scope: 'versioned', namespaces: { 'org.lilyproject.resttest': 'b' } } }"); ResponseAndContent response = post("/schema/fieldTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); // Create a versioned-mutable field type body = json("{action: 'create', fieldType: {name: 'b$state', valueType: 'STRING', " + "scope: 'versioned_mutable', namespaces: { 'org.lilyproject.resttest': 'b' } } }"); response = post("/schema/fieldTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); // Create a record type body = json("{action: 'create', recordType: {name: 'b$article', " + "fields: [ {name: 'b$subject'}, {name: 'b$state'} ]," + "namespaces: { 'org.lilyproject.resttest': 'b' } } }"); response = post("/schema/recordTypeById", body); assertStatus(HttpStatus.SC_CREATED, response); // Create a record body = json("{ type: 'b$article', fields: { 'b$subject': 'Peace', 'b$state': 'draft' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = put("/record/USER.peace", body); assertStatus(HttpStatus.SC_CREATED, response); // Read the record response = get("/record/USER.peace"); assertStatus(HttpStatus.SC_OK, response); JsonNode json = readJson(response); JsonNode fieldsNode = json.get("fields"); String prefix = json.get("namespaces").get("org.lilyproject.resttest").getTextValue(); assertEquals("draft", fieldsNode.get(prefix + "$state").getValueAsText()); assertEquals("Peace", fieldsNode.get(prefix + "$subject").getValueAsText()); // Update the versioned-mutable field body = json("{ fields: { 'b$state': 'in_review' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = put("/record/USER.peace/version/1", body); assertStatus(HttpStatus.SC_OK, response); response = get("/record/USER.peace"); assertStatus(HttpStatus.SC_OK, response); json = readJson(response); fieldsNode = json.get("fields"); prefix = json.get("namespaces").get("org.lilyproject.resttest").getTextValue(); assertEquals(1L, json.get("version").getLongValue()); // no new version created assertEquals("in_review", fieldsNode.get(prefix + "$state").getValueAsText()); // Update versioned-mutable field without changes, change to versioned field should be ignored body = json("{ fields: { 'b$subject': 'Peace2', 'b$state': 'in_review' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = put("/record/USER.peace/version/1", body); assertStatus(HttpStatus.SC_OK, response); response = get("/record/USER.peace"); assertStatus(HttpStatus.SC_OK, response); json = readJson(response); fieldsNode = json.get("fields"); prefix = json.get("namespaces").get("org.lilyproject.resttest").getTextValue(); assertEquals(1L, json.get("version").getLongValue()); // no new version created assertEquals("Peace", fieldsNode.get(prefix + "$subject").getValueAsText()); } @Test public void testConditionUpdate() throws Exception { makeBookSchema(); String body = json("{ type: 'b$book', fields: { 'b$title' : 'ABC1' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); ResponseAndContent response = put("/record/USER.ABC", body); assertStatus(HttpStatus.SC_CREATED, response); // Test update with failing condition body = json("{ action: 'update', record: { fields: { 'b$title' : 'ABC2' } }, " + "conditions: [{field: 'b$title', value: 'ABC5'}], " + "namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = post("/record/USER.ABC", body); assertStatus(HttpStatus.SC_CONFLICT, response); // Verify update did not go through response = get("/record/USER.ABC"); assertStatus(HttpStatus.SC_OK, response); JsonNode json = readJson(response); assertEquals("ABC1", getFieldValue(json, "title").getTextValue()); // Test update with succeeding condition body = json("{ action: 'update', record: { fields: { 'b$title' : 'ABC2' } }, " + "conditions: [{field: 'b$title', value: 'ABC1'}], " + "namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = post("/record/USER.ABC", body); assertStatus(HttpStatus.SC_OK, response); // Verify update did not through response = get("/record/USER.ABC"); assertStatus(HttpStatus.SC_OK, response); json = readJson(response); assertEquals("ABC2", getFieldValue(json, "title").getTextValue()); // // Test with custom compare operator // body = json("{ action: 'update', record: { fields: { 'b$title' : 'ABC3' } }, " + "conditions: [{field: 'b$title', value: 'ABC2', operator: 'not_equal'}], " + "namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = post("/record/USER.ABC", body); assertStatus(HttpStatus.SC_CONFLICT, response); // // Test allowMissing flag // body = json("{ action: 'update', record: { fields: { 'b$title' : 'ABC3' } }, " + "conditions: [{field: 'b$summary', value: 'some summary', allowMissing: false}], " + "namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = post("/record/USER.ABC", body); assertStatus(HttpStatus.SC_CONFLICT, response); body = json("{ action: 'update', record: { fields: { 'b$title' : 'ABC3' } }, " + "conditions: [{field: 'b$summary', value: 'some summary', allowMissing: true}], " + "namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = post("/record/USER.ABC", body); assertStatus(HttpStatus.SC_OK, response); // // Test null value // // First remove summary field again body = json("{ action: 'update', record: { " + "fieldsToDelete: ['b$summary'], " + "namespaces : { 'org.lilyproject.resttest': 'b' } } }"); response = post("/record/USER.ABC", body); assertStatus(HttpStatus.SC_OK, response); body = json("{ action: 'update', record: { fields: { 'b$title' : 'ABC4' } }, " + "conditions: [{field: 'b$summary', value: null, operator: 'not_equal'}], " + "namespaces : { 'org.lilyproject.resttest': 'b' } } }"); response = post("/record/USER.ABC", body); assertStatus(HttpStatus.SC_CONFLICT, response); body = json("{ action: 'update', record: { fields: { 'b$title' : 'ABC4' } }, " + "conditions: [{field: 'b$summary', value: null, operator: 'equal'}], " + "namespaces : { 'org.lilyproject.resttest': 'b' } } }"); response = post("/record/USER.ABC", body); assertStatus(HttpStatus.SC_OK, response); // // Test system field check // // Create a new record body = json("{ type: 'b$book', fields: { 'b$title' : 'ABC1' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = put("/record/USER.sysfieldcheck", body); assertStatus(HttpStatus.SC_CREATED, response); // Test update with failing condition body = json("{ action: 'update', record: { fields: { 'b$title' : 'ABC2' } }, " + "conditions: [{field: 's$version', value: 2}], " + "namespaces : { 'org.lilyproject.resttest': 'b', 'org.lilyproject.system': 's' } }"); response = post("/record/USER.sysfieldcheck", body); assertStatus(HttpStatus.SC_CONFLICT, response); // Test update with succeeding condition body = json("{ action: 'update', record: { fields: { 'b$title' : 'ABC2' } }, " + "conditions: [{field: 's$version', value: 1}], " + "namespaces : { 'org.lilyproject.resttest': 'b', 'org.lilyproject.system': 's' } }"); response = post("/record/USER.sysfieldcheck", body); assertStatus(HttpStatus.SC_OK, response); } @Test public void testConditionDelete() throws Exception { makeBookSchema(); String body = json("{ type: 'b$book', fields: { 'b$title' : 'CondDel' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); ResponseAndContent response = put("/record/USER.ConDel", body); assertStatus(HttpStatus.SC_CREATED, response); body = json("{ action: 'delete'," + "conditions: [{field: 'b$title', value: 'foo'}], " + "namespaces : { 'org.lilyproject.resttest': 'b' } } }"); response = post("/record/USER.ConDel", body); assertStatus(HttpStatus.SC_CONFLICT, response); body = json("{ action: 'delete'," + "conditions: [{field: 'b$title', value: 'CondDel'}], " + "namespaces : { 'org.lilyproject.resttest': 'b' } } }"); response = post("/record/USER.ConDel", body); assertStatus(HttpStatus.SC_NO_CONTENT, response); } private void setupRecordScannerTest () throws Exception{ makeBookSchema(); // Create a record using PUT and a user ID //TODO: validate response String body = json("{ type: 'b$book', fields: { 'b$title' : 'Faster Fishing' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); ResponseAndContent response = put("/record/USER.scan_faster_fishing", body); body = json("{ type: 'b$book', fields: { 'b$title' : 'Fister Fashing' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = put("/record/USER.scan_fister_fashing", body); body = json("{ type: 'b$book', fields: { 'b$title' : 'Fly fishing with Flash' }, namespaces : { 'org.lilyproject.resttest': 'b' } }"); response = put("/record/USER.scan_fly_fishing_with_flash", body); } @Test public void testRecordScanPost() throws Exception { setupRecordScannerTest(); // Create a scanner to see if it gest created. Check the location header String body = json("{'recordFilter' : { '@class' : 'org.lilyproject.repository.api.filter.RecordIdPrefixFilter', " + "'recordId' : 'USER.scan_'}}, 'caching' : 1024, 'cacheBlocks' : false}"); ResponseAndContent response = post("/scan", body); assertStatus(HttpStatus.SC_CREATED, response); String location = response.getLocationRef().toString(); assertTrue(location.contains("/repository/scan/")); } @Test public void testRecordScanGet() throws Exception { setupRecordScannerTest(); // Create a scanner to see if it gest created. Check the location header String body = json("{'recordFilter' : { '@class' : 'org.lilyproject.repository.api.filter.RecordIdPrefixFilter', " + "'recordId' : 'USER.scan_'}}, 'caching' : 1024, 'cacheBlocks' : false}"); ResponseAndContent response = post("/scan", body); assertStatus(HttpStatus.SC_CREATED, response); String location = response.getLocationRef().toString(); // Check if the scanner can be retrieved. Retrieve 1 record by default response = getUri(location); assertStatus(HttpStatus.SC_OK, response); JsonNode json = readJson(response); assertTrue(json.get("results").size() == 1); // Check to see if the batch parameter gets more records response = getUri(location + "?batch=2"); assertStatus(HttpStatus.SC_OK, response); json = readJson(response); assertTrue(json.get("results").size() == 2); // When the scanner runs out send 204 NO CONTENT response = getUri(location); assertStatus(HttpStatus.SC_NO_CONTENT, response); // GETs on non existing scanners get 404 response = getUri(location + "blablabla"); assertStatus(HttpStatus.SC_NOT_FOUND, response); } @Test public void testRecordScanDelete() throws Exception { setupRecordScannerTest(); // Create a scanner to see if it gest created. Check the location header String body = json("{'recordFilter' : { '@class' : 'org.lilyproject.repository.api.filter.RecordIdPrefixFilter', " + "'recordId' : 'USER.scan_'}}, 'caching' : 1024, 'cacheBlocks' : false}"); ResponseAndContent response = post("/scan", body); assertStatus(HttpStatus.SC_CREATED, response); String location = response.getLocationRef().toString(); // Delete scanner response = deleteUri(location); assertStatus(HttpStatus.SC_OK, response); // Check that scanner is gone response = getUri(location); assertStatus(HttpStatus.SC_NOT_FOUND, response); // Delete a non existing scanner gets a 404 response = deleteUri(location + "not_here"); assertStatus(HttpStatus.SC_NOT_FOUND, response); } }