From d79b1dc5dab0202bc409d100baab8ef9ad06cb6f Mon Sep 17 00:00:00 2001 From: Ken Zangelin Date: Sat, 20 Jan 2024 11:10:20 +0100 Subject: [PATCH] Implemented expansion of RHS in 'q' for values of LHS VocabularyProperty (hinted by URL param 'expandValues') --- CHANGES_NEXT_RELEASE | 2 +- src/lib/orionld/common/orionldState.h | 4 + src/lib/orionld/mhd/mhdConnectionInit.cpp | 5 ++ src/lib/orionld/mhd/mhdConnectionTreat.cpp | 37 +++++++++ src/lib/orionld/q/qLex.cpp | 26 ++++++- src/lib/orionld/q/qParse.cpp | 3 +- .../orionld/service/orionldServiceInit.cpp | 1 + src/lib/orionld/types/OrionLdRestService.h | 1 + .../0000_ngsild/ngsild_vocab-property.test | 77 +++++++++++++++++++ 9 files changed, 153 insertions(+), 3 deletions(-) diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 0fbb83c741..283c45d0ae 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -2,5 +2,5 @@ Fixed Issue: * #1535 No matching with 'q' when matching subscriptions for deletion of an entity New Features: - * Support for VocabularyProperty (highly untested and 'q' expansions for vocab-properties is not yet implemented) + * Support for VocabularyProperty * Support for the new URL parameter "format" for output formats (normalized, concise, simplified) diff --git a/src/lib/orionld/common/orionldState.h b/src/lib/orionld/common/orionldState.h index 2c0577d0fb..34578b3094 100644 --- a/src/lib/orionld/common/orionldState.h +++ b/src/lib/orionld/common/orionldState.h @@ -58,6 +58,7 @@ extern "C" #include "orionld/types/Verb.h" // Verb #include "orionld/types/OrionldRenderFormat.h" // OrionldRenderFormat #include "orionld/types/OrionldMimeType.h" // MimeType +#include "orionld/types/QNode.h" // QNode #include "orionld/types/ApiVersion.h" // ApiVersion #include "orionld/common/performance.h" // REQUEST_PERFORMANCE #include "orionld/kjTree/kjTreeLog.h" // Because it is so often used but then removed again ... @@ -129,6 +130,7 @@ typedef struct OrionldUriParams int limit; bool count; char* q; + char* expandValues; char* qCopy; char* mq; char* geometry; @@ -263,6 +265,7 @@ typedef struct OrionldStateIn StringArray idList; StringArray typeList; StringArray attrList; + StringArray expandValuesList; // Entity Map EntityMap* entityMap; @@ -343,6 +346,7 @@ typedef struct OrionldConnectionState OrionldContext* contextP; ApiVersion apiVersion; int requestNo; + QNode* qVariable; // Aux for qLex - to know whether a string should be expanded (VocabularyProperty) KjNode* geoAttr[10]; // Preallocated array of GeoProperties KjNode** geoAttrV; // Array of GeoProperty attributes diff --git a/src/lib/orionld/mhd/mhdConnectionInit.cpp b/src/lib/orionld/mhd/mhdConnectionInit.cpp index 4011881512..3f6e77b385 100644 --- a/src/lib/orionld/mhd/mhdConnectionInit.cpp +++ b/src/lib/orionld/mhd/mhdConnectionInit.cpp @@ -678,6 +678,11 @@ MHD_Result orionldUriArgumentGet(void* cbDataP, MHD_ValueKind kind, const char* orionldState.uriParams.options = (char*) value; orionldState.uriParams.mask |= ORIONLD_URIPARAM_OPTIONS; } + else if (strcmp(key, "expandValues") == 0) + { + orionldState.uriParams.expandValues = (char*) value; + orionldState.uriParams.mask |= ORIONLD_URIPARAM_EXPAND_VALUES; + } else if (strcmp(key, "format") == 0) { orionldState.uriParams.format = (char*) value; diff --git a/src/lib/orionld/mhd/mhdConnectionTreat.cpp b/src/lib/orionld/mhd/mhdConnectionTreat.cpp index 9f8661b01a..c90f471daf 100644 --- a/src/lib/orionld/mhd/mhdConnectionTreat.cpp +++ b/src/lib/orionld/mhd/mhdConnectionTreat.cpp @@ -827,6 +827,42 @@ static bool pCheckUrlPathAttributeName(void) +// ----------------------------------------------------------------------------- +// +// pCheckExpandValuesParam - +// +static bool pCheckExpandValuesParam(void) +{ + if (orionldState.uriParams.expandValues == NULL) + return true; + + int items = commaCount(orionldState.uriParams.expandValues) + 1; + char* arraysDup = kaStrdup(&orionldState.kalloc, orionldState.uriParams.expandValues); // To not destroy the original value + + orionldState.in.expandValuesList.items = items; + orionldState.in.expandValuesList.array = (char**) kaAlloc(&orionldState.kalloc, sizeof(char*) * items); + + if (orionldState.in.expandValuesList.array == NULL) + { + LM_E(("Out of memory (allocating an /expandValues/ array of %d char pointers)", items)); + orionldError(OrionldInternalError, "Out of memory", "allocating the array for /expandValues/ URI param", 500); + return false; + } + + int splitItems = kStringSplit(arraysDup, ',', orionldState.in.expandValuesList.array, items); + + if (splitItems != items) + { + LM_E(("kStringSplit didn't find exactly %d items (it found %d)", items, splitItems)); + orionldError(OrionldInternalError, "Internal Error", "kStringSplit does not agree with commaCount", 500); + return false; + } + + return true; +} + + + // ----------------------------------------------------------------------------- // // uriParamExpansion - @@ -845,6 +881,7 @@ static bool uriParamExpansion(void) if (pCheckUriParamGeometryProperty() == false) return false; if (pCheckPayloadEntityType() == false) return false; if (pCheckUrlPathAttributeName() == false) return false; + if (pCheckExpandValuesParam() == false) return false; // This one isn't expanded, it's just a StringArray of attribute names for VocabularyProperty // Can't do anything about 'q' - needs to be parsed first - expansion done in 'orionld/q/qParse.cpp' diff --git a/src/lib/orionld/q/qLex.cpp b/src/lib/orionld/q/qLex.cpp index 1f0adfb92a..b356ce4fd1 100644 --- a/src/lib/orionld/q/qLex.cpp +++ b/src/lib/orionld/q/qLex.cpp @@ -27,6 +27,7 @@ #include "orionld/types/QNode.h" // QNode #include "orionld/common/orionldState.h" // orionldState #include "orionld/common/dateTime.h" // dateTimeFromString +#include "orionld/context/orionldContextItemExpand.h" // orionldContextItemExpand #include "orionld/q/qNode.h" // qNode #include "orionld/q/qNodeType.h" // qNodeType #include "orionld/q/qLexCheck.h" // qLexCheck @@ -44,7 +45,24 @@ static QNode* qStringPush(QNode* prev, char* stringValue) { QNode* qNodeP = qNode(QNodeStringValue); - LM_T(LmtQ, ("Pushing a String: '%s'", stringValue)); + LM_T(LmtQ, ("Pushing a String: '%s'", stringValue)); + if (orionldState.qVariable != NULL) + { + LM_T(LmtQ, ("For Variable: '%s'", orionldState.qVariable->value.s)); + if (orionldState.uriParams.expandValues != NULL) + { + LM_T(LmtQ, ("And, expandValues is: '%s'", orionldState.uriParams.expandValues)); + for (int ix = 0; ix < orionldState.in.expandValuesList.items; ix++) + { + LM_T(LmtQ, ("expandValuesList[%d]: '%s'", ix, orionldState.in.expandValuesList.array[ix])); + if (strcmp(orionldState.qVariable->value.s, orionldState.in.expandValuesList.array[ix]) == 0) + { + LM_T(LmtQ, ("The string '%s' needs to be expanded", stringValue)); + stringValue = orionldContextItemExpand(orionldState.contextP, stringValue, true, NULL); + } + } + } + } if (orionldState.useMalloc == false) qNodeP->value.s = stringValue; @@ -179,6 +197,12 @@ static QNode* qTermPush(QNode* prev, char* term, bool* lastTermIsTimestampP, cha QNode* qNodeP = qNode(type); + if (type == QNodeVariable) + { + orionldState.qVariable = qNodeP; + LM_T(LmtQ, ("'%s' IS a VARIABLE", term)); + } + if (dateTime == true) { LM_T(LmtQ, ("'%s' might be a DateTime", term)); diff --git a/src/lib/orionld/q/qParse.cpp b/src/lib/orionld/q/qParse.cpp index 72398231a3..1b08f892e9 100644 --- a/src/lib/orionld/q/qParse.cpp +++ b/src/lib/orionld/q/qParse.cpp @@ -34,6 +34,7 @@ extern "C" #include "orionld/common/orionldState.h" // orionldState #include "orionld/context/orionldAttributeExpand.h" // orionldAttributeExpand #include "orionld/context/orionldSubAttributeExpand.h" // orionldSubAttributeExpand +#include "orionld/context/orionldContextItemExpand.h" // orionldContextItemExpand #include "orionld/q/qVariableFix.h" // qVariableFix #include "orionld/q/qClone.h" // qClone #include "orionld/q/qNodeType.h" // qNodeType @@ -188,7 +189,7 @@ QNode* qParse(QNode* qLexList, QNode* endNodeP, bool forDb, bool qToDbModel, cha case QNodeTrueValue: case QNodeFalseValue: case QNodeRegexpValue: - if (compOpP == NULL) // Still no operatore - this is on the Left-Hand side - saved for later + if (compOpP == NULL) // Still no operator - this is on the Left-Hand side - saved for later { leftP = qLexP; } diff --git a/src/lib/orionld/service/orionldServiceInit.cpp b/src/lib/orionld/service/orionldServiceInit.cpp index a2040314d3..afa0292778 100644 --- a/src/lib/orionld/service/orionldServiceInit.cpp +++ b/src/lib/orionld/service/orionldServiceInit.cpp @@ -225,6 +225,7 @@ static void restServicePrepare(OrionLdRestService* serviceP, OrionLdRestServiceS serviceP->uriParams |= ORIONLD_URIPARAM_IDPATTERN; serviceP->uriParams |= ORIONLD_URIPARAM_ATTRS; serviceP->uriParams |= ORIONLD_URIPARAM_Q; + serviceP->uriParams |= ORIONLD_URIPARAM_EXPAND_VALUES; serviceP->uriParams |= ORIONLD_URIPARAM_CSF; serviceP->uriParams |= ORIONLD_URIPARAM_GEOREL; serviceP->uriParams |= ORIONLD_URIPARAM_GEOMETRY; diff --git a/src/lib/orionld/types/OrionLdRestService.h b/src/lib/orionld/types/OrionLdRestService.h index 3e1656961a..e63887cb27 100644 --- a/src/lib/orionld/types/OrionLdRestService.h +++ b/src/lib/orionld/types/OrionLdRestService.h @@ -155,6 +155,7 @@ typedef struct OrionLdRestServiceSimplifiedVector #define ORIONLD_URIPARAM_ONLYIDS (UINT64_C(1) << 38) #define ORIONLD_URIPARAM_ENTITYMAP (UINT64_C(1) << 39) #define ORIONLD_URIPARAM_FORMAT (UINT64_C(1) << 40) +#define ORIONLD_URIPARAM_EXPAND_VALUES (UINT64_C(1) << 41) diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_vocab-property.test b/test/functionalTest/cases/0000_ngsild/ngsild_vocab-property.test index 8529990a0e..cbb0070725 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_vocab-property.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_vocab-property.test @@ -42,6 +42,9 @@ accumulatorStart --pretty-print # 06. Dump/Reset accumulator, see urn:E1+V1 and urn:E2+V2 # # 07. GET urn:E2 in simplified format +# 08. Query entities with q=V1==abc => see nothing +# 09. Query entities with q=V1==abc&expandValues=V1,V2 => see urn:E1 +# 10. Query entities with q=V2==id&expandValues=V1,V2 => see urn:E2 # echo '01. Create a subscription on entity type T' @@ -134,6 +137,27 @@ echo echo +echo "08. Query entities with q=V1==abc => see nothing" +echo "================================================" +orionCurl --url /ngsi-ld/v1/entities?q=V1==%22abc%22 +echo +echo + + +echo "09. Query entities with q=V1==abc&expandValues=V1,V2 => see urn:E1" +echo "==================================================================" +orionCurl --url '/ngsi-ld/v1/entities?q=V1==%22abc%22&expandValues=V1,V2' +echo +echo + + +echo "10. Query entities with q=V2==id&expandValues=V1,V2 => see urn:E2" +echo "=================================================================" +orionCurl --url '/ngsi-ld/v1/entities?q=V2==%22id%22&expandValues=V1,V2' +echo +echo + + --REGEXPECT-- 01. Create a subscription on entity type T ========================================== @@ -343,6 +367,59 @@ Link: see nothing +================================================ +HTTP/1.1 200 OK +Content-Length: 2 +Content-Type: application/json +Date: REGEX(.*) + +[] + + +09. Query entities with q=V1==abc&expandValues=V1,V2 => see urn:E1 +================================================================== +HTTP/1.1 200 OK +Content-Length: 77 +Content-Type: application/json +Date: REGEX(.*) +Link: see urn:E2 +================================================================= +HTTP/1.1 200 OK +Content-Length: 86 +Content-Type: application/json +Date: REGEX(.*) +Link: