Implement RD delete.
authorTodd Malsbary <todd.malsbary@intel.com>
Fri, 2 Dec 2016 19:59:43 +0000 (11:59 -0800)
committerHabib Virji <habib.virji@samsung.com>
Thu, 9 Feb 2017 09:31:39 +0000 (09:31 +0000)
Change-Id: I492034351d388b58a349024270d5abd9506da86d
Signed-off-by: Todd Malsbary <todd.malsbary@intel.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/15075
Tested-by: jenkins-iotivity <jenkins@iotivity.org>
Reviewed-by: Habib Virji <habib.virji@samsung.com>
resource/csdk/include/octypes.h
resource/csdk/resource-directory/include/rd_database.h
resource/csdk/resource-directory/src/RDClient.cpp
resource/csdk/resource-directory/src/internal/rd_database.c
resource/csdk/resource-directory/src/rd_client.c
resource/csdk/resource-directory/src/rd_server.c
resource/csdk/resource-directory/unittests/rddatabase.cpp
resource/csdk/resource-directory/unittests/rdtests.cpp
resource/csdk/stack/include/internal/ocresourcehandler.h
resource/csdk/stack/src/ocserverrequest.c
resource/csdk/stack/src/oicresourcedirectory.c

index 71fb314..48d553e 100755 (executable)
@@ -98,6 +98,9 @@ extern "C" {
 /** Separator for multiple query string.*/
 #define OC_QUERY_SEPARATOR                    "&;"
 
+/** Delimeter for keys and values in query string.*/
+#define OC_KEY_VALUE_DELIMITER                "="
+
 /**
  *  OC_DEFAULT_PRESENCE_TTL_SECONDS sets the default time to live (TTL) for presence.
  */
index f23330c..b544859 100644 (file)
@@ -47,22 +47,17 @@ OCStackResult OCRDDatabaseInit(const char *path);
 OCStackResult OCRDDatabaseStoreResources(const OCRepPayload *payload, const OCDevAddr *address);
 
 /**
- * Updates the RD resource
+ * Delete the RD resources
  *
- * @param deviceId of the device for which resources will be update.
+ * @param deviceId of the device containing the resource(s) to be deleted.
+ * @param instanceIds the resource(s) to be deleted.  If NULL then all resources
+ *                    belonging to the device will be deleted.
+ * @param nInstanceIds the number of instanceIds
  *
  * @return ::OC_STACK_OK in case of success or else other value.
  */
-OCStackResult OCRDDatabaseUpdateDevice(const char *deviceId);
-
-/**
- * Delete the RD resource
- *
- * @param deviceId of the device for which resources will be deleted.
- *
- * @return ::OC_STACK_OK in case of success or else other value.
- */
-OCStackResult OCRDDatabaseDeleteDevice(const char *deviceId);
+OCStackResult OCRDDatabaseDeleteResources(const char *deviceId, const uint8_t *instanceIds,
+                                          uint8_t nInstanceIds);
 
 /**
  * Close the RD publish database.
index 115c451..93cfc0e 100644 (file)
@@ -73,26 +73,8 @@ OCStackApplicationResult publishResourceToRDCallback(void* ctx, OCDoHandle /*han
 
     try
     {
-        // Update resource unique id in stack.
         if (clientResponse)
         {
-            if (clientResponse->payload)
-            {
-                OCRepPayload *rdPayload = (OCRepPayload *) clientResponse->payload;
-                OCRepPayload **links = NULL;
-
-                size_t dimensions[MAX_REP_ARRAY_DEPTH];
-                OCRepPayloadGetPropObjectArray(rdPayload, OC_RSRVD_LINKS, &links, dimensions);
-                for(size_t i = 0; i < dimensions[0]; i++)
-                {
-                    char *uri = NULL;
-                    OCRepPayloadGetPropString(links[i], OC_RSRVD_HREF, &uri);
-                    OCResourceHandle handle = OCGetResourceHandleAtUri(uri);
-                    int64_t ins = 0;
-                    OCRepPayloadGetPropInt(links[i], OC_RSRVD_INS, &ins);
-                    OCBindResourceInsToResource(handle, ins);
-                }
-            }
             OCRepresentation rep = parseRDResponseCallback(clientResponse);
             std::thread exec(context->callback, rep, clientResponse->result);
             exec.detach();
index abcb367..d502c94 100644 (file)
@@ -175,7 +175,7 @@ static int storeResourceType(char **link, size_t size, uint8_t rowid)
         if (link[i])
         {
             VERIFY_SQLITE(sqlite3_bind_text(stmtRT, rt_value_index, link[i],
-                    strlen(link[i])+1, SQLITE_STATIC));
+                                            strlen(link[i]), SQLITE_STATIC));
 
             VERIFY_SQLITE(sqlite3_bind_int(stmtRT, rt_link_id_index, rowid));
         }
@@ -211,7 +211,7 @@ static int storeInterfaceType(char **link, size_t size, uint8_t rowid)
 
         if (link[i])
         {
-            VERIFY_SQLITE(sqlite3_bind_text(stmtIF, if_value_index, link[i], strlen(link[i])+1, SQLITE_STATIC));
+            VERIFY_SQLITE(sqlite3_bind_text(stmtIF, if_value_index, link[i], strlen(link[i]), SQLITE_STATIC));
             VERIFY_SQLITE(sqlite3_bind_int(stmtIF, if_link_id_index, rowid));
         }
         if (sqlite3_step(stmtIF) != SQLITE_DONE)
@@ -231,22 +231,36 @@ static int storeInterfaceType(char **link, size_t size, uint8_t rowid)
 
 static int storeLinkPayload(OCRepPayload *rdPayload, int64_t rowid)
 {
-    OCRepPayload **links = NULL;
-    size_t dimensions[MAX_REP_ARRAY_DEPTH];
     int res = 1 ;
     size_t j = 0;
 
-    if (OCRepPayloadGetPropObjectArray(rdPayload, OC_RSRVD_LINKS, &links, dimensions))
+    /*
+     * Iterate over the properties manually rather than OCRepPayloadGetPropObjectArray to avoid
+     * the clone since we want to insert the 'ins' values into the payload.
+     */
+    OCRepPayloadValue *links;
+    for (links = rdPayload->values; links; links = links->next)
+    {
+        if (0 == strcmp(links->name, OC_RSRVD_LINKS))
+        {
+            if (links->type != OCREP_PROP_ARRAY || links->arr.type != OCREP_PROP_OBJECT)
+            {
+                links = NULL;
+            }
+            break;
+        }
+    }
+    if (links != NULL)
     {
         const char *insertDeviceLLList = "INSERT INTO RD_DEVICE_LINK_LIST VALUES(?,?,?,?,?,?,?,?)";
         sqlite3_stmt *stmt = 0;
 
-        for (size_t i = 0; i < dimensions[0]; i++)
+        for (size_t i = 0; i < links->arr.dimensions[0]; i++)
         {
             VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, insertDeviceLLList, strlen(insertDeviceLLList) + 1, &stmt, NULL));
             VERIFY_SQLITE(sqlite3_exec(gRDDB, "BEGIN TRANSACTION", NULL, NULL, NULL));
 
-            OCRepPayload *link = links[i];
+            OCRepPayload *link = links->arr.objArray[i];
             char *uri = NULL;
             if (OCRepPayloadGetPropString(link, OC_RSRVD_HREF, &uri))
             {
@@ -267,7 +281,7 @@ static int storeLinkPayload(OCRepPayload *rdPayload, int64_t rowid)
             char **mediaType = NULL;
             if (OCRepPayloadGetStringArray(link, OC_RSRVD_MEDIA_TYPE, &mediaType, mtDim))
             {
-                VERIFY_SQLITE(sqlite3_bind_text(stmt, mt_index, mediaType[0], mtDim[0], SQLITE_STATIC));
+                VERIFY_SQLITE(sqlite3_bind_text(stmt, mt_index, mediaType[0], strlen(mediaType[0]), SQLITE_STATIC));
             }
 
             VERIFY_SQLITE(sqlite3_bind_int(stmt, d_index, rowid));
@@ -289,6 +303,11 @@ static int storeLinkPayload(OCRepPayload *rdPayload, int64_t rowid)
             VERIFY_SQLITE(sqlite3_exec(gRDDB, "COMMIT", NULL, NULL, NULL));
 
             int64_t ins = sqlite3_last_insert_rowid(gRDDB);
+            if (!OCRepPayloadSetPropInt(link, OC_RSRVD_INS, ins))
+            {
+                OIC_LOG_V(ERROR, TAG, "Error setting 'ins' value");
+                return OC_STACK_ERROR;
+            }
             VERIFY_SQLITE(storeResourceType(rt, rtDim[0], ins));
             VERIFY_SQLITE(storeInterfaceType(itf, itfDim[0], ins));
             OICFree(uri);
@@ -331,7 +350,7 @@ OCStackResult OCRDDatabaseStoreResources(OCRepPayload *payload, const OCDevAddr
     char *deviceid = NULL;
     if (OCRepPayloadGetPropString(payload, OC_RSRVD_DEVICE_ID, &deviceid))
     {
-        VERIFY_SQLITE(sqlite3_bind_text(stmt, device_index, deviceid, strlen(deviceid) + 1, SQLITE_STATIC));
+        VERIFY_SQLITE(sqlite3_bind_text(stmt, device_index, deviceid, strlen(deviceid), SQLITE_STATIC));
     }
 
     int64_t ttl = 0;
@@ -343,7 +362,7 @@ OCStackResult OCRDDatabaseStoreResources(OCRepPayload *payload, const OCDevAddr
     char rdAddress[MAX_URI_LENGTH];
     snprintf(rdAddress, MAX_URI_LENGTH, "%s:%d", address->addr, address->port);
     OIC_LOG_V(DEBUG, TAG, "Address: %s", rdAddress);
-    VERIFY_SQLITE(sqlite3_bind_text(stmt, address_index, rdAddress, strlen(rdAddress) + 1, SQLITE_STATIC));
+    VERIFY_SQLITE(sqlite3_bind_text(stmt, address_index, rdAddress, strlen(rdAddress), SQLITE_STATIC));
 
     if (sqlite3_step(stmt) != SQLITE_DONE)
     {
@@ -364,17 +383,52 @@ OCStackResult OCRDDatabaseStoreResources(OCRepPayload *payload, const OCDevAddr
     return OC_STACK_OK;
 }
 
-OCStackResult OCRDDatabaseDeleteDevice(const char *deviceId)
+OCStackResult OCRDDatabaseDeleteResources(const char *deviceId, const uint8_t *instanceIds, uint8_t nInstanceIds)
 {
     CHECK_DATABASE_INIT;
-    VERIFY_SQLITE(sqlite3_exec(gRDDB, "BEGIN TRANSACTION", NULL, NULL, NULL));
 
+    VERIFY_SQLITE(sqlite3_exec(gRDDB, "BEGIN TRANSACTION", NULL, NULL, NULL));
     sqlite3_stmt *stmt = 0;
-    char *delDevice = "DELETE FROM RD_DEVICE_LIST WHERE "XSTR(OC_RSRVD_DEVICE_ID)" = ?";
-    VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, delDevice, strlen(delDevice) + 1, &stmt, NULL));
+    if (!instanceIds || !nInstanceIds)
+    {
+        char *delDevice = "DELETE FROM RD_DEVICE_LIST WHERE "XSTR(OC_RSRVD_DEVICE_ID)" = ?";
+        VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, delDevice, strlen(delDevice) + 1, &stmt, NULL));
 
-    VERIFY_SQLITE(sqlite3_bind_text(stmt, 1, deviceId, strlen(deviceId) + 1, SQLITE_STATIC));
+        VERIFY_SQLITE(sqlite3_bind_text(stmt, 1, deviceId, strlen(deviceId), SQLITE_STATIC));
+    }
+    else
+    {
+        const char pre[] = "DELETE FROM RD_DEVICE_LINK_LIST "
+            "WHERE ins IN ("
+            "SELECT RD_DEVICE_LINK_LIST.ins FROM RD_DEVICE_LINK_LIST "
+            "INNER JOIN RD_DEVICE_LIST ON RD_DEVICE_LINK_LIST.DEVICE_ID=RD_DEVICE_LIST.ID "
+            "WHERE RD_DEVICE_LIST.di = ? AND RD_DEVICE_LINK_LIST.ins IN (";
+        size_t inLen = nInstanceIds + (nInstanceIds - 1);
+        const char post[] = "))";
+        size_t len = sizeof(pre) + inLen + sizeof(post);
+        char *delResource = OICCalloc(len, 1);
+        if (!delResource)
+        {
+            OIC_LOG(ERROR, TAG, "SQL query is NULL");
+            return OC_STACK_ERROR;
+        }
+        OICStrcat(delResource, len, pre);
+        OICStrcat(delResource, len, "?");
+        for (uint8_t i = 1; i < nInstanceIds; ++i)
+        {
+            OICStrcat(delResource, len, ",?");
+        }
+        OICStrcat(delResource, len, post);
+        int prepareResult = sqlite3_prepare_v2(gRDDB, delResource, strlen(delResource) + 1, &stmt, NULL);
+        OICFree(delResource);
+        VERIFY_SQLITE(prepareResult);
 
+        VERIFY_SQLITE(sqlite3_bind_text(stmt, 1, deviceId, strlen(deviceId), SQLITE_STATIC));
+        for (uint8_t i = 0; i < nInstanceIds; ++i)
+        {
+            VERIFY_SQLITE(sqlite3_bind_int(stmt, 2 + i, instanceIds[i]));
+        }
+    }
     if (sqlite3_step(stmt) != SQLITE_DONE)
     {
         sqlite3_exec(gRDDB, "ROLLBACK", NULL, NULL, NULL);
index be33873..5439be3 100644 (file)
@@ -51,6 +51,68 @@ OCStackResult OCRDDiscover(OCConnectivityType connectivityType, OCCallbackData *
                         cbBiasFactor, NULL, 0);
 }
 
+static void RDPublishContextDeleter(void *ctx)
+{
+    OCCallbackData *cbData = (OCCallbackData*)ctx;
+    if (cbData->cd)
+    {
+        cbData->cd(cbData->context);
+    }
+    OICFree(cbData);
+}
+
+OCStackApplicationResult RDPublishCallback(void *ctx,
+                                           OCDoHandle handle,
+                                           OCClientResponse *clientResponse)
+{
+    OCCallbackData *cbData = (OCCallbackData*)ctx;
+
+    // Update resource unique id in stack.
+    OCRepPayload **links = NULL;
+    size_t dimensions[MAX_REP_ARRAY_DEPTH] = {0};
+    if (clientResponse && clientResponse->payload)
+    {
+        OCRepPayload *rdPayload = (OCRepPayload *) clientResponse->payload;
+        if (!OCRepPayloadGetPropObjectArray(rdPayload, OC_RSRVD_LINKS, &links, dimensions))
+        {
+            OIC_LOG_V(DEBUG, TAG, "No links in publish response");
+            goto exit;
+        }
+        for(size_t i = 0; i < dimensions[0]; i++)
+        {
+            char *uri = NULL;
+            if (!OCRepPayloadGetPropString(links[i], OC_RSRVD_HREF, &uri))
+            {
+                OIC_LOG_V(ERROR, TAG, "Missing 'href' in publish response");
+                goto next;
+            }
+            OCResourceHandle handle = OCGetResourceHandleAtUri(uri);
+            if (handle == NULL)
+            {
+                OIC_LOG_V(ERROR, TAG, "No resource exists with uri: %s", uri);
+                goto next;
+            }
+            int64_t ins = 0;
+            if (!OCRepPayloadGetPropInt(links[i], OC_RSRVD_INS, &ins))
+            {
+                OIC_LOG_V(ERROR, TAG, "Missing 'ins' in publish response");
+                goto next;
+            }
+            OCBindResourceInsToResource(handle, ins);
+        next:
+            OICFree(uri);
+        }
+    }
+
+exit:
+    for (size_t i = 0; i < dimensions[0]; i++)
+    {
+        OCRepPayloadDestroy(links[i]);
+    }
+    OICFree(links);
+    return cbData->cb(cbData->context, handle, clientResponse);
+}
+
 OCStackResult OCRDPublish(const char *host, OCConnectivityType connectivityType,
                           OCResourceHandle *resourceHandles, uint8_t nHandles,
                           OCCallbackData *cbData, OCQualityOfService qos)
@@ -190,12 +252,6 @@ OCStackResult OCRDPublishWithDeviceId(const char *host, const unsigned char *id,
                 OCRepPayloadSetStringArrayAsOwner(link, OC_RSRVD_INTERFACE, itf, ifDim);
             }
 
-            uint8_t ins = 0;
-            if (OC_STACK_OK == OCGetResourceIns(handle, &ins))
-            {
-                OCRepPayloadSetPropInt(link, OC_RSRVD_INS, ins);
-            }
-
             size_t mtDim[MAX_REP_ARRAY_DEPTH] = {1, 0, 0};
             char **mediaType = (char **)OICMalloc(sizeof(char *) * 1);
             if (!mediaType)
@@ -234,8 +290,18 @@ OCStackResult OCRDPublishWithDeviceId(const char *host, const unsigned char *id,
     }
     OICFree(linkArr);
 
+    OCCallbackData *rdPublishContext = (OCCallbackData*)OICMalloc(sizeof(OCCallbackData));
+    if (!rdPublishContext)
+    {
+        return OC_STACK_NO_MEMORY;
+    }
+    memcpy(rdPublishContext, cbData, sizeof(OCCallbackData));
+    OCCallbackData rdPublishCbData;
+    rdPublishCbData.context = rdPublishContext;
+    rdPublishCbData.cb = RDPublishCallback;
+    rdPublishCbData.cd = RDPublishContextDeleter;
     return OCDoResource(NULL, OC_REST_POST, targetUri, NULL, (OCPayload *)rdPayload,
-                        connectivityType, qos, cbData, NULL, 0);
+                        connectivityType, qos, &rdPublishCbData, NULL, 0);
 }
 
 OCStackResult OCRDDelete(const char *host, OCConnectivityType connectivityType,
index f8150e3..cb243e4 100644 (file)
 
 #include "rd_database.h"
 
+#include <string.h>
 #include "payload_logging.h"
 #include "ocpayload.h"
 #include "octypes.h"
+#include "oic_string.h"
 
 #define TAG PCF("OIC_RD_SERVER")
 
@@ -137,11 +139,99 @@ static OCEntityHandlerResult handlePublishRequest(const OCEntityHandlerRequest *
 
 static OCEntityHandlerResult handleDeleteRequest(const OCEntityHandlerRequest *ehRequest)
 {
-    OIC_LOG(DEBUG, TAG, "handleDeleteRequest - IN");
+    OCEntityHandlerResult ehResult = OC_EH_ERROR;
+    char *key = NULL;
+    char *value = NULL;
+    char *queryDup = NULL;
+    char *restOfQuery = NULL;
+    char *keyValuePair = NULL;
+    char *di = NULL;
+    size_t nIns = 0;
+    uint8_t *ins = NULL;
 
-    (void) ehRequest;  // eliminates release warning
+    if (!ehRequest)
+    {
+        OIC_LOG(DEBUG, TAG, "Invalid request pointer");
+        return OC_EH_ERROR;
+    }
 
-    OCEntityHandlerResult ehResult = OC_EH_OK;
+    OIC_LOG_V(DEBUG, TAG, "Received OC_REST_DELETE from client with query: %s.", ehRequest->query);
+
+    if (OCRDDatabaseInit(NULL) != OC_STACK_OK)
+    {
+        goto exit;
+    }
+
+#define OC_RSRVD_INS_KEY OC_RSRVD_INS OC_KEY_VALUE_DELIMITER /* "ins=" */
+    keyValuePair = strstr(ehRequest->query, OC_RSRVD_INS_KEY);
+    while (keyValuePair)
+    {
+        ++nIns;
+        keyValuePair = strstr(keyValuePair + sizeof(OC_RSRVD_INS_KEY), OC_RSRVD_INS_KEY);
+    }
+    if (nIns)
+    {
+        ins = OICMalloc(nIns * sizeof(uint8_t));
+        if (!ins)
+        {
+            OIC_LOG_V(ERROR, TAG, "ins is NULL");
+            goto exit;
+        }
+    }
+
+    nIns = 0;
+    queryDup = OICStrdup(ehRequest->query);
+    if (NULL == queryDup)
+    {
+        OIC_LOG_V(ERROR, TAG, "Creating duplicate string failed!");
+        goto exit;
+    }
+    keyValuePair = strtok_r(queryDup, OC_QUERY_SEPARATOR, &restOfQuery);
+    while (keyValuePair)
+    {
+        key = strtok_r(keyValuePair, OC_KEY_VALUE_DELIMITER, &value);
+        if (!key || !value)
+        {
+            OIC_LOG_V(ERROR, TAG, "Invalid query parameter!");
+            goto exit;
+        }
+        else if (strncasecmp(key, OC_RSRVD_DEVICE_ID, sizeof(OC_RSRVD_DEVICE_ID) - 1) == 0)
+        {
+            di = value;
+        }
+        else if (strncasecmp(key, OC_RSRVD_INS, sizeof(OC_RSRVD_INS) - 1) == 0)
+        {
+            char *endptr = NULL;
+            long int i = strtol(value, &endptr, 0);
+            if (*endptr != '\0' || i < 0 || i > UINT8_MAX)
+            {
+                OIC_LOG_V(ERROR, TAG, "Invalid ins query parameter: %s", value);
+                goto exit;
+            }
+            ins[nIns++] = i;
+        }
+
+        keyValuePair = strtok_r(NULL, OC_QUERY_SEPARATOR, &restOfQuery);
+    }
+    if (!di)
+    {
+        OIC_LOG_V(ERROR, TAG, "Missing required di query parameter!");
+        goto exit;
+    }
+
+    if (OCRDDatabaseDeleteResources(di, ins, nIns) == OC_STACK_OK)
+    {
+        OIC_LOG_V(DEBUG, TAG, "Deleted resource(s).");
+        ehResult = OC_EH_OK;
+    }
+
+exit:
+    OICFree(ins);
+    OICFree(queryDup);
+    if (sendResponse(ehRequest, NULL, ehResult) != OC_STACK_OK)
+    {
+        OIC_LOG(ERROR, TAG, "Sending response failed.");
+    }
     return ehResult;
 }
 
index 97207e9..1958346 100644 (file)
@@ -75,30 +75,22 @@ class RDDatabaseTests : public testing::Test {
     protected:
     virtual void SetUp()
     {
+        remove("RD.db");
         OCInit("127.0.0.1", 5683, OC_CLIENT_SERVER);
+        EXPECT_EQ(OC_STACK_OK, OCRDDatabaseInit(NULL));
     }
 
     virtual void TearDown()
     {
+        EXPECT_EQ(OC_STACK_OK, OCRDDatabaseClose());
         OCStop();
     }
 };
 
-TEST_F(RDDatabaseTests, CreateDatabase)
+static OCRepPayload *CreateResources(const char *deviceId)
 {
-    itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
-    EXPECT_EQ(OC_STACK_OK, OCRDDatabaseInit(NULL));
-    EXPECT_EQ(OC_STACK_OK, OCRDDatabaseClose());
-}
-
-TEST_F(RDDatabaseTests, PublishDatabase)
-{
-    // itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
-    EXPECT_EQ(OC_STACK_OK, OCRDDatabaseInit(NULL));
     OCRepPayload *repPayload = OCRepPayloadCreate();
-    ASSERT_TRUE(repPayload != NULL);
-    const char *deviceId = "7a960f46-a52e-4837-bd83-460b1a6dd56b";
-    ASSERT_TRUE(deviceId != NULL);
+    EXPECT_TRUE(repPayload != NULL);
     EXPECT_TRUE(OCRepPayloadSetPropString(repPayload, OC_RSRVD_DEVICE_ID, deviceId));
     EXPECT_TRUE(OCRepPayloadSetPropInt(repPayload, OC_RSRVD_DEVICE_TTL, 86400));
     OCDevAddr address;
@@ -153,6 +145,22 @@ TEST_F(RDDatabaseTests, PublishDatabase)
     OCRepPayloadSetPropObjectArray(repPayload, OC_RSRVD_LINKS, linkArr, dimensions);
 
     OIC_LOG_PAYLOAD(DEBUG, (OCPayload *)repPayload);
+    return repPayload;
+}
+
+TEST_F(RDDatabaseTests, Create)
+{
+    itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
+}
+
+TEST_F(RDDatabaseTests, StoreResources)
+{
+    itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
+    const char *deviceId = "7a960f46-a52e-4837-bd83-460b1a6dd56b";
+    OCRepPayload *repPayload = CreateResources(deviceId);
+    OCDevAddr address;
+    address.port = 54321;
+    OICStrcpy(address.addr,MAX_ADDR_STR_SIZE, "192.168.1.1");
 
     EXPECT_EQ(OC_STACK_OK, OCRDDatabaseStoreResources(repPayload, &address));
 
@@ -170,6 +178,48 @@ TEST_F(RDDatabaseTests, PublishDatabase)
     OCDiscoveryPayloadDestroy(discPayload);
     discPayload = NULL;
 
-    EXPECT_EQ(OC_STACK_OK, OCRDDatabaseDeleteDevice(deviceId));
-    EXPECT_EQ(OC_STACK_OK, OCRDDatabaseClose());
+    OCPayloadDestroy((OCPayload *)repPayload);
+}
+
+TEST_F(RDDatabaseTests, DeleteResourcesDevice)
+{
+    itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
+    const char *deviceIds[2] =
+    {
+        "7a960f46-a52e-4837-bd83-460b1a6dd56b",
+        "983656a7-c7e5-49c2-a201-edbeb7606fb5",
+    };
+    OCRepPayload *payloads[2];
+    payloads[0] = CreateResources(deviceIds[0]);
+    payloads[1] = CreateResources(deviceIds[1]);
+    OCDevAddr address;
+    address.port = 54321;
+    OICStrcpy(address.addr,MAX_ADDR_STR_SIZE, "192.168.1.1");
+    EXPECT_EQ(OC_STACK_OK, OCRDDatabaseStoreResources(payloads[0], &address));
+    EXPECT_EQ(OC_STACK_OK, OCRDDatabaseStoreResources(payloads[1], &address));
+
+    EXPECT_EQ(OC_STACK_OK, OCRDDatabaseDeleteResources(deviceIds[0], NULL, 0));
+
+    OCDiscoveryPayload *discPayload = NULL;
+    EXPECT_EQ(OC_STACK_OK, OCRDDatabaseDiscoveryPayloadCreate(OC_RSRVD_INTERFACE_LL, NULL, &discPayload));
+    bool found0 = false;
+    bool found1 = false;
+    for (OCDiscoveryPayload *payload = discPayload; payload; payload = payload->next)
+    {
+        if (!strcmp((const char *) deviceIds[0], payload->sid))
+        {
+            found0 = true;
+        }
+        if (!strcmp((const char *) deviceIds[1], payload->sid))
+        {
+            found1 = true;
+        }
+    }
+    EXPECT_FALSE(found0);
+    EXPECT_TRUE(found1);
+    OCDiscoveryPayloadDestroy(discPayload);
+    discPayload = NULL;
+
+    OCPayloadDestroy((OCPayload *)payloads[0]);
+    OCPayloadDestroy((OCPayload *)payloads[1]);
 }
index 3973c81..2fa9319 100644 (file)
@@ -80,21 +80,23 @@ static OCStackApplicationResult handleDiscoveryCB(__attribute__((unused))void *c
 #ifdef RD_CLIENT
 static OCStackApplicationResult handlePublishCB(__attribute__((unused))void *ctx,
                                                 __attribute__((unused)) OCDoHandle handle,
-                                                __attribute__((unused))
                                                 OCClientResponse *clientResponse)
 {
     OIC_LOG(DEBUG, TAG, "Successfully published resources.");
 
+    EXPECT_EQ(OC_STACK_RESOURCE_CHANGED, clientResponse->result);
+
     return OC_STACK_DELETE_TRANSACTION;
 }
 
 static OCStackApplicationResult handleDeleteCB(__attribute__((unused))void *ctx,
                                                __attribute__((unused)) OCDoHandle handle,
-                                               __attribute__((unused))
                                                OCClientResponse *clientResponse)
 {
     OIC_LOG(DEBUG, TAG, "Successfully delete resources.");
 
+    EXPECT_EQ(OC_STACK_RESOURCE_CHANGED, clientResponse->result);
+
     return OC_STACK_DELETE_TRANSACTION;
 }
 #endif
@@ -1089,4 +1091,191 @@ TEST_F(RDDiscoverTests, Baseline)
                                         OC_HIGH_QOS, discoverCB, NULL, 0));
     discoverCB.Wait();
 }
+
+static OCStackApplicationResult DeleteDeviceVerify(void *ctx, OCDoHandle handle,
+        OCClientResponse *response)
+{
+    (void) ctx;
+    (void) handle;
+    EXPECT_EQ(OC_STACK_OK, response->result);
+    EXPECT_TRUE(response->payload != NULL);
+    if (response->payload)
+    {
+        EXPECT_EQ(PAYLOAD_TYPE_DISCOVERY, response->payload->type);
+        for (OCDiscoveryPayload *payload = (OCDiscoveryPayload *) response->payload; payload;
+             payload = payload->next)
+        {
+            EXPECT_STRNE((const char *) RDDiscoverTests::di[0], payload->sid);
+        }
+    }
+    return OC_STACK_DELETE_TRANSACTION;
+}
+
+TEST_F(RDDiscoverTests, DeleteDevice)
+{
+    itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
+
+    OCResourceHandle handles[1];
+    EXPECT_EQ(OC_STACK_OK, OCCreateResource(&handles[0], "core.light",
+                                            "oic.if.baseline", "/a/light", rdEntityHandler,
+                                            NULL, (OC_DISCOVERABLE | OC_OBSERVABLE)));
+    Callback publishCB(&handlePublishCB);
+    EXPECT_EQ(OC_STACK_OK, OCRDPublishWithDeviceId("127.0.0.1", di[0], CT_ADAPTER_IP, handles,
+              1, publishCB, OC_LOW_QOS));
+    publishCB.Wait();
+
+    Callback deleteCB(&handleDeleteCB);
+    EXPECT_EQ(OC_STACK_OK, OCRDDeleteWithDeviceId("127.0.0.1", di[0], CT_ADAPTER_IP, nullptr, 0,
+                                                  deleteCB, OC_HIGH_QOS));
+    deleteCB.Wait();
+
+    Callback discoverCB(&DeleteDeviceVerify);
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, "/oic/res", NULL, 0,
+                                        CT_DEFAULT,
+                                        OC_HIGH_QOS, discoverCB, NULL, 0));
+    discoverCB.Wait();
+}
+
+static OCStackApplicationResult Delete1Verify(void *ctx, OCDoHandle handle,
+        OCClientResponse *response)
+{
+    (void) ctx;
+    (void) handle;
+    EXPECT_EQ(OC_STACK_OK, response->result);
+    EXPECT_TRUE(response->payload != NULL);
+    if (response->payload)
+    {
+        EXPECT_EQ(PAYLOAD_TYPE_DISCOVERY, response->payload->type);
+        bool foundId = false;
+        bool foundLight = false;
+        bool foundLight2 = false;
+        for (OCDiscoveryPayload *payload = (OCDiscoveryPayload *) response->payload; payload;
+             payload = payload->next)
+        {
+            if (!strcmp((const char *) RDDiscoverTests::di[0], payload->sid))
+            {
+                foundId = true;
+                for (OCResourcePayload *resource = payload->resources; resource; resource = resource->next)
+                {
+                    if (!strcmp("/a/light", resource->uri))
+                    {
+                        foundLight = true;
+                    }
+                    if (!strcmp("/a/light2", resource->uri))
+                    {
+                        foundLight2 = true;
+                    }
+                }
+            }
+        }
+        EXPECT_TRUE(foundId);
+        EXPECT_FALSE(foundLight);
+        EXPECT_TRUE(foundLight2);
+    }
+    return OC_STACK_DELETE_TRANSACTION;
+}
+
+TEST_F(RDDiscoverTests, Delete1)
+{
+    itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
+
+    OCResourceHandle handles[2];
+    EXPECT_EQ(OC_STACK_OK, OCCreateResource(&handles[0], "core.light",
+                                            "oic.if.baseline", "/a/light", rdEntityHandler,
+                                            NULL, (OC_DISCOVERABLE | OC_OBSERVABLE)));
+    EXPECT_EQ(OC_STACK_OK, OCCreateResource(&handles[1], "core.light",
+                                            "oic.if.baseline", "/a/light2", rdEntityHandler,
+                                            NULL, (OC_DISCOVERABLE | OC_OBSERVABLE)));
+    Callback publishCB(&handlePublishCB);
+    EXPECT_EQ(OC_STACK_OK, OCRDPublishWithDeviceId("127.0.0.1", di[0], CT_ADAPTER_IP, handles,
+              2, publishCB, OC_LOW_QOS));
+    publishCB.Wait();
+
+    Callback deleteCB(&handleDeleteCB);
+    EXPECT_EQ(OC_STACK_OK, OCRDDeleteWithDeviceId("127.0.0.1", di[0], CT_ADAPTER_IP, handles, 1,
+                                                  deleteCB, OC_HIGH_QOS));
+    deleteCB.Wait();
+
+    Callback discoverCB(&Delete1Verify);
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, "/oic/res", NULL, 0,
+                                        CT_DEFAULT,
+                                        OC_HIGH_QOS, discoverCB, NULL, 0));
+    discoverCB.Wait();
+}
+
+static OCStackApplicationResult DeleteNVerify(void *ctx, OCDoHandle handle,
+        OCClientResponse *response)
+{
+    (void) ctx;
+    (void) handle;
+    EXPECT_EQ(OC_STACK_OK, response->result);
+    EXPECT_TRUE(response->payload != NULL);
+    if (response->payload)
+    {
+        EXPECT_EQ(PAYLOAD_TYPE_DISCOVERY, response->payload->type);
+        bool foundId = false;
+        bool foundLight = false;
+        bool foundLight2 = false;
+        bool foundLight3 = false;
+        for (OCDiscoveryPayload *payload = (OCDiscoveryPayload *) response->payload; payload;
+             payload = payload->next)
+        {
+            if (!strcmp((const char *) RDDiscoverTests::di[0], payload->sid))
+            {
+                foundId = true;
+                for (OCResourcePayload *resource = payload->resources; resource; resource = resource->next)
+                {
+                    if (!strcmp("/a/light", resource->uri))
+                    {
+                        foundLight = true;
+                    }
+                    if (!strcmp("/a/light2", resource->uri))
+                    {
+                        foundLight2 = true;
+                    }
+                    if (!strcmp("/a/light3", resource->uri))
+                    {
+                        foundLight3 = true;
+                    }
+                }
+            }
+        }
+        EXPECT_TRUE(foundId);
+        EXPECT_FALSE(foundLight);
+        EXPECT_FALSE(foundLight2);
+        EXPECT_TRUE(foundLight3);
+    }
+    return OC_STACK_DELETE_TRANSACTION;
+}
+
+TEST_F(RDDiscoverTests, DeleteN)
+{
+    itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
+
+    OCResourceHandle handles[3];
+    EXPECT_EQ(OC_STACK_OK, OCCreateResource(&handles[0], "core.light",
+                                            "oic.if.baseline", "/a/light", rdEntityHandler,
+                                            NULL, (OC_DISCOVERABLE | OC_OBSERVABLE)));
+    EXPECT_EQ(OC_STACK_OK, OCCreateResource(&handles[1], "core.light",
+                                            "oic.if.baseline", "/a/light2", rdEntityHandler,
+                                            NULL, (OC_DISCOVERABLE | OC_OBSERVABLE)));
+    EXPECT_EQ(OC_STACK_OK, OCCreateResource(&handles[2], "core.light",
+                                            "oic.if.baseline", "/a/light3", rdEntityHandler,
+                                            NULL, (OC_DISCOVERABLE | OC_OBSERVABLE)));
+    Callback publishCB(&handlePublishCB);
+    EXPECT_EQ(OC_STACK_OK, OCRDPublishWithDeviceId("127.0.0.1", di[0], CT_ADAPTER_IP, handles,
+              3, publishCB, OC_LOW_QOS));
+    publishCB.Wait();
+
+    Callback deleteCB(&handleDeleteCB);
+    EXPECT_EQ(OC_STACK_OK, OCRDDeleteWithDeviceId("127.0.0.1", di[0], CT_ADAPTER_IP, handles, 2,
+                                                  deleteCB, OC_HIGH_QOS));
+    deleteCB.Wait();
+
+    Callback discoverCB(&DeleteNVerify);
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, "/oic/res", NULL, 0,
+                                        CT_DEFAULT,
+                                        OC_HIGH_QOS, discoverCB, NULL, 0));
+    discoverCB.Wait();
+}
 #endif
index 12a5490..2ebe6a2 100644 (file)
@@ -40,7 +40,6 @@ extern "C" {
 #define OC_JSON_SUFFIX_LEN                 (sizeof(OC_JSON_SUFFIX) - 1)
 #define OC_JSON_SEPARATOR                  ','
 #define OC_JSON_SEPARATOR_STR              ","
-#define OC_KEY_VALUE_DELIMITER             "="
 
 /**
  * Static values for various JSON attributes.
index ee87703..1136573 100644 (file)
@@ -442,7 +442,7 @@ CAResponseResult_t ConvertEHResultToCAResult (OCEntityHandlerResult result, OCMe
         case OC_EH_OK:
         case OC_EH_CHANGED: // 2.04
         case OC_EH_CONTENT: // 2.05
-            if (method == OC_REST_POST || method == OC_REST_PUT)
+            if (method == OC_REST_POST || method == OC_REST_PUT || method == OC_REST_DELETE)
             {
                 caResult = CA_CHANGED;
             }
index e8e1eac..f0496c8 100644 (file)
@@ -147,7 +147,7 @@ static OCStackResult ResourcePayloadCreate(sqlite3_stmt *stmt, OCDiscoveryPayloa
 
         sqlite3_stmt *stmtRT = 0;
         const char *rt = "SELECT rt FROM RD_LINK_RT WHERE LINK_ID=?";
-        VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, rt, -1, &stmtRT, NULL));
+        VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, rt, strlen(rt) + 1, &stmtRT, NULL));
         VERIFY_SQLITE(sqlite3_bind_int(stmtRT, 1, id));
         while (SQLITE_ROW == sqlite3_step(stmtRT))
         {
@@ -162,7 +162,7 @@ static OCStackResult ResourcePayloadCreate(sqlite3_stmt *stmt, OCDiscoveryPayloa
 
         sqlite3_stmt *stmtIF = 0;
         const char *itf = "SELECT if FROM RD_LINK_IF WHERE LINK_ID=?";
-        VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, itf, -1, &stmtIF, NULL));
+        VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, itf, strlen(itf) + 1, &stmtIF, NULL));
         VERIFY_SQLITE(sqlite3_bind_int(stmtIF, 1, id));
         while (SQLITE_ROW == sqlite3_step(stmtIF))
         {
@@ -185,7 +185,7 @@ static OCStackResult ResourcePayloadCreate(sqlite3_stmt *stmt, OCDiscoveryPayloa
         const uint8_t address_index = 1;
 
         sqlite3_stmt *stmt1 = 0;
-        VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, address, -1, &stmt1, NULL));
+        VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, address, strlen(address) + 1, &stmt1, NULL));
         VERIFY_SQLITE(sqlite3_bind_int(stmt1, 1, deviceId));
 
         res = sqlite3_step(stmt1);
@@ -245,10 +245,10 @@ static OCStackResult CheckResources(const char *interfaceType, const char *resou
                                 "INNER JOIN RD_DEVICE_LIST ON RD_DEVICE_LINK_LIST.DEVICE_ID=RD_DEVICE_LIST.ID "
                                 "INNER JOIN RD_LINK_RT ON RD_DEVICE_LINK_LIST.INS=RD_LINK_RT.LINK_ID "
                                 "WHERE RD_DEVICE_LIST.di LIKE ?1 AND RD_LINK_RT.rt LIKE ?2";
-            VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, input, -1, &stmt, NULL));
-            VERIFY_SQLITE(sqlite3_bind_text(stmt, 1, discPayload->sid, strlen(discPayload->sid) + 1,
+            VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, input, strlen(input) + 1, &stmt, NULL));
+            VERIFY_SQLITE(sqlite3_bind_text(stmt, 1, discPayload->sid, strlen(discPayload->sid),
                                             SQLITE_STATIC));
-            VERIFY_SQLITE(sqlite3_bind_text(stmt, 2, resourceType, strlen(resourceType) + 1, SQLITE_STATIC));
+            VERIFY_SQLITE(sqlite3_bind_text(stmt, 2, resourceType, strlen(resourceType), SQLITE_STATIC));
         }
         else
         {
@@ -257,11 +257,11 @@ static OCStackResult CheckResources(const char *interfaceType, const char *resou
                                 "INNER JOIN RD_LINK_RT ON RD_DEVICE_LINK_LIST.INS=RD_LINK_RT.LINK_ID "
                                 "INNER JOIN RD_LINK_IF ON RD_DEVICE_LINK_LIST.INS=RD_LINK_IF.LINK_ID "
                                 "WHERE RD_DEVICE_LIST.di LIKE ?1 AND RD_LINK_RT.rt LIKE ?2 AND RD_LINK_IF.if LIKE ?3";
-            VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, input, -1, &stmt, NULL));
-            VERIFY_SQLITE(sqlite3_bind_text(stmt, 1, discPayload->sid, strlen(discPayload->sid) + 1,
+            VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, input, strlen(input) + 1, &stmt, NULL));
+            VERIFY_SQLITE(sqlite3_bind_text(stmt, 1, discPayload->sid, strlen(discPayload->sid),
                                             SQLITE_STATIC));
-            VERIFY_SQLITE(sqlite3_bind_text(stmt, 2, resourceType, strlen(resourceType) + 1, SQLITE_STATIC));
-            VERIFY_SQLITE(sqlite3_bind_text(stmt, 3, interfaceType, strlen(interfaceType) + 1, SQLITE_STATIC));
+            VERIFY_SQLITE(sqlite3_bind_text(stmt, 2, resourceType, strlen(resourceType), SQLITE_STATIC));
+            VERIFY_SQLITE(sqlite3_bind_text(stmt, 3, interfaceType, strlen(interfaceType), SQLITE_STATIC));
         }
         result = ResourcePayloadCreate(stmt, discPayload);
     }
@@ -272,8 +272,8 @@ static OCStackResult CheckResources(const char *interfaceType, const char *resou
             const char *input = "SELECT * FROM RD_DEVICE_LINK_LIST "
                                 "INNER JOIN RD_DEVICE_LIST ON RD_DEVICE_LINK_LIST.DEVICE_ID=RD_DEVICE_LIST.ID "
                                 "WHERE RD_DEVICE_LIST.di LIKE ?1";
-            VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, input, -1, &stmt, NULL));
-            VERIFY_SQLITE(sqlite3_bind_text(stmt, 1, discPayload->sid, strlen(discPayload->sid) + 1,
+            VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, input, strlen(input) + 1, &stmt, NULL));
+            VERIFY_SQLITE(sqlite3_bind_text(stmt, 1, discPayload->sid, strlen(discPayload->sid),
                                             SQLITE_STATIC));
         }
         else
@@ -282,10 +282,10 @@ static OCStackResult CheckResources(const char *interfaceType, const char *resou
                                 "INNER JOIN RD_DEVICE_LIST ON RD_DEVICE_LINK_LIST.DEVICE_ID=RD_DEVICE_LIST.ID "
                                 "INNER JOIN RD_LINK_IF ON RD_DEVICE_LINK_LIST.INS=RD_LINK_IF.LINK_ID "
                                 "WHERE RD_DEVICE_LIST.di LIKE ?1 AND RD_LINK_IF.if LIKE ?2";
-            VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, input, -1, &stmt, NULL));
-            VERIFY_SQLITE(sqlite3_bind_text(stmt, 1, discPayload->sid, strlen(discPayload->sid) + 1,
+            VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, input, strlen(input) + 1, &stmt, NULL));
+            VERIFY_SQLITE(sqlite3_bind_text(stmt, 1, discPayload->sid, strlen(discPayload->sid),
                                             SQLITE_STATIC));
-            VERIFY_SQLITE(sqlite3_bind_text(stmt, 2, interfaceType, strlen(interfaceType) + 1, SQLITE_STATIC));
+            VERIFY_SQLITE(sqlite3_bind_text(stmt, 2, interfaceType, strlen(interfaceType), SQLITE_STATIC));
         }
         result = ResourcePayloadCreate(stmt, discPayload);
     }
@@ -322,7 +322,7 @@ OCStackResult OCRDDatabaseDiscoveryPayloadCreate(const char *interfaceType,
     sqlite3_stmt *stmt = 0;
     const char *input = "SELECT di FROM RD_DEVICE_LIST";
     const uint8_t di_index = 0;
-    VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, input, -1, &stmt, NULL));
+    VERIFY_SQLITE(sqlite3_prepare_v2(gRDDB, input, strlen(input) + 1, &stmt, NULL));
     while (SQLITE_ROW == sqlite3_step(stmt))
     {
         const unsigned char *di = sqlite3_column_text(stmt, di_index);