diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 39e15ed2ba..a738036a9b 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -15,6 +15,8 @@ Fixed issues: * #1458 - Supporting system timestamps (createdAt/modifiedAt) in q * #1451 - Bug fix - removed a trailing ampersand from the URI for the connection to mongodb * #1418 - Performance - Faster startup when there are GeoProperties in DB - * #1478 - Fixed issue + * #1478 - Fixed issue about PATCH on non-existent entity in federation giving a 204 instead of a 404 * #1479 - Fixed problem with "operations" in registrations + * #280 - Using Via header for loop detection (includes a new field 'hostAlias' in the registrations) * #280 - Fixed a bug in the lookup of pernot subscriptions + * #1496 - Fixed a crash - a double free() diff --git a/CMakeLists.txt b/CMakeLists.txt index 217c8c80f7..6467ee2697 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -236,6 +236,7 @@ SET (ORION_LIBS orionld_dbModel orionld_apiModel orionld_common + orionld_entityMaps orionld_types parse apiTypesV2 @@ -367,6 +368,7 @@ if (error EQUAL 0) ADD_SUBDIRECTORY(src/lib/jsonParse) ADD_SUBDIRECTORY(src/lib/jsonParseV2) ADD_SUBDIRECTORY(src/lib/rest) + ADD_SUBDIRECTORY(src/lib/orionld/entityMaps) ADD_SUBDIRECTORY(src/lib/orionld/pernot) ADD_SUBDIRECTORY(src/lib/orionld/socketService) ADD_SUBDIRECTORY(src/lib/orionld/notifications) diff --git a/scripts/style_check_in_makefile.sh b/scripts/style_check_in_makefile.sh index 48950c9c4d..4ee9339592 100755 --- a/scripts/style_check_in_makefile.sh +++ b/scripts/style_check_in_makefile.sh @@ -46,16 +46,16 @@ function style_check } +style_check src/lib/orionld/serviceRoutines +style_check src/lib/orionld/forwarding style_check src/lib/orionld/apiModel style_check src/lib/orionld/regCache -style_check src/lib/orionld/forwarding style_check src/lib/orionld/mongoc style_check src/lib/orionld/dbModel style_check src/lib/orionld/notifications style_check src/lib/orionld/common style_check src/lib/orionld/prometheus style_check src/lib/orionld/payloadCheck -style_check src/lib/orionld/serviceRoutines style_check src/lib/orionld/legacyDriver style_check src/lib/orionld/q style_check src/lib/orionld/rest diff --git a/src/app/orionld/orionld.cpp b/src/app/orionld/orionld.cpp index b8c80c408d..3f9d83e952 100644 --- a/src/app/orionld/orionld.cpp +++ b/src/app/orionld/orionld.cpp @@ -125,6 +125,7 @@ extern "C" #include "orionld/context/orionldContextFromUrl.h" // contextDownloadListInit, contextDownloadListRelease #include "orionld/contextCache/orionldContextCacheRelease.h" // orionldContextCacheRelease #include "orionld/rest/orionldServiceInit.h" // orionldServiceInit +#include "orionld/entityMaps/entityMapsRelease.h" // entityMapsRelease #include "orionld/db/dbInit.h" // dbInit #include "orionld/mqtt/mqttRelease.h" // mqttRelease #include "orionld/regCache/regCacheInit.h" // regCacheInit @@ -132,6 +133,7 @@ extern "C" #include "orionld/regCache/regCacheRelease.h" // regCacheRelease #include "orionld/pernot/pernotSubCacheInit.h" // pernotSubCacheInit #include "orionld/pernot/pernotLoop.h" // pernotLoopStart +#include "orionld/pernot/pernotRelease.h" // pernotRelease #include "orionld/version.h" #include "orionld/orionRestServices.h" @@ -147,9 +149,6 @@ extern "C" #include "orionld/troe/pgConnectionPoolsFree.h" // pgConnectionPoolsFree #include "orionld/troe/pgConnectionPoolsPresent.h" // pgConnectionPoolsPresent #include "orionld/forwarding/distOpInit.h" // distOpInit -#include "orionld/socketService/socketServiceInit.h" // socketServiceInit -#include "orionld/socketService/socketServiceRun.h" // socketServiceRun - #include "orionld/version.h" #include "orionld/orionRestServices.h" @@ -250,6 +249,8 @@ int troePoolSize; bool socketService; unsigned short socketServicePort; bool distributed; +char brokerId[136]; +char wip[512]; bool noNotifyFalseUpdate; bool idIndex; bool noswap; @@ -334,6 +335,8 @@ bool triggerOperation = false; #define SOCKET_SERVICE_DESC "enable the socket service - accept connections via a normal TCP socket" #define SOCKET_SERVICE_PORT_DESC "port to receive new socket service connections" #define DISTRIBUTED_DESC "turn on distributed operation" +#define BROKER_ID_DESC "identity of this broker instance for registrations - for the Via header" +#define WIP_DESC "Enable concepts that are 'Work In Progress' (e.g. -wip entityMaps)" #define FORWARDING_DESC "turn on distributed operation (deprecated)" #define ID_INDEX_DESC "automatic mongo index on _id.id" #define NOSWAP_DESC "no swapping - for testing only!!!" @@ -348,7 +351,7 @@ bool triggerOperation = false; #define DBURI_DESC "complete URI for database connection" #define DEBUG_CURL_DESC "turn on debugging of libcurl - to the broker's logfile" #define CSUBCOUNTERS_DESC "number of subscription counter updates before flush from sub-cache to DB (0: never, 1: always)" -#define CORE_CONTEXT_DESC "Core context version (v1.0|v1.3|v1.4|v1.5|v1.6|v1.7) - v1.6 is default" +#define CORE_CONTEXT_DESC "core context version (v1.0|v1.3|v1.4|v1.5|v1.6|v1.7) - v1.6 is default" @@ -435,12 +438,14 @@ PaArgument paArgs[] = { "-troeUser", troeUser, "TROE_USER", PaString, PaOpt, _i "postgres", PaNL, PaNL, TROE_HOST_USER }, { "-troePwd", troePwd, "TROE_PWD", PaString, PaOpt, _i "password", PaNL, PaNL, TROE_HOST_PWD }, { "-troePoolSize", &troePoolSize, "TROE_POOL_SIZE", PaInt, PaOpt, 10, 0, 1000, TROE_POOL_DESC }, - { "-distributed", &distributed, "DISTRIBUTED", PaBool, PaOpt, false, false, true, DISTRIBUTED_DESC }, { "-noNotifyFalseUpdate", &noNotifyFalseUpdate, "NO_NOTIFY_FALSE_UPDATE", PaBool, PaOpt, false, false, true, NO_NOTIFY_FALSE_UPDATE_DESC }, - { "-triggerOperation", &triggerOperation, "TRIGGER_OPERATION", PaBool, PaHid, false, false, true, TRIGGER_OPERATION_DESC }, { "-experimental", &experimental, "EXPERIMENTAL", PaBool, PaOpt, false, false, true, EXPERIMENTAL_DESC }, { "-mongocOnly", &mongocOnly, "MONGOCONLY", PaBool, PaOpt, false, false, true, MONGOCONLY_DESC }, { "-cSubCounters", &cSubCounters, "CSUB_COUNTERS", PaInt, PaOpt, 20, 0, PaNL, CSUBCOUNTERS_DESC }, + { "-distributed", &distributed, "DISTRIBUTED", PaBool, PaOpt, false, false, true, DISTRIBUTED_DESC }, + { "-brokerId", &brokerId, "BROKER_ID", PaStr, PaOpt, _i "", PaNL, PaNL, BROKER_ID_DESC }, + { "-wip", wip, "WIP", PaStr, PaHid, _i "", PaNL, PaNL, WIP_DESC }, + { "-triggerOperation", &triggerOperation, "TRIGGER_OPERATION", PaBool, PaHid, false, false, true, TRIGGER_OPERATION_DESC }, { "-forwarding", &distributed, "FORWARDING", PaBool, PaHid, false, false, true, FORWARDING_DESC }, { "-socketService", &socketService, "SOCKET_SERVICE", PaBool, PaHid, false, false, true, SOCKET_SERVICE_DESC }, { "-ssPort", &socketServicePort, "SOCKET_SERVICE_PORT", PaUShort, PaHid, 1027, PaNL, PaNL, SOCKET_SERVICE_PORT_DESC }, @@ -630,6 +635,14 @@ void exitFunc(void) pgConnectionPoolsFree(); } + // Cleanup entity maps + if (entityMaps != NULL) + entityMapsRelease(); + + // Cleanup periodic notifications + if (pernot == true) + pernotRelease(); + kaBufferReset(&kalloc, KFALSE); } @@ -926,7 +939,7 @@ static char* coreContextUrlSetup(const char* version) */ int main(int argC, char* argV[]) { -# if 0 +#if 0 // // Just an experiment. // It's an interesting way of "comparing strings" @@ -1039,7 +1052,7 @@ int main(int argC, char* argV[]) } // - // If trace levels are set, turn set logLevel to DEBUG, so that the trace messages will actually pass through + // If trace levels are set, set logLevel to DEBUG, so that the trace messages will actually pass through // if (paIsSet(argC, argV, paArgs, "-t")) strncpy(paLogLevel, "DEBUG", sizeof(paLogLevel) - 1); @@ -1052,6 +1065,18 @@ int main(int argC, char* argV[]) lmTimeFormat(0, (char*) "%Y-%m-%dT%H:%M:%S"); + if ((debugCurl == true) && ((lmTraceIsSet(LmtCurl) == false) || (strcmp(paLogLevel, "DEBUG") != 0))) + { + strncpy(paLogLevel, "DEBUG", sizeof(paLogLevel) - 1); + lmTraceLevelSet(LmtCurl, true); + } + + if (wip[0] != 0) + { + if (strcmp(wip, "entityMaps") == 0) + entityMapsEnabled = true; + } + #if 0 // // Uncomment this piece of code and run the functests (-ld) to make sure everything works "more or less" with the Legacy Driver disabled. @@ -1097,14 +1122,10 @@ int main(int argC, char* argV[]) paCleanup(); if (strlen(dbName) > DB_NAME_MAX_LEN) - { LM_X(1, ("dbName too long (max %d characters)", DB_NAME_MAX_LEN)); - } if (useOnlyIPv6 && useOnlyIPv4) - { LM_X(1, ("Fatal Error (-ipv4 and -ipv6 can not be activated at the same time. They are incompatible)")); - } if (https) { @@ -1160,7 +1181,6 @@ int main(int argC, char* argV[]) contextDownloadListInit(); - // // Initialize the KBASE library // This call redirects all log messages from the K-libs to the brokers log file. @@ -1191,6 +1211,12 @@ int main(int argC, char* argV[]) // localIpAndPort - IP:port for X-Forwarded-For snprintf(localIpAndPort, sizeof(localIpAndPort), "%s:%d", orionldHostName, port); + // brokerId - for the Via header + if (brokerId[0] == 0) + strncpy(brokerId, localIpAndPort, sizeof(brokerId) - 1); + else + distributed = true; // Turn on forwarding if the brokerId CLI is used + orionldStateInit(NULL); // mongocInit calls mongocGeoIndexInit - tenant0 must be ready for that diff --git a/src/app/orionld/orionldRestServices.cpp b/src/app/orionld/orionldRestServices.cpp index 2c518b0e41..f153138ffe 100644 --- a/src/app/orionld/orionldRestServices.cpp +++ b/src/app/orionld/orionldRestServices.cpp @@ -65,6 +65,8 @@ #include "orionld/serviceRoutines/orionldOptions.h" #include "orionld/serviceRoutines/orionldPutEntity.h" #include "orionld/serviceRoutines/orionldPutAttribute.h" +#include "orionld/serviceRoutines/orionldGetEntityMap.h" +#include "orionld/serviceRoutines/orionldDeleteEntityMap.h" #include "orionld/serviceRoutines/orionldGetTemporalEntities.h" #include "orionld/serviceRoutines/orionldGetTemporalEntity.h" @@ -90,6 +92,7 @@ static OrionLdRestServiceSimplified getServiceV[] = { "/ngsi-ld/ex/v1/ping", orionldGetPing }, { "/ngsi-ld/v1/entities/*", orionldGetEntity }, { "/ngsi-ld/v1/entities", orionldGetEntities }, + { "/ngsi-ld/v1/entityMaps/*", orionldGetEntityMap }, { "/ngsi-ld/v1/types/*", orionldGetEntityType }, { "/ngsi-ld/v1/types", orionldGetEntityTypes }, { "/ngsi-ld/v1/attributes/*", orionldGetEntityAttribute }, @@ -173,6 +176,7 @@ static OrionLdRestServiceSimplified deleteServiceV[] = { { "/ngsi-ld/v1/entities/*/attrs/*", orionldDeleteAttribute }, { "/ngsi-ld/v1/entities/*", orionldDeleteEntity }, + { "/ngsi-ld/v1/entityMaps/*", orionldDeleteEntityMap }, { "/ngsi-ld/v1/subscriptions/*", orionldDeleteSubscription }, { "/ngsi-ld/v1/csourceRegistrations/*", orionldDeleteRegistration }, { "/ngsi-ld/v1/jsonldContexts/*", orionldDeleteContext }, diff --git a/src/lib/logMsg/traceLevels.h b/src/lib/logMsg/traceLevels.h index f8abd8ce76..30aeee3ed8 100644 --- a/src/lib/logMsg/traceLevels.h +++ b/src/lib/logMsg/traceLevels.h @@ -72,54 +72,76 @@ typedef enum TraceLevels LmtRegCache = 60, // Registration Cache // - // Distributed Operations + // Distributed Operations - requests // - LmtDistOpMsgs = 70, // Distributed Operations: messages - LmtDistOpRequest, // ONLY the verb, path, and body of a distributed request - LmtDistOpResponse, // ONLY the body and status code of the response to a distributed request - LmtDistOp207, // Merging of the final 207 response + LmtDistOpRequest = 70, // ONLY the verb, path, and body of a distributed request + LmtDistOpRequestHeaders, // HTTP headers of distributed requests + LmtDistOpRequestParams, // URL parameters of distributed requests + + // + // Distributed Operations - responses + // + LmtDistOpResponse = 80, // ONLY the body and status code of the response to a distributed request LmtDistOpResponseBuf, // Specific debugging of the incoming response of a distributed message LmtDistOpResponseDetail, // Details on responses to distributed requests LmtDistOpResponseHeaders, // HTTP headers of responses to distributed requests - LmtDistOpRequestHeaders, // HTTP headers of request of distributed requests - LmtDistOpList, // Linked list of DistOps + + // + // Distributed Operations - misc + // + LmtDistOpList = 90, // Linked list of DistOps + LmtDistOpAttributes, // The union of attributes URL-Param / Registered Attributes + LmtDistOpMerge, // Merge of responses from forwsrded requests (GET /entities) + LmtDistOpLoop, // Loop detection in forwarded messages + LmtDistOp207, // Merging of the final 207 response // // Context // - LmtContexts = 80, // Contexts + LmtContexts = 100, // Contexts LmtContextTree, // Context Tree LmtContextCache, // Context Cache LmtContextDownload, // Context Download LmtCoreContext, // Core Context // GeoJSON - LmtGeoJSON = 90, // GeoJSON ... everything (for now) + LmtGeoJSON = 110, // GeoJSON ... everything (for now) // // Pernot sub-cache // - LmtPernot = 100, // Periodic Notification Subscription cache + LmtPernot = 120, // Periodic Notification Subscription cache LmtPernotLoop, // Pernot loop, when each sub is triggered in time LmtPernotLoopTimes, // Pernot loop, details on timestamps LmtPernotFlush, // Pernot flush to DB LmtPernotQuery, // Pernot query + // + // Pagination + // + LmtEntityMap = 130, // The arrays of registrations per entity - distributed GET /entities + LmtEntityMapRetrieve, // Retrieval of an entity map + LmtEntityMapDetail, // Details of the entity-registration maps + // // Misc // - LmtMongoc = 230, // Entire mongoc library - LmtSR, // Service Routine (whatever it is doing) + LmtMongoc = 200, // Entire mongoc library + LmtSR, // Service Routine (whatever it is it's doing) + LmtCount, // NGSILD-Results-Count header, details for distops LmtSemaphore, // Semaphores LmtKjlParse, // Trace level start for K libs LmtLegacy, // Old code (mongoBackend, json parsers, etc) LmtLegacySubMatch, // Old code - update/subscription match for subs/notifs LmtLegacySubCacheRefresh, // Old code - sub-cache-refresh LmtMqtt, // MQTT notifications - LmtQ, // Query language + LmtQ, // Query Language + LmtPostgres, // Postgres (TRoE) LmtSql, // SQL command for TRoE LmtPgPool, // Postgres Connection Pool + LmtTenants, // Well, tenants :) LmtSocketService, // Socket Service + LmtRegex, // Regular expressions - all of them LmtCurl = 250, // CURL library LmtToDo, // To Do list diff --git a/src/lib/ngsiNotify/Notifier.cpp b/src/lib/ngsiNotify/Notifier.cpp index 50b615c89e..e4f65a4775 100644 --- a/src/lib/ngsiNotify/Notifier.cpp +++ b/src/lib/ngsiNotify/Notifier.cpp @@ -95,7 +95,7 @@ void Notifier::sendNotifyContextRequest bool blackList ) { - pthread_t tid; + pthread_t tid; std::vector* paramsV = Notifier::buildSenderParams(ncrP, httpInfo, diff --git a/src/lib/ngsiNotify/QueueWorkers.cpp b/src/lib/ngsiNotify/QueueWorkers.cpp index 66463e0421..e00400db22 100644 --- a/src/lib/ngsiNotify/QueueWorkers.cpp +++ b/src/lib/ngsiNotify/QueueWorkers.cpp @@ -157,7 +157,7 @@ static void* workerFunc(void* pSyncQ) if (ngsildSubscription == false) subscriptionId = NULL; - + LM_T(LmtNotificationMsg, ("Sending HTTP Notification for subscription '%s'", params->subscriptionId.c_str())); r = httpRequestSendWithCurl(curl, params->ip, diff --git a/src/lib/orionld/common/orionldState.cpp b/src/lib/orionld/common/orionldState.cpp index 1db09926bf..43b06a2f0b 100644 --- a/src/lib/orionld/common/orionldState.cpp +++ b/src/lib/orionld/common/orionldState.cpp @@ -105,9 +105,12 @@ char pgPortString[16]; char mongoServerVersion[32]; char userAgentHeaderNoLF[64]; // "User-Agent: orionld/" + ORIONLD_VERSION - initialized in orionldServiceInit() char hostHeaderNoLF[128]; -char hostHeader[128]; // Host: xxx +char hostHeader[256]; // Host: xxx size_t hostHeaderLen; PernotSubCache pernotSubCache; +EntityMap* entityMaps = NULL; // Used by GET /entities in the distributed case, for pagination +bool entityMapsEnabled = false; + // diff --git a/src/lib/orionld/common/orionldState.h b/src/lib/orionld/common/orionldState.h index 38be351f1c..511b0b2ee9 100644 --- a/src/lib/orionld/common/orionldState.h +++ b/src/lib/orionld/common/orionldState.h @@ -60,6 +60,8 @@ extern "C" #include "orionld/types/OrionldHeader.h" // OrionldHeaderSet #include "orionld/types/OrionldAlteration.h" // OrionldAlteration #include "orionld/types/StringArray.h" // StringArray +#include "orionld/types/EntityMap.h" // EntityMap +#include "orionld/forwarding/DistOp.h" // DistOp #include "orionld/troe/troe.h" // TroeMode #include "orionld/pernot/PernotSubCache.h" // PernotSubCache #include "orionld/context/OrionldContext.h" // OrionldContext @@ -131,6 +133,7 @@ typedef struct OrionldUriParams int limit; bool count; char* q; + char* qCopy; char* mq; char* geometry; char* coordinates; @@ -165,6 +168,8 @@ typedef struct OrionldUriParams char* observedAt; char* lang; bool local; + bool onlyIds; + bool entityMap; double observedAtAsDouble; uint64_t mask; @@ -235,6 +240,7 @@ typedef struct OrionldStateIn char* host; char* xRealIp; char* xForwardedFor; + char* via; char* connection; char* servicePath; char* xAuthToken; @@ -242,6 +248,8 @@ typedef struct OrionldStateIn char* tenant; char* legacy; // Use legacy mongodb driver / mongoBackend bool performance; + bool aerOS; // Special treatment for aerOS specific features + char* wip; // Incoming payload char* payload; @@ -259,6 +267,9 @@ typedef struct OrionldStateIn StringArray typeList; StringArray attrList; + // Entity Map + EntityMap* entityMap; + // Processed wildcards char* pathAttrExpanded; } OrionldStateIn; @@ -426,6 +437,8 @@ typedef struct OrionldConnectionState // General Behavior // bool distOpAttrsCompacted; + int distOpNo; + DistOp* distOpList; uint32_t acceptMask; // "1 << MimeType" mask for all accepted Mime Types, regardless of which is chosen and of weight // @@ -589,6 +602,7 @@ extern char troePwd[256]; // From orionld.cpp extern int troePoolSize; // From orionld.cpp extern char pgPortString[16]; extern bool distributed; // From orionld.cpp +extern char brokerId[136]; // From orionld.cpp extern const char* orionldVersion; extern OrionldGeoIndex* geoIndexList; extern OrionldPhase orionldPhase; @@ -608,7 +622,10 @@ extern bool debugCurl; // From orionld.cpp extern bool noCache; // From orionld.cpp extern uint32_t cSubCounters; // Number of subscription counter updates before flush from sub-cache to DB extern PernotSubCache pernotSubCache; -extern char localIpAndPort[135]; // Local address for X-Forwarded-For (from orionld.cpp) +extern EntityMap* entityMaps; // Used by GET /entities in the distributed case, for pagination +extern bool entityMapsEnabled; + +extern char localIpAndPort[135]; // Local address for X-Forwarded-For (from orionld.cpp) extern unsigned long long inReqPayloadMaxSize; extern unsigned long long outReqMsgMaxSize; diff --git a/src/lib/orionld/common/uuidGenerate.cpp b/src/lib/orionld/common/uuidGenerate.cpp index bf37eae1cf..f0126bbc05 100644 --- a/src/lib/orionld/common/uuidGenerate.cpp +++ b/src/lib/orionld/common/uuidGenerate.cpp @@ -35,22 +35,27 @@ // // uuidGenerate - // -void uuidGenerate(char* buf, int bufSize, bool uri) +char* uuidGenerate(char* buf, int bufSize, const char* prefix) { uuid_t uuid; int bufIx = 0; - int minBufSize = 37 + (uri == true)? 33 : 0; + int minBufSize = 37; + + if (prefix != NULL) + minBufSize += strlen(prefix); if (bufSize < minBufSize) LM_X(1, ("Implementation Error (not enough room to generate a UUID (%d bytes needed, %d supplied)", minBufSize, bufSize)); uuid_generate_time_safe(uuid); - if (uri == true) + if (prefix != NULL) { - strncpy(buf, "urn:ngsi-ld:attribute:instance:", bufSize); - bufIx = 31; + strncpy(buf, prefix, bufSize); + bufIx = strlen(prefix); } uuid_unparse_lower(uuid, &buf[bufIx]); + + return &buf[bufIx]; } diff --git a/src/lib/orionld/common/uuidGenerate.h b/src/lib/orionld/common/uuidGenerate.h index 5405f7f172..cefdd98feb 100644 --- a/src/lib/orionld/common/uuidGenerate.h +++ b/src/lib/orionld/common/uuidGenerate.h @@ -32,6 +32,6 @@ // // uuidGenerate - // -extern void uuidGenerate(char* buf, int bufSize, bool uri); +extern char* uuidGenerate(char* buf, int bufSize, const char* prefix); #endif // SRC_LIB_ORIONLD_COMMON_UUIDGENERATE_H_ diff --git a/src/lib/orionld/context/orionldContextFromUrl.cpp b/src/lib/orionld/context/orionldContextFromUrl.cpp index aacf978f01..773974195c 100644 --- a/src/lib/orionld/context/orionldContextFromUrl.cpp +++ b/src/lib/orionld/context/orionldContextFromUrl.cpp @@ -245,7 +245,7 @@ static OrionldContext* contextCacheWait(char* url) // OrionldContext* orionldContextFromUrl(char* url, char* id) { - LM_T(LmtContextDownload, ("Downloading a context URL: '%s'", url)); + LM_T(LmtContextDownload, ("Possibly downloading a context URL: '%s'", url)); OrionldContext* contextP = orionldContextCacheLookup(url); diff --git a/src/lib/orionld/context/orionldContextUrlGenerate.cpp b/src/lib/orionld/context/orionldContextUrlGenerate.cpp index 82a20c7c90..f2a63f8f8a 100644 --- a/src/lib/orionld/context/orionldContextUrlGenerate.cpp +++ b/src/lib/orionld/context/orionldContextUrlGenerate.cpp @@ -27,6 +27,7 @@ extern "C" #include "kalloc/kaAlloc.h" // kaAlloc } +#include "logMsg/logMsg.h" // LM* #include "orionld/common/uuidGenerate.h" // uuidGenerate #include "orionld/common/orionldState.h" // orionldHostName, orionldHostNameLen #include "orionld/context/orionldContextUrlGenerate.h" // Own interface @@ -38,22 +39,20 @@ extern "C" // orionldContextUrlGenerate - // // The size used in the call to kaAlloc: -// - strlen("http://HOSTNAME:PORT"): 12 +// - strlen("http://HOSTNAME:PORT"): 13 (port is max 5 chars - 65535) + orionldHostNameLen // - strlen("/ngsi-ld/v1/jsonldContexts/"): 27 // - uuidGenerate: 37 // - zero termination: 1 -// - orionldHostNameLen // -// => 77 + orionldHostNameLen +// => 78 + orionldHostNameLen // char* orionldContextUrlGenerate(char** contextIdP) { - char* url = (char*) kaAlloc(&kalloc, 77 + orionldHostNameLen); + int urlSize = 78 + orionldHostNameLen; + char* url = (char*) kaAlloc(&kalloc, urlSize); + int prefixLen = snprintf(url, urlSize, "http://%s:%d/ngsi-ld/v1/jsonldContexts/", orionldHostName, portNo); - snprintf(url, 77 + orionldHostNameLen, "http://%s:%d/ngsi-ld/v1/jsonldContexts/", orionldHostName, portNo); - uuidGenerate(&url[39 + orionldHostNameLen], 100, false); - - *contextIdP = &url[39 + orionldHostNameLen]; + *contextIdP = uuidGenerate(&url[prefixLen], urlSize - prefixLen, NULL); return url; } diff --git a/src/lib/orionld/db/dbEntityAttributesGet.cpp b/src/lib/orionld/db/dbEntityAttributesGet.cpp index 5306c5c794..94eb18fe4d 100644 --- a/src/lib/orionld/db/dbEntityAttributesGet.cpp +++ b/src/lib/orionld/db/dbEntityAttributesGet.cpp @@ -61,8 +61,7 @@ static KjNode* getEntityAttributesResponse(KjNode* sortedArrayP) { char entityAttributesId[128]; - strncpy(entityAttributesId, "urn:ngsi-ld:AttributeList:", sizeof(entityAttributesId) - 1); - uuidGenerate(&entityAttributesId[26], sizeof(entityAttributesId) - 26, false); + uuidGenerate(entityAttributesId, sizeof(entityAttributesId), "urn:ngsi-ld:AttributeList:"); KjNode* attributeNodeResponseP = kjObject(orionldState.kjsonP, NULL); KjNode* idNodeP = kjString(orionldState.kjsonP, "id", entityAttributesId); diff --git a/src/lib/orionld/db/dbEntityTypesGet.cpp b/src/lib/orionld/db/dbEntityTypesGet.cpp index bdd3503c5d..6d91520301 100644 --- a/src/lib/orionld/db/dbEntityTypesGet.cpp +++ b/src/lib/orionld/db/dbEntityTypesGet.cpp @@ -190,8 +190,7 @@ static KjNode* getEntityTypesResponse(KjNode* sortedArrayP) { char entityTypesId[64]; - strncpy(entityTypesId, "urn:ngsi-ld:EntityTypeList:", sizeof(entityTypesId) - 1); - uuidGenerate(&entityTypesId[27], sizeof(entityTypesId) - 27, false); + uuidGenerate(entityTypesId, sizeof(entityTypesId), "urn:ngsi-ld:EntityTypeList:"); KjNode* typeNodeResponseP = kjObject(orionldState.kjsonP, NULL); KjNode* idNodeP = kjString(orionldState.kjsonP, "id", entityTypesId); @@ -482,10 +481,8 @@ KjNode* dbEntityTypesGet(OrionldProblemDetails* pdP, bool details) if ((remote == NULL) && (local == NULL)) { - char entityTypesId[64]; - - strncpy(entityTypesId, "urn:ngsi-ld:EntityTypeList:", sizeof(entityTypesId) - 1); - uuidGenerate(&entityTypesId[27], sizeof(entityTypesId) - 27, false); + char entityTypesId[64]; + uuidGenerate(entityTypesId, sizeof(entityTypesId), "urn:ngsi-ld:EntityTypeList:"); KjNode* noTypesObject = kjObject(orionldState.kjsonP, NULL); KjNode* idP = kjString(orionldState.kjsonP, "id", entityTypesId); diff --git a/src/lib/orionld/dbModel/CMakeLists.txt b/src/lib/orionld/dbModel/CMakeLists.txt index e1ba3332e4..f48ccbf962 100644 --- a/src/lib/orionld/dbModel/CMakeLists.txt +++ b/src/lib/orionld/dbModel/CMakeLists.txt @@ -48,6 +48,7 @@ SET (SOURCES dbModelAttributeCreatedAtLookup.cpp dbModelAttributeCreatedAtSet.cpp dbModelAttributeLookup.cpp + dbModelToEntityIdAndTypeObject.cpp ) # Include directories diff --git a/src/lib/orionld/dbModel/dbModelToApiAttribute.cpp b/src/lib/orionld/dbModel/dbModelToApiAttribute.cpp index 33f6658328..84ff1ab6d3 100644 --- a/src/lib/orionld/dbModel/dbModelToApiAttribute.cpp +++ b/src/lib/orionld/dbModel/dbModelToApiAttribute.cpp @@ -231,7 +231,7 @@ void dbModelToApiLangPropertySimplified(KjNode* dbAttrP, const char* lang) // // dbModelToApiAttribute2 - // -KjNode* dbModelToApiAttribute2(KjNode* dbAttrP, KjNode* datasetP, bool sysAttrs, RenderFormat renderFormat, char* lang, bool compacted, OrionldProblemDetails* pdP) +KjNode* dbModelToApiAttribute2(KjNode* dbAttrP, KjNode* datasetP, bool sysAttrs, RenderFormat renderFormat, const char* lang, bool compacted, OrionldProblemDetails* pdP) { if ((renderFormat == RF_CROSS_APIS_NORMALIZED) || (renderFormat == RF_CROSS_APIS_KEYVALUES)) compacted = false; diff --git a/src/lib/orionld/dbModel/dbModelToApiAttribute.h b/src/lib/orionld/dbModel/dbModelToApiAttribute.h index 9cd4d2bb9a..e93fad30a2 100644 --- a/src/lib/orionld/dbModel/dbModelToApiAttribute.h +++ b/src/lib/orionld/dbModel/dbModelToApiAttribute.h @@ -47,6 +47,6 @@ extern void dbModelToApiAttribute(KjNode* attrP, bool sysAttrs, bool eqsForDots) // // dbModelToApiAttribute2 - // -extern KjNode* dbModelToApiAttribute2(KjNode* dbAttrP, KjNode* datasetP, bool sysAttrs, RenderFormat renderFormat, char* lang, bool compacted, OrionldProblemDetails* pdP); +extern KjNode* dbModelToApiAttribute2(KjNode* dbAttrP, KjNode* datasetP, bool sysAttrs, RenderFormat renderFormat, const char* lang, bool compacted, OrionldProblemDetails* pdP); #endif // SRC_LIB_ORIONLD_DBMODEL_DBMODELTOAPIATTRIBUTE_H_ diff --git a/src/lib/orionld/dbModel/dbModelToApiEntity.cpp b/src/lib/orionld/dbModel/dbModelToApiEntity.cpp index 166347a077..38b14f9a1b 100644 --- a/src/lib/orionld/dbModel/dbModelToApiEntity.cpp +++ b/src/lib/orionld/dbModel/dbModelToApiEntity.cpp @@ -166,7 +166,7 @@ KjNode* datasetExtract(KjNode* datasetsP, const char* attrName) // Add "concise/keyValues" and "lang" treatment to "dbModelToApiEntity2()" // Remove "concise/keyValues" and "lang" treatment from "kjTreeFromQueryContextResponse()" // -KjNode* dbModelToApiEntity2(KjNode* dbEntityP, bool sysAttrs, RenderFormat renderFormat, char* lang, bool compacted, OrionldProblemDetails* pdP) +KjNode* dbModelToApiEntity2(KjNode* dbEntityP, bool sysAttrs, RenderFormat renderFormat, const char* lang, bool compacted, OrionldProblemDetails* pdP) { KjNode* _idP = NULL; KjNode* attrsP = NULL; diff --git a/src/lib/orionld/dbModel/dbModelToApiEntity.h b/src/lib/orionld/dbModel/dbModelToApiEntity.h index a7cd3e07bf..05bd6e7f15 100644 --- a/src/lib/orionld/dbModel/dbModelToApiEntity.h +++ b/src/lib/orionld/dbModel/dbModelToApiEntity.h @@ -61,6 +61,6 @@ extern KjNode* dbModelToApiEntity(KjNode* attrP, bool sysAttrs, char* entityId); // - orionldGetEntities (GET /entities) // - orionldPostQuery (POST /entityOperations/query) // -extern KjNode* dbModelToApiEntity2(KjNode* dbEntityP, bool sysAttrs, RenderFormat renderFormat, char* lang, bool compacted, OrionldProblemDetails* pdP); +extern KjNode* dbModelToApiEntity2(KjNode* dbEntityP, bool sysAttrs, RenderFormat renderFormat, const char* lang, bool compacted, OrionldProblemDetails* pdP); #endif // SRC_LIB_ORIONLD_DBMODEL_DBMODELTOAPIENTITY_H_ diff --git a/src/lib/orionld/dbModel/dbModelToApiRegistration.cpp b/src/lib/orionld/dbModel/dbModelToApiRegistration.cpp index 8cdd047b42..85bf41ec9b 100644 --- a/src/lib/orionld/dbModel/dbModelToApiRegistration.cpp +++ b/src/lib/orionld/dbModel/dbModelToApiRegistration.cpp @@ -253,8 +253,11 @@ bool dbModelToApiRegistration(KjNode* dbRegP, bool sysAttrs, bool forCache) // Remove "properties" from dbRegP and link the contexts of "properties" to "dbRegP" kjChildRemove(dbRegP, propertiesP); - dbRegP->lastChild->next = propertiesP->value.firstChildP; - dbRegP->lastChild = propertiesP->lastChild; + if (propertiesP->value.firstChildP != NULL) + { + dbRegP->lastChild->next = propertiesP->value.firstChildP; + dbRegP->lastChild = propertiesP->lastChild; + } } } diff --git a/src/lib/orionld/dbModel/dbModelToApiSubAttribute.cpp b/src/lib/orionld/dbModel/dbModelToApiSubAttribute.cpp index e45c204b94..8c5c1d2cbd 100644 --- a/src/lib/orionld/dbModel/dbModelToApiSubAttribute.cpp +++ b/src/lib/orionld/dbModel/dbModelToApiSubAttribute.cpp @@ -94,7 +94,7 @@ void dbModelToApiSubAttribute(KjNode* dbSubAttrP) // // dbModelToApiSubAttribute2 - transform a sub-attribute from DB Model to API format // -KjNode* dbModelToApiSubAttribute2(KjNode* dbSubAttributeP, bool sysAttrs, RenderFormat renderFormat, char* lang, OrionldProblemDetails* pdP) +KjNode* dbModelToApiSubAttribute2(KjNode* dbSubAttributeP, bool sysAttrs, RenderFormat renderFormat, const char* lang, OrionldProblemDetails* pdP) { if (strcmp(dbSubAttributeP->name, "observedAt") == 0) return dbModelToObservedAt(dbSubAttributeP); diff --git a/src/lib/orionld/dbModel/dbModelToApiSubAttribute.h b/src/lib/orionld/dbModel/dbModelToApiSubAttribute.h index 216135941d..53e3fd3af2 100644 --- a/src/lib/orionld/dbModel/dbModelToApiSubAttribute.h +++ b/src/lib/orionld/dbModel/dbModelToApiSubAttribute.h @@ -46,6 +46,6 @@ extern void dbModelToApiSubAttribute(KjNode* saP); // // dbModelToApiSubAttribute2 - transform a sub-attribute from DB Model to API format // -KjNode* dbModelToApiSubAttribute2(KjNode* dbSubAttributeP, bool sysAttrs, RenderFormat renderFormat, char* lang, OrionldProblemDetails* pdP); +KjNode* dbModelToApiSubAttribute2(KjNode* dbSubAttributeP, bool sysAttrs, RenderFormat renderFormat, const char* lang, OrionldProblemDetails* pdP); #endif // SRC_LIB_ORIONLD_DBMODEL_DBMODELTOAPISUBATTRIBUTE_H_ diff --git a/src/lib/orionld/dbModel/dbModelToEntityIdAndTypeObject.cpp b/src/lib/orionld/dbModel/dbModelToEntityIdAndTypeObject.cpp new file mode 100644 index 0000000000..797474f707 --- /dev/null +++ b/src/lib/orionld/dbModel/dbModelToEntityIdAndTypeObject.cpp @@ -0,0 +1,67 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjLookup.h" // kjLookup +#include "kjson/kjBuilder.h" // kjObject +} + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/context/orionldContextItemAliasLookup.h" // orionldContextItemAliasLookup +#include "orionld/dbModel/dbModelToEntityIdAndTypeObject.h" // Own interface + + + +// ---------------------------------------------------------------------------- +// +// dbModelToEntityIdAndTypeObject - FIXME: rename to dbModelToEntityMap +// +// INPUT: [ { "_id": { "id": "urn:E1", "type": "urn:...:T1" } }, { "_id": { "id": "urn:E2", "type": "urn:...:T2" } }, ... ] +// OUTPUT: ["urn:E1", "urn:E2", ...] +// +KjNode* dbModelToEntityIdAndTypeObject(KjNode* localDbMatches) +{ + KjNode* matchIds = kjArray(orionldState.kjsonP, NULL); + + for (KjNode* dbEntityP = localDbMatches->value.firstChildP; dbEntityP != NULL; dbEntityP = dbEntityP->next) + { + KjNode* _idP = kjLookup(dbEntityP, "_id"); + + if (_idP == NULL) + continue; // DB Error !!! + + KjNode* idP = kjLookup(_idP, "id"); + + if (idP == NULL) + continue; // DB Error !!! + + KjNode* idNodeP = kjString(orionldState.kjsonP, NULL, idP->value.s); + + kjChildAdd(matchIds, idNodeP); + } + + return matchIds; +} diff --git a/src/lib/orionld/dbModel/dbModelToEntityIdAndTypeObject.h b/src/lib/orionld/dbModel/dbModelToEntityIdAndTypeObject.h new file mode 100644 index 0000000000..d97c972031 --- /dev/null +++ b/src/lib/orionld/dbModel/dbModelToEntityIdAndTypeObject.h @@ -0,0 +1,44 @@ +#ifndef SRC_LIB_ORIONLD_DBMODEL_DBMODELTOENTITYIDANDTYPEOBJECT_H_ +#define SRC_LIB_ORIONLD_DBMODEL_DBMODELTOENTITYIDANDTYPEOBJECT_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +} + + + +// ---------------------------------------------------------------------------- +// +// dbModelToEntityIdAndTypeObject - +// +// INPUT: [ { "_id": { "id": "urn:E1", "type": "urn:...:T1" } }, { "_id": { "id": "urn:E2", "type": "urn:...:T2" } }, ... ] +// OUTPUT: { "urn:E1": "urn:...:T1", "urn:E2", "urn:...:T2" } +// +extern KjNode* dbModelToEntityIdAndTypeObject(KjNode* localDbMatches); + +#endif // SRC_LIB_ORIONLD_DBMODEL_DBMODELTOENTITYIDANDTYPEOBJECT_H_ diff --git a/src/lib/orionld/entityMaps/CMakeLists.txt b/src/lib/orionld/entityMaps/CMakeLists.txt new file mode 100644 index 0000000000..fbf50f6a7d --- /dev/null +++ b/src/lib/orionld/entityMaps/CMakeLists.txt @@ -0,0 +1,39 @@ +# Copyright 2023 FIWARE Foundation e.V. +# +# This file is part of Orion-LD Context Broker. +# +# Orion-LD Context Broker is free software: you can redistribute it and/or +# modify it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Orion-LD Context Broker is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +# General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +# +# For those usages not covered by this license please contact with +# orionld at fiware dot org + +CMAKE_MINIMUM_REQUIRED(VERSION 3.5) + +SET (SOURCES + entityMapCreate.cpp + entityMapRemove.cpp + entityMapLookup.cpp + entityMapItemAdd.cpp + entityMapRelease.cpp + entityMapsRelease.cpp +) + +# Include directories +# ----------------------------------------------------------------- +include_directories("${PROJECT_SOURCE_DIR}/src/lib") + + +# Library declaration +# ----------------------------------------------------------------- +ADD_LIBRARY(orionld_entityMaps STATIC ${SOURCES}) diff --git a/src/lib/orionld/entityMaps/entityMapCreate.cpp b/src/lib/orionld/entityMaps/entityMapCreate.cpp new file mode 100644 index 0000000000..3e99c52e43 --- /dev/null +++ b/src/lib/orionld/entityMaps/entityMapCreate.cpp @@ -0,0 +1,266 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // malloc + +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjParse.h" // kjParse +#include "kjson/kjBuilder.h" // kjObject +#include "kjson/kjLookup.h" // kjLookup +#include "kjson/kjRender.h" // kjFastRender (for debugging purposes - LM_T) +} + +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/common/uuidGenerate.h" // uuidGenerate +#include "orionld/types/EntityMap.h" // EntityMap +#include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo +#include "orionld/q/QNode.h" // QNode +#include "orionld/kjTree/kjChildCount.h" // kjChildCount +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/forwarding/distOpLookupByCurlHandle.h" // distOpLookupByCurlHandle +#include "orionld/forwarding/distOpListDebug.h" // distOpListDebug2 +#include "orionld/forwarding/distOpsSend.h" // distOpsSend +#include "orionld/mongoc/mongocEntitiesQuery.h" // mongocEntitiesQuery +#include "orionld/dbModel/dbModelToEntityIdAndTypeObject.h" // dbModelToEntityIdAndTypeObject +#include "orionld/kjTree/kjSort.h" // kjStringArraySort +#include "orionld/entityMaps/entityMapItemAdd.h" // entityMapItemAdd +#include "orionld/entityMaps/entityMapCreate.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// DistOpResponseTreatFunction - +// +typedef int (*DistOpResponseTreatFunction)(DistOp* distOpP, void* callbackParam); + + + +// ----------------------------------------------------------------------------- +// +// distOpsReceive - FIXME: move to orionld/forwarding/distOpsReceive.cpp/h +// +void distOpsReceive(DistOp* distOpList, DistOpResponseTreatFunction treatFunction, void* callbackParam, int requestsSent) +{ + LM_T(LmtCount, ("Receiving %d responses", requestsSent)); + // + // Read the responses to the forwarded requests + // + CURLMsg* msgP; + int msgsLeft; + int responses = 0; + + while ((msgP = curl_multi_info_read(orionldState.curlDoMultiP, &msgsLeft)) != NULL) + { + if (msgP->msg != CURLMSG_DONE) + continue; + + if (msgP->data.result == CURLE_OK) + { + DistOp* distOpP = distOpLookupByCurlHandle(distOpList, msgP->easy_handle); + + if (distOpP == NULL) + { + LM_E(("Unable to find the curl handle of a message, presumably a response to a forwarded request")); + continue; + } + + curl_easy_getinfo(msgP->easy_handle, CURLINFO_RESPONSE_CODE, &distOpP->httpResponseCode); + + LM_T(LmtDistOpResponse, ("%s: received a %d response for a forwarded request; %s", distOpP->regP->regId, distOpP->httpResponseCode, distOpP->rawResponse)); + + if ((distOpP->rawResponse != NULL) && (distOpP->rawResponse[0] != 0)) + distOpP->responseBody = kjParse(orionldState.kjsonP, distOpP->rawResponse); + + treatFunction(distOpP, callbackParam); + ++responses; + } + } + + LM_W(("********************** Expected %d responses, got %d", requestsSent, responses)); +} + + + +// ----------------------------------------------------------------------------- +// +// idListResponse - callback function for distOpMatchIdsGet +// +static int idListResponse(DistOp* distOpP, void* callbackParam) +{ + EntityMap* entityMap = (EntityMap*) callbackParam; + + if ((distOpP->httpResponseCode == 200) && (distOpP->responseBody != NULL)) + { + kjTreeLog(distOpP->responseBody, "DistOp RESPONSE", LmtCount); + for (KjNode* eIdNodeP = distOpP->responseBody->value.firstChildP; eIdNodeP != NULL; eIdNodeP = eIdNodeP->next) + { + // FIXME: The response is supposed to be an array of entity ids + // However, when 3 inter-registered brokers run, I get the second response instead of + // the first - the first response seems to go missing somewhere... + // This is an UGLY attempt to "make it work" + // + KjNode* eP = (eIdNodeP->type == KjObject)? kjLookup(eIdNodeP, "id") : eIdNodeP; + LM_T(LmtCount, ("JSON Type of array item: %s", kjValueType(eIdNodeP->type))); + + char* entityId = eP->value.s; + + LM_T(LmtEntityMap, ("o Entity '%s', distOp '%s', registration '%s'", entityId, distOpP->id, distOpP->regP->regId)); + entityMapItemAdd(entityMap, entityId, distOpP); + } + } + + return 0; +} + + + +// ----------------------------------------------------------------------------- +// +// distOpMatchIdsRequest - +// +static void distOpMatchIdsRequest(DistOp* distOpList, EntityMap* entityMap) +{ + if (distOpList == NULL) + return; + + distOpListDebug2(distOpList, "DistOps before sending the onlyId=true requests"); + // Send all distributed requests + int forwards = distOpsSend(distOpList, orionldState.in.aerOS); + + // Await all responses, if any + if (forwards > 0) + distOpsReceive(distOpList, idListResponse, entityMap, forwards); +} + + + +// ----------------------------------------------------------------------------- +// +// entityMapCreate +// +EntityMap* entityMapCreate(DistOp* distOpList, char* idPattern, QNode* qNode, OrionldGeoInfo* geoInfoP) +{ + EntityMap* entityMap = (EntityMap*) malloc(sizeof(EntityMap)); + if (entityMap == NULL) + LM_X(1, ("Out of memory allocating a memory map")); + + entityMap->map = kjObject(NULL, "EntityMap"); + if (entityMap->map == NULL) + LM_X(1, ("Out of memory allocating a memory map")); + + uuidGenerate(entityMap->id, sizeof(entityMap->id), "urn:ngsi-ld:entity-map:"); + + LM_T(LmtDistOpList, ("Created an entity map at %p (%s)", entityMap, entityMap->id)); + + // + // Send requests to all matching registration-endpoints, to fill in the entity map + // + distOpMatchIdsRequest(distOpList, entityMap); // Not including local hits + + kjTreeLog(entityMap->map, "entityMap", LmtSR); + + char* geojsonGeometryLongName = NULL; + if (orionldState.out.contentType == GEOJSON) + geojsonGeometryLongName = orionldState.in.geometryPropertyExpanded; + + // Get the local matches + KjNode* localEntityV = NULL; + LM_T(LmtMongoc, ("orionldState.in.attrList.items: %d", orionldState.in.attrList.items)); + LM_T(LmtMongoc, ("Calling mongocEntitiesQuery")); + + // + // Can't do any pagination in this step, and we only really need the Entity ID + // Need to teporarily modify the users input for that + // + int offset = orionldState.uriParams.offset; + int limit = orionldState.uriParams.limit; + + orionldState.uriParams.offset = 0; + orionldState.uriParams.limit = 1000; + + KjNode* localDbMatches = mongocEntitiesQuery(&orionldState.in.typeList, + &orionldState.in.idList, + idPattern, + &orionldState.in.attrList, + qNode, + geoInfoP, + NULL, + geojsonGeometryLongName, + true); + + orionldState.uriParams.offset = offset; + orionldState.uriParams.limit = limit; + + if (localDbMatches != NULL) + { + localEntityV = dbModelToEntityIdAndTypeObject(localDbMatches); + LM_T(LmtEntityMap, ("Adding local entities to the entityMap")); + + for (KjNode* eidNodeP = localEntityV->value.firstChildP; eidNodeP != NULL; eidNodeP = eidNodeP->next) + { + const char* entityId = eidNodeP->value.s; + + LM_T(LmtEntityMap, ("o Entity '%s', distOp 'local'", entityId)); + entityMapItemAdd(entityMap, entityId, NULL); + } + } + + // Sort all regs of the entities + for (KjNode* entityP = entityMap->map->value.firstChildP; entityP != NULL; entityP = entityP->next) + { + kjStringArraySort(entityP); + } + + entityMap->count = kjChildCount(entityMap->map); + +#if 0 + // ------------------- + if (lmTraceIsSet(LmtEntityMap) == true) + { + int ix = 0; + + LM_T(LmtEntityMap, ("Entity Maps (%d):", entityMap->count)); + kjTreeLog(entityMap->map, "EntityMap", LmtEntityMap); + + for (KjNode* entityP = entityMap->map->value.firstChildP; entityP != NULL; entityP = entityP->next) + { + char rBuf[1024]; + + bzero(rBuf, 1024); + kjFastRender(entityP, rBuf); + LM_T(LmtEntityMap, (" %03d '%s': %s", ix, entityP->name, rBuf)); + ++ix; + } + } + kjTreeLog(entityMap->map, "EntityMap", LmtSR); + // ---------------- +#endif + + return entityMap; +} diff --git a/src/lib/orionld/entityMaps/entityMapCreate.h b/src/lib/orionld/entityMaps/entityMapCreate.h new file mode 100644 index 0000000000..b956d32351 --- /dev/null +++ b/src/lib/orionld/entityMaps/entityMapCreate.h @@ -0,0 +1,41 @@ +#ifndef SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPCREATE_H_ +#define SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPCREATE_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/types/EntityMap.h" // EntityMap +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo +#include "orionld/q/QNode.h" // QNode + + + +// ----------------------------------------------------------------------------- +// +// entityMapCreate +// +extern EntityMap* entityMapCreate(DistOp* distOpList, char* idPattern, QNode* qNode, OrionldGeoInfo* geoInfoP); + +#endif // SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPCREATE_H_ diff --git a/src/lib/orionld/entityMaps/entityMapItemAdd.cpp b/src/lib/orionld/entityMaps/entityMapItemAdd.cpp new file mode 100644 index 0000000000..833430c233 --- /dev/null +++ b/src/lib/orionld/entityMaps/entityMapItemAdd.cpp @@ -0,0 +1,68 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjLookup.h" // kjLookup +#include "kjson/kjBuilder.h" // kjChildAdd +} + +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/types/EntityMap.h" // EntityMap +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/entityMaps/entityMapItemAdd.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// entityMapItemAdd - +// +void entityMapItemAdd(EntityMap* entityMap, const char* entityId, DistOp* distOpP) +{ + KjNode* matchP = kjLookup(entityMap->map, entityId); + + LM_T(LmtCount, ("entity id: '%s'", entityId)); + + if (matchP == NULL) + { + // + // The entity ID is not present in the list - must be added + // + LM_T(LmtEntityMap, ("The entity ID '%s' is not present in the list - adding it", entityId)); + matchP = kjArray(NULL, entityId); + kjChildAddSorted(entityMap->map, matchP); + } + + // + // Add DistOp ID to matchP's array - remember it's global, can't use kaAlloc + // + const char* distOpId = (distOpP != NULL)? distOpP->regP->regId : "@none"; + KjNode* distOpIdNodeP = kjString(NULL, NULL, distOpId); + + LM_T(LmtEntityMap, ("Adding DistOp '%s' to entity '%s'", distOpId, matchP->name)); + kjChildAdd(matchP, distOpIdNodeP); // This is sorted later - in entityMapCreate +} diff --git a/src/lib/orionld/entityMaps/entityMapItemAdd.h b/src/lib/orionld/entityMaps/entityMapItemAdd.h new file mode 100644 index 0000000000..2326cc98f6 --- /dev/null +++ b/src/lib/orionld/entityMaps/entityMapItemAdd.h @@ -0,0 +1,39 @@ +#ifndef SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPITEMADD_H_ +#define SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPITEMADD_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/types/EntityMap.h" // EntityMap +#include "orionld/forwarding/DistOp.h" // DistOp + + + +// ----------------------------------------------------------------------------- +// +// entityMapItemAdd - +// +extern void entityMapItemAdd(EntityMap* entityMap, const char* entityId, DistOp* distOpP); + +#endif // SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPITEMADD_H_ diff --git a/src/lib/orionld/entityMaps/entityMapLookup.cpp b/src/lib/orionld/entityMaps/entityMapLookup.cpp new file mode 100644 index 0000000000..0ed42c2b8d --- /dev/null +++ b/src/lib/orionld/entityMaps/entityMapLookup.cpp @@ -0,0 +1,50 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // strcmp + +#include "orionld/common/orionldState.h" // entityMaps +#include "orionld/types/EntityMap.h" // EntityMap +#include "orionld/entityMaps/entityMapLookup.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// entityMapLookup +// +EntityMap* entityMapLookup(const char* mapId) +{ + EntityMap* emP = entityMaps; + + while (emP != NULL) + { + if (strcmp(emP->id, mapId) == 0) + return emP; + + emP = emP->next; + } + + return NULL; +} diff --git a/src/lib/orionld/pernot/PernotSubCache.cpp b/src/lib/orionld/entityMaps/entityMapLookup.h similarity index 70% rename from src/lib/orionld/pernot/PernotSubCache.cpp rename to src/lib/orionld/entityMaps/entityMapLookup.h index 6305c02276..54990fb969 100644 --- a/src/lib/orionld/pernot/PernotSubCache.cpp +++ b/src/lib/orionld/entityMaps/entityMapLookup.h @@ -1,3 +1,6 @@ +#ifndef SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPLOOKUP_H_ +#define SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPLOOKUP_H_ + /* * * Copyright 2023 FIWARE Foundation e.V. @@ -22,19 +25,14 @@ * * Author: Ken Zangelin */ -#include "common/RenderFormat.h" // RenderFormat - -#include "orionld/q/QNode.h" // QNode -#include "orionld/context/OrionldContext.h" // OrionldContext -#include "orionld/pernot/PernotSubCache.h" // Own interface +#include "orionld/types/EntityMap.h" // EntityMap // ----------------------------------------------------------------------------- // -// pernotSubCacheRemove - +// entityMapLookup // -bool pernotSubCacheRemove(PernotSubscription* pSubP) -{ - return true; -} +extern EntityMap* entityMapLookup(const char* mapId); + +#endif // SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPLOOKUP_H_ diff --git a/src/lib/orionld/entityMaps/entityMapRelease.cpp b/src/lib/orionld/entityMaps/entityMapRelease.cpp new file mode 100644 index 0000000000..2199049ef8 --- /dev/null +++ b/src/lib/orionld/entityMaps/entityMapRelease.cpp @@ -0,0 +1,45 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // free + +extern "C" +{ +#include "kjson/kjFree.h" // kjFree +} + +#include "orionld/types/EntityMap.h" // EntityMap +#include "orionld/entityMaps/entityMapRelease.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// entityMapsRelease - +// +void entityMapRelease(EntityMap* emP) +{ + kjFree(emP->map); + free(emP); +} diff --git a/src/lib/orionld/entityMaps/entityMapRelease.h b/src/lib/orionld/entityMaps/entityMapRelease.h new file mode 100644 index 0000000000..afd3b5d333 --- /dev/null +++ b/src/lib/orionld/entityMaps/entityMapRelease.h @@ -0,0 +1,38 @@ +#ifndef SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPRELEASE_H_ +#define SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPRELEASE_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/types/EntityMap.h" // EntityMap + + + +// ----------------------------------------------------------------------------- +// +// entityMapRelease - +// +extern void entityMapRelease(EntityMap* entityMap); + +#endif // SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPRELEASE_H_ diff --git a/src/lib/orionld/entityMaps/entityMapRemove.cpp b/src/lib/orionld/entityMaps/entityMapRemove.cpp new file mode 100644 index 0000000000..0722f662ce --- /dev/null +++ b/src/lib/orionld/entityMaps/entityMapRemove.cpp @@ -0,0 +1,63 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // strcmp + +#include "orionld/common/orionldState.h" // entityMaps +#include "orionld/types/EntityMap.h" // EntityMap +#include "orionld/entityMaps/entityMapRemove.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// entityMapRemove - +// +EntityMap* entityMapRemove(const char* mapId) +{ + EntityMap* emP = entityMaps; + EntityMap* prev = NULL; + + while (emP != NULL) + { + if (strcmp(emP->id, mapId) == 0) + break; + prev = emP; + + emP = emP->next; + } + + if (emP == NULL) + LM_RE(NULL, ("Internal Error (can't remove the entity map '%s' - not found", mapId)); + + // First? + if (emP == entityMaps) + entityMaps = emP->next; + else if (emP->next == NULL) + prev->next = NULL; + else + prev->next = emP->next; + + return emP; +} diff --git a/src/lib/orionld/pernot/pernotSubCacheRemove.cpp b/src/lib/orionld/entityMaps/entityMapRemove.h similarity index 79% rename from src/lib/orionld/pernot/pernotSubCacheRemove.cpp rename to src/lib/orionld/entityMaps/entityMapRemove.h index 357b970c71..834f6f1869 100644 --- a/src/lib/orionld/pernot/pernotSubCacheRemove.cpp +++ b/src/lib/orionld/entityMaps/entityMapRemove.h @@ -1,3 +1,6 @@ +#ifndef SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPREMOVE_H_ +#define SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPREMOVE_H_ + /* * * Copyright 2023 FIWARE Foundation e.V. @@ -22,16 +25,13 @@ * * Author: Ken Zangelin */ -#include "orionld/pernot/PernotSubscription.h" // PernotSubscription -#include "orionld/pernot/pernotSubCacheRemove.h" // Own interface // ----------------------------------------------------------------------------- // -// pernotSubCacheRemove - +// entityMapRemove - // -bool pernotSubCacheRemove(PernotSubscription* pSubP) -{ - return true; -} +extern EntityMap* entityMapRemove(const char* mapId); + +#endif // SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPREMOVE_H_ diff --git a/src/lib/orionld/entityMaps/entityMapsRelease.cpp b/src/lib/orionld/entityMaps/entityMapsRelease.cpp new file mode 100644 index 0000000000..dd4d210203 --- /dev/null +++ b/src/lib/orionld/entityMaps/entityMapsRelease.cpp @@ -0,0 +1,48 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // NULL + +#include "orionld/common/orionldState.h" // entityMaps +#include "orionld/types/EntityMap.h" // EntityMap +#include "orionld/entityMaps/entityMapRelease.h" // entityMapRelease +#include "orionld/entityMaps/entityMapsRelease.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// entityMapsRelease - +// +void entityMapsRelease(void) +{ + EntityMap* emP = entityMaps; + + while (emP != NULL) + { + EntityMap* next = emP->next; + entityMapRelease(emP); + emP = next; + } +} diff --git a/src/lib/orionld/entityMaps/entityMapsRelease.h b/src/lib/orionld/entityMaps/entityMapsRelease.h new file mode 100644 index 0000000000..a5a0da496d --- /dev/null +++ b/src/lib/orionld/entityMaps/entityMapsRelease.h @@ -0,0 +1,38 @@ +#ifndef SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPSRELEASE_H_ +#define SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPSRELEASE_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/types/EntityMap.h" // EntityMap + + + +// ----------------------------------------------------------------------------- +// +// entityMapsRelease - +// +extern void entityMapsRelease(void); + +#endif // SRC_LIB_ORIONLD_ENTITYMAP_ENTITYMAPRELEASE_H_ diff --git a/src/lib/orionld/forwarding/CMakeLists.txt b/src/lib/orionld/forwarding/CMakeLists.txt index 9140a84362..f08e4b2aaa 100644 --- a/src/lib/orionld/forwarding/CMakeLists.txt +++ b/src/lib/orionld/forwarding/CMakeLists.txt @@ -24,14 +24,25 @@ SET (SOURCES DistOpType.cpp distOpInit.cpp distOpSend.cpp + distOpsSend.cpp + distOpListsMerge.cpp distOpListRelease.cpp + distOpListDebug.cpp distOpSuccess.cpp distOpFailure.cpp - distOpListsMerge.cpp distOpLookupByCurlHandle.cpp + distOpLookupByRegId.cpp distOpEntityMerge.cpp distOpRequests.cpp distOpResponses.cpp + distOpCreate.cpp + distOpAttrs.cpp + distOpItemListDebug.cpp + distOpListItemCreate.cpp + distOpListItemAdd.cpp + distOpResponseMergeIntoEntityArray.cpp + distOpsReceive2.cpp + distOpsSendAndReceive.cpp regMatchEntityInfo.cpp regMatchOperation.cpp regMatchInformationItem.cpp @@ -43,8 +54,14 @@ SET (SOURCES regMatchForEntityCreation.cpp regMatchForEntityGet.cpp regMatchForBatchDelete.cpp + regMatchInformationArrayForQuery.cpp + regMatchInformationItemForQuery.cpp + regMatchAttributesForQuery.cpp + regMatchEntityInfoForQuery.cpp xForwardedForCompose.cpp xForwardedForMatch.cpp + viaCompose.cpp + viaMatch.cpp ) # Include directories diff --git a/src/lib/orionld/forwarding/DistOp.h b/src/lib/orionld/forwarding/DistOp.h index 96d654c150..ecc7ca7831 100644 --- a/src/lib/orionld/forwarding/DistOp.h +++ b/src/lib/orionld/forwarding/DistOp.h @@ -33,6 +33,8 @@ extern "C" } #include "orionld/types/StringArray.h" // StringArray +#include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo +#include "orionld/q/QNode.h" // QNode #include "orionld/forwarding/DistOpType.h" // DistOpType #include "orionld/regCache/RegCache.h" // RegCacheItem @@ -44,19 +46,31 @@ extern "C" // typedef struct DistOp { - RegCacheItem* regP; - DistOpType operation; + char id[16]; // Unique identifier for this DistOp + RegCacheItem* regP; // Pointer to the registration cache item + DistOpType operation; // Operation KjNode* requestBody; // For Create/Update Requests (also used for GET - tree of response) char* rawResponse; // Response buffer as raw ASCII as it was received by libcurl (parsed and stored as DistOp::body) uint64_t httpResponseCode; // Response HTTP Status Code (64 bit due to libcurl curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE)) KjNode* responseBody; // Parsed body of the response - char* entityId; // Used by GET /entities/{entityId} (and as intermediate result for BATCH Delete) - char* entityType; // Used by GET /entities/{entityId} + char* entityId; // Used by GET /entities/{entityId}, BATCH /delete, and GET /entities + char* entityIdPattern; // Used by GET /entities + char* entityType; // Used by GET /entities/{entityId} and GET /entities char* attrName; // Used by PATCH /entities/{entityId}/attrs/{attrName} - StringArray* attrList; // URI Param "attrs" for GET Requests - char* geoProp; // URI Param "geometryProperty" for GET Requests + StringArray* attrList; // Attribute list - for URI Param "attrs" for GET Requests + char* attrsParam; // Rendered attrs URL parameter - to do it just once + int attrsParamLen; // Length of attrsParam to avoid a call to strlen() + + StringArray* idList; // Used by GET /entities (unless entityId is used) + StringArray* typeList; // Used by GET /entities (unless entityType is used) + bool onlyIds; // Used to compile the list of entity ids in the preparation for GET /entities + + OrionldGeoInfo geoInfo; + QNode* qNode; + char* lang; + char* geometryProperty; // URI Param "geometryProperty" for GET Requests bool error; char* title; @@ -67,4 +81,17 @@ typedef struct DistOp struct DistOp* next; } DistOp; + + +// ----------------------------------------------------------------------------- +// +// DistOpListItem - +// +typedef struct DistOpListItem +{ + DistOp* distOpP; + char* entityIds; + struct DistOpListItem* next; +} DistOpListItem; + #endif // SRC_LIB_ORIONLD_FORWARDING_DISTOP_H_ diff --git a/src/lib/orionld/forwarding/distOpAttrs.cpp b/src/lib/orionld/forwarding/distOpAttrs.cpp new file mode 100644 index 0000000000..74292399b6 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpAttrs.cpp @@ -0,0 +1,91 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // strlen, strcpy +#include // bzero + +extern "C" +{ +#include "kalloc/kaAlloc.h" // kaAlloc +} + +#include "orionld/common/orionldState.h" // orionldState, kjTreeLog +#include "orionld/types/StringArray.h" // StringArray +#include "orionld/context/orionldContextItemAliasLookup.h" // orionldContextItemAliasLookup +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/forwarding/distOpAttrs.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// distOpAttrs - +// +void distOpAttrs(DistOp* distOpP, StringArray* attrList) +{ + // + // The attributes are in longnames but ... should probably compact them. + // A registration can have its own @context, in cSourceInfo - for now, we use the @context of the original request. + // The attrList is always cloned, so, no problem modifying it. + // + int attrsLen = 0; + for (int ix = 0; ix < attrList->items; ix++) + { + attrList->array[ix] = orionldContextItemAliasLookup(orionldState.contextP, attrList->array[ix], NULL, NULL); + attrsLen += strlen(attrList->array[ix]) + 1; + } + + // Make room for "attrs=" and the string-end zero + attrsLen += 7; + + char* attrs = kaAlloc(&orionldState.kalloc, attrsLen); + + if (attrs == NULL) + LM_X(1, ("Out of memory")); + + bzero(attrs, attrsLen); + + strcpy(attrs, "attrs="); + + int pos = 6; + for (int ix = 0; ix < attrList->items; ix++) + { + int len = strlen(attrList->array[ix]); + strcpy(&attrs[pos], attrList->array[ix]); + + // Add comma unless it's the last attr (in which case we add a zero, just in case) + pos += len; + + if (ix != attrList->items - 1) // Not the last attr + { + attrs[pos] = ','; + pos += 1; + } + else + attrs[pos] = 0; + } + + distOpP->attrsParam = attrs; + distOpP->attrsParamLen = pos; +} diff --git a/src/lib/orionld/forwarding/distOpAttrs.h b/src/lib/orionld/forwarding/distOpAttrs.h new file mode 100644 index 0000000000..2a94958e47 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpAttrs.h @@ -0,0 +1,39 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_DISTOPATTRS_H_ +#define SRC_LIB_ORIONLD_FORWARDING_DISTOPATTRS_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/types/StringArray.h" // StringArray +#include "orionld/forwarding/DistOp.h" // DistOp + + + +// ----------------------------------------------------------------------------- +// +// distOpAttrs - +// +extern void distOpAttrs(DistOp* distOpP, StringArray* attrList); + +#endif // SRC_LIB_ORIONLD_FORWARDING_DISTOPATTRS_H_ diff --git a/src/lib/orionld/forwarding/distOpCreate.cpp b/src/lib/orionld/forwarding/distOpCreate.cpp new file mode 100644 index 0000000000..df7976af69 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpCreate.cpp @@ -0,0 +1,84 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // strlen + +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/types/StringArray.h" // StringArray, stringArrayClone +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/forwarding/distOpAttrs.h" // distOpAttrs +#include "orionld/forwarding/DistOpType.h" // DistOpType +#include "orionld/forwarding/distOpCreate.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// distOpCreate - +// +DistOp* distOpCreate +( + DistOpType operation, + RegCacheItem* regP, + StringArray* idList, + StringArray* typeList, + StringArray* attrList // As it arrives in the GET request (URL param 'attrs') +) +{ + DistOp* distOpP = (DistOp*) kaAlloc(&orionldState.kalloc, sizeof(DistOp)); + + if (distOpP == NULL) + LM_X(1, ("Out of memory")); + + bzero(distOpP, sizeof(DistOp)); + + distOpP->regP = regP; + distOpP->operation = operation; + distOpP->idList = idList; + distOpP->typeList = typeList; + + attrList = (attrList != NULL)? stringArrayClone(attrList) : NULL; + if ((attrList != NULL) && (attrList->items > 0)) + distOpAttrs(distOpP, attrList); + else + distOpP->attrsParam = NULL; + + // Assign an ID to this DistOp + if (regP != NULL) + { + snprintf(distOpP->id, sizeof(distOpP->id), "DO-%04d", orionldState.distOpNo); + ++orionldState.distOpNo; + } + else + strncpy(distOpP->id, "@none", sizeof(distOpP->id)); + + if (distOpP->regP != NULL) + LM_T(LmtDistOpList, ("Created distOp '%s', for reg '%s'", distOpP->id, distOpP->regP->regId)); + else + LM_T(LmtDistOpList, ("Created distOp '%s', for 'local DB'", distOpP->id)); + + return distOpP; +} diff --git a/src/lib/orionld/forwarding/distOpCreate.h b/src/lib/orionld/forwarding/distOpCreate.h new file mode 100644 index 0000000000..21437b9b2c --- /dev/null +++ b/src/lib/orionld/forwarding/distOpCreate.h @@ -0,0 +1,47 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_DISTOPCREATE_H_ +#define SRC_LIB_ORIONLD_FORWARDING_DISTOPCREATE_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/types/StringArray.h" // StringArray +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/forwarding/DistOpType.h" // DistOpType + + + +// ----------------------------------------------------------------------------- +// +// distOpCreate - +// +extern DistOp* distOpCreate +( + DistOpType operation, + RegCacheItem* regP, + StringArray* idListP, + StringArray* typeListP, + StringArray* attrListP +); + +#endif // SRC_LIB_ORIONLD_FORWARDING_DISTOPCREATE_H_ diff --git a/src/lib/orionld/forwarding/distOpEntityMerge.cpp b/src/lib/orionld/forwarding/distOpEntityMerge.cpp index 7979672c3a..9d90fa040c 100644 --- a/src/lib/orionld/forwarding/distOpEntityMerge.cpp +++ b/src/lib/orionld/forwarding/distOpEntityMerge.cpp @@ -33,6 +33,8 @@ extern "C" #include "logMsg/logMsg.h" // LM_* +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/kjTree/kjEntityIdLookupInEntityArray.h" // kjEntityIdLookupInEntityArray #include "orionld/forwarding/distOpEntityMerge.h" // Own interface @@ -77,15 +79,15 @@ static KjNode* newerAttribute(KjNode* currentP, KjNode* pretenderP) { if (strcmp(currentModifiedAt->value.s, pretenderModifiedAt->value.s) >= 0) return currentP; - else - return pretenderP; + + return pretenderP; } else if (currentModifiedAt != NULL) return currentP; else if (pretenderModifiedAt != NULL) return pretenderP; - else - return currentP; // Just pick one ... + + return currentP; // Just pick one ... } else if (currentObservedAt == NULL) return pretenderP; @@ -95,12 +97,9 @@ static KjNode* newerAttribute(KjNode* currentP, KjNode* pretenderP) { if (strcmp(currentObservedAt->value.s, pretenderObservedAt->value.s) >= 0) return currentP; - else - return pretenderP; - } - LM_W(("Not sure how we got here ... keeping the current - no replace")); - return currentP; + return pretenderP; + } } @@ -109,7 +108,9 @@ static KjNode* newerAttribute(KjNode* currentP, KjNode* pretenderP) // // distOpEntityMerge - // -bool distOpEntityMerge(KjNode* apiEntityP, KjNode* additionP, bool sysAttrs, bool auxiliary) +// FIXME: createdAt and modifiedAt should be converted to floats before comparing! +// +void distOpEntityMerge(KjNode* apiEntityP, KjNode* additionP, bool sysAttrs, bool auxiliary) { KjNode* idP = kjLookup(additionP, "id"); KjNode* typeP = kjLookup(additionP, "type"); @@ -139,25 +140,62 @@ bool distOpEntityMerge(KjNode* apiEntityP, KjNode* additionP, bool sysAttrs, boo { next = attrP->next; - KjNode* currentP = kjLookup(apiEntityP, attrP->name); + KjNode* currentP = kjLookup(apiEntityP, attrP->name); + bool createdAt = strcmp(attrP->name, "createdAt") == 0; + bool modifiedAt = strcmp(attrP->name, "modifiedAt") == 0; if (currentP == NULL) { + LM_T(LmtDistOpMerge, ("New Attribute '%s' - adding it to the entity", attrP->name)); kjChildRemove(additionP, attrP); kjChildAdd(apiEntityP, attrP); } + else if (createdAt == true) // Special attribute - need to keep the oldest, not the newest + { + LM_T(LmtDistOpMerge, ("'createdAt' in any type of registration")); + LM_T(LmtDistOpMerge, ("Current createdAt: %s", currentP->value.s)); + LM_T(LmtDistOpMerge, ("Candidate createdAt: %s", attrP->value.s)); + if (strcmp(attrP->value.s, currentP->value.s) > 0) + { + LM_T(LmtDistOpMerge, ("Existing Attribute '%s' - keeping it as it is OLDER than the old one", attrP->name)); + kjChildRemove(apiEntityP, currentP); + kjChildRemove(additionP, attrP); + kjChildAdd(apiEntityP, attrP); + } + else + LM_T(LmtDistOpMerge, ("Existing Attribute '%s' - ignoring it as it is NEWER than the old one", attrP->name)); + } + else if (modifiedAt == true) // Special attribute - non-reified + { + LM_T(LmtDistOpMerge, ("'modifiedAt' in any type of registration")); + LM_T(LmtDistOpMerge, ("Current modifiedAt: %s", currentP->value.s)); + LM_T(LmtDistOpMerge, ("Candidate modifiedAt: %s", attrP->value.s)); + if (strcmp(attrP->value.s, currentP->value.s) < 0) + { + LM_T(LmtDistOpMerge, ("Existing Attribute '%s' - keeping it as it is newer than the old one", attrP->name)); + kjChildRemove(apiEntityP, currentP); + kjChildRemove(additionP, attrP); + kjChildAdd(apiEntityP, attrP); + } + else + LM_T(LmtDistOpMerge, ("Existing Attribute '%s' - ignoring it as it is older than the old one", attrP->name)); + } else if (auxiliary == false) // two copies of the same attr ... and NOT from an auxiliary registration { + LM_T(LmtDistOpMerge, ("Existing Attribute '%s' in non-Auxiliary registration", attrP->name)); if (newerAttribute(currentP, attrP) == attrP) { + LM_T(LmtDistOpMerge, ("Existing Attribute '%s' - keeping it as it is newer than the old one", attrP->name)); kjChildRemove(apiEntityP, currentP); kjChildRemove(additionP, attrP); kjChildAdd(apiEntityP, attrP); } + else + LM_T(LmtDistOpMerge, ("Existing Attribute '%s' - ignoring it as it is older than the old one", attrP->name)); } + else + LM_T(LmtDistOpMerge, ("Existing Attribute '%s' - ignoring it as the registration is Auxiliary", attrP->name)); attrP = next; } - - return true; } diff --git a/src/lib/orionld/forwarding/distOpEntityMerge.h b/src/lib/orionld/forwarding/distOpEntityMerge.h index a1e8a62706..cb279410db 100644 --- a/src/lib/orionld/forwarding/distOpEntityMerge.h +++ b/src/lib/orionld/forwarding/distOpEntityMerge.h @@ -36,6 +36,6 @@ extern "C" // // distOpEntityMerge - // -extern bool distOpEntityMerge(KjNode* apiEntityP, KjNode* additionP, bool sysAttrs, bool auxiliary); +extern void distOpEntityMerge(KjNode* apiEntityP, KjNode* additionP, bool sysAttrs, bool auxiliary); #endif // SRC_LIB_ORIONLD_FORWARDING_DISTOPENTITYMERGE_H_ diff --git a/src/lib/orionld/forwarding/distOpItemListDebug.cpp b/src/lib/orionld/forwarding/distOpItemListDebug.cpp new file mode 100644 index 0000000000..ccd179fc65 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpItemListDebug.cpp @@ -0,0 +1,48 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/forwarding/DistOp.h" // DistOpListItem +#include "orionld/forwarding/distOpItemListDebug.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// distOpItemListDebug - +// +void distOpItemListDebug(DistOpListItem* distOpList, const char* msg) +{ + LM_T(LmtDistOpList, ("------------- %s -----------------", msg)); + DistOpListItem* itemP = distOpList; + while (itemP != NULL) + { + LM_T(LmtDistOpList, (" DistOp: %s", itemP->distOpP->id)); + LM_T(LmtDistOpList, (" Entities: %s", itemP->entityIds)); + LM_T(LmtDistOpList, (" ------------------------------")); + + itemP = itemP->next; + } +} diff --git a/src/lib/orionld/forwarding/distOpItemListDebug.h b/src/lib/orionld/forwarding/distOpItemListDebug.h new file mode 100644 index 0000000000..e51b9d48f4 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpItemListDebug.h @@ -0,0 +1,38 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_DISTOPITEMLISTDEBUG_H_ +#define SRC_LIB_ORIONLD_FORWARDING_DISTOPITEMLISTDEBUG_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/forwarding/DistOp.h" // DistOpListItem + + + +// ----------------------------------------------------------------------------- +// +// distOpItemListDebug - +// +extern void distOpItemListDebug(DistOpListItem* distOpList, const char* msg); + +#endif // SRC_LIB_ORIONLD_FORWARDING_DISTOPITEMLISTDEBUG_H_ diff --git a/src/lib/orionld/forwarding/distOpListDebug.cpp b/src/lib/orionld/forwarding/distOpListDebug.cpp new file mode 100644 index 0000000000..9926412513 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpListDebug.cpp @@ -0,0 +1,156 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "logMsg/logMsg.h" // LM_T, lmTraceIsSet +#include "logMsg/traceLevels.h" // distOpListDebug2 + +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/forwarding/distOpListDebug.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// distOpListDebug - +// +void distOpListDebug(DistOp* distOpList, const char* what) +{ + if (lmTraceIsSet(LmtDistOpList) == false) + return; + + LM_T(LmtDistOpList, ("Matching registrations (%s):", what)); + + if (distOpList == NULL) + LM_T(LmtDistOpList, (" None")); + + int ix = 0; + for (DistOp* distOpP = distOpList; distOpP != NULL; distOpP = distOpP->next) + { + LM_T(LmtDistOpList, (" DistOp %d: Reg Id: %s", ix, distOpP->regP->regId)); + ++ix; + } +} + + + +// ----------------------------------------------------------------------------- +// +// distOpListDebug2 - +// +void distOpListDebug2(DistOp* distOpP, const char* what) +{ + if (lmTraceIsSet(LmtDistOpList) == false) + return; + + LM_T(LmtDistOpList, ("----- DistOp List: %s", what)); + + if (distOpP == NULL) + LM_T(LmtDistOpList, (" None")); + + while (distOpP != NULL) + { + LM_T(LmtDistOpList, (" DistOp ID: %s", distOpP->id)); + LM_T(LmtDistOpList, (" Registration: %s", (distOpP->regP != NULL)? distOpP->regP->regId : "local DB")); + LM_T(LmtDistOpList, (" Operation: %s", distOpTypes[distOpP->operation])); + + if (distOpP->error == true) + { + LM_T(LmtDistOpList, (" Error:")); + LM_T(LmtDistOpList, (" Title: %s", distOpP->title)); + LM_T(LmtDistOpList, (" Detail: %s", distOpP->detail)); + LM_T(LmtDistOpList, (" Status: %d", distOpP->httpResponseCode)); + } + + if (distOpP->requestBody != NULL) + { + if (distOpP->operation == DoDeleteBatch) + { + LM_T(LmtDistOpList, (" Entity IDs:")); + for (KjNode* eIdNodeP = distOpP->requestBody->value.firstChildP; eIdNodeP != NULL; eIdNodeP = eIdNodeP->next) + { + LM_T(LmtDistOpList, (" o %s", eIdNodeP->value.s)); + } + } + else + { + LM_T(LmtDistOpList, (" Attributes:")); + + int ix = 0; + for (KjNode* attrP = distOpP->requestBody->value.firstChildP; attrP != NULL; attrP = attrP->next) + { + if ((strcmp(attrP->name, "id") != 0) && (strcmp(attrP->name, "type") != 0)) + { + LM_T(LmtDistOpList, (" Attribute %d: '%s'", ix, attrP->name)); + ++ix; + } + } + } + } + + if (distOpP->attrList != NULL) + { + LM_T(LmtDistOpList, (" URL Attributes: %d", distOpP->attrList->items)); + for (int ix = 0; ix < distOpP->attrList->items; ix++) + { + LM_T(LmtDistOpList, (" Attribute %d: '%s'", ix, distOpP->attrList->array[ix])); + } + } + + if (distOpP->attrsParam != NULL) + { + LM_T(LmtDistOpList, (" URL Attributes: '%s' (len: %d)", distOpP->attrsParam, distOpP->attrsParamLen)); + } + + if (distOpP->typeList != NULL) + { + LM_T(LmtDistOpList, (" URL Entity Types: %d", distOpP->typeList->items)); + for (int ix = 0; ix < distOpP->typeList->items; ix++) + { + LM_T(LmtDistOpList, (" Entity Type %02d: '%s'", ix, distOpP->typeList->array[ix])); + } + } + + if (distOpP->idList != NULL) + { + LM_T(LmtDistOpList, (" URL Entity IDs: %d", distOpP->idList->items)); + for (int ix = 0; ix < distOpP->idList->items; ix++) + { + LM_T(LmtDistOpList, (" Entity ID %02d: '%s'", ix, distOpP->idList->array[ix])); + } + } + + if (distOpP->entityId != NULL) + LM_T(LmtDistOpList, (" URL Entity ID: %s", distOpP->entityId)); + if (distOpP->entityIdPattern != NULL) + LM_T(LmtDistOpList, (" URL Entity ID Pattern: %s", distOpP->entityIdPattern)); + if (distOpP->entityType != NULL) + LM_T(LmtDistOpList, (" URL Entity TYPE: %s", distOpP->entityType)); + + LM_T(LmtDistOpList, ("----------------------------------------")); + + distOpP = distOpP->next; + } + + LM_T(LmtDistOpList, ("---------------------")); +} diff --git a/src/lib/orionld/forwarding/distOpListDebug.h b/src/lib/orionld/forwarding/distOpListDebug.h new file mode 100644 index 0000000000..6c3550f10f --- /dev/null +++ b/src/lib/orionld/forwarding/distOpListDebug.h @@ -0,0 +1,46 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_DISTOPLISTDEBUG_H_ +#define SRC_LIB_ORIONLD_FORWARDING_DISTOPLISTDEBUG_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/forwarding/DistOp.h" // DistOp + + + +// ----------------------------------------------------------------------------- +// +// distOpListDebug - +// +extern void distOpListDebug(DistOp* distOpList, const char* what); + + + +// ----------------------------------------------------------------------------- +// +// distOpListDebug2 - +// +extern void distOpListDebug2(DistOp* distOpP, const char* what); + +#endif // SRC_LIB_ORIONLD_FORWARDING_DISTOPLISTDEBUG_H_ diff --git a/src/lib/orionld/forwarding/distOpListItemAdd.cpp b/src/lib/orionld/forwarding/distOpListItemAdd.cpp new file mode 100644 index 0000000000..4b7729a259 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpListItemAdd.cpp @@ -0,0 +1,50 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/forwarding/DistOp.h" // DistOpListItem +#include "orionld/forwarding/distOpListItemCreate.h" // distOpListItemCreate +#include "orionld/forwarding/distOpListItemAdd.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// distOpListItemAdd - +// +DistOpListItem* distOpListItemAdd(DistOpListItem* distOpList, const char* distOpId, char* idString) +{ + LM_T(LmtEntityMap, ("Creating DistOpListItem for DistOp '%s', entities '%s'", distOpId, idString)); + + DistOpListItem* doliP = distOpListItemCreate(distOpId, idString); + + if (doliP == NULL) + return distOpList; + + if (distOpList != NULL) + doliP->next = distOpList; + + return doliP; +} diff --git a/src/lib/orionld/forwarding/distOpListItemAdd.h b/src/lib/orionld/forwarding/distOpListItemAdd.h new file mode 100644 index 0000000000..0729274f59 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpListItemAdd.h @@ -0,0 +1,38 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_DISTOPLISTITEMADD_H_ +#define SRC_LIB_ORIONLD_FORWARDING_DISTOPLISTITEMADD_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/forwarding/DistOp.h" // DistOpListItem + + + +// ----------------------------------------------------------------------------- +// +// distOpListItemAdd - +// +extern DistOpListItem* distOpListItemAdd(DistOpListItem* distOpList, const char* distOpId, char* idString); + +#endif // SRC_LIB_ORIONLD_FORWARDING_DISTOPLISTITEMADD_H_ diff --git a/src/lib/orionld/forwarding/distOpListItemCreate.cpp b/src/lib/orionld/forwarding/distOpListItemCreate.cpp new file mode 100644 index 0000000000..71423754d6 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpListItemCreate.cpp @@ -0,0 +1,78 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kalloc/kaAlloc.h" // kaAlloc +} + +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/common/orionldState.h" // orionldState, entityMaps +#include "orionld/regCache/regCacheItemLookup.h" // regCacheItemLookup +#include "orionld/forwarding/DistOp.h" // DistOpListItem +#include "orionld/forwarding/distOpCreate.h" // distOpCreate +#include "orionld/forwarding/distOpLookupByRegId.h" // distOpLookupByRegId + + + +// ----------------------------------------------------------------------------- +// +// distOpListItemCreate - +// +DistOpListItem* distOpListItemCreate(const char* distOpId, char* idString) +{ + LM_T(LmtDistOpList, ("orionldState.distOpList at %p", orionldState.distOpList)); + DistOp* distOpP = distOpLookupByRegId(orionldState.distOpList, distOpId); + LM_T(LmtDistOpList, ("Response: %p", distOpP)); + + if (distOpP == NULL) + { +#if 0 + // + // I think this LM_RE here is incorrect. + // The DistOps are for one request only. + // So, create a new DistOp if it does not exist. + // + LM_RE(NULL, ("Internal Error (unable to find the DistOp '%s'", distOpId)); +#else + RegCacheItem* rciP = regCacheItemLookup(orionldState.tenantP->regCache, distOpId); + distOpP = distOpCreate(DoQueryEntity, rciP, NULL, NULL, NULL); + // Add DistOp to the linked list of DistOps + distOpP->next = orionldState.distOpList; + orionldState.distOpList = distOpP; +#endif + } + + DistOpListItem* itemP = (DistOpListItem*) kaAlloc(&orionldState.kalloc, sizeof(DistOpListItem)); + if (itemP == NULL) + LM_X(1, ("Out of memory")); + + itemP->distOpP = distOpP; + itemP->next = NULL; + itemP->entityIds = idString; + + return itemP; +} + diff --git a/src/lib/orionld/forwarding/distOpListItemCreate.h b/src/lib/orionld/forwarding/distOpListItemCreate.h new file mode 100644 index 0000000000..d9305a78c9 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpListItemCreate.h @@ -0,0 +1,38 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_DISTOPLISTITEMCREATE_H_ +#define SRC_LIB_ORIONLD_FORWARDING_DISTOPLISTITEMCREATE_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/forwarding/DistOp.h" // DistOpListItem + + + +// ----------------------------------------------------------------------------- +// +// distOpListItemCreate - +// +extern DistOpListItem* distOpListItemCreate(const char* distOpId, char* idString); + +#endif // SRC_LIB_ORIONLD_FORWARDING_DISTOPLISTITEMCREATE_H_ diff --git a/src/lib/orionld/forwarding/distOpListRelease.cpp b/src/lib/orionld/forwarding/distOpListRelease.cpp index 5be0ef117b..7d0d4fd748 100644 --- a/src/lib/orionld/forwarding/distOpListRelease.cpp +++ b/src/lib/orionld/forwarding/distOpListRelease.cpp @@ -41,14 +41,22 @@ void distOpListRelease(DistOp* distOpList) while (distOpP != NULL) { if (distOpP->curlHandle != NULL) + { + LM_T(LmtLeak, ("Cleaning up a curl handle at %p", distOpP->curlHandle)); curl_easy_cleanup(distOpP->curlHandle); + distOpP->curlHandle = NULL; + } if (distOpP->curlHeaders != NULL) + { curl_slist_free_all(distOpP->curlHeaders); + distOpP->curlHeaders = NULL; + } distOpP = distOpP->next; } - curl_multi_cleanup(orionldState.curlDoMultiP); + if (orionldState.curlDoMultiP != NULL) + curl_multi_cleanup(orionldState.curlDoMultiP); orionldState.curlDoMultiP = NULL; } diff --git a/src/lib/orionld/forwarding/distOpLookupByRegId.cpp b/src/lib/orionld/forwarding/distOpLookupByRegId.cpp new file mode 100644 index 0000000000..b3b3349389 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpLookupByRegId.cpp @@ -0,0 +1,66 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // NULL +#include // curl + +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/forwarding/distOpLookupByRegId.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// distOpLookupByRegId - +// +DistOp* distOpLookupByRegId(DistOp* distOpList, const char* regId) +{ + DistOp* distOpP = distOpList; + bool local = strcmp(regId, "@none") == 0; + + LM_T(LmtDistOpList, ("Looking for DistOp '%s'", regId)); + while (distOpP != NULL) + { + if ((local == true) && (distOpP->regP == NULL)) + { + LM_T(LmtDistOpList, ("Found DistOp '%s'", regId)); + return distOpP; + } + + if ((distOpP->regP != NULL) && (distOpP->regP->regId != NULL)) + { + LM_T(LmtDistOpList, ("Comparing with '%s'", distOpP->regP->regId)); + if (strcmp(distOpP->regP->regId, regId) == 0) + { + LM_T(LmtDistOpList, ("Found DistOp for reg '%s'", regId)); + return distOpP; + } + } + + distOpP = distOpP->next; + } + + LM_T(LmtDistOpList, ("DistOp for reg '%s' NOT FOUND", regId)); + return NULL; +} diff --git a/src/lib/orionld/forwarding/distOpLookupByRegId.h b/src/lib/orionld/forwarding/distOpLookupByRegId.h new file mode 100644 index 0000000000..542ce1294e --- /dev/null +++ b/src/lib/orionld/forwarding/distOpLookupByRegId.h @@ -0,0 +1,40 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_DISTOPLOOKUPBYREGID_H_ +#define SRC_LIB_ORIONLD_FORWARDING_DISTOPLOOKUPBYREGID_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // CURL + +#include "orionld/forwarding/DistOp.h" // DistOp + + + +// ----------------------------------------------------------------------------- +// +// distOpLookupByRegId - +// +extern DistOp* distOpLookupByRegId(DistOp* distOpList, const char* regId); + +#endif // SRC_LIB_ORIONLD_FORWARDING_DISTOPLOOKUPBYREGID_H_ diff --git a/src/lib/orionld/forwarding/distOpRequests.cpp b/src/lib/orionld/forwarding/distOpRequests.cpp index 7592cc3080..006f61deda 100644 --- a/src/lib/orionld/forwarding/distOpRequests.cpp +++ b/src/lib/orionld/forwarding/distOpRequests.cpp @@ -39,6 +39,7 @@ extern "C" #include "orionld/forwarding/distOpListsMerge.h" // distOpListsMerge #include "orionld/forwarding/distOpSend.h" // distOpSend #include "orionld/forwarding/xForwardedForCompose.h" // xForwardedForCompose +#include "orionld/forwarding/viaCompose.h" // viaCompose #include "orionld/forwarding/regMatchForEntityCreation.h" // regMatchForEntityCreation @@ -130,6 +131,7 @@ DistOp* distOpRequests(char* entityId, char* entityType, DistOpType operation, K // Now that we've found all matching registrations we can add ourselves to the X-forwarded-For header char* xff = xForwardedForCompose(orionldState.in.xForwardedFor, localIpAndPort); + char* via = viaCompose(orionldState.in.via, brokerId); // Enqueue all forwarded requests int forwards = 0; // Debugging purposees @@ -138,7 +140,7 @@ DistOp* distOpRequests(char* entityId, char* entityType, DistOpType operation, K // Send the forwarded request and await all responses if ((distOpP->regP != NULL) && (distOpP->error == false)) { - if (distOpSend(distOpP, dateHeader, xff) == 0) + if (distOpSend(distOpP, dateHeader, xff, via, false, NULL) == 0) { distOpP->error = false; orionldState.distOp.requests += 1; diff --git a/src/lib/orionld/forwarding/distOpResponseMergeIntoEntityArray.cpp b/src/lib/orionld/forwarding/distOpResponseMergeIntoEntityArray.cpp new file mode 100644 index 0000000000..2a0a82c906 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpResponseMergeIntoEntityArray.cpp @@ -0,0 +1,94 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjBuilder.h" // kjChildRemove, kjChildAdd, ... +#include "kjson/kjLookup.h" // kjLookup +} + +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/common/orionldState.h" // orionldState, entityMaps +#include "orionld/kjTree/kjEntityIdLookupInEntityArray.h" // kjEntityIdLookupInEntityArray +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/forwarding/distOpEntityMerge.h" // distOpEntityMerge +#include "orionld/forwarding/distOpResponseMergeIntoEntityArray.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// distOpResponseMergeIntoEntityArray - +// +void distOpResponseMergeIntoEntityArray(DistOp* distOpP, KjNode* entityArray) +{ + LM_W(("Merging entities for DistOp '%s' (aux: %s)", distOpP->id, (distOpP->regP->mode == RegModeAuxiliary)? "YES" : "NO")); + LM_T(LmtSR, ("Got a response. status code: %d. entityArray: %p", distOpP->httpResponseCode, entityArray)); + + kjTreeLog(distOpP->responseBody, "Response", LmtSR); + + if ((distOpP->httpResponseCode == 200) && (distOpP->responseBody != NULL)) + { + LM_T(LmtSR, ("Got a body from endpoint registered in reg '%s'", distOpP->regP->regId)); + LM_T(LmtSR, ("Must merge these new entities with the ones already received")); + + KjNode* entityP = distOpP->responseBody->value.firstChildP; + KjNode* next; + while (entityP != NULL) + { + next = entityP->next; + kjChildRemove(distOpP->responseBody, entityP); + + // Lookup the entity in entityArray and: + // - Add entityP if not found in entityArray + // - Merge the two if found in entityArray + // + KjNode* entityIdNodeP = kjLookup(entityP, "id"); + + if (entityIdNodeP != NULL) + { + char* entityId = entityIdNodeP->value.s; + KjNode* baseEntityP = kjEntityIdLookupInEntityArray(entityArray, entityId); + + if (baseEntityP == NULL) + { + LM_T(LmtDistOpMerge, ("New Entity '%s' - adding it to the entity array (at %p)", entityId, entityArray)); + kjChildAdd(entityArray, entityP); + } + else + { + LM_T(LmtDistOpMerge, ("Existing Entity '%s' - merging it in the entity array (reg-mode: %s)", entityId, registrationModeToString(distOpP->regP->mode))); + distOpEntityMerge(baseEntityP, entityP, orionldState.uriParamOptions.sysAttrs, distOpP->regP->mode == RegModeAuxiliary); + } + } + else + LM_W(("No Entity ID in response from forwarded GET /entities request (reg %s)", distOpP->regP->regId)); + + entityP = next; + } + } +} + diff --git a/src/lib/orionld/forwarding/distOpResponseMergeIntoEntityArray.h b/src/lib/orionld/forwarding/distOpResponseMergeIntoEntityArray.h new file mode 100644 index 0000000000..ddeafbfbf5 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpResponseMergeIntoEntityArray.h @@ -0,0 +1,43 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_DISTOPRESPONSEMERGEINTOENTITYARRAY_H_ +#define SRC_LIB_ORIONLD_FORWARDING_DISTOPRESPONSEMERGEINTOENTITYARRAY_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +} + +#include "orionld/forwarding/DistOp.h" // DistOp + + + +// ----------------------------------------------------------------------------- +// +// distOpResponseMergeIntoEntityArray - +// +extern void distOpResponseMergeIntoEntityArray(DistOp* distOpP, KjNode* entityArray); + +#endif // SRC_LIB_ORIONLD_FORWARDING_DISTOPRESPONSEMERGEINTOENTITYARRAY_H_ diff --git a/src/lib/orionld/forwarding/distOpResponses.cpp b/src/lib/orionld/forwarding/distOpResponses.cpp index 0a79e4caea..763af3d081 100644 --- a/src/lib/orionld/forwarding/distOpResponses.cpp +++ b/src/lib/orionld/forwarding/distOpResponses.cpp @@ -280,7 +280,7 @@ void entityResponseAccumulate(DistOp* distOpP, KjNode* responseBody, KjNode* suc } else if (httpResponseCode == 0) { - LM_T(LmtDistOpMsgs, ("%s: Seems like the request wasn't even sent ... (%s)", distOpP->regP->regId, distOpP->regP->ipAndPort)); + LM_T(LmtDistOpResponse, ("%s: Seems like the request wasn't even sent ... (%s)", distOpP->regP->regId, distOpP->regP->ipAndPort)); int statusCode = 500; const char* title = "Unable to send distributed request"; @@ -292,9 +292,9 @@ void entityResponseAccumulate(DistOp* distOpP, KjNode* responseBody, KjNode* suc distOpFailure(responseBody, distOpP, title, detail, statusCode, NULL); } else if (httpResponseCode == 200) - LM_T(LmtDistOpMsgs, ("Reg %s: unexpected status code %d (using the accumulator?)", distOpP->regP->regId, httpResponseCode)); + LM_W(("%s: unexpected status code %d (using the accumulator?)", distOpP->regP->regId, httpResponseCode)); else - LM_W(("Reg %s: unexpected status code %d", distOpP->regP->regId, httpResponseCode)); + LM_W(("%s: unexpected status code %d", distOpP->regP->regId, httpResponseCode)); } @@ -333,7 +333,7 @@ void distOpResponseAccumulate(DistOp* distOpP, KjNode* responseBody, KjNode* suc if (msgP->data.result == CURLE_OK) { if ((httpResponseCode >= 200) && (httpResponseCode <= 299)) - distOpSuccess(responseBody, distOpP, NULL); + distOpSuccess(responseBody, distOpP, NULL, NULL); else if (httpResponseCode == 404) distOpFailure(responseBody, distOpP, "Not Found", NULL, 404, NULL); } diff --git a/src/lib/orionld/forwarding/distOpSend.cpp b/src/lib/orionld/forwarding/distOpSend.cpp index ec1739dac3..be32614098 100644 --- a/src/lib/orionld/forwarding/distOpSend.cpp +++ b/src/lib/orionld/forwarding/distOpSend.cpp @@ -38,10 +38,12 @@ extern "C" #include "logMsg/logMsg.h" // LM_* +#include "common/globals.h" // NGSI_LD_V1 #include "orionld/common/orionldState.h" // orionldState #include "orionld/common/tenantList.h" // tenant0 #include "orionld/context/orionldCoreContext.h" // orionldCoreContextP #include "orionld/context/orionldContextItemAliasLookup.h" // orionldContextItemAliasLookup +#include "orionld/q//qRender.h" // qRender #include "orionld/forwarding/DistOp.h" // DistOp #include "orionld/forwarding/distOpSend.h" // Own interface @@ -91,7 +93,7 @@ typedef struct SList // typedef struct ForwardUrlParts { - DistOp* distOpP; // From here we need "op", "entityId", "attrName", ... + DistOp* distOpP; // From here we need "op", "entityId", "attrName", ... SList* params; // Linked list of strings (URI params) SList* last; // Last item in the 'params' list - used for linking in new items } ForwardUrlParts; @@ -153,7 +155,7 @@ char* urlCompose(ForwardUrlParts* urlPartsP, KjNode* endpointP) // // uriParamAdd - // -void uriParamAdd(ForwardUrlParts* urlPartsP, const char* key, const char* value, int totalLen) +static void uriParamAdd(ForwardUrlParts* urlPartsP, const char* key, const char* value, int totalLen) { SList* sListP = (SList*) kaAlloc(&orionldState.kalloc, sizeof(SList)); @@ -171,6 +173,8 @@ void uriParamAdd(ForwardUrlParts* urlPartsP, const char* key, const char* value, sListP->sLen = snprintf(sListP->sP, sLen, "%s=%s", key, value); } + LM_T(LmtDistOpRequestParams, ("DistOp Request URL Param: %s", sListP->sP)); + sListP->next = NULL; if (urlPartsP->params == NULL) @@ -230,7 +234,7 @@ static int responseSave(void* chunk, size_t size, size_t members, void* userP) snprintf(httpResponseP->buf, newSize + 1023, "%s%s", oldBuf, chunkP); LM_T(LmtDistOpResponseBuf, ("httpResponseP->buf: '%s'", httpResponseP->buf)); } - else // > 20k: use malloc + else { httpResponseP->buf = (char*) malloc(newSize + 1024); if (httpResponseP->buf == NULL) @@ -258,57 +262,6 @@ static int responseSave(void* chunk, size_t size, size_t members, void* userP) -// ----------------------------------------------------------------------------- -// -// attrsParam - -// -void attrsParam(OrionldContext* contextP, ForwardUrlParts* urlPartsP, StringArray* attrList) -{ - if (contextP == NULL) - contextP = orionldCoreContextP; - - // - // The attributes are in longnames but ... should probably compact them. - // A registration can have its own @context, in cSourceInfo - for now, we use the @context of the original request. - // The attrList is always cloned, so, no problem modifying it. - // - int attrsLen = 0; - for (int ix = 0; ix < attrList->items; ix++) - { - attrList->array[ix] = orionldContextItemAliasLookup(contextP, attrList->array[ix], NULL, NULL); - attrsLen += strlen(attrList->array[ix]) + 1; - } - - // Make room for "attrs=" and the string-end zero - attrsLen += 7; - - char* attrs = kaAlloc(&orionldState.kalloc, attrsLen); - - strcpy(attrs, "attrs="); - - int pos = 6; - for (int ix = 0; ix < attrList->items; ix++) - { - int len = strlen(attrList->array[ix]); - strcpy(&attrs[pos], attrList->array[ix]); - - // Add comma unless it's the last attr (in which case we add a zero, just in case - pos += len; - - if (ix != attrList->items - 1) // Not the last attr - { - attrs[pos] = ','; - pos += 1; - } - else - attrs[pos] = 0; - } - - uriParamAdd(urlPartsP, attrs, NULL, pos); -} - - - // ----------------------------------------------------------------------------- // // responseHeaderDebug - @@ -376,7 +329,7 @@ void bodyCompact(DistOpType operation, KjNode* requestBody, OrionldContext* fwdC // // distOpSend - // -bool distOpSend(DistOp* distOpP, const char* dateHeader, const char* xForwardedForHeader) +bool distOpSend(DistOp* distOpP, const char* dateHeader, const char* xForwardedForHeader, const char* viaHeader, bool local, const char* entityIds) { // // Figure out the @context to use for the forwarded request @@ -417,6 +370,7 @@ bool distOpSend(DistOp* distOpP, const char* dateHeader, const char* xForwardedF } distOpP->curlHandle = curl_easy_init(); + LM_T(LmtLeak, ("Got a curl handle at %p", distOpP->curlHandle)); if (distOpP->curlHandle == NULL) { curl_multi_cleanup(orionldState.curlDoMultiP); @@ -440,16 +394,42 @@ bool distOpSend(DistOp* distOpP, const char* dateHeader, const char* xForwardedF // // Add URI Params // + LM_T(LmtDistOpRequestParams, ("%s: ---- URL Parameters for %s ------------------------", distOpP->regP->regId, distOpP->id)); if (orionldState.verb == GET) { - if ((distOpP->attrList != NULL) && (distOpP->attrList->items > 0)) - attrsParam(fwdContextP, &urlParts, distOpP->attrList); + if (distOpP->attrsParam != NULL) + uriParamAdd(&urlParts, distOpP->attrsParam, NULL, distOpP->attrsParamLen); // // Forwarded requests are ALWAYS sent with options=sysAttrs (normalized is already default - no need to add that) // They MUST be sent with NORMALIZED and SYSATTRS, as without that, there's no way to pick attributes in case we have clashes // - uriParamAdd(&urlParts, "options=sysAttrs", NULL, 16); + // There is one exception though - when only the entity ids are wanted as output + // + if (distOpP->onlyIds == true) + uriParamAdd(&urlParts, "onlyIds=true", NULL, 12); + else + uriParamAdd(&urlParts, "options=sysAttrs", NULL, 16); + + if (distOpP->operation == DoQueryEntity) + { + if (entityIds != NULL) + uriParamAdd(&urlParts, "id", entityIds, -1); + else if (distOpP->entityId != NULL) + uriParamAdd(&urlParts, "id", distOpP->entityId, -1); + } + + if (local == true) + uriParamAdd(&urlParts, "local=true", NULL, 10); + + if (distOpP->qNode != NULL) + { + char buf[256]; + qRender(distOpP->qNode, NGSI_LD_V1, buf, sizeof(buf), NULL); + LM_T(LmtDistOpRequestParams, ("DistOp %s has a Q: %s", distOpP->regP->regId, buf)); + if (orionldState.uriParams.q != NULL) + LM_T(LmtDistOpRequestParams, ("The initial request also has a 'q'")); + } // // If we know the Entity Type, we pass that piece of information as well @@ -467,6 +447,14 @@ bool distOpSend(DistOp* distOpP, const char* dateHeader, const char* xForwardedF if ((distOpP->operation == DoAppendAttrs) && (orionldState.uriParamOptions.noOverwrite == true)) uriParamAdd(&urlParts, "options", "noOverwrite", 11); + if (orionldState.uriParams.qCopy != NULL) + { + uriParamAdd(&urlParts, "q", orionldState.uriParams.qCopy, -1); + LM_T(LmtDistOpRequestHeaders, ("%s: orionldState.uriParams.q: '%s'", distOpP->regP->regId, orionldState.uriParams.qCopy)); + } + + LM_T(LmtDistOpRequestParams, ("%s: ---- End of URL Parameters -----------------", distOpP->regP->regId)); + // // Compose the entire URL and pass it to CURL // @@ -494,16 +482,12 @@ bool distOpSend(DistOp* distOpP, const char* dateHeader, const char* xForwardedF // Date headers = curl_slist_append(headers, dateHeader); -#if 0 - // Host - char hostHeader[256]; - snprintf(hostHeader, sizeof(hostHeader), "Host: %s", ip); - headers = curl_slist_append(headers, hostHeader); -#endif - // X-Forwarded-For headers = curl_slist_append(headers, xForwardedForHeader); + // Via + headers = curl_slist_append(headers, viaHeader); + // Custom headers from Registration::contextSourceInfo char* infoTenant = NULL; char* accept = NULL; @@ -555,15 +539,8 @@ bool distOpSend(DistOp* distOpP, const char* dateHeader, const char* xForwardedF contentType = JSON; continue; } - if (strcasecmp(keyP->value.s, "Accept") == 0) - { accept = valueP->value.s; - char header[256]; // Assuming 256 is enough - snprintf(header, sizeof(header), "Accept: %s", valueP->value.s); - headers = curl_slist_append(headers, header); - continue; - } // None of the above, adding the header char header[256]; // Assuming 256 is enough @@ -633,7 +610,7 @@ bool distOpSend(DistOp* distOpP, const char* dateHeader, const char* xForwardedF struct curl_slist* sP = headers; while (sP != NULL) { - LM_T(LmtDistOpMsgs, ("FWD: Added header '%s'", sP->data)); + LM_T(LmtDistOpRequest, ("FWD: Added header '%s'", sP->data)); sP = sP->next; } #endif @@ -724,7 +701,7 @@ bool distOpSend(DistOp* distOpP, const char* dateHeader, const char* xForwardedF // - // Reading the response + // Set the callback function (responseSave) for reading the response // HttpResponse* httpResponseP = (HttpResponse*) kaAlloc(&orionldState.kalloc, sizeof(HttpResponse)); @@ -750,9 +727,9 @@ bool distOpSend(DistOp* distOpP, const char* dateHeader, const char* xForwardedF curl_easy_setopt(distOpP->curlHandle, CURLOPT_HTTPHEADER, headers); // - // SEND (sort of) + // SEND (sort of - enqueue the request) // - LM_T(LmtDistOpRequest, ("%s: distributed request %s %s '%s'", distOpP->regP->regId, orionldState.verbString, url, payloadBody)); + LM_T(LmtDistOpRequest, ("%s: distributed request '%s' %s %s '%s'", distOpP->regP->regId, distOpP->id, orionldState.verbString, url, (payloadBody == NULL)? "no body" : payloadBody)); curl_multi_add_handle(orionldState.curlDoMultiP, distOpP->curlHandle); return 0; diff --git a/src/lib/orionld/forwarding/distOpSend.h b/src/lib/orionld/forwarding/distOpSend.h index aae8f151e7..e44199e23e 100644 --- a/src/lib/orionld/forwarding/distOpSend.h +++ b/src/lib/orionld/forwarding/distOpSend.h @@ -33,6 +33,6 @@ // // distOpSend - // -extern bool distOpSend(DistOp* distOpP, const char* dateHeader, const char* xForwardedForHeader); +extern bool distOpSend(DistOp* distOpP, const char* dateHeader, const char* xForwardedForHeader, const char* viaHeader, bool local, const char* entityIds); #endif // SRC_LIB_ORIONLD_FORWARDING_DISTOPSEND_H_ diff --git a/src/lib/orionld/forwarding/distOpSuccess.cpp b/src/lib/orionld/forwarding/distOpSuccess.cpp index e841f4b4eb..eb50936fdb 100644 --- a/src/lib/orionld/forwarding/distOpSuccess.cpp +++ b/src/lib/orionld/forwarding/distOpSuccess.cpp @@ -70,7 +70,7 @@ static void attrNameToSuccess(KjNode* successV, KjNode* failureV, char* attrName // // distOpSuccess - // -void distOpSuccess(KjNode* responseBody, DistOp* distOpP, char* attrName) +void distOpSuccess(KjNode* responseBody, DistOp* distOpP, const char* entityId, char* attrName) { KjNode* successV = kjLookup(responseBody, "success"); KjNode* failureV = kjLookup(responseBody, "failure"); @@ -81,13 +81,19 @@ void distOpSuccess(KjNode* responseBody, DistOp* distOpP, char* attrName) kjChildAdd(responseBody, successV); } + if ((distOpP == NULL) && (attrName == NULL)) + { + KjNode* entityIdNodeP = kjString(orionldState.kjsonP, NULL, entityId); + kjChildAdd(successV, entityIdNodeP); + } + if (attrName != NULL) attrNameToSuccess(successV, failureV, attrName); else if (distOpP != NULL) { if (distOpP->attrName != NULL) attrNameToSuccess(successV, failureV, distOpP->attrName); - else + else if (distOpP->requestBody != NULL) { for (KjNode* attrNameP = distOpP->requestBody->value.firstChildP; attrNameP != NULL; attrNameP = attrNameP->next) { diff --git a/src/lib/orionld/forwarding/distOpSuccess.h b/src/lib/orionld/forwarding/distOpSuccess.h index 36db330fff..8d5fd75534 100644 --- a/src/lib/orionld/forwarding/distOpSuccess.h +++ b/src/lib/orionld/forwarding/distOpSuccess.h @@ -41,8 +41,9 @@ extern "C" // PARAMETERS // responseBody a KjNode object with two fields: "success" and "failure" // distOpP linked list of DistOp's +// entityId the id of the entity // attrName instead of 'distOp', here's the only attribute name already // -extern void distOpSuccess(KjNode* responseBody, DistOp* distOpP, char* attrName); +extern void distOpSuccess(KjNode* responseBody, DistOp* distOpP, const char* entityId, char* attrName); #endif // SRC_LIB_ORIONLD_FORWARDING_DISTOPSUCCESS_H_ diff --git a/src/lib/orionld/forwarding/distOpsReceive2.cpp b/src/lib/orionld/forwarding/distOpsReceive2.cpp new file mode 100644 index 0000000000..be09e2e1e1 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpsReceive2.cpp @@ -0,0 +1,95 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // curl_multi_info_read, curl_easy_getinfo, ... + +extern "C" +{ +#include "kjson/kjParse.h" // kjParse +} + +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/common/orionldState.h" // orionldState, entityMaps +#include "orionld/forwarding/DistOp.h" // DistOp, DistOpListItem +#include "orionld/forwarding/distOpLookupByCurlHandle.h" // distOpLookupByCurlHandle +#include "orionld/forwarding/distOpResponseMergeIntoEntityArray.h" // distOpResponseMergeIntoEntityArray +#include "orionld/forwarding/distOpsReceive2.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// distOpsReceive2 - FIXME: try to combine with distOpsReceive() +// +void distOpsReceive2(DistOpListItem* distOpListItem, DistOpResponseTreatFunction treatFunction, void* callbackParam) +{ + LM_T(LmtSR, ("Receiving responses")); + // + // Read the responses to the forwarded requests + // + CURLMsg* msgP; + int msgsLeft; + + while ((msgP = curl_multi_info_read(orionldState.curlDoMultiP, &msgsLeft)) != NULL) + { + if (msgP->msg != CURLMSG_DONE) + continue; + + if (msgP->data.result == CURLE_OK) + { + DistOp* distOpP = distOpLookupByCurlHandle(orionldState.distOpList, msgP->easy_handle); + + if (distOpP == NULL) + { + LM_E(("Unable to find the curl handle of a message, presumably a response to a forwarded request")); + continue; + } + + curl_easy_getinfo(msgP->easy_handle, CURLINFO_RESPONSE_CODE, &distOpP->httpResponseCode); + + if ((distOpP->rawResponse != NULL) && (distOpP->rawResponse[0] != 0)) + distOpP->responseBody = kjParse(orionldState.kjsonP, distOpP->rawResponse); + + LM_T(LmtDistOpResponse, ("%s: received a response for a forwarded request", distOpP->regP->regId, distOpP->httpResponseCode)); + LM_T(LmtDistOpResponse, ("%s: response for a forwarded request: %s", distOpP->regP->regId, distOpP->rawResponse)); + + // + // Treating here all non-auxiliar registrations. + // The Auxiliar registrations are treated LAST + // + if (distOpP->regP->mode != RegModeAuxiliary) + treatFunction(distOpP, callbackParam); + } + } + + // + // Now that all non-auxiliar registrations have been treated, time for the aux regs + // + for (DistOp* distOpP = orionldState.distOpList; distOpP != NULL; distOpP = distOpP->next) + { + if ((distOpP->regP != NULL) && (distOpP->regP->mode == RegModeAuxiliary)) + distOpResponseMergeIntoEntityArray(distOpP, (KjNode*) callbackParam); + } +} diff --git a/src/lib/orionld/forwarding/distOpsReceive2.h b/src/lib/orionld/forwarding/distOpsReceive2.h new file mode 100644 index 0000000000..38c6c65e42 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpsReceive2.h @@ -0,0 +1,46 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_DISTOPSRECEIVE2_H_ +#define SRC_LIB_ORIONLD_FORWARDING_DISTOPSRECEIVE2_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/forwarding/DistOp.h" // DistOp, DistOpListItem + + + +// ----------------------------------------------------------------------------- +// +// DistOpResponseTreatFunction - +// +typedef int (*DistOpResponseTreatFunction)(DistOp* distOpP, void* callbackParam); + + + +// ----------------------------------------------------------------------------- +// +// distOpsReceive2 - +// +extern void distOpsReceive2(DistOpListItem* distOpListItem, DistOpResponseTreatFunction treatFunction, void* callbackParam); + +#endif // SRC_LIB_ORIONLD_FORWARDING_DISTOPSRECEIVE2_H_ diff --git a/src/lib/orionld/forwarding/distOpsSend.cpp b/src/lib/orionld/forwarding/distOpsSend.cpp new file mode 100644 index 0000000000..6a15c8061b --- /dev/null +++ b/src/lib/orionld/forwarding/distOpsSend.cpp @@ -0,0 +1,167 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "logMsg/logMsg.h" // LM_* +#include "logMsg/traceLevels.h" // LmtMongoc + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/forwarding/distOpSend.h" // distOpSend +#include "orionld/forwarding/xForwardedForCompose.h" // xForwardedForCompose +#include "orionld/forwarding/viaCompose.h" // viaCompose +#include "orionld/forwarding/distOpsSend.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// distOpsSend - +// +int distOpsSend(DistOp* distOpList, bool local) +{ + char* xff = xForwardedForCompose(orionldState.in.xForwardedFor, localIpAndPort); + char* via = viaCompose(orionldState.in.via, brokerId); + + char dateHeader[70]; + snprintf(dateHeader, sizeof(dateHeader), "Date: %s", orionldState.requestTimeString); // MOVE to orionldStateInit, for example + + int forwards = 0; // Debugging purposees + for (DistOp* distOpP = distOpList; distOpP != NULL; distOpP = distOpP->next) + { + // Send the forwarded request and await all responses + if ((distOpP->regP != NULL) && (distOpP->error == false)) + { + distOpP->onlyIds = true; + + if (distOpSend(distOpP, dateHeader, xff, via, local, NULL) == 0) + distOpP->error = false; + else + distOpP->error = true; + + ++forwards; // Also when error? + } + } + + int stillRunning = 1; + int loops = 0; + + if (forwards > 0) + { + while (stillRunning != 0) + { + CURLMcode cm = curl_multi_perform(orionldState.curlDoMultiP, &stillRunning); + if (cm != 0) + { + LM_E(("Internal Error (curl_multi_perform: error %d)", cm)); + return -1; + } + + if (stillRunning != 0) + { + cm = curl_multi_wait(orionldState.curlDoMultiP, NULL, 0, 1000, NULL); + if (cm != CURLM_OK) + { + LM_E(("Internal Error (curl_multi_wait: error %d", cm)); + return -2; + } + } + + if ((++loops >= 50) && ((loops % 25) == 0)) + LM_W(("curl_multi_perform doesn't seem to finish ... (%d loops)", loops)); + } + + if (loops >= 100) + LM_W(("curl_multi_perform finally finished! (%d loops)", loops)); + } + + return forwards; +} + + + +// ----------------------------------------------------------------------------- +// +// distOpsSend2 - +// +int distOpsSend2(DistOpListItem* distOpList) +{ + char* xff = xForwardedForCompose(orionldState.in.xForwardedFor, localIpAndPort); + char* via = viaCompose(orionldState.in.via, brokerId); + + char dateHeader[70]; + snprintf(dateHeader, sizeof(dateHeader), "Date: %s", orionldState.requestTimeString); // MOVE to orionldStateInit, for example + + int forwards = 0; // Debugging purposees + for (DistOpListItem* doItemP = distOpList; doItemP != NULL; doItemP = doItemP->next) + { + DistOp* distOpP = doItemP->distOpP; + + // Send the forwarded request and await all responses + if ((distOpP->regP != NULL) && (distOpP->error == false)) + { + distOpP->onlyIds = false; + + if (distOpSend(distOpP, dateHeader, xff, via, false, doItemP->entityIds) == 0) + distOpP->error = false; + else + distOpP->error = true; + + ++forwards; // Also when error? + } + } + + int stillRunning = 1; + int loops = 0; + + if (forwards > 0) + { + while (stillRunning != 0) + { + CURLMcode cm = curl_multi_perform(orionldState.curlDoMultiP, &stillRunning); + if (cm != 0) + { + LM_E(("Internal Error (curl_multi_perform: error %d)", cm)); + return -1; + } + + if (stillRunning != 0) + { + cm = curl_multi_wait(orionldState.curlDoMultiP, NULL, 0, 1000, NULL); + if (cm != CURLM_OK) + { + LM_E(("Internal Error (curl_multi_wait: error %d", cm)); + return -2; + } + } + + if ((++loops >= 50) && ((loops % 25) == 0)) + LM_W(("curl_multi_perform doesn't seem to finish ... (%d loops)", loops)); + } + + if (loops >= 100) + LM_W(("curl_multi_perform finally finished! (%d loops)", loops)); + } + + return forwards; +} diff --git a/src/lib/orionld/forwarding/distOpsSend.h b/src/lib/orionld/forwarding/distOpsSend.h new file mode 100644 index 0000000000..a80a8e3542 --- /dev/null +++ b/src/lib/orionld/forwarding/distOpsSend.h @@ -0,0 +1,46 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_DISTOPSSEND_H_ +#define SRC_LIB_ORIONLD_FORWARDING_DISTOPSSEND_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/forwarding/DistOp.h" // DistOp, DistOpListItem + + + +// ----------------------------------------------------------------------------- +// +// distOpsSend - +// +extern int distOpsSend(DistOp* distOpList, bool local); + + + +// ----------------------------------------------------------------------------- +// +// distOpsSend2 - +// +extern int distOpsSend2(DistOpListItem* distOpList); + +#endif // SRC_LIB_ORIONLD_FORWARDING_DISTOPSSEND_H_ diff --git a/src/lib/orionld/forwarding/distOpsSendAndReceive.cpp b/src/lib/orionld/forwarding/distOpsSendAndReceive.cpp new file mode 100644 index 0000000000..7b033da46f --- /dev/null +++ b/src/lib/orionld/forwarding/distOpsSendAndReceive.cpp @@ -0,0 +1,45 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/forwarding/DistOp.h" // DistOpListItem +#include "orionld/forwarding/distOpsReceive2.h" // DistOpResponseTreatFunction, distOpsReceive2 +#include "orionld/forwarding/distOpsSend.h" // distOpsSend2 +#include "orionld/forwarding/distOpsSendAndReceive.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// distOpsSendAndReceive - +// +void distOpsSendAndReceive(DistOpListItem* distOpListItem, DistOpResponseTreatFunction treatFunction, void* callbackParam) +{ + // Send all distributed requests + + int forwards = distOpsSend2(distOpListItem); + + // Await all responses, if any + if (forwards > 0) + distOpsReceive2(distOpListItem, treatFunction, callbackParam); +} diff --git a/src/lib/orionld/forwarding/distOpsSendAndReceive.h b/src/lib/orionld/forwarding/distOpsSendAndReceive.h new file mode 100644 index 0000000000..cb506c270e --- /dev/null +++ b/src/lib/orionld/forwarding/distOpsSendAndReceive.h @@ -0,0 +1,39 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_DISTOPSSENDANDRECEIVE_H_ +#define SRC_LIB_ORIONLD_FORWARDING_DISTOPSSENDANDRECEIVE_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/forwarding/DistOp.h" // DistOpListItem +#include "orionld/forwarding/distOpsReceive2.h" // DistOpResponseTreatFunction + + + +// ----------------------------------------------------------------------------- +// +// distOpsSendAndReceive - +// +extern void distOpsSendAndReceive(DistOpListItem* distOpListItem, DistOpResponseTreatFunction treatFunction, void* callbackParam); + +#endif // SRC_LIB_ORIONLD_FORWARDING_DISTOPSSENDANDRECEIVE_H_ diff --git a/src/lib/orionld/forwarding/regMatchAttributes.cpp b/src/lib/orionld/forwarding/regMatchAttributes.cpp index a57b278e27..1ea82c5fb9 100644 --- a/src/lib/orionld/forwarding/regMatchAttributes.cpp +++ b/src/lib/orionld/forwarding/regMatchAttributes.cpp @@ -47,7 +47,7 @@ KjNode* regMatchAttributes(RegCacheItem* regP, DistOpType operation, KjNode* pro { // // Registration of entire entity? - // Only 'inclusive' registrations can do that + // 'exclusive' registrations can't do that // if ((propertyNamesP == NULL) && (relationshipNamesP == NULL)) { diff --git a/src/lib/orionld/forwarding/regMatchAttributesForGet.cpp b/src/lib/orionld/forwarding/regMatchAttributesForGet.cpp index f0490b37fa..908753e5bd 100644 --- a/src/lib/orionld/forwarding/regMatchAttributesForGet.cpp +++ b/src/lib/orionld/forwarding/regMatchAttributesForGet.cpp @@ -24,14 +24,13 @@ */ extern "C" { -#include "kalloc/kaStrdup.h" // kaStrdup #include "kjson/kjLookup.h" // kjLookup } #include "logMsg/logMsg.h" // LM_* #include "orionld/common/orionldState.h" // orionldState -#include "orionld/types/StringArray.h" // StringArray +#include "orionld/types/StringArray.h" // StringArray, stringArrayClone #include "orionld/kjTree/kjStringValueLookupInArray.h" // kjStringValueLookupInArray #include "orionld/regCache/RegCache.h" // RegCacheItem #include "orionld/forwarding/regMatchAttributesForGet.h" // Own interface @@ -76,27 +75,6 @@ static bool stringArrayRemoveItem(StringArray* saP, int ix) -// ----------------------------------------------------------------------------- -// -// stringArrayClone - FIXME: Move to common/stringArrayClone.h/cpp -// -StringArray* stringArrayClone(StringArray* attrV) -{ - StringArray* clone = (StringArray*) kaAlloc(&orionldState.kalloc, sizeof(StringArray)); - - clone->items = attrV->items; - clone->array = (char**) kaAlloc(&orionldState.kalloc, attrV->items * sizeof(char*)); - - for (int ix = 0; ix < clone->items; ix++) - { - clone->array[ix] = kaStrdup(&orionldState.kalloc, attrV->array[ix]); - } - - return clone; -} - - - // ----------------------------------------------------------------------------- // // regMatchAttributesForGet - @@ -111,37 +89,45 @@ StringArray* regMatchAttributesForGet RegCacheItem* regP, KjNode* propertyNamesP, KjNode* relationshipNamesP, - StringArray* attrV, + StringArray* attrListP, const char* geoProp ) { - bool allAttributes = (propertyNamesP == NULL) && (relationshipNamesP == NULL); + bool allAttributes = (propertyNamesP == NULL) && (relationshipNamesP == NULL); + + LM_T(LmtDistOpAttributes, ("Creating the union of attributes GET URL-Param vs Registered Attributes")); if (allAttributes == true) { // - // Note, attrV can be NULL (if no 'attrs' URL param has been used) + // Note, attrListP can be NULL (if no 'attrs' URL param has been used) // However, a return value of NULL means "Nothing matches" and that's exactly what we don't want here // // So, we'll need an empty StringArray - meaning EVERYTHING matches => don't include 'attrs' URI param in the forwarded request // That comes a few lines down (right after allocating the StringArray, see 'if (allAttributes == true)') // - if (attrV != NULL) - return stringArrayClone(attrV); + // Now, if the query is with an "attrs" parameter, we'll create a copy of it for this DistOp + // + if (attrListP != NULL) + { + LM_T(LmtDistOpAttributes, ("Keeping the URL-Param Attributes as the registration has no attributes specified")); + return stringArrayClone(attrListP); + } } StringArray* sList = (StringArray*) kaAlloc(&orionldState.kalloc, sizeof(StringArray)); - int items; + int items = 0; - if (allAttributes == true) // We know that attrV == NULL in such case (see a few lines up) + if (allAttributes == true) // We know that attrListP == NULL (otherwise it would have returned already a few lines up) { // Everything matches - return an empty array sList->items = 0; sList->array = NULL; + LM_T(LmtDistOpAttributes, ("Using ALL Attributes as the registration has no attributes specified and the Query also not")); return sList; } - else if (attrV != NULL) - items = attrV->items; + else if ((attrListP != NULL) && (attrListP->items > 0)) + items = attrListP->items; else { // Count items in propertyNamesP + relationshipNamesP @@ -152,9 +138,10 @@ StringArray* regMatchAttributesForGet } sList->items = items; - sList->array = (char**) kaAlloc(&orionldState.kalloc, sizeof(char*) * items); + if (items > 0) + sList->array = (char**) kaAlloc(&orionldState.kalloc, sizeof(char*) * items); - if (attrV == NULL) + if ((attrListP == NULL) || (attrListP->items == 0)) { // Just copy all propertyNames + relationshipNames to sList int ix = 0; @@ -162,6 +149,7 @@ StringArray* regMatchAttributesForGet { for (KjNode* pName = propertyNamesP->value.firstChildP; pName != NULL; pName = pName->next) { + LM_T(LmtDistOpAttributes, ("Adding '%s' to the attrList of the DistOp", pName->value.s)); sList->array[ix++] = pName->value.s; } } @@ -170,6 +158,7 @@ StringArray* regMatchAttributesForGet { for (KjNode* rName = relationshipNamesP->value.firstChildP; rName != NULL; rName = rName->next) { + LM_T(LmtDistOpAttributes, ("Adding '%s' to the attrList of the DistOp", rName->value.s)); sList->array[ix++] = rName->value.s; } } @@ -179,23 +168,24 @@ StringArray* regMatchAttributesForGet else { int matches = 0; - for (int ix = 0; ix < attrV->items; ix++) + for (int ix = 0; ix < attrListP->items; ix++) { bool match = false; if (propertyNamesP != NULL) - match = (kjStringValueLookupInArray(propertyNamesP, attrV->array[ix]) != NULL); + match = (kjStringValueLookupInArray(propertyNamesP, attrListP->array[ix]) != NULL); if ((match == false) && (relationshipNamesP != NULL)) - match = (kjStringValueLookupInArray(relationshipNamesP, attrV->array[ix]) != NULL); + match = (kjStringValueLookupInArray(relationshipNamesP, attrListP->array[ix]) != NULL); if (match == false) continue; - sList->array[matches++] = attrV->array[ix]; + LM_T(LmtDistOpAttributes, ("Adding '%s' to the attrList of the DistOp", attrListP->array[ix])); + sList->array[matches++] = attrListP->array[ix]; if (regP->mode == RegModeExclusive) - stringArrayRemoveItem(attrV, ix); + stringArrayRemoveItem(attrListP, ix); } if (matches == 0) @@ -204,5 +194,6 @@ StringArray* regMatchAttributesForGet sList->items = matches; } + LM_T(LmtDistOpAttributes, ("Returning an attrList of %d items", sList->items)); return sList; } diff --git a/src/lib/orionld/forwarding/regMatchAttributesForQuery.cpp b/src/lib/orionld/forwarding/regMatchAttributesForQuery.cpp new file mode 100644 index 0000000000..445c943d54 --- /dev/null +++ b/src/lib/orionld/forwarding/regMatchAttributesForQuery.cpp @@ -0,0 +1,42 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +} + +#include "orionld/types/StringArray.h" // StringArray +#include "orionld/regCache/RegCache.h" // RegCacheItem + + + +// ----------------------------------------------------------------------------- +// +// regMatchAttributesForQuery - +// +StringArray* regMatchAttributesForQuery(RegCacheItem* regP, KjNode* propertyNamesP, KjNode* relationshipNamesP, StringArray* attrListP) +{ + return NULL; +} diff --git a/src/lib/orionld/forwarding/regMatchAttributesForQuery.h b/src/lib/orionld/forwarding/regMatchAttributesForQuery.h new file mode 100644 index 0000000000..a6f4e018e8 --- /dev/null +++ b/src/lib/orionld/forwarding/regMatchAttributesForQuery.h @@ -0,0 +1,44 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_REGMATCHATTRIBUTESFORQUERY_H_ +#define SRC_LIB_ORIONLD_FORWARDING_REGMATCHATTRIBUTESFORQUERY_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +} + +#include "orionld/types/StringArray.h" // StringArray +#include "orionld/regCache/RegCache.h" // RegCacheItem + + + +// ----------------------------------------------------------------------------- +// +// regMatchAttributesForQuery - +// +extern StringArray* regMatchAttributesForQuery(RegCacheItem* regP, KjNode* propertyNamesP, KjNode* relationshipNamesP, StringArray* attrListP); + +#endif // SRC_LIB_ORIONLD_FORWARDING_REGMATCHATTRIBUTESFORQUERY_H_ diff --git a/src/lib/orionld/forwarding/regMatchEntityInfo.cpp b/src/lib/orionld/forwarding/regMatchEntityInfo.cpp index 045ef95a48..4ee82368a7 100644 --- a/src/lib/orionld/forwarding/regMatchEntityInfo.cpp +++ b/src/lib/orionld/forwarding/regMatchEntityInfo.cpp @@ -1,3 +1,4 @@ + /* * * Copyright 2022 FIWARE Foundation e.V. @@ -40,9 +41,9 @@ extern "C" // ----------------------------------------------------------------------------- // -// regIdPatternLookup - +// regIdPatternLookup - FIXME: Own module orionld/forwarding/regIdPatternLookup.cpp/h // -static RegIdPattern* regIdPatternLookup(RegCacheItem* regP, KjNode* idPatternP) +RegIdPattern* regIdPatternLookup(RegCacheItem* regP, KjNode* idPatternP) { RegIdPattern* ripP = regP->idPatternRegexList; @@ -83,6 +84,13 @@ bool regMatchEntityInfo(RegCacheItem* regP, KjNode* entityInfoP, const char* ent { KjNode* idP = kjLookup(entityInfoP, "id"); KjNode* idPatternP = kjLookup(entityInfoP, "idPattern"); + KjNode* typeP = kjLookup(entityInfoP, "type"); + + if (typeP == NULL) + { + LM_E(("%s: invalid registration (no type in information::entities)")); + return false; + } // // "id" is mandatory for 'exclusive' registrations diff --git a/src/lib/orionld/forwarding/regMatchEntityInfoForQuery.cpp b/src/lib/orionld/forwarding/regMatchEntityInfoForQuery.cpp new file mode 100644 index 0000000000..fd1c4ebb71 --- /dev/null +++ b/src/lib/orionld/forwarding/regMatchEntityInfoForQuery.cpp @@ -0,0 +1,176 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjLookup.h" // kjLookup +} + +#include "orionld/regCache/RegCache.h" // RegCacheItem, RegIdPattern +#include "orionld/types/StringArray.h" // StringArray +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/forwarding/regMatchEntityInfoForQuery.h" // regMatchEntityInfoForQuery + + + +// ----------------------------------------------------------------------------- +// +// regIdPatternLookup - from regMatchEntityInfo.cpp +// +extern RegIdPattern* regIdPatternLookup(RegCacheItem* regP, KjNode* idPatternP); + + + +// ----------------------------------------------------------------------------- +// +// regMatchEntityInfoForQuery - +// +// Parameters: +// regP: Pointer to the registration in the reg-cache +// entityInfoP: { "type": "T", "id": "urn:E1" } +// idListP: StringArray of Entity Ids (can be empty) +// typeListP: StringArray of Entity Types (can be empty) +// +bool regMatchEntityInfoForQuery(RegCacheItem* regP, KjNode* entityInfoP, StringArray* idListP, StringArray* typeListP, DistOp* distOpP) +{ + KjNode* regEntityTypeP = kjLookup(entityInfoP, "type"); + KjNode* regEntityIdP = kjLookup(entityInfoP, "id"); + KjNode* regEntityIdPatternP = kjLookup(entityInfoP, "idPattern"); + + // This should never happen + if (regEntityTypeP == NULL) + { + LM_E(("%s: invalid registration (no type in information::entities)")); + return false; + } + + + // + // If the query had any entity types present we need to match the entityInfoP against that + // + if (typeListP->items > 0) + { + char* entityType = regEntityTypeP->value.s; + bool match = false; + + for (int ix = 0; ix < typeListP->items; ix++) + { + if (strcmp(typeListP->array[ix], entityType) == 0) + { + match = true; + break; + } + } + + if (match == false) + { + LM_T(LmtRegMatch, ("%s: No Reg Match due to entity type ('%s' in reg)", regP->regId, entityType)); + return false; + } + } + + // + // If the query had any entity ids present we need to match the entityInfoP against that + // This can be either matched against the id in the regiostration OR the idPattern + // + if (idListP->items > 0) + { + if (regEntityIdP != NULL) + { + char* regEntityId = regEntityIdP->value.s; + bool match = false; + + for (int ix = 0; ix < idListP->items; ix++) + { + if (strcmp(idListP->array[ix], regEntityId) == 0) + { + match = true; + break; + } + } + + if (match == false) + { + LM_T(LmtRegMatch, ("%s: No Reg Match due to entity id ('%s' in reg)", regP->regId, regEntityId)); + return false; + } + } + else if (regEntityIdPatternP != NULL) + { + RegIdPattern* ripP = regIdPatternLookup(regP, regEntityIdPatternP); + + if (ripP == NULL) + { + LM_E(("%s: Internal Error (the regex corresponding to this idPattern could not be found)", regP->regId)); + return false; + } + else + { + bool match = false; + + for (int ix = 0; ix < idListP->items; ix++) + { + char* entityId = idListP->array[ix]; + + if (regexec(&ripP->regex, entityId, 0, NULL, 0) != 0) + { + match = true; + break; + } + } + + if (match == false) + { + LM_T(LmtRegMatch, ("%s: No Reg Match due to entity id (idPattern in registration VS query entity-id-list)", regP->regId)); + return false; + } + } + } + } + + if (regEntityIdP != NULL) + { + distOpP->entityId = regEntityIdP->value.s; + distOpP->idList = NULL; + } + else + distOpP->idList = idListP; + + if ((regEntityIdP == NULL) && (regEntityIdPatternP != NULL)) + { + distOpP->entityIdPattern = regEntityIdPatternP->value.s; + distOpP->idList = NULL; + } + + if (regEntityTypeP != NULL) // "type" is mandatory. regEntityTypeP cannot be NULL + { + distOpP->entityType = regEntityTypeP->value.s; + distOpP->typeList = NULL; + } + else + distOpP->typeList = typeListP; + + return true; +} diff --git a/src/lib/orionld/forwarding/regMatchEntityInfoForQuery.h b/src/lib/orionld/forwarding/regMatchEntityInfoForQuery.h new file mode 100644 index 0000000000..f3da9e53fc --- /dev/null +++ b/src/lib/orionld/forwarding/regMatchEntityInfoForQuery.h @@ -0,0 +1,52 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_REGMATCHENTITYINFOFORQUERY_H_ +#define SRC_LIB_ORIONLD_FORWARDING_REGMATCHENTITYINFOFORQUERY_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +} + +#include "orionld/regCache/RegCache.h" // RegCacheItem +#include "orionld/types/StringArray.h" // StringArray +#include "orionld/forwarding/DistOp.h" // DistOp + + + +// ----------------------------------------------------------------------------- +// +// regMatchEntityInfoForQuery - +// +extern bool regMatchEntityInfoForQuery +( + RegCacheItem* regP, + KjNode* entityInfoP, + StringArray* idListP, + StringArray* typeListP, + DistOp* distOpP +); + +#endif // SRC_LIB_ORIONLD_FORWARDING_REGMATCHENTITYINFOFORQUERY_H_ diff --git a/src/lib/orionld/forwarding/regMatchForBatchDelete.cpp b/src/lib/orionld/forwarding/regMatchForBatchDelete.cpp index fcb2f38f3a..165db6b471 100644 --- a/src/lib/orionld/forwarding/regMatchForBatchDelete.cpp +++ b/src/lib/orionld/forwarding/regMatchForBatchDelete.cpp @@ -36,6 +36,7 @@ extern "C" #include "orionld/forwarding/regMatchOperation.h" // regMatchOperation #include "orionld/forwarding/regMatchInformationArrayForGet.h" // regMatchInformationArrayForGet #include "orionld/forwarding/xForwardedForMatch.h" // xForwardedForMatch +#include "orionld/forwarding/viaMatch.h" // viaMatch #include "orionld/forwarding/regMatchForBatchDelete.h" // Own interface @@ -56,22 +57,28 @@ DistOp* regMatchForBatchDelete for (RegCacheItem* regP = orionldState.tenantP->regCache->regList; regP != NULL; regP = regP->next) { + if ((regP->mode & regMode) == 0) + { + LM_T(LmtRegMatch, ("%s: No match due to regMode", regP->regId)); + continue; + } + // Loop detection - if (xForwardedForMatch(orionldState.in.xForwardedFor, regP->ipAndPort) == true) + if (viaMatch(orionldState.in.via, regP->hostAlias) == true) { - LM_T(LmtRegMatch, ("%s: No Reg Match due to loop detection", regP->regId)); + LM_T(LmtRegMatch, ("%s: No Reg Match due to Loop (Via)", regP->regId)); continue; } - if ((regP->mode & regMode) == 0) + if (xForwardedForMatch(orionldState.in.xForwardedFor, regP->ipAndPort) == true) { - LM_T(LmtRegMatch, ("%s: No Reg Match due to regMode", regP->regId)); + LM_T(LmtRegMatch, ("%s: No match due to loop detection", regP->regId)); continue; } if (regMatchOperation(regP, operation) == false) { - LM_T(LmtRegMatch, ("%s: No Reg Match due to Operation", regP->regId)); + LM_T(LmtRegMatch, ("%s: No match due to Operation (operation == %d: '%s')", regP->regId, operation, distOpTypes[operation])); continue; } @@ -97,7 +104,7 @@ DistOp* regMatchForBatchDelete distOpTail = distOpP; distOpTail->next = NULL; - LM_T(LmtRegMatch, ("%s: Reg Match !", regP->regId)); + LM_T(LmtRegMatch, ("%s: Match!", regP->regId)); } } } diff --git a/src/lib/orionld/forwarding/regMatchForEntityCreation.cpp b/src/lib/orionld/forwarding/regMatchForEntityCreation.cpp index eda9c66d90..a4863a6edf 100644 --- a/src/lib/orionld/forwarding/regMatchForEntityCreation.cpp +++ b/src/lib/orionld/forwarding/regMatchForEntityCreation.cpp @@ -36,10 +36,15 @@ extern "C" #include "orionld/forwarding/regMatchOperation.h" // regMatchOperation #include "orionld/forwarding/regMatchInformationArray.h" // regMatchInformationArray #include "orionld/forwarding/xForwardedForMatch.h" // xForwardedForMatch +#include "orionld/forwarding/viaMatch.h" // viaMatch #include "orionld/forwarding/regMatchForEntityCreation.h" // Own interface #if 0 +// ----------------------------------------------------------------------------- +// +// distOpListDebug - +// void distOpListDebug(DistOp* distOpP, const char* what) { LM_T(LmtDistOpList, ("----- DistOp List: %s", what)); @@ -114,16 +119,22 @@ DistOp* regMatchForEntityCreation for (RegCacheItem* regP = orionldState.tenantP->regCache->regList; regP != NULL; regP = regP->next) { + if ((regP->mode & regMode) == 0) + { + // LM_T(LmtRegMatch, ("%s: No Reg Match due to regMode (0x%x vs 0x%x)", regP->regId, regP->mode, regMode)); + continue; + } + // Loop detection - if (xForwardedForMatch(orionldState.in.xForwardedFor, regP->ipAndPort) == true) + if (viaMatch(orionldState.in.via, regP->hostAlias) == true) { - LM_T(LmtRegMatch, ("%s: No Reg Match due to loop detection", regP->regId)); + LM_T(LmtRegMatch, ("%s: No Reg Match due to Loop (Via)", regP->regId)); continue; } - if ((regP->mode & regMode) == 0) + if (xForwardedForMatch(orionldState.in.xForwardedFor, regP->ipAndPort) == true) { - // LM_T(LmtRegMatch, ("%s: No Reg Match due to regMode (0x%x vs 0x%x)", regP->regId, regP->mode, regMode)); + LM_T(LmtRegMatch, ("%s: No Reg Match due to loop detection", regP->regId)); continue; } diff --git a/src/lib/orionld/forwarding/regMatchForEntityGet.cpp b/src/lib/orionld/forwarding/regMatchForEntityGet.cpp index a67677838a..3a479348b3 100644 --- a/src/lib/orionld/forwarding/regMatchForEntityGet.cpp +++ b/src/lib/orionld/forwarding/regMatchForEntityGet.cpp @@ -28,6 +28,8 @@ extern "C" #include "kjson/kjLookup.h" // kjLookup } +#include "logMsg/logMsg.h" // LM_* + #include "orionld/common/orionldState.h" // orionldState #include "orionld/types/RegistrationMode.h" // registrationMode #include "orionld/types/StringArray.h" // StringArray @@ -37,6 +39,7 @@ extern "C" #include "orionld/forwarding/regMatchOperation.h" // regMatchOperation #include "orionld/forwarding/regMatchInformationArrayForGet.h" // regMatchInformationArrayForGet #include "orionld/forwarding/xForwardedForMatch.h" // xForwardedForMatch +#include "orionld/forwarding/viaMatch.h" // viaMatch #include "orionld/forwarding/regMatchForEntityGet.h" // Own interface @@ -72,18 +75,22 @@ DistOp* regMatchForEntityGet // FIXME: +entity-type regP->regId = (regIdP != NULL)? regIdP->value.s : (char*) "unknown registration"; } - LM_T(LmtRegMatch, ("Treating registration '%s' for registrations of mode '%s'", regP->regId, registrationModeToString(regMode))); + if ((regP->mode & regMode) == 0) + { + LM_T(LmtRegMatch, ("%s: No match due to regMode", regP->regId)); + continue; + } // Loop detection - if (xForwardedForMatch(orionldState.in.xForwardedFor, regP->ipAndPort) == true) + if (viaMatch(orionldState.in.via, regP->hostAlias) == true) { - LM_T(LmtRegMatch, ("No Reg Match due to loop detection")); + LM_T(LmtRegMatch, ("%s: No Reg Match due to Loop (Via)", regP->regId)); continue; } - if ((regP->mode & regMode) == 0) + if (xForwardedForMatch(orionldState.in.xForwardedFor, regP->ipAndPort) == true) { - LM_T(LmtRegMatch, ("%s: No Reg Match due to regMode", regP->regId)); + LM_T(LmtRegMatch, ("No Reg Match due to loop detection")); continue; } diff --git a/src/lib/orionld/forwarding/regMatchInformationArray.cpp b/src/lib/orionld/forwarding/regMatchInformationArray.cpp index 5a0996024c..ae0ec3db39 100644 --- a/src/lib/orionld/forwarding/regMatchInformationArray.cpp +++ b/src/lib/orionld/forwarding/regMatchInformationArray.cpp @@ -34,6 +34,7 @@ extern "C" #include "orionld/regCache/RegCache.h" // RegCacheItem #include "orionld/forwarding/DistOp.h" // DistOp #include "orionld/forwarding/DistOpType.h" // DistOpType +#include "orionld/forwarding/distOpCreate.h" // distOpCreate #include "orionld/forwarding/regMatchInformationItem.h" // regMatchInformationItem @@ -77,9 +78,8 @@ DistOp* regMatchInformationArray } } - DistOp* distOpP = (DistOp*) kaAlloc(&orionldState.kalloc, sizeof(DistOp)); + DistOp* distOpP = distOpCreate(operation, regP, NULL, NULL, NULL); - distOpP->regP = regP; distOpP->requestBody = attrUnion; if (distOpList == NULL) diff --git a/src/lib/orionld/forwarding/regMatchInformationArrayForGet.cpp b/src/lib/orionld/forwarding/regMatchInformationArrayForGet.cpp index c169e506f4..706efd4780 100644 --- a/src/lib/orionld/forwarding/regMatchInformationArrayForGet.cpp +++ b/src/lib/orionld/forwarding/regMatchInformationArrayForGet.cpp @@ -34,7 +34,9 @@ extern "C" #include "orionld/types/StringArray.h" // StringArray #include "orionld/regCache/RegCache.h" // RegCacheItem #include "orionld/forwarding/DistOp.h" // DistOp -#include "orionld/forwarding/regMatchInformationItemForGet.h" // regMatchInformationItemForGet +#include "orionld/forwarding/distOpCreate.h" // distOpCreate +#include "orionld/forwarding/distOpAttrs.h" // distOpAttrs +#include "orionld/forwarding/regMatchInformationItemForGet.h" // Own interface @@ -54,11 +56,12 @@ DistOp* regMatchInformationArrayForGet(RegCacheItem* regP, const char* entityId, continue; // If we get this far, then it's a match and we can create the DistOp item and return - DistOp* distOpP = (DistOp*) kaAlloc(&orionldState.kalloc, sizeof(DistOp)); + DistOp* distOpP = distOpCreate(DoQueryEntity, regP, NULL, NULL, attrList); - distOpP->regP = regP; - distOpP->attrList = attrList; - distOpP->geoProp = (char*) geoProp; + if ((distOpP->attrList != NULL) && (distOpP->attrList->items > 0)) + distOpAttrs(distOpP, distOpP->attrList); + + distOpP->geometryProperty = (char*) geoProp; return distOpP; } diff --git a/src/lib/orionld/forwarding/regMatchInformationArrayForQuery.cpp b/src/lib/orionld/forwarding/regMatchInformationArrayForQuery.cpp new file mode 100644 index 0000000000..9b2a18ce96 --- /dev/null +++ b/src/lib/orionld/forwarding/regMatchInformationArrayForQuery.cpp @@ -0,0 +1,65 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kalloc/kaAlloc.h" // kaAlloc +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjLookup.h" // kjLookup +#include "kjson/kjBuilder.h" // kjString, kjChildAdd +} + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/types/StringArray.h" // StringArray +#include "orionld/regCache/RegCache.h" // RegCacheItem +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/forwarding/distOpListsMerge.h" // distOpListsMerge +#include "orionld/forwarding/regMatchInformationItemForQuery.h" // regMatchInformationItemForQuery +#include "orionld/forwarding/regMatchInformationArrayForQuery.h" // Own interface + + +// ----------------------------------------------------------------------------- +// +// regMatchInformationArrayForQuery - +// +DistOp* regMatchInformationArrayForQuery(RegCacheItem* regP, StringArray* idListP, StringArray* typeListP, StringArray* attrListP) +{ + DistOp* distOpList = NULL; + KjNode* informationV = kjLookup(regP->regTree, "information"); + + for (KjNode* infoP = informationV->value.firstChildP; infoP != NULL; infoP = infoP->next) + { + DistOp* distOps = regMatchInformationItemForQuery(regP, infoP, idListP, typeListP, attrListP); + + // + // regMatchEntityInfoForQuery fills in the entity id/type fields for the DistOps, + // That is how we "respect" the registration + // The search criteria is narrowed down using info from the registration + // + if (distOps != NULL) + distOpList = distOpListsMerge(distOpList, distOps); + } + + return distOpList; +} diff --git a/src/lib/orionld/forwarding/regMatchInformationArrayForQuery.h b/src/lib/orionld/forwarding/regMatchInformationArrayForQuery.h new file mode 100644 index 0000000000..f7aa4918a7 --- /dev/null +++ b/src/lib/orionld/forwarding/regMatchInformationArrayForQuery.h @@ -0,0 +1,45 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_REGMATCHINFORMATIONARRAYFORQUERY_H_ +#define SRC_LIB_ORIONLD_FORWARDING_REGMATCHINFORMATIONARRAYFORQUERY_H_ + +/* +* +* Copyright 2022 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +} + +#include "orionld/types/StringArray.h" // StringArray +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/regCache/RegCache.h" // RegCacheItem + + + +// ----------------------------------------------------------------------------- +// +// regMatchInformationArrayForQuery - +// +extern DistOp* regMatchInformationArrayForQuery(RegCacheItem* regP, StringArray* idListP, StringArray* typeListP, StringArray* attrListP); + +#endif // SRC_LIB_ORIONLD_FORWARDING_REGMATCHINFORMATIONARRAYFORQUERY_H_ diff --git a/src/lib/orionld/forwarding/regMatchInformationItemForQuery.cpp b/src/lib/orionld/forwarding/regMatchInformationItemForQuery.cpp new file mode 100644 index 0000000000..813f03c54b --- /dev/null +++ b/src/lib/orionld/forwarding/regMatchInformationItemForQuery.cpp @@ -0,0 +1,123 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjLookup.h" // kjLookup +} + +#include "orionld/common/orionldState.h" // orionldState, kjTreeLog +#include "orionld/regCache/RegCache.h" // RegCacheItem +#include "orionld/types/StringArray.h" // StringArray +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/forwarding/distOpCreate.h" // distOpCreate +#include "orionld/forwarding/distOpListsMerge.h" // distOpListsMerge +#include "orionld/forwarding/regMatchEntityInfoForQuery.h" // regMatchEntityInfoForQuery +#include "orionld/forwarding/regMatchAttributesForGet.h" // regMatchAttributesForGet +#include "orionld/forwarding/distOpAttrs.h" // distOpAttrs +#include "orionld/forwarding/regMatchInformationItemForQuery.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// regMatchInformationItemForQuery - +// +DistOp* regMatchInformationItemForQuery +( + RegCacheItem* regP, + KjNode* infoP, + StringArray* idListP, + StringArray* typeListP, + StringArray* attrListP +) +{ + KjNode* entities = kjLookup(infoP, "entities"); + DistOp* distOpList = NULL; + + if (entities != NULL) + { + DistOp* distOpP = NULL; + + for (KjNode* entityInfoP = entities->value.firstChildP; entityInfoP != NULL; entityInfoP = entityInfoP->next) + { + // + // Creating a DistOp, in case entityInfoP matches (regMatchEntityInfoForQuery fills it in on hit). + // If used: + // - distOpP is inserted in distOpList, + // - it is NULLed, and + // - it is allocated again in the next round of the loop + // + distOpP = distOpCreate(DoQueryEntity, regP, idListP, typeListP, attrListP); + + if (regMatchEntityInfoForQuery(regP, entityInfoP, idListP, typeListP, distOpP) == true) + { + if (distOpList == NULL) + distOpList = distOpP; + else + { + distOpP->next = distOpList; + distOpList = distOpP; + } + } + } + + if (distOpList == NULL) + return NULL; + } + else + { + DistOp* distOpP = distOpCreate(DoQueryEntity, regP, NULL, typeListP, attrListP); + + if (distOpList == NULL) + distOpList = distOpP; + else + { + distOpP->next = distOpList; + distOpList = distOpP; + } + } + + // + // The attributes are the same for all matching entity array items + // + KjNode* propertyNamesP = kjLookup(infoP, "propertyNames"); + KjNode* relationshipNamesP = kjLookup(infoP, "relationshipNames"); + StringArray* attrUnionP = regMatchAttributesForGet(regP, propertyNamesP, relationshipNamesP, attrListP, NULL); + + if (attrUnionP == NULL) + return NULL; + + for (DistOp* distOpP = distOpList; distOpP != NULL; distOpP = distOpP->next) + { + distOpP->attrList = attrUnionP; + if ((distOpP->attrList != NULL) && (distOpP->attrList->items > 0)) + distOpAttrs(distOpP, distOpP->attrList); + else + distOpP->attrsParam = NULL; + } + + return distOpList; +} diff --git a/src/lib/orionld/forwarding/regMatchInformationItemForQuery.h b/src/lib/orionld/forwarding/regMatchInformationItemForQuery.h new file mode 100644 index 0000000000..76ba974705 --- /dev/null +++ b/src/lib/orionld/forwarding/regMatchInformationItemForQuery.h @@ -0,0 +1,52 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_REGMATCHINFORMATIONITEMFORQUERY_H_ +#define SRC_LIB_ORIONLD_FORWARDING_REGMATCHINFORMATIONITEMFORQUERY_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +} + +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/regCache/RegCache.h" // RegCacheItem +#include "orionld/types/StringArray.h" // StringArray + + + +// ----------------------------------------------------------------------------- +// +// regMatchInformationItemForQuery - +// +extern DistOp* regMatchInformationItemForQuery +( + RegCacheItem* regP, + KjNode* infoP, + StringArray* idListP, + StringArray* typeListP, + StringArray* attrListP +); + +#endif // SRC_LIB_ORIONLD_FORWARDING_REGMATCHINFORMATIONITEMFORQUERY_H_ diff --git a/src/lib/orionld/forwarding/viaCompose.cpp b/src/lib/orionld/forwarding/viaCompose.cpp new file mode 100644 index 0000000000..ada63610fe --- /dev/null +++ b/src/lib/orionld/forwarding/viaCompose.cpp @@ -0,0 +1,64 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // snprintf +#include // strlen + +extern "C" +{ +#include "kalloc/kaAlloc.h" // kaAlloc +} + +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/forwarding/viaCompose.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// viaCompose - +// +char* viaCompose(char* via, char* self) +{ + int viaLen = 6; // strlen("Via: ") + '\0' + + if (via != NULL) + viaLen += strlen(via); + viaLen += 6 + strlen(self); // 4: ", 1.1 " + + char* viaHeader = kaAlloc(&orionldState.kalloc, viaLen); + + if (viaHeader == NULL) + LM_X(1, ("Out of memory (kaAlloc failed to allocate %d bytes", viaLen)); + + if (via != NULL) + snprintf(viaHeader, viaLen, "Via: %s, 1.1 %s", via, self); // Hardcoding HTTP version 1.1 ... + else + snprintf(viaHeader, viaLen, "Via: 1.1 %s", self); + + LM_T(LmtHeaders, ("via: '%s'", viaHeader)); + return viaHeader; +} diff --git a/src/lib/orionld/forwarding/viaCompose.h b/src/lib/orionld/forwarding/viaCompose.h new file mode 100644 index 0000000000..533e81beb6 --- /dev/null +++ b/src/lib/orionld/forwarding/viaCompose.h @@ -0,0 +1,37 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_VIACOMPOSE_H_ +#define SRC_LIB_ORIONLD_FORWARDING_VIACOMPOSE_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ + + + +// ----------------------------------------------------------------------------- +// +// viaCompose - +// +extern char* viaCompose(char* via, char* self); + +#endif // SRC_LIB_ORIONLD_FORWARDING_VIACOMPOSE_H_ diff --git a/src/lib/orionld/forwarding/viaMatch.cpp b/src/lib/orionld/forwarding/viaMatch.cpp new file mode 100644 index 0000000000..762faabc83 --- /dev/null +++ b/src/lib/orionld/forwarding/viaMatch.cpp @@ -0,0 +1,82 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // strstr + +#include "logMsg/logMsg.h" // LM_* + + + +// ----------------------------------------------------------------------------- +// +// viaMatch - +// +bool viaMatch(char* currentVia, char* next) +{ + LM_T(LmtDistOpLoop, ("Loop Detection: Via header: '%s' (from 'forwarder')", currentVia)); + LM_T(LmtDistOpLoop, ("Loop Detection: Forward to source: '%s' (next 'forwardee')", next)); + + // + // For example: + // currentVia == "Via: 1.1 CB, 1.1 CP1" + // next == "CP1" + // + // It's a match if (all three match): + // 1. "next" is found as a substring of currentVia + // 2. the char before is ' ' AND + // 3. The char after is either '\0', ' ', or ',' + // + if (currentVia == NULL) + { + LM_T(LmtDistOpLoop, ("Loop Detection: No Via header present, so no loop detected")); + return false; + } + + char* subString = strstr(currentVia, next); + if (subString == NULL) + { + LM_T(LmtDistOpLoop, ("Loop Detection: IP/port (%s) not in Via header, so no loop detected", next)); + return false; + } + + char charBefore = (subString == currentVia)? ' ' : subString[-1]; + char charAfter = subString[strlen(next)]; + + LM_T(LmtDistOpLoop, ("Loop Detection: subString: '%s'", subString)); + LM_T(LmtDistOpLoop, ("Loop Detection: charBefore=0x%x, charAfter=0x%x", charBefore & 0xFF, charAfter & 0xFF)); + if (charBefore != ' ') + { + LM_T(LmtDistOpLoop, ("Loop Detection: No Match")); + return false; + } + + if ((charAfter != 0) && (charAfter != ',') && (charAfter != ' ')) + { + LM_T(LmtDistOpLoop, ("Loop Detection: No Match")); + return false; + } + + LM_T(LmtDistOpLoop, ("Loop Detection: Detected a loop - must stop it!")); + return true; +} diff --git a/src/lib/orionld/forwarding/viaMatch.h b/src/lib/orionld/forwarding/viaMatch.h new file mode 100644 index 0000000000..922c92f4bc --- /dev/null +++ b/src/lib/orionld/forwarding/viaMatch.h @@ -0,0 +1,37 @@ +#ifndef SRC_LIB_ORIONLD_FORWARDING_VIAMATCH_H_ +#define SRC_LIB_ORIONLD_FORWARDING_VIAMATCH_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ + + + +// ----------------------------------------------------------------------------- +// +// viaMatch - +// +extern bool viaMatch(char* hostsHeader, char* host); + +#endif // SRC_LIB_ORIONLD_FORWARDING_VIAMATCH_H_ diff --git a/src/lib/orionld/forwarding/xForwardedForMatch.cpp b/src/lib/orionld/forwarding/xForwardedForMatch.cpp index a957aa215b..8dd81a23e6 100644 --- a/src/lib/orionld/forwarding/xForwardedForMatch.cpp +++ b/src/lib/orionld/forwarding/xForwardedForMatch.cpp @@ -24,6 +24,8 @@ */ #include // strstr +#include "logMsg/logMsg.h" // LM_* + // ----------------------------------------------------------------------------- @@ -32,6 +34,9 @@ // bool xForwardedForMatch(char* hostsHeader, char* host) { + LM_T(LmtDistOpLoop, ("Loop Detection: X-Forwarded-For header: '%s' (from 'forwarder')", hostsHeader)); + LM_T(LmtDistOpLoop, ("Loop Detection: Forward to IP/port: '%s' (next 'forwardee')", host)); + // // For example: // hostsHeader == "X-Forwarded-For: host1:1026, host2:1028" @@ -43,20 +48,35 @@ bool xForwardedForMatch(char* hostsHeader, char* host) // 3. The char after is either '\0', ' ', or ',' // if (hostsHeader == NULL) + { + LM_T(LmtDistOpLoop, ("Loop Detection: No X-Forwarded-For header present, so no loop detected")); return false; + } char* subString = strstr(hostsHeader, host); if (subString == NULL) + { + LM_T(LmtDistOpLoop, ("Loop Detection: IP/port (%s) not in X-Forwarded-For header, so no loop detected", host)); return false; + } - char charBefore = subString[-1]; - char charAFter = subString[strlen(host)]; + char charBefore = (subString == hostsHeader)? ' ' : subString[-1]; + char charAfter = subString[strlen(host)]; + LM_T(LmtDistOpLoop, ("Loop Detection: subString: '%s'", subString)); + LM_T(LmtDistOpLoop, ("Loop Detection: charBefore=0x%x, charAfter=0x%x", charBefore & 0xFF, charAfter & 0xFF)); if ((charBefore != ' ') && (charBefore != ':') && (charBefore != ',')) + { + LM_T(LmtDistOpLoop, ("Loop Detection: No Match")); return false; + } - if ((charAFter != 0) && (charAFter != ',') && (charAFter != ' ')) + if ((charAfter != 0) && (charAfter != ',') && (charAfter != ' ')) + { + LM_T(LmtDistOpLoop, ("Loop Detection: No Match")); return false; + } + LM_T(LmtDistOpLoop, ("Loop Detection: Detected a loop - must stop it!")); return true; } diff --git a/src/lib/orionld/kjTree/kjSort.cpp b/src/lib/orionld/kjTree/kjSort.cpp index efb59d4ead..dbac3efffd 100644 --- a/src/lib/orionld/kjTree/kjSort.cpp +++ b/src/lib/orionld/kjTree/kjSort.cpp @@ -121,7 +121,7 @@ void kjStringArraySort(KjNode* arrayP) // while (rawP->value.firstChildP != NULL) { - // Set the very first item as the smaalest one (minP) + // Set the very first item as the smallest one (minP) KjNode* minP = rawP->value.firstChildP; // Compare with all the rest and set minP accordingly diff --git a/src/lib/orionld/kjTree/kjTreeFromNotification.cpp b/src/lib/orionld/kjTree/kjTreeFromNotification.cpp index dab5c9b1d5..e7ef0bdd6c 100644 --- a/src/lib/orionld/kjTree/kjTreeFromNotification.cpp +++ b/src/lib/orionld/kjTree/kjTreeFromNotification.cpp @@ -65,8 +65,7 @@ KjNode* kjTreeFromNotification(NotifyContextRequest* ncrP, const char* context, { // id char notificationId[80]; - strncpy(notificationId, "urn:ngsi-ld:Notification:", sizeof(notificationId) - 1); - uuidGenerate(¬ificationId[25], sizeof(notificationId) - 25, false); + uuidGenerate(notificationId, sizeof(notificationId), "urn:ngsi-ld:Notification:"); nodeP = kjString(orionldState.kjsonP, "id", notificationId); kjChildAdd(rootP, nodeP); diff --git a/src/lib/orionld/kjTree/kjTreeToRegistration.cpp b/src/lib/orionld/kjTree/kjTreeToRegistration.cpp index bb7707c6fd..de1967949f 100644 --- a/src/lib/orionld/kjTree/kjTreeToRegistration.cpp +++ b/src/lib/orionld/kjTree/kjTreeToRegistration.cpp @@ -184,8 +184,7 @@ bool kjTreeToRegistration(ngsiv2::Registration* regP, char** regIdPP) if (orionldState.payloadIdNode == NULL) { char registrationId[100]; - strncpy(registrationId, "urn:ngsi-ld:ContextSourceRegistration:", sizeof(registrationId) - 1); - uuidGenerate(®istrationId[38], sizeof(registrationId) - 38, false); + uuidGenerate(registrationId, sizeof(registrationId), "urn:ngsi-ld:ContextSourceRegistration:"); regP->id = registrationId; } else diff --git a/src/lib/orionld/kjTree/kjTreeToSubscription.cpp b/src/lib/orionld/kjTree/kjTreeToSubscription.cpp index 7df8a44135..b80cce0d9a 100644 --- a/src/lib/orionld/kjTree/kjTreeToSubscription.cpp +++ b/src/lib/orionld/kjTree/kjTreeToSubscription.cpp @@ -177,8 +177,7 @@ bool kjTreeToSubscription(ngsiv2::Subscription* subP, char** subIdPP, KjNode** e if (orionldState.payloadIdNode == NULL) { char subscriptionId[80]; - strncpy(subscriptionId, "urn:ngsi-ld:Subscription:", sizeof(subscriptionId) - 1); - uuidGenerate(&subscriptionId[25], sizeof(subscriptionId) - 25, false); + uuidGenerate(subscriptionId, sizeof(subscriptionId), "urn:ngsi-ld:Subscription:"); subP->id = subscriptionId; } else diff --git a/src/lib/orionld/mongoc/mongocAuxAttributesFilter.cpp b/src/lib/orionld/mongoc/mongocAuxAttributesFilter.cpp index 4063e41c3b..296e4d04a1 100644 --- a/src/lib/orionld/mongoc/mongocAuxAttributesFilter.cpp +++ b/src/lib/orionld/mongoc/mongocAuxAttributesFilter.cpp @@ -40,12 +40,15 @@ extern "C" // // mongocAuxAttributesFilter - // -bool mongocAuxAttributesFilter(bson_t* mongoFilterP, StringArray* attrList, bson_t* projectionP, const char* geojsonGeometry) +bool mongocAuxAttributesFilter(bson_t* mongoFilterP, StringArray* attrList, bson_t* projectionP, const char* geojsonGeometry, bool onlyIds) { char path[512]; // Assuming 512 is always enough ... bson_t exists; bool geojsonGeometryToProjection = (geojsonGeometry == NULL)? false : true; // if GEOJSON, the "geometry" must be present + if (onlyIds == true) + geojsonGeometryToProjection = false; + bson_init(&exists); bson_append_int32(&exists, "$exists", 7, 1); @@ -73,7 +76,9 @@ bool mongocAuxAttributesFilter(bson_t* mongoFilterP, StringArray* attrList, bson dotForEq(&path[6]); bson_append_document(&attrExists, path, len, &exists); - bson_append_bool(projectionP, path, len, true); + + if (onlyIds == false) + bson_append_bool(projectionP, path, len, true); bson_append_document(&array, &num[2-numLen], numLen, &attrExists); @@ -96,29 +101,32 @@ bool mongocAuxAttributesFilter(bson_t* mongoFilterP, StringArray* attrList, bson // // Then "@datasets.X" - geojsonGeometryToProjection is already taken care of by "attrs" loop // - int offset = attrList->items; - for (int ix = 0; ix < attrList->items; ix++) + if (onlyIds == false) { - int len = snprintf(path, sizeof(path) - 1, "@datasets.%s", attrList->array[ix]); - bson_t attrExists; + int offset = attrList->items; + for (int ix = 0; ix < attrList->items; ix++) + { + int len = snprintf(path, sizeof(path) - 1, "@datasets.%s", attrList->array[ix]); + bson_t attrExists; - bson_init(&attrExists); - dotForEq(&path[10]); + bson_init(&attrExists); + dotForEq(&path[10]); - bson_append_document(&attrExists, path, len, &exists); - bson_append_bool(projectionP, path, len, true); + bson_append_document(&attrExists, path, len, &exists); + bson_append_bool(projectionP, path, len, true); - bson_append_document(&array, &num[2-numLen], numLen, &attrExists); + bson_append_document(&array, &num[2-numLen], numLen, &attrExists); - num[1] += 1; - if (((offset + ix + 1) % 10) == 0) - { - num[1] = '0'; - num[0] += 1; - numLen = 2; - } + num[1] += 1; + if (((offset + ix + 1) % 10) == 0) + { + num[1] = '0'; + num[0] += 1; + numLen = 2; + } - bson_destroy(&attrExists); + bson_destroy(&attrExists); + } } bson_append_array(mongoFilterP, "$or", 3, &array); diff --git a/src/lib/orionld/mongoc/mongocAuxAttributesFilter.h b/src/lib/orionld/mongoc/mongocAuxAttributesFilter.h index 442c800df9..56e2eefc92 100644 --- a/src/lib/orionld/mongoc/mongocAuxAttributesFilter.h +++ b/src/lib/orionld/mongoc/mongocAuxAttributesFilter.h @@ -40,6 +40,6 @@ extern "C" // // mongocAuxAttributesFilter - // -extern bool mongocAuxAttributesFilter(bson_t* mongoFilterP, StringArray* attrList, bson_t* projectionP, const char* geojsonGeometry); +extern bool mongocAuxAttributesFilter(bson_t* mongoFilterP, StringArray* attrList, bson_t* projectionP, const char* geojsonGeometry, bool onlyIds); #endif // SRC_LIB_ORIONLD_MONGOC_MONGOCAUXATTRIBUTESFILTER_H_ diff --git a/src/lib/orionld/mongoc/mongocEntitiesQuery.cpp b/src/lib/orionld/mongoc/mongocEntitiesQuery.cpp index 867ec76660..4502ca293b 100644 --- a/src/lib/orionld/mongoc/mongocEntitiesQuery.cpp +++ b/src/lib/orionld/mongoc/mongocEntitiesQuery.cpp @@ -660,7 +660,8 @@ KjNode* mongocEntitiesQuery QNode* qNode, OrionldGeoInfo* geoInfoP, int64_t* countP, - const char* geojsonGeometry + const char* geojsonGeometry, + bool onlyIds ) { if ((attrList != NULL) && (attrList->items > 99)) @@ -681,34 +682,40 @@ KjNode* mongocEntitiesQuery // Sort, Limit, Offset // bson_t options; - bson_t sortDoc; - int limit = orionldState.uriParams.limit; - int offset = orionldState.uriParams.offset; + bson_t projection; bson_init(&options); - bson_init(&sortDoc); - - bson_append_int32(&sortDoc, "creDate", 7, 1); - bson_append_document(&options, "sort", 4, &sortDoc); - bson_destroy(&sortDoc); - - bson_append_int32(&options, "limit", 5, limit); - if (offset != 0) - bson_append_int32(&options, "skip", 4, offset); - - - // - // Projection (will be added to if attrList != NULL) - // - bson_t projection; bson_init(&projection); - bson_append_bool(&projection, "_id.id", 6, true); - bson_append_bool(&projection, "_id.type", 8, true); - bson_append_bool(&projection, "attrNames", 9, true); - bson_append_bool(&projection, "creDate", 7, true); - bson_append_bool(&projection, "modDate", 7, true); - bson_append_bool(&projection, "lastCorrelator", 14, true); + if (onlyIds == false) + { + bson_t sortDoc; + int limit = orionldState.uriParams.limit; + int offset = orionldState.uriParams.offset; + + bson_init(&sortDoc); + + bson_append_int32(&sortDoc, "creDate", 7, 1); + bson_append_document(&options, "sort", 4, &sortDoc); + bson_destroy(&sortDoc); + + bson_append_int32(&options, "limit", 5, limit); + if (offset != 0) + bson_append_int32(&options, "skip", 4, offset); + + + // + // Projection (will be added to if attrList != NULL) + // + bson_append_bool(&projection, "_id.id", 6, true); + bson_append_bool(&projection, "_id.type", 8, true); + bson_append_bool(&projection, "attrNames", 9, true); + bson_append_bool(&projection, "creDate", 7, true); + bson_append_bool(&projection, "modDate", 7, true); + bson_append_bool(&projection, "lastCorrelator", 14, true); + } + else + bson_append_bool(&projection, "_id.id", 6, true); // // Create the filter for the query @@ -739,13 +746,16 @@ KjNode* mongocEntitiesQuery // Attribute List if ((attrList != NULL) && (attrList->items > 0)) { - if (mongocAuxAttributesFilter(&mongoFilter, attrList, &projection, geojsonGeometry) == false) + if (mongocAuxAttributesFilter(&mongoFilter, attrList, &projection, geojsonGeometry, onlyIds) == false) return NULL; } else { - bson_append_bool(&projection, "attrs", 5, true); - bson_append_bool(&projection, "@datasets", 9, true); + if (onlyIds == false) + { + bson_append_bool(&projection, "attrs", 5, true); + bson_append_bool(&projection, "@datasets", 9, true); + } } // Query Language @@ -773,7 +783,7 @@ KjNode* mongocEntitiesQuery // semTake(&mongoEntitiesSem); // count? - if (orionldState.uriParams.count == true) + if (countP != NULL) { bson_error_t error; @@ -790,9 +800,9 @@ KjNode* mongocEntitiesQuery // KjNode* entityArray = kjArray(orionldState.kjsonP, NULL); - if (limit != 0) + if (orionldState.uriParams.limit != 0) { - MONGOC_RLOG("Lookup Entities", orionldState.tenantP->mongoDbName, "entities", &mongoFilter, LmtMongoc); + MONGOC_RLOG("Querying Entities", orionldState.tenantP->mongoDbName, "entities", &mongoFilter, &options, LmtMongoc); mongoCursorP = mongoc_collection_find_with_opts(orionldState.mongoc.entitiesP, &mongoFilter, &options, readPrefs); bson_destroy(&options); @@ -805,13 +815,9 @@ KjNode* mongocEntitiesQuery return NULL; } -#if 1 - // const bson_t* lastError = mongoc_collection_get_last_error(orionldState.mongoc.entitiesP); if (lastError != NULL) LM_E(("MongoC Error: %s", bson_as_canonical_extended_json(lastError, NULL))); - // -#endif int hits = 0; while (mongoc_cursor_next(mongoCursorP, &mongoDocP)) diff --git a/src/lib/orionld/mongoc/mongocEntitiesQuery.h b/src/lib/orionld/mongoc/mongocEntitiesQuery.h index 1e4177699a..674842b6c4 100644 --- a/src/lib/orionld/mongoc/mongocEntitiesQuery.h +++ b/src/lib/orionld/mongoc/mongocEntitiesQuery.h @@ -49,7 +49,8 @@ extern KjNode* mongocEntitiesQuery QNode* qNode, OrionldGeoInfo* geoInfoP, int64_t* countP, - const char* geojsonGeometry + const char* geojsonGeometry, + bool onlyIds ); #endif // SRC_LIB_ORIONLD_MONGOC_MONGOCENTITIESQUERY_H_ diff --git a/src/lib/orionld/mongoc/mongocEntitiesQuery2.cpp b/src/lib/orionld/mongoc/mongocEntitiesQuery2.cpp index 93ef62336c..859f48e5d7 100644 --- a/src/lib/orionld/mongoc/mongocEntitiesQuery2.cpp +++ b/src/lib/orionld/mongoc/mongocEntitiesQuery2.cpp @@ -249,7 +249,7 @@ static bool attributesFilter(bson_t* mongoFilterP, KjNode* attrArray, bson_t* pr // ----------------------------------------------------------------------------- // -// mongocEntitiesQuery2 - +// mongocEntitiesQuery2 - needed for POST Query // // Four parameters are passed via orionldState: // - orionldState.uriParams.offset (URL parameter) @@ -380,7 +380,7 @@ KjNode* mongocEntitiesQuery2 bson_free(optionsString); #endif - MONGOC_RLOG("Lookup Entities", orionldState.tenantP->mongoDbName, "entities", &mongoFilter, LmtMongoc); + MONGOC_RLOG("Lookup Entities", orionldState.tenantP->mongoDbName, "entities", &mongoFilter, &options, LmtMongoc); mongoCursorP = mongoc_collection_find_with_opts(orionldState.mongoc.entitiesP, &mongoFilter, &options, readPrefs); bson_destroy(&options); diff --git a/src/lib/orionld/mongoc/mongocEntityLookup.cpp b/src/lib/orionld/mongoc/mongocEntityLookup.cpp index 620d9d349b..1d3734af4c 100644 --- a/src/lib/orionld/mongoc/mongocEntityLookup.cpp +++ b/src/lib/orionld/mongoc/mongocEntityLookup.cpp @@ -96,7 +96,7 @@ KjNode* mongocEntityLookup(const char* entityId, const char* entityType, StringA // Attribute List AND GeoJSON Geometry if ((attrsV != NULL) && (attrsV->items > 0)) { - if (mongocAuxAttributesFilter(&mongoFilter, attrsV, &projection, geojsonGeometry) == false) + if (mongocAuxAttributesFilter(&mongoFilter, attrsV, &projection, geojsonGeometry, false) == false) { if (detailP != NULL) *detailP = (char*) "mongocAuxAttributesFilter failed"; @@ -117,7 +117,7 @@ KjNode* mongocEntityLookup(const char* entityId, const char* entityType, StringA // // Run the query // - MONGOC_RLOG("Entity Lookup", orionldState.tenantP->mongoDbName, "entities", &mongoFilter, LmtMongoc); + MONGOC_RLOG("Entity Lookup", orionldState.tenantP->mongoDbName, "entities", &mongoFilter, &options, LmtMongoc); if ((mongoCursorP = mongoc_collection_find_with_opts(orionldState.mongoc.entitiesP, &mongoFilter, &options, readPrefs)) == NULL) { LM_E(("Internal Error (mongoc_collection_find_with_opts ERROR)")); diff --git a/src/lib/orionld/mongoc/mongocGeoIndexInit.cpp b/src/lib/orionld/mongoc/mongocGeoIndexInit.cpp index f8f24200e3..c6151b7837 100644 --- a/src/lib/orionld/mongoc/mongocGeoIndexInit.cpp +++ b/src/lib/orionld/mongoc/mongocGeoIndexInit.cpp @@ -117,23 +117,20 @@ bool mongocGeoIndexInit(void) "}"); unwind = BCON_NEW("$unwind", BCON_UTF8("$attributeKeys")); - - group = BCON_NEW("$group", "{", - "_id", BCON_UTF8("$attributeKeys.k"), - "}"); + group = BCON_NEW("$group", "{", "_id", BCON_UTF8("$attributeKeys.k"), "}"); bson_append_document(objectArray, "0", 1, project); bson_append_document(objectArray, "1", 1, unwind); bson_append_document(objectArray, "2", 1, group); - // Append the array to the document + // Append the array to the pipeline bson_append_array(pipeline, "pipeline", 8, objectArray); - // Print the BSON document as a JSON string if (lmTraceIsSet(LmtMongoc)) { char* str = bson_as_relaxed_extended_json(pipeline, NULL); LM_T(LmtMongoc, ("%s", str)); + bson_free(str); } // @@ -176,11 +173,21 @@ bool mongocGeoIndexInit(void) LM_T(LmtMongoc, ("Found geoProperty: '%s'", geoPropertyName)); if (dbGeoIndexLookup(tenantP->tenant, geoPropertyName) == NULL) + { mongocGeoIndexCreate(tenantP, geoPropertyName); + LM_T(LmtMongoc, ("Creating index for property '%s'", geoPropertyName)); + } } mongoc_cursor_destroy(mongoCursorP); mongoc_collection_destroy(mCollectionP); + + bson_destroy(objectArray); + bson_destroy(group); + bson_destroy(unwind); + bson_destroy(project); + bson_destroy(pipeline); + tenantP = tenantP->next; } diff --git a/src/lib/orionld/mongoc/mongocKjTreeFromBson.cpp b/src/lib/orionld/mongoc/mongocKjTreeFromBson.cpp index 4395d17d47..4bd7688b49 100644 --- a/src/lib/orionld/mongoc/mongocKjTreeFromBson.cpp +++ b/src/lib/orionld/mongoc/mongocKjTreeFromBson.cpp @@ -59,7 +59,6 @@ KjNode* mongocKjTreeFromBson(const void* dataP, char** titleP, char** detailsP) else { treeP = kjParse(orionldState.kjsonP, json); - LM_T(LmtPernot, ("")); if (treeP == NULL) { *titleP = (char*) "Internal Error"; diff --git a/src/lib/orionld/mongoc/mongocKjTreeToBson.cpp b/src/lib/orionld/mongoc/mongocKjTreeToBson.cpp index bfe1f7f71f..ca1a5938ed 100644 --- a/src/lib/orionld/mongoc/mongocKjTreeToBson.cpp +++ b/src/lib/orionld/mongoc/mongocKjTreeToBson.cpp @@ -54,13 +54,13 @@ static void kjTreeToBson(KjNode* nodeP, bson_t* parentP, bool inArray, int array { slen = snprintf(nameBuf, sizeof(nameBuf), "%d", arrayIndex); name = nameBuf; - LM_T(LmtMongoc, ("Array Index: '%s'", name)); + // LM_T(LmtMongoc, ("Array Index: '%s'", name)); } else { name = nodeP->name; slen = strlen(name); - LM_T(LmtMongoc, ("Name: '%s'", name)); + // LM_T(LmtMongoc, ("Name: '%s'", name)); } if (nodeP->type == KjString) bson_append_utf8(parentP, name, slen, nodeP->value.s, -1); @@ -77,7 +77,7 @@ static void kjTreeToBson(KjNode* nodeP, bson_t* parentP, bool inArray, int array for (KjNode* itemP = nodeP->value.firstChildP; itemP != NULL; itemP = itemP->next) { - LM_T(LmtMongoc, ("Calling kjTreeToBson for item '%s' of container '%s'", itemP->name, nodeP->name)); + // LM_T(LmtMongoc, ("Calling kjTreeToBson for item '%s' of container '%s'", itemP->name, nodeP->name)); kjTreeToBson(itemP, &bObject, false); } bson_append_document_end(parentP, &bObject); @@ -91,7 +91,7 @@ static void kjTreeToBson(KjNode* nodeP, bson_t* parentP, bool inArray, int array bson_append_array_begin(parentP, name, slen, &bArray); for (KjNode* itemP = nodeP->value.firstChildP; itemP != NULL; itemP = itemP->next) { - LM_T(LmtMongoc, ("Calling kjTreeToBson for array item %d of '%s'", arrayIndex, nodeP->name)); + // LM_T(LmtMongoc, ("Calling kjTreeToBson for array item %d of '%s'", arrayIndex, nodeP->name)); kjTreeToBson(itemP, &bArray, true, arrayIndex); arrayIndex += 1; } @@ -115,7 +115,7 @@ void mongocKjTreeToBson(KjNode* treeP, bson_t* bsonP) int arrayIx = 0; for (KjNode* itemP = treeP->value.firstChildP; itemP != NULL; itemP = itemP->next) { - LM_T(LmtMongoc, ("Calling kjTreeToBson for '%s' of '%s'", itemP->name, treeP->name)); + // LM_T(LmtMongoc, ("Calling kjTreeToBson for '%s' of '%s'", itemP->name, treeP->name)); kjTreeToBson(itemP, bsonP, inArray, arrayIx); if (inArray == true) diff --git a/src/lib/orionld/mongoc/mongocRegistrationsIter.cpp b/src/lib/orionld/mongoc/mongocRegistrationsIter.cpp index b668731839..213a978baa 100644 --- a/src/lib/orionld/mongoc/mongocRegistrationsIter.cpp +++ b/src/lib/orionld/mongoc/mongocRegistrationsIter.cpp @@ -71,7 +71,7 @@ int mongocRegistrationsIter(RegCache* rcP, RegCacheIterFunc callback) // // Run the query // - MONGOC_RLOG("Query for all regs", rcP->tenantP->mongoDbName, "registrations", NULL, LmtMongoc); + MONGOC_RLOG("Query for all regs", rcP->tenantP->mongoDbName, "registrations", NULL, NULL, LmtMongoc); mongoCursorP = mongoc_collection_find_with_opts(regsCollectionP, &mongoFilter, NULL, readPrefs); if (mongoCursorP == NULL) { @@ -86,6 +86,7 @@ int mongocRegistrationsIter(RegCache* rcP, RegCacheIterFunc callback) { char* json = bson_as_relaxed_extended_json(mongoDocP, NULL); LM_T(LmtMongoc, ("Found a registration in the DB: '%s'", json)); + bson_free(json); KjNode* dbRegP = mongocKjTreeFromBson(mongoDocP, &title, &details); if (dbRegP == NULL) diff --git a/src/lib/orionld/mongoc/mongocSubCachePopulateByTenant.cpp b/src/lib/orionld/mongoc/mongocSubCachePopulateByTenant.cpp index f808d7fe4c..8566903ce6 100644 --- a/src/lib/orionld/mongoc/mongocSubCachePopulateByTenant.cpp +++ b/src/lib/orionld/mongoc/mongocSubCachePopulateByTenant.cpp @@ -82,7 +82,7 @@ bool mongocSubCachePopulateByTenant(OrionldTenant* tenantP, bool refresh) // Run the query // // semTake(&mongoSubscriptionsSem); - MONGOC_RLOG("Subscription for sub-cache", tenantP->mongoDbName, "subscriptions", &mongoFilter, LmtMongoc); + MONGOC_RLOG("Subscription for sub-cache", tenantP->mongoDbName, "subscriptions", &mongoFilter, NULL, LmtMongoc); if ((mongoCursorP = mongoc_collection_find_with_opts(subscriptionsP, &mongoFilter, NULL, NULL)) == NULL) { mongoc_client_pool_push(mongocPool, connectionP); diff --git a/src/lib/orionld/mongoc/mongocSubscriptionLookup.cpp b/src/lib/orionld/mongoc/mongocSubscriptionLookup.cpp index 19ec53a221..ffccc7f427 100644 --- a/src/lib/orionld/mongoc/mongocSubscriptionLookup.cpp +++ b/src/lib/orionld/mongoc/mongocSubscriptionLookup.cpp @@ -68,7 +68,7 @@ KjNode* mongocSubscriptionLookup(const char* subscriptionId) // Run the query // // semTake(&mongoSubscriptionsSem); - MONGOC_RLOG("Lookup Subscription", orionldState.tenantP->mongoDbName, "subscriptions", &mongoFilter, LmtMongoc); + MONGOC_RLOG("Lookup Subscription", orionldState.tenantP->mongoDbName, "subscriptions", &mongoFilter, NULL, LmtMongoc); if ((mongoCursorP = mongoc_collection_find_with_opts(orionldState.mongoc.subscriptionsP, &mongoFilter, NULL, NULL)) == NULL) { LM_E(("Internal Error (mongoc_collection_find_with_opts ERROR)")); diff --git a/src/lib/orionld/mongoc/mongocWriteLog.cpp b/src/lib/orionld/mongoc/mongocWriteLog.cpp index 53a2eb5352..982ca5c629 100644 --- a/src/lib/orionld/mongoc/mongocWriteLog.cpp +++ b/src/lib/orionld/mongoc/mongocWriteLog.cpp @@ -84,3 +84,53 @@ void mongocWriteLog bson_free(request); } } + + + +// ----------------------------------------------------------------------------- +// +// mongocReadLog - +// +void mongocReadLog +( + const char* msg, + const char* dbName, + const char* collectionName, + bson_t* filterP, + bson_t* optionsP, + const char* path, + int lineNo, + const char* functionName, + int traceLevel +) +{ + char* fileNameOnly = fileName(path); + + char line[2048]; + + snprintf(line, sizeof(line), "---------- %s ----------", msg); + lmOut(line, 'T', fileNameOnly, lineNo, functionName, traceLevel, NULL); + + snprintf(line, sizeof(line), " * Database Name: '%s'", dbName); + lmOut(line, 'T', fileNameOnly, lineNo, functionName, traceLevel, NULL); + + snprintf(line, sizeof(line), " * Collection Name: '%s'", collectionName); + lmOut(line, 'T', fileNameOnly, lineNo, functionName, traceLevel, NULL); + + + if (filterP != NULL) + { + char* filter = bson_as_json(filterP, NULL); + snprintf(line, sizeof(line), " * Filter: '%s'", filter); + lmOut(line, 'T', fileNameOnly, lineNo, functionName, traceLevel, NULL); + bson_free(filter); + } + + if (optionsP != NULL) + { + char* options = bson_as_json(optionsP, NULL); + snprintf(line, sizeof(line), " * Options: '%s'", options); + lmOut(line, 'T', fileNameOnly, lineNo, functionName, traceLevel, NULL); + bson_free(options); + } +} diff --git a/src/lib/orionld/mongoc/mongocWriteLog.h b/src/lib/orionld/mongoc/mongocWriteLog.h index 04e94f843c..924e860365 100644 --- a/src/lib/orionld/mongoc/mongocWriteLog.h +++ b/src/lib/orionld/mongoc/mongocWriteLog.h @@ -48,11 +48,11 @@ do // // MONGOC_RLOG - // -#define MONGOC_RLOG(msg, dbName, collectionName, selectorP, traceLevel) \ +#define MONGOC_RLOG(msg, dbName, collectionName, filterP, optionsP, traceLevel) \ do \ { \ if (LM_MASK(LogLevelDebug) && lmOk('T', traceLevel) == LmsOk) \ - mongocWriteLog(msg, dbName, collectionName, selectorP, NULL, __FILE__, __LINE__, __FUNCTION__, traceLevel); \ + mongocReadLog(msg, dbName, collectionName, filterP, optionsP, __FILE__, __LINE__, __FUNCTION__, traceLevel); \ } while (0) @@ -77,4 +77,26 @@ extern void mongocWriteLog int traceLevel ); + + +// ----------------------------------------------------------------------------- +// +// mongocReadLog - +// +// NOTE +// This function is not meant to be called directly. Always via the MONGOC_RLOG macro +// +extern void mongocReadLog +( + const char* msg, + const char* dbName, + const char* collectionName, + bson_t* filterP, + bson_t* optionsP, + const char* fileName, + int lineNo, + const char* functionName, + int traceLevel +); + #endif // SRC_LIB_ORIONLD_MONGOC_MONGOCWRITELOG_H_ diff --git a/src/lib/orionld/notifications/notificationSend.cpp b/src/lib/orionld/notifications/notificationSend.cpp index 65d0216700..54a700fb3a 100644 --- a/src/lib/orionld/notifications/notificationSend.cpp +++ b/src/lib/orionld/notifications/notificationSend.cpp @@ -539,8 +539,7 @@ static KjNode* notificationTree(OrionldAlterationMatch* matchList) KjNode* notificationP = kjObject(orionldState.kjsonP, NULL); char notificationId[80]; - strncpy(notificationId, "urn:ngsi-ld:Notification:", sizeof(notificationId) - 1); // notificationId, could be a thread variable ... - uuidGenerate(¬ificationId[25], sizeof(notificationId) - 25, false); + uuidGenerate(notificationId, sizeof(notificationId), "urn:ngsi-ld:Notification:"); // notificationId could be a thread variable ... KjNode* idNodeP = kjString(orionldState.kjsonP, "id", notificationId); KjNode* typeNodeP = kjString(orionldState.kjsonP, "type", "Notification"); diff --git a/src/lib/orionld/payloadCheck/CMakeLists.txt b/src/lib/orionld/payloadCheck/CMakeLists.txt index f3a3405fca..0c9f3017a7 100644 --- a/src/lib/orionld/payloadCheck/CMakeLists.txt +++ b/src/lib/orionld/payloadCheck/CMakeLists.txt @@ -47,6 +47,7 @@ SET (SOURCES pcheckEntityInfo.cpp pcheckEntityInfoArray.cpp pcheckGeoPropertyValue.cpp + pCheckGeo.cpp pcheckGeoQ.cpp pCheckGeometry.cpp pCheckGeoCoordinates.cpp diff --git a/src/lib/orionld/payloadCheck/pCheckGeo.cpp b/src/lib/orionld/payloadCheck/pCheckGeo.cpp new file mode 100644 index 0000000000..1fcdc3d89d --- /dev/null +++ b/src/lib/orionld/payloadCheck/pCheckGeo.cpp @@ -0,0 +1,114 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // bzero +#include // NULL + +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjParse.h" // kjParse +} + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/common/orionldError.h" // orionldError +#include "orionld/common/urlDecode.h" // urlDecode +#include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo +#include "orionld/context/orionldAttributeExpand.h" // orionldAttributeExpand +#include "orionld/payloadCheck/pCheckGeometry.h" // pCheckGeometry +#include "orionld/payloadCheck/pCheckGeorelString.h" // pCheckGeorelString +#include "orionld/payloadCheck/pCheckGeoCoordinates.h" // pCheckGeoCoordinates +#include "orionld/payloadCheck/pCheckGeo.h" // Own interface + + + +// ---------------------------------------------------------------------------- +// +// pCheckGeo - +// +bool pCheckGeo(OrionldGeoInfo* geoInfoP, char* geometry, char* georel, char* coordinates, char* geoproperty) +{ + bzero(geoInfoP, sizeof(OrionldGeoInfo)); + + if ((geometry != NULL) || + (georel != NULL) || + (coordinates != NULL) || + (geoproperty != NULL)) + { + // + // geometry + // + if (geometry == NULL) + { + orionldError(OrionldBadRequestData, "Invalid Geo-Filter", "missing: geometry", 400); + return false; + } + if (pCheckGeometry(geometry, &geoInfoP->geometry, false) == false) + return false; + + // + // georel + // + if (georel == NULL) + { + orionldError(OrionldBadRequestData, "Invalid Geo-Filter", "missing: georel", 400); + return false; + } + + if (pCheckGeorelString(georel, geoInfoP) == false) + return false; + + // + // coordinates + // + if (coordinates == NULL) + { + orionldError(OrionldBadRequestData, "Invalid Geo-Filter", "missing: coordinates", 400); + return false; + } + + urlDecode(coordinates); + geoInfoP->coordinates = kjParse(orionldState.kjsonP, coordinates); + + if (geoInfoP->coordinates == NULL) + { + orionldError(OrionldBadRequestData, "Invalid Geo-Filter", "invalid coordinates", 400); + return false; + } + + if (pCheckGeoCoordinates(geoInfoP->coordinates, geoInfoP->geometry) == false) + return false; + + + // + // geoproperty + // + if ((geoproperty != NULL) && (strcmp(geoproperty, "location") != 0)) + geoInfoP->geoProperty = orionldAttributeExpand(orionldState.contextP, geoproperty, true, NULL); + else + geoInfoP->geoProperty = (char*) "location"; + } + + return true; +} diff --git a/src/lib/orionld/payloadCheck/pCheckGeo.h b/src/lib/orionld/payloadCheck/pCheckGeo.h new file mode 100644 index 0000000000..d3beaf21b1 --- /dev/null +++ b/src/lib/orionld/payloadCheck/pCheckGeo.h @@ -0,0 +1,38 @@ +#ifndef SRC_LIB_ORIONLD_PAYLOADCHECK_PCHECKGEO_H_ +#define SRC_LIB_ORIONLD_PAYLOADCHECK_PCHECKGEO_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo + + + +// ---------------------------------------------------------------------------- +// +// pCheckGeo - +// +extern bool pCheckGeo(OrionldGeoInfo* geoInfoP, char* geometry, char* georel, char* coordinates, char* geoproperty); + +#endif // SRC_LIB_ORIONLD_PAYLOADCHECK_PCHECKGEO_H_ diff --git a/src/lib/orionld/payloadCheck/pcheckInformationItem.cpp b/src/lib/orionld/payloadCheck/pcheckInformationItem.cpp index 8a1c6ca55d..ec86658e8b 100644 --- a/src/lib/orionld/payloadCheck/pcheckInformationItem.cpp +++ b/src/lib/orionld/payloadCheck/pcheckInformationItem.cpp @@ -306,7 +306,7 @@ bool pCheckOverlappingEntities(KjNode* entitiesP, KjNode* propertiesP, KjNode* r if (entitiesP == NULL) { - KjNode* entityArray = mongocEntitiesQuery(NULL, NULL, NULL, &attrsV, NULL, NULL, NULL, NULL); + KjNode* entityArray = mongocEntitiesQuery(NULL, NULL, NULL, &attrsV, NULL, NULL, NULL, NULL, false); if ((entityArray != NULL) && (entityArray->value.firstChildP != NULL)) { KjNode* _idP = kjLookup(entityArray->value.firstChildP, "_id"); @@ -349,7 +349,7 @@ bool pCheckOverlappingEntities(KjNode* entitiesP, KjNode* propertiesP, KjNode* r entityTypeList.array = entityTypeArray; entityTypeArray[0] = entityType; - KjNode* entityArray = mongocEntitiesQuery(&entityTypeList, NULL, entityIdPattern, &attrsV, NULL, NULL, NULL, NULL); + KjNode* entityArray = mongocEntitiesQuery(&entityTypeList, NULL, entityIdPattern, &attrsV, NULL, NULL, NULL, NULL, false); if ((entityArray != NULL) && (entityArray->value.firstChildP != NULL)) { KjNode* _idP = kjLookup(entityArray->value.firstChildP, "_id"); diff --git a/src/lib/orionld/payloadCheck/pcheckRegistration.cpp b/src/lib/orionld/payloadCheck/pcheckRegistration.cpp index b9beacdc4d..f97bac820e 100644 --- a/src/lib/orionld/payloadCheck/pcheckRegistration.cpp +++ b/src/lib/orionld/payloadCheck/pcheckRegistration.cpp @@ -96,6 +96,7 @@ bool pcheckRegistration(const char* regModeString, KjNode* registrationP, const KjNode* contextSourceInfoP = NULL; KjNode* scopeP = NULL; KjNode* modeP = NULL; + KjNode* hostAliasP = NULL; KjNode* operationsP = NULL; KjNode* managementP = NULL; KjNode* propertyTree = kjObject(orionldState.kjsonP, "properties"); // Temp storage for properties @@ -271,6 +272,12 @@ bool pcheckRegistration(const char* regModeString, KjNode* registrationP, const if (pCheckKeyValueArray(contextSourceInfoP, contextPP) == false) return false; } + else if (strcmp(nodeP->name, "hostAlias") == 0) + { + DUPLICATE_CHECK(hostAliasP, "hostAlias", nodeP); + STRING_CHECK(nodeP, "hostAlias"); + EMPTY_STRING_CHECK(nodeP, "hostAlias"); + } else if (strcmp(nodeP->name, "scope") == 0) { DUPLICATE_CHECK(scopeP, "scope", nodeP); diff --git a/src/lib/orionld/pernot/CMakeLists.txt b/src/lib/orionld/pernot/CMakeLists.txt index f27a44126a..91d51eb3ea 100644 --- a/src/lib/orionld/pernot/CMakeLists.txt +++ b/src/lib/orionld/pernot/CMakeLists.txt @@ -23,7 +23,8 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.5) SET (SOURCES pernotSubCacheInit.cpp pernotSubCacheAdd.cpp - pernotSubCacheRemove.cpp + pernotItemRelease.cpp + pernotRelease.cpp pernotSubCacheLookup.cpp pernotLoop.cpp pernotTreat.cpp diff --git a/src/lib/orionld/pernot/pernotItemRelease.cpp b/src/lib/orionld/pernot/pernotItemRelease.cpp new file mode 100644 index 0000000000..2f772be33b --- /dev/null +++ b/src/lib/orionld/pernot/pernotItemRelease.cpp @@ -0,0 +1,55 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/kjFree.h" // kjFree +} + +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription +#include "orionld/pernot/pernotItemRelease.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// pernotItemRelease - +// +bool pernotItemRelease(PernotSubscription* pSubP) +{ + LM_T(LmtLeak, ("Releasing pernot at %p", pSubP)); + if (pSubP->subscriptionId != NULL) + { + LM_T(LmtLeak, ("Releasing pernot id '%s'", pSubP->subscriptionId)); + free(pSubP->subscriptionId); + } + + if (pSubP->kjSubP != NULL) + { + LM_T(LmtLeak, ("Releasing pernot kj-tree at %p", pSubP->kjSubP)); + kjFree(pSubP->kjSubP); + } + + return true; +} diff --git a/src/lib/orionld/pernot/pernotSubCacheRemove.h b/src/lib/orionld/pernot/pernotItemRelease.h similarity index 85% rename from src/lib/orionld/pernot/pernotSubCacheRemove.h rename to src/lib/orionld/pernot/pernotItemRelease.h index b40898124d..57813a4a3b 100644 --- a/src/lib/orionld/pernot/pernotSubCacheRemove.h +++ b/src/lib/orionld/pernot/pernotItemRelease.h @@ -1,5 +1,5 @@ -#ifndef SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHEREMOVE_H_ -#define SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHEREMOVE_H_ +#ifndef SRC_LIB_ORIONLD_PERNOT_PERNOTITEMRELEASE_H_ +#define SRC_LIB_ORIONLD_PERNOT_PERNOTITEMRELEASE_H_ /* * @@ -38,8 +38,8 @@ // ----------------------------------------------------------------------------- // -// pernotSubCacheRemove - +// pernotItemRelease - // -extern bool pernotSubCacheRemove(PernotSubscription* pSubP); +extern bool pernotItemRelease(PernotSubscription* pSubP); -#endif // SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHEREMOVE_H_ +#endif // SRC_LIB_ORIONLD_PERNOT_PERNOTITEMRELEASE_H_ diff --git a/src/lib/orionld/pernot/pernotRelease.cpp b/src/lib/orionld/pernot/pernotRelease.cpp new file mode 100644 index 0000000000..7031fc9461 --- /dev/null +++ b/src/lib/orionld/pernot/pernotRelease.cpp @@ -0,0 +1,52 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/kjFree.h" // kjFree +} + +#include "orionld/common/orionldState.h" // pernotSubCache +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription +#include "orionld/pernot/pernotItemRelease.h" // pernotItemRelease +#include "orionld/pernot/pernotRelease.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// pernotRelease - +// +void pernotRelease(void) +{ + LM_T(LmtPernot, ("Releasing all pernot subscriptions")); + + PernotSubscription* psP = pernotSubCache.head; + while (psP != NULL) + { + LM_T(LmtPernot, ("Releasing pernot subscription %s (at %p)", psP->subscriptionId, psP)); + pernotItemRelease(psP); + psP = psP->next; + } +} diff --git a/src/lib/orionld/pernot/pernotRelease.h b/src/lib/orionld/pernot/pernotRelease.h new file mode 100644 index 0000000000..1449cba0e9 --- /dev/null +++ b/src/lib/orionld/pernot/pernotRelease.h @@ -0,0 +1,37 @@ +#ifndef SRC_LIB_ORIONLD_PERNOT_PERNOTRELEASE_H_ +#define SRC_LIB_ORIONLD_PERNOT_PERNOTRELEASE_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ + + + +// ----------------------------------------------------------------------------- +// +// pernotRelease - +// +extern void pernotRelease(void); + +#endif // SRC_LIB_ORIONLD_PERNOT_PERNOTRELEASE_H_ diff --git a/src/lib/orionld/pernot/pernotSend.cpp b/src/lib/orionld/pernot/pernotSend.cpp index 8b42cc8e28..ba9ca03f43 100644 --- a/src/lib/orionld/pernot/pernotSend.cpp +++ b/src/lib/orionld/pernot/pernotSend.cpp @@ -68,8 +68,7 @@ static KjNode* notificationTree(PernotSubscription* subP, KjNode* entityArray) KjNode* notificationP = kjObject(orionldState.kjsonP, NULL); char notificationId[80]; - strncpy(notificationId, "urn:ngsi-ld:Notification:", sizeof(notificationId) - 1); // notificationId, could be a thread variable ... - uuidGenerate(¬ificationId[25], sizeof(notificationId) - 25, false); + uuidGenerate(notificationId, sizeof(notificationId), "urn:ngsi-ld:Notification:"); char date[64]; numberToDate(subP->lastNotificationTime, date, sizeof(date)); diff --git a/src/lib/orionld/pernot/pernotSubCacheAdd.cpp b/src/lib/orionld/pernot/pernotSubCacheAdd.cpp index f4d799396a..7b0b23b9aa 100644 --- a/src/lib/orionld/pernot/pernotSubCacheAdd.cpp +++ b/src/lib/orionld/pernot/pernotSubCacheAdd.cpp @@ -149,6 +149,8 @@ PernotSubscription* pernotSubCacheAdd PernotSubscription* pSubP = (PernotSubscription*) malloc(sizeof(PernotSubscription)); bzero(pSubP, sizeof(PernotSubscription)); + LM_T(LmtPernot, ("Creating pernot subscription %s (at %p)", subscriptionId, pSubP)); + if (subscriptionId == NULL) { KjNode* idP = kjLookup(apiSubP, "id"); @@ -157,11 +159,13 @@ PernotSubscription* pernotSubCacheAdd subscriptionId = idP->value.s; } - LM_T(LmtPernot, ("Adding pernot subscription '%s' to cache", subscriptionId)); + LM_T(LmtPernot, ("Adding pernot subscription '%s' to cache (top level name: '%s')", subscriptionId, apiSubP->name)); pSubP->subscriptionId = strdup(subscriptionId); pSubP->timeInterval = timeInterval; pSubP->kjSubP = kjClone(NULL, apiSubP); + LM_T(LmtLeak, ("Cloned an apiSubP: %p", pSubP->kjSubP)); + kjTreeLog(pSubP->kjSubP, "apiSubP", LmtLeak); pSubP->tenantP = tenantP; pSubP->renderFormat = renderFormat; pSubP->sysAttrs = (sysAttrsP == NULL)? false : sysAttrsP->value.b; diff --git a/src/lib/orionld/pernot/pernotTreat.cpp b/src/lib/orionld/pernot/pernotTreat.cpp index 8fa6f1f962..8c85c14997 100644 --- a/src/lib/orionld/pernot/pernotTreat.cpp +++ b/src/lib/orionld/pernot/pernotTreat.cpp @@ -27,6 +27,7 @@ extern "C" { #include "kalloc/kaBufferInit.h" // kaBufferInit +#include "kalloc/kaBufferReset.h" // kaBufferReset #include "kjson/kjBufferCreate.h" // kjBufferCreate #include "kjson/KjNode.h" // KjNode #include "kjson/kjBuilder.h" // kjArray, ... @@ -123,14 +124,8 @@ static void* pernotTreat(void* vP) orionldState.uriParams.count = true; // Need the count to be able to paginate orionldState.tenantP = subP->tenantP; - kjTreeLog(subP->eSelector, "eSelector", LmtPernotQuery); - kjTreeLog(subP->attrsSelector, "attrsSelector", LmtPernotQuery); - LM_T(LmtPernotQuery, ("qSelector at %p", subP->qSelector)); - dbEntityArray = mongocEntitiesQuery2(subP->eSelector, subP->attrsSelector, subP->qSelector, NULL, subP->lang, &count); - LM_T(LmtPernot, ("mongocEntitiesQuery2 gave a count of %d", count)); - if ((dbEntityArray == NULL) || (count == 0)) { LM_T(LmtPernotFlush, ("mongocEntitiesQuery2 found no matches (noMatch was %d)", subP->noMatch)); @@ -193,7 +188,9 @@ static void* pernotTreat(void* vP) done: subP->notificationAttempts += 1; // timesSent + kaBufferReset(&orionldState.kalloc, true); pthread_exit(0); + return NULL; } diff --git a/src/lib/orionld/q/qLex.cpp b/src/lib/orionld/q/qLex.cpp index 1d8144c570..a3b739d962 100644 --- a/src/lib/orionld/q/qLex.cpp +++ b/src/lib/orionld/q/qLex.cpp @@ -94,10 +94,13 @@ static QNode* qTermPush(QNode* prev, char* term, bool* lastTermIsTimestampP, cha LM_T(LmtQ, ("term: '%s' (termLen: %d)", term, termLen)); - if (strcmp(&term[termLen - 10], "modifiedAt") == 0) *lastTermIsTimestampP = true; - else if (strcmp(&term[termLen - 9], "createdAt") == 0) *lastTermIsTimestampP = true; - else if (strcmp(&term[termLen - 10], "observedAt") == 0) *lastTermIsTimestampP = true; - else *lastTermIsTimestampP = false; + *lastTermIsTimestampP = false; + if (termLen >= 9) + { + if (strcmp(&term[termLen - 10], "modifiedAt") == 0) *lastTermIsTimestampP = true; + else if (strcmp(&term[termLen - 9], "createdAt") == 0) *lastTermIsTimestampP = true; + else if (strcmp(&term[termLen - 10], "observedAt") == 0) *lastTermIsTimestampP = true; + } if (*lastTermIsTimestampP == true) LM_T(LmtQ, ("Pushing a Timestamp term: '%s'", term)); diff --git a/src/lib/orionld/q/qPresent.cpp b/src/lib/orionld/q/qPresent.cpp index a91e2c9f50..eb14bfb672 100644 --- a/src/lib/orionld/q/qPresent.cpp +++ b/src/lib/orionld/q/qPresent.cpp @@ -25,7 +25,6 @@ #include // memset #include "logMsg/logMsg.h" // TraceLevels, LM_* - #include "orionld/q/QNode.h" // QNode #include "orionld/q/qPresent.h" // Own interface @@ -37,6 +36,9 @@ // static void qTreePresent(QNode* qP, int indent, const char* prefix, TraceLevels tLevel) { + if (lmTraceIsSet(LmtQ) == false) + return; + char indentV[100]; memset(indentV, 0x20202020, sizeof(indentV)); diff --git a/src/lib/orionld/regCache/CMakeLists.txt b/src/lib/orionld/regCache/CMakeLists.txt index 0ac70c140d..2bc71761fc 100644 --- a/src/lib/orionld/regCache/CMakeLists.txt +++ b/src/lib/orionld/regCache/CMakeLists.txt @@ -32,6 +32,7 @@ SET (SOURCES regCacheIdPatternRegexCompile.cpp regCacheItemRegexRelease.cpp regCacheDebug.cpp + regCachePresent.cpp ) # Include directories diff --git a/src/lib/orionld/regCache/RegCache.h b/src/lib/orionld/regCache/RegCache.h index 6420c4c7de..41fbb9df3c 100644 --- a/src/lib/orionld/regCache/RegCache.h +++ b/src/lib/orionld/regCache/RegCache.h @@ -79,10 +79,12 @@ typedef struct RegCacheItem // "Shortcuts" and transformed info, all copies from the regTree - for improved performance RegistrationMode mode; uint64_t opMask; - OrionldContext* contextP; // Set when creating/patching registration - bool acceptJsonld; // application/ld+json - char* ipAndPort; // IP:port - for X-Forwarded-For + OrionldContext* contextP; // Set when creating/patching registration + bool acceptJsonld; // application/ld+json + char* ipAndPort; // IP:port - for X-Forwarded-For RegIdPattern* idPatternRegexList; + char* hostAlias; // Broker identity - for the Via header (replacing X-Forwarded-For) + struct RegCacheItem* next; } RegCacheItem; diff --git a/src/lib/orionld/regCache/regCacheItemAdd.cpp b/src/lib/orionld/regCache/regCacheItemAdd.cpp index 98eb0d2c88..c263f2e700 100644 --- a/src/lib/orionld/regCache/regCacheItemAdd.cpp +++ b/src/lib/orionld/regCache/regCacheItemAdd.cpp @@ -167,6 +167,13 @@ RegCacheItem* regCacheItemAdd(RegCache* rcP, const char* registrationId, KjNode* rciP->ipAndPort = regIpAndPortExtract(regP); rciP->next = NULL; + // Host Alias + KjNode* hostAliasP = kjLookup(rciP->regTree, "hostAlias"); + if (hostAliasP != NULL) + rciP->hostAlias = strdup(hostAliasP->value.s); + else + rciP->hostAlias = rciP->ipAndPort; // Fallback solution + // Counters and timestamps - create if they don't exist if (fromDb == false) { diff --git a/src/lib/orionld/regCache/regCacheItemRemove.cpp b/src/lib/orionld/regCache/regCacheItemRemove.cpp index bffd078e20..09b1623858 100644 --- a/src/lib/orionld/regCache/regCacheItemRemove.cpp +++ b/src/lib/orionld/regCache/regCacheItemRemove.cpp @@ -96,6 +96,9 @@ bool regCacheItemRemove(RegCache* rcP, const char* regId) if (rciP->ipAndPort != NULL) free(rciP->ipAndPort); + if ((rciP->hostAlias != NULL) && (rciP->hostAlias != rciP->ipAndPort)) + free(rciP->hostAlias); + // And finally, free the entire struct free(rciP); diff --git a/src/lib/orionld/regCache/regCachePresent.cpp b/src/lib/orionld/regCache/regCachePresent.cpp new file mode 100644 index 0000000000..8d095147a0 --- /dev/null +++ b/src/lib/orionld/regCache/regCachePresent.cpp @@ -0,0 +1,77 @@ +/* +* +* Copyright 2022 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjLookup.h" // kjLookup +} + +#include "logMsg/logMsg.h" // LM_T + +#include "orionld/types/OrionldTenant.h" // OrionldTenant +#include "orionld/regCache/RegCache.h" // RegCacheItem +#include "orionld/common/tenantList.h" // tenant0 + + + +// ----------------------------------------------------------------------------- +// +// regCachePresent - +// +void regCachePresent(void) +{ + for (OrionldTenant* tenantP = &tenant0; tenantP != NULL; tenantP = tenantP->next) + { + if (tenantP->regCache == NULL) + LM_T(LmtRegCache, ("Tenant '%s': No regCache", tenantP->mongoDbName)); + else + { + LM_T(LmtRegCache, ("Tenant '%s':", tenantP->mongoDbName)); + RegCacheItem* rciP = tenantP->regCache->regList; + + while (rciP != NULL) + { + KjNode* regIdP = kjLookup(rciP->regTree, "id"); + + LM_T(LmtRegCache, (" o Registration %s:", (regIdP != NULL)? regIdP->value.s : "unknown")); + LM_T(LmtRegCache, (" o mode: %s", registrationModeToString(rciP->mode))); + LM_T(LmtRegCache, (" o ops: 0x%x", rciP->opMask)); + + if (rciP->idPatternRegexList != NULL) + { + LM_T(LmtRegCache, (" o patterns:")); + for (RegIdPattern* ripP = rciP->idPatternRegexList; ripP != NULL; ripP = ripP->next) + { + LM_T(LmtRegCache, (" o %s (idPattern at %p)", ripP->owner->value.s, ripP->owner)); + } + } + else + LM_T(LmtRegCache, (" o patterns: NONE")); + LM_T(LmtRegCache, (" -----------------------------------")); + rciP = rciP->next; + } + } + } +} diff --git a/src/lib/orionld/regCache/regCachePresent.h b/src/lib/orionld/regCache/regCachePresent.h new file mode 100644 index 0000000000..daa39230f0 --- /dev/null +++ b/src/lib/orionld/regCache/regCachePresent.h @@ -0,0 +1,37 @@ +#ifndef SRC_LIB_ORIONLD_REGCACHE_REGCACHEPRESENT_H_ +#define SRC_LIB_ORIONLD_REGCACHE_REGCACHEPRESENT_H_ + +/* +* +* Copyright 2022 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ + + + +// ----------------------------------------------------------------------------- +// +// regCachePresent - +// +extern void regCachePresent(void); + +#endif // SRC_LIB_ORIONLD_REGCACHE_REGCACHEPRESENT_H_ diff --git a/src/lib/orionld/regCache/regCacheRelease.cpp b/src/lib/orionld/regCache/regCacheRelease.cpp index 9e0a4b7985..a4a22cb25e 100644 --- a/src/lib/orionld/regCache/regCacheRelease.cpp +++ b/src/lib/orionld/regCache/regCacheRelease.cpp @@ -60,6 +60,9 @@ void regCacheRelease(RegCache* regCacheP) if (rciP->ipAndPort != NULL) free(rciP->ipAndPort); + if ((rciP->hostAlias != NULL) && (rciP->hostAlias != rciP->ipAndPort)) + free(rciP->hostAlias); + free(rciP); rciP = next; diff --git a/src/lib/orionld/rest/OrionLdRestService.h b/src/lib/orionld/rest/OrionLdRestService.h index 358ec62459..9343e73364 100644 --- a/src/lib/orionld/rest/OrionLdRestService.h +++ b/src/lib/orionld/rest/OrionLdRestService.h @@ -152,6 +152,8 @@ typedef struct OrionLdRestServiceSimplifiedVector #define ORIONLD_URIPARAM_LOCAL (UINT64_C(1) << 35) #define ORIONLD_URIPARAM_RESET (UINT64_C(1) << 36) #define ORIONLD_URIPARAM_LEVEL (UINT64_C(1) << 37) +#define ORIONLD_URIPARAM_ONLYIDS (UINT64_C(1) << 38) +#define ORIONLD_URIPARAM_ENTITYMAP (UINT64_C(1) << 39) diff --git a/src/lib/orionld/rest/orionldMhdConnectionInit.cpp b/src/lib/orionld/rest/orionldMhdConnectionInit.cpp index 737b1b663d..ff0f39310a 100644 --- a/src/lib/orionld/rest/orionldMhdConnectionInit.cpp +++ b/src/lib/orionld/rest/orionldMhdConnectionInit.cpp @@ -29,6 +29,8 @@ extern "C" { #include "kbase/kMacros.h" // K_FT, K_VEC_SIZE #include "kbase/kTime.h" // kTimeGet, kTimeDiff +#include "kalloc/kaAlloc.h" // kaAlloc +#include "kalloc/kaStrdup.h" // kaStrdup #include "kjson/kjBuilder.h" // kjString, kjChildAdd } @@ -51,6 +53,7 @@ extern "C" #include "orionld/common/orionldTenantLookup.h" // orionldTenantLookup #include "orionld/serviceRoutines/orionldBadVerb.h" // orionldBadVerb #include "orionld/payloadCheck/pCheckUri.h" // pCheckUri +#include "orionld/entityMaps/entityMapLookup.h" // entityMapLookup #include "orionld/rest/orionldServiceInit.h" // orionldRestServiceV #include "orionld/rest/orionldServiceLookup.h" // orionldServiceLookup #include "orionld/rest/OrionLdRestService.h" // ORIONLD_URIPARAM_LIMIT, ... @@ -190,7 +193,11 @@ static void optionsParse(const char* options) else if (strcmp(optionStart, "sysAttrs") == 0) orionldState.uriParamOptions.sysAttrs = true; else if (strcmp(optionStart, "fromDb") == 0) orionldState.uriParamOptions.fromDb = true; else if (strcmp(optionStart, "append") == 0) orionldState.uriParamOptions.append = true; // NGSIv2 compatibility - else if (strcmp(optionStart, "count") == 0) orionldState.uriParams.count = true; // NGSIv2 compatibility + else if (strcmp(optionStart, "count") == 0) + { + orionldState.uriParams.count = true; // NGSIv2 compatibility + LM_T(LmtCount, ("Count is ON")); + } else if (strcmp(optionStart, "values") == 0) orionldState.uriParamOptions.values = true; // NGSIv2 compatibility else if (strcmp(optionStart, "unique") == 0) orionldState.uriParamOptions.uniqueValues = true; // NGSIv2 compatibility else if (strcmp(optionStart, "dateCreated") == 0) orionldState.uriParamOptions.dateCreated = true; // NGSIv2 compatibility @@ -427,6 +434,13 @@ static MHD_Result orionldHttpHeaderReceive(void* cbDataP, MHD_ValueKind kind, co } else if (strcasecmp(key, "Performance") == 0) orionldState.in.performance = true; + else if (strcasecmp(key, "ORIONLD-WIP") == 0) + orionldState.in.wip = (char*) value; + else if (strcasecmp(key, "aerOS") == 0) + { + if (strcasecmp(value, "true") == 0) + orionldState.in.aerOS = true; + } else if (strcasecmp(key, "NGSILD-Scope") == 0) { orionldState.scopes = strSplit((char*) value, ',', orionldState.scopeV, K_VEC_SIZE(orionldState.scopeV)); @@ -436,6 +450,12 @@ static MHD_Result orionldHttpHeaderReceive(void* cbDataP, MHD_ValueKind kind, co orionldError(OrionldBadRequestData, "Bad value for HTTP header /NGSILD-Scope/", value, 400); } } + else if (strcasecmp(key, "NGSILD-EntityMap") == 0) + { + orionldState.in.entityMap = entityMapLookup(value); + if (orionldState.in.entityMap == NULL) + orionldError(OrionldResourceNotFound, "Entity-Map not found", value, 404); + } else if (strcasecmp(key, "Accept") == 0) { orionldState.out.contentType = acceptHeaderParse((char*) value, false); @@ -459,6 +479,7 @@ static MHD_Result orionldHttpHeaderReceive(void* cbDataP, MHD_ValueKind kind, co else if (strcasecmp(key, "X-Real-IP") == 0) orionldState.in.xRealIp = (char*) value; else if (strcasecmp(key, "Connection") == 0) orionldState.in.connection = (char*) value; else if (strcasecmp(key, "X-Forwarded-For") == 0) orionldState.in.xForwardedFor = (char*) value; + else if (strcasecmp(key, "Via") == 0) orionldState.in.via = (char*) value; else if (strcasecmp(key, "Content-Type") == 0) { orionldState.in.contentType = mimeTypeFromString(value, NULL, false, false, &orionldState.acceptMask); @@ -505,6 +526,50 @@ static MHD_Result orionldHttpHeaderReceive(void* cbDataP, MHD_ValueKind kind, co } +// ----------------------------------------------------------------------------- +// +// hyphensEncode - +// +static char* hyphensEncode(char* value) +{ + int doubleQuotes = 0; + + // 1. Count the number of double quotes + char* cP = value; + while (*cP != 0) + { + if (*cP == '"') + ++doubleQuotes; + ++cP; + } + + if (doubleQuotes == 0) + return kaStrdup(&orionldState.kalloc, value); + + int len = strlen(value) + doubleQuotes * 2 + 1; // 2 extra chars needed to encode " to %22 + + char* out = kaAlloc(&orionldState.kalloc, len); + int outIx = 0; + + while (*value != 0) + { + if (*value != '"') + out[outIx++] = *value; + else + { + out[outIx++] = '%'; + out[outIx++] = '2'; + out[outIx++] = '2'; + } + + ++value; + } + + out[outIx] = 0; + return out; +} + + // ----------------------------------------------------------------------------- // @@ -521,6 +586,8 @@ MHD_Result orionldUriArgumentGet(void* cbDataP, MHD_ValueKind kind, const char* return MHD_YES; } + LM_T(LmtUriParams, ("URI Param: %s=%s", key, value)); + // // Forbidden characters in URI param value - not for NGSI-LD - for now at least ... // @@ -653,7 +720,10 @@ MHD_Result orionldUriArgumentGet(void* cbDataP, MHD_ValueKind kind, const char* else if (strcmp(key, "count") == 0) { if (strcmp(value, "true") == 0) + { orionldState.uriParams.count = true; + LM_T(LmtCount, ("Count is ON")); + } else if (strcmp(value, "false") != 0) { orionldError(OrionldBadRequestData, "Bad value for URI parameter /count/", value, 400); @@ -664,7 +734,13 @@ MHD_Result orionldUriArgumentGet(void* cbDataP, MHD_ValueKind kind, const char* } else if (strcmp(key, "q") == 0) { - orionldState.uriParams.q = (char*) value; + char* qraw = (char*) value; + orionldState.uriParams.q = (char*) value; + + if (strchr(qraw, '"') != NULL) + orionldState.uriParams.qCopy = hyphensEncode(qraw); + else + orionldState.uriParams.qCopy = kaStrdup(&orionldState.kalloc, qraw); orionldState.uriParams.mask |= ORIONLD_URIPARAM_Q; } else if (strcmp(key, "mq") == 0) @@ -893,6 +969,28 @@ MHD_Result orionldUriArgumentGet(void* cbDataP, MHD_ValueKind kind, const char* orionldState.uriParams.mask |= ORIONLD_URIPARAM_LOCAL; } + else if (strcmp(key, "entityMap") == 0) + { + if (strcmp(value, "true") == 0) + orionldState.uriParams.entityMap = true; + + orionldState.uriParams.mask |= ORIONLD_URIPARAM_ENTITYMAP; + } + else if (strcmp(key, "onlyIds") == 0) + { + if (strcmp(value, "true") == 0) + { + orionldState.uriParams.onlyIds = true; + LM_T(LmtCount, ("onlyIds is ON")); + } + else if (strcmp(key, "false") != 0) + { + orionldError(OrionldBadRequestData, "Invalid value for uri parameter /onlyIds/", value, 400); + return MHD_YES; + } + + orionldState.uriParams.mask |= ORIONLD_URIPARAM_ONLYIDS; + } else if (strcmp(key, "entity::type") == 0) // Is NGSIv1 ?entity::type=X the same as NGSIv2 ?type=X ? { orionldState.uriParams.type = (char*) value; @@ -1006,6 +1104,7 @@ MHD_Result orionldMhdConnectionInit // Save URL path in ConnectionInfo orionldState.urlPath = (char*) url; + // // Does the URL path end in a '/'? // If so, remove it. @@ -1060,6 +1159,7 @@ MHD_Result orionldMhdConnectionInit // 5. GET URI params // MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, orionldUriArgumentGet, NULL); + LM_T(LmtCount, ("orionldState.uriParams.count: %s", K_FT(orionldState.uriParams.count))); // // Format of response payload @@ -1119,13 +1219,6 @@ MHD_Result orionldMhdConnectionInit return MHD_YES; } - // Check payload too big - if (orionldState.in.contentLength > 2000000) - { - orionldError(OrionldBadRequestData, "Invalid Payload", "Payload too large", 400); - return MHD_YES; - } - // Check that GET/DELETE has no payload // Check that POST/PUT/PATCH has payload // Check validity of tenant diff --git a/src/lib/orionld/rest/orionldMhdConnectionTreat.cpp b/src/lib/orionld/rest/orionldMhdConnectionTreat.cpp index 135efc628e..22331cdee7 100644 --- a/src/lib/orionld/rest/orionldMhdConnectionTreat.cpp +++ b/src/lib/orionld/rest/orionldMhdConnectionTreat.cpp @@ -612,9 +612,9 @@ static int commaCount(char* s) // ----------------------------------------------------------------------------- // -// pCheckUriParamAttrs - +// pCheckAttrsParam - // -static bool pCheckUriParamAttrs(void) +static bool pCheckAttrsParam(void) { if (orionldState.uriParams.attrs == NULL) return true; @@ -654,13 +654,13 @@ static bool pCheckUriParamAttrs(void) // ----------------------------------------------------------------------------- // -// pCheckUriParamId - +// pCheckEntityIdParam - // // NOTE // No need to check ORIONLD_SERVICE_OPTION_EXPAND_TYPE here as the URI-parameter 'type' // is only alloowed by those services that need expansion (GET /entities and GET /registrations) // -static bool pCheckUriParamId(void) +static bool pCheckEntityIdParam(void) { if (orionldState.uriParams.id == NULL) return true; @@ -706,13 +706,13 @@ static bool pCheckUriParamId(void) // ----------------------------------------------------------------------------- // -// pCheckUriParamType - +// pCheckEntityTypeParam - // // NOTE // No need to check ORIONLD_SERVICE_OPTION_EXPAND_TYPE here as the URI-parameter 'type' // is only allowed by those services that need expansion (GET /entities and GET /registrations) // -static bool pCheckUriParamType(void) +static bool pCheckEntityTypeParam(void) { if (orionldState.uriParams.type == NULL) return true; @@ -845,9 +845,9 @@ static bool pCheckUrlPathAttributeName(void) // static bool uriParamExpansion(void) { - if (pCheckUriParamId() == false) return false; - if (pCheckUriParamType() == false) return false; - if (pCheckUriParamAttrs() == false) return false; + if (pCheckEntityIdParam() == false) return false; + if (pCheckEntityTypeParam() == false) return false; + if (pCheckAttrsParam() == false) return false; if (pCheckUriParamGeoProperty() == false) return false; if (pCheckUriParamGeometryProperty() == false) return false; if (pCheckPayloadEntityType() == false) return false; @@ -1161,16 +1161,8 @@ MHD_Result orionldMhdConnectionTreat(void) // if ((orionldState.pd.status >= 400) && (orionldState.responseTree == NULL) && (orionldState.pd.status != 405)) { - if (orionldState.pd.status != 0) // Perhaps the error is in orionldState.pd ? - { - errorTree(orionldState.pd.type, orionldState.pd.title, orionldState.pd.detail); - orionldState.httpStatusCode = orionldState.pd.status; - } - else - { - errorTree(OrionldInternalError, "Unknown Error", "The reason for this error is unknown"); - orionldState.httpStatusCode = 500; - } + errorTree(orionldState.pd.type, orionldState.pd.title, orionldState.pd.detail); + orionldState.httpStatusCode = orionldState.pd.status; } // diff --git a/src/lib/orionld/rest/orionldServiceInit.cpp b/src/lib/orionld/rest/orionldServiceInit.cpp index 002329331f..658e3f5c5b 100644 --- a/src/lib/orionld/rest/orionldServiceInit.cpp +++ b/src/lib/orionld/rest/orionldServiceInit.cpp @@ -232,6 +232,8 @@ static void restServicePrepare(OrionLdRestService* serviceP, OrionLdRestServiceS serviceP->uriParams |= ORIONLD_URIPARAM_GEOMETRYPROPERTY; serviceP->uriParams |= ORIONLD_URIPARAM_LANG; serviceP->uriParams |= ORIONLD_URIPARAM_LOCAL; + serviceP->uriParams |= ORIONLD_URIPARAM_ONLYIDS; + serviceP->uriParams |= ORIONLD_URIPARAM_ENTITYMAP; } else if (serviceP->serviceRoutine == orionldGetEntity) { @@ -245,6 +247,8 @@ static void restServicePrepare(OrionLdRestService* serviceP, OrionLdRestServiceS else if (serviceP->serviceRoutine == orionldDeleteEntity) { serviceP->options |= ORIONLD_SERVICE_OPTION_NO_CONTEXT_NEEDED; + + serviceP->uriParams |= ORIONLD_URIPARAM_TYPELIST; } else if (serviceP->serviceRoutine == orionldPostEntity) { diff --git a/src/lib/orionld/serviceRoutines/CMakeLists.txt b/src/lib/orionld/serviceRoutines/CMakeLists.txt index 2d0b103cba..ab1f8e8b75 100644 --- a/src/lib/orionld/serviceRoutines/CMakeLists.txt +++ b/src/lib/orionld/serviceRoutines/CMakeLists.txt @@ -27,7 +27,12 @@ SET (SOURCES orionldPostRegistrations.cpp orionldPostBatchDelete.cpp orionldGetEntities.cpp + orionldGetEntitiesDistributed.cpp + orionldGetEntitiesPage.cpp + orionldGetEntitiesLocal.cpp orionldGetEntity.cpp + orionldGetEntityMap.cpp + orionldDeleteEntityMap.cpp orionldGetSubscriptions.cpp orionldGetSubscription.cpp orionldGetRegistrations.cpp diff --git a/src/lib/orionld/serviceRoutines/orionldDeleteAttribute.cpp b/src/lib/orionld/serviceRoutines/orionldDeleteAttribute.cpp index 697901a81e..2e2cdf933e 100644 --- a/src/lib/orionld/serviceRoutines/orionldDeleteAttribute.cpp +++ b/src/lib/orionld/serviceRoutines/orionldDeleteAttribute.cpp @@ -50,6 +50,7 @@ extern "C" #include "orionld/forwarding/distOpLookupByCurlHandle.h" // distOpLookupByCurlHandle #include "orionld/forwarding/distOpListRelease.h" // distOpListRelease #include "orionld/forwarding/xForwardedForCompose.h" // xForwardedForCompose +#include "orionld/forwarding/viaCompose.h" // viaCompose #include "orionld/serviceRoutines/orionldDeleteAttribute.h" // Own interface @@ -85,6 +86,7 @@ static DistOp* distributedDelete(KjNode* responseBody, char* entityId, char* ent // Enqueue all forwarded requests // Now that we've found all matching registrations we can add ourselves to the X-forwarded-For header char* xff = xForwardedForCompose(orionldState.in.xForwardedFor, localIpAndPort); + char* via = viaCompose(orionldState.in.via, brokerId); int forwards = 0; for (DistOp* distOpP = distOpList; distOpP != NULL; distOpP = distOpP->next) @@ -95,7 +97,7 @@ static DistOp* distributedDelete(KjNode* responseBody, char* entityId, char* ent char dateHeader[70]; snprintf(dateHeader, sizeof(dateHeader), "Date: %s", orionldState.requestTimeString); - if (distOpSend(distOpP, dateHeader, xff) == 0) + if (distOpSend(distOpP, dateHeader, xff, via, false, NULL) == 0) { ++forwards; distOpP->error = false; @@ -264,7 +266,7 @@ bool orionldDeleteAttribute(void) distOpFailure(responseBody, NULL, "Database Error", "(ToDo: get error from mongoc)", 500, attrName); } else - distOpSuccess(responseBody, NULL, attrName); + distOpSuccess(responseBody, NULL, entityId, attrName); } if (distOpList != NULL) diff --git a/src/lib/orionld/serviceRoutines/orionldDeleteEntity.cpp b/src/lib/orionld/serviceRoutines/orionldDeleteEntity.cpp index 6e2f4f9d30..cbd7db4085 100644 --- a/src/lib/orionld/serviceRoutines/orionldDeleteEntity.cpp +++ b/src/lib/orionld/serviceRoutines/orionldDeleteEntity.cpp @@ -50,6 +50,7 @@ extern "C" #include "orionld/forwarding/distOpSend.h" // distOpSend #include "orionld/forwarding/distOpLookupByCurlHandle.h" // distOpLookupByCurlHandle #include "orionld/forwarding/xForwardedForCompose.h" // xForwardedForCompose +#include "orionld/forwarding/viaCompose.h" // viaCompose #include "orionld/forwarding/distOpResponses.h" // distOpResponses #include "orionld/forwarding/distOpSuccess.h" // distOpSuccess #include "orionld/forwarding/distOpFailure.h" // distOpFailure @@ -112,6 +113,7 @@ static DistOp* distributedDelete(char* entityId, char* entityTypeExpanded, char* // Enqueue all forwarded requests // Now that we've found all matching registrations we can add ourselves to the X-forwarded-For header char* xff = xForwardedForCompose(orionldState.in.xForwardedFor, localIpAndPort); + char* via = viaCompose(orionldState.in.via, brokerId); int forwards = 0; for (DistOp* distOpP = distOpList; distOpP != NULL; distOpP = distOpP->next) @@ -122,7 +124,7 @@ static DistOp* distributedDelete(char* entityId, char* entityTypeExpanded, char* char dateHeader[70]; snprintf(dateHeader, sizeof(dateHeader), "Date: %s", orionldState.requestTimeString); - if (distOpSend(distOpP, dateHeader, xff) == 0) + if (distOpSend(distOpP, dateHeader, xff, via, false, NULL) == 0) { ++forwards; distOpP->error = false; @@ -217,10 +219,15 @@ bool orionldDeleteEntity(void) // Delete the entity in the local DB // - Error if the entity is not found locally, and not subject to forwarding // - if ((dbEntityP == NULL) && (distOpList == NULL)) + if (dbEntityP == NULL) { - orionldError(OrionldResourceNotFound, "Entity not found", entityId, 404); - return false; + if (distOpList == NULL) + { + orionldError(OrionldResourceNotFound, "Entity not found", entityId, 404); + return false; + } + else + distOpFailure(responseBody, NULL, "Not Found", entityId, 404, NULL); } // @@ -228,15 +235,23 @@ bool orionldDeleteEntity(void) // Give 404 if the entity is not present locally nor triggered any forwarded requests // char* detail = NULL; - if ((dbEntityP != NULL) && (mongocEntityDelete(entityId, &detail) == false)) + if (dbEntityP != NULL) { - if (distOpList == NULL) // pure local request + if (mongocEntityDelete(entityId, &detail) == true) { - orionldError(OrionldInternalError, "Database Error", detail, 500); - return false; + // Add a success to the "success" member + distOpSuccess(responseBody, NULL, entityId, NULL); } else - distOpFailure(responseBody, NULL, "Database Error", detail, 500, NULL); + { + if (distOpList == NULL) // pure local request + { + orionldError(OrionldInternalError, "Database Error", detail, 500); + return false; + } + else + distOpFailure(responseBody, NULL, "Database Error", detail, 500, NULL); + } } if (dbEntityP != NULL) @@ -248,6 +263,7 @@ bool orionldDeleteEntity(void) distOpListRelease(distOpList); } + kjTreeLog(responseBody, "responseBody", LmtSR); responseFix(responseBody, DoDeleteEntity, 204, entityId); return true; diff --git a/src/lib/orionld/serviceRoutines/orionldDeleteEntityMap.cpp b/src/lib/orionld/serviceRoutines/orionldDeleteEntityMap.cpp new file mode 100644 index 0000000000..2c22f2a2ac --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldDeleteEntityMap.cpp @@ -0,0 +1,55 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // strcmp + +#include "orionld/common/orionldState.h" // orionldState, orionldEntityMapId +#include "orionld/common/orionldError.h" // orionldError +#include "orionld/types/EntityMap.h" // EntityMap +#include "orionld/entityMaps/entityMapRemove.h" // entityMapRemove +#include "orionld/entityMaps/entityMapRelease.h" // entityMapRelease +#include "orionld/serviceRoutines/orionldDeleteEntityMap.h" // Own interface + + + +// ---------------------------------------------------------------------------- +// +// orionldDeleteEntityMap - +// +bool orionldDeleteEntityMap(void) +{ + const char* entityMapId = orionldState.wildcard[0]; + EntityMap* entityMap = entityMapRemove(entityMapId); + + if (entityMap == NULL) + { + orionldError(OrionldResourceNotFound, "EntityMap Not Found", entityMapId, 404); + return false; + } + + LM_T(LmtSR, ("Deleting entity map '%s'", entityMapId)); + entityMapRelease(entityMap); + + return true; +} diff --git a/src/lib/orionld/serviceRoutines/orionldDeleteEntityMap.h b/src/lib/orionld/serviceRoutines/orionldDeleteEntityMap.h new file mode 100644 index 0000000000..44578975bf --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldDeleteEntityMap.h @@ -0,0 +1,37 @@ +#ifndef SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDDELETEENTITYMAP_H_ +#define SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDDELETEENTITYMAP_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ + + + +// ---------------------------------------------------------------------------- +// +// orionldDeleteEntityMap - +// +extern bool orionldDeleteEntityMap(void); + +#endif // SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDDELETEENTITYMAP_H_ diff --git a/src/lib/orionld/serviceRoutines/orionldGetEntities.cpp b/src/lib/orionld/serviceRoutines/orionldGetEntities.cpp index 8bf9f949a7..dd2b309752 100644 --- a/src/lib/orionld/serviceRoutines/orionldGetEntities.cpp +++ b/src/lib/orionld/serviceRoutines/orionldGetEntities.cpp @@ -22,109 +22,37 @@ * * Author: Ken Zangelin */ -#include // bzero - extern "C" { -#include "kjson/KjNode.h" // KjNode -#include "kjson/kjParse.h" // kjParse -#include "kjson/kjBuilder.h" // kjString, kjObject, ... -#include "kjson/kjLookup.h" // kjLookup -#include "kjson/kjClone.h" // kjClone +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjClone.h" // kjClone } -#include "logMsg/logMsg.h" // LM_* - -#include "orionld/common/orionldState.h" // orionldState -#include "orionld/common/orionldError.h" // orionldError -#include "orionld/common/urlDecode.h" // urlDecode -#include "orionld/types/OrionldHeader.h" // orionldHeaderAdd, HttpResultsCount -#include "orionld/types/OrionldGeometry.h" // OrionldGeometry -#include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo -#include "orionld/context/orionldAttributeExpand.h" // orionldAttributeExpand -#include "orionld/legacyDriver/legacyGetEntities.h" // legacyGetEntities -#include "orionld/mongoc/mongocEntitiesQuery.h" // mongocEntitiesQuery -#include "orionld/kjTree/kjTreeLog.h" // kjTreeLog -#include "orionld/q/qLex.h" // qLex -#include "orionld/q/qParse.h" // qParse -#include "orionld/kjTree/kjChildPrepend.h" // kjChildPrepend -#include "orionld/dbModel/dbModelToApiEntity.h" // dbModelToApiEntity2 -#include "orionld/payloadCheck/pCheckGeometry.h" // pCheckGeometry -#include "orionld/payloadCheck/pCheckGeorelString.h" // pCheckGeorelString -#include "orionld/payloadCheck/pCheckGeoCoordinates.h" // pCheckGeoCoordinates -#include "orionld/serviceRoutines/orionldGetEntities.h" // Own interface - - - -// ----------------------------------------------------------------------------- -// -// geoCheck - -// -static bool geoCheck(OrionldGeoInfo* geoInfoP) -{ - bzero(geoInfoP, sizeof(OrionldGeoInfo)); - - if ((orionldState.uriParams.geometry != NULL) || - (orionldState.uriParams.georel != NULL) || - (orionldState.uriParams.coordinates != NULL) || - (orionldState.uriParams.geoproperty != NULL)) - { - // - // geometry - // - if (orionldState.uriParams.geometry == NULL) - { - orionldError(OrionldBadRequestData, "Invalid Geo-Filter", "missing: geometry", 400); - return false; - } - if (pCheckGeometry(orionldState.uriParams.geometry, &geoInfoP->geometry, false) == false) - return false; - - // - // georel - // - if (orionldState.uriParams.georel == NULL) - { - orionldError(OrionldBadRequestData, "Invalid Geo-Filter", "missing: georel", 400); - return false; - } - - if (pCheckGeorelString(orionldState.uriParams.georel, geoInfoP) == false) - return false; - - // - // coordinates - // - if (orionldState.uriParams.coordinates == NULL) - { - orionldError(OrionldBadRequestData, "Invalid Geo-Filter", "missing: coordinates", 400); - return false; - } - - urlDecode(orionldState.uriParams.coordinates); - geoInfoP->coordinates = kjParse(orionldState.kjsonP, orionldState.uriParams.coordinates); - - if (geoInfoP->coordinates == NULL) - { - orionldError(OrionldBadRequestData, "Invalid Geo-Filter", "invalid coordinates", 400); - return false; - } - - if (pCheckGeoCoordinates(geoInfoP->coordinates, geoInfoP->geometry) == false) - return false; - - - // - // geoproperty - // - if ((orionldState.uriParams.geoproperty != NULL) && (strcmp(orionldState.uriParams.geoproperty, "location") != 0)) - geoInfoP->geoProperty = orionldAttributeExpand(orionldState.contextP, orionldState.uriParams.geoproperty, true, NULL); - else - geoInfoP->geoProperty = (char*) "location"; - } - - return true; -} +#include "logMsg/logMsg.h" // LM_* +#include "logMsg/traceLevels.h" // Lmt* + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/common/orionldError.h" // orionldError +#include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo +#include "orionld/legacyDriver/legacyGetEntities.h" // legacyGetEntities +#include "orionld/kjTree/kjTreeLog.h" // kjTreeLog +#include "orionld/q/QNode.h" // QNode +#include "orionld/q/qLex.h" // qLex +#include "orionld/q/qParse.h" // qParse +#include "orionld/q/qClone.h" // qClone +#include "orionld/payloadCheck/pCheckGeo.h" // pCheckGeo +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/forwarding/distOpRequests.h" // distOpRequests +#include "orionld/forwarding/distOpListsMerge.h" // distOpListsMerge +#include "orionld/forwarding/distOpListDebug.h" // distOpListDebug +#include "orionld/forwarding/xForwardedForMatch.h" // xForwardedForMatchº +#include "orionld/forwarding/viaMatch.h" // viaMatch +#include "orionld/forwarding/regMatchOperation.h" // regMatchOperation +#include "orionld/forwarding/regMatchInformationArrayForQuery.h" // regMatchInformationArrayForQuery +#include "orionld/forwarding/distOpCreate.h" // distOpCreate +#include "orionld/serviceRoutines/orionldGetEntitiesDistributed.h" // orionldGetEntitiesDistributed +#include "orionld/serviceRoutines/orionldGetEntitiesLocal.h" // orionldGetEntitiesLocal +#include "orionld/serviceRoutines/orionldGetEntities.h" // Own interface @@ -159,103 +87,134 @@ static QNode* qCheck(char* qString) // ----------------------------------------------------------------------------- // -// apiEntityToGeoJson - transform an API Entity into a GEOJSON Entity +// pCheckQueryParams - // -KjNode* apiEntityToGeoJson(KjNode* apiEntityP, KjNode* geometryNodeP, bool geoPropertyFromProjection) +bool pCheckQueryParams(char* id, char* type, char* idPattern, char* q, char* geometry, char* attrs, bool local, EntityMap* entityMap, QNode** qNodeP, OrionldGeoInfo* geoInfoP) { - KjNode* propertiesP = kjObject(orionldState.kjsonP, "properties"); - - // - // 1. Find 'type' in the original entity - remove it and wait until everything is moved to 'properties' before putting 'type' back // - KjNode* typeP = kjLookup(apiEntityP, "type"); - if (typeP != NULL) // Can't really be NULL, can it? - kjChildRemove(apiEntityP, typeP); - - - // - // 2. Find the 'id' in the original entity and remove it temporarily - will be put back again once 'properties' has been filled + // URI param validity check // - KjNode* idP = kjLookup(apiEntityP, "id"); - if (idP != NULL) // Can't really be NULL, can it? - kjChildRemove(apiEntityP, idP); - // 3. In case the geometryProperty was added to the projection even though it was not part of the "attrs" URI param - just remove it - if ((geoPropertyFromProjection == true) && (geometryNodeP != NULL)) - kjChildRemove(apiEntityP, geometryNodeP); + if ((id == NULL) && + (idPattern == NULL) && + (type == NULL) && + (geometry == NULL) && + (attrs == NULL) && + (q == NULL) && + (local == false) && + (orionldState.in.entityMap == NULL)) + { + orionldError(OrionldBadRequestData, + "Too broad query", + "Need at least one of: entity-id, entity-type, geo-location, attribute-list, Q-filter, local=true, or an entity map", + 400); - // 4. Move EVERYTHING from "apiEntityP" to "properties" - propertiesP->value.firstChildP = apiEntityP->value.firstChildP; - propertiesP->lastChild = apiEntityP->lastChild; + return false; + } - // Clear out apiEntityP - apiEntityP->value.firstChildP = NULL; - apiEntityP->lastChild = NULL; // - // Should the @context be added to the payload body? + // If ONE or ZERO types in URI param 'type', the prepared array isn't used, just a simple char-pointer (named "type") // - if (orionldState.linkHeaderAdded == false) - { - // Only if Prefer is not set to body=json - if ((orionldState.preferHeader == NULL) || (strcasecmp(orionldState.preferHeader, "body=json") != 0)) - { - KjNode* contextP; + if (orionldState.in.typeList.items == 0) type = (char*) ".*"; + else if (orionldState.in.typeList.items == 1) type = orionldState.in.typeList.array[0]; - if (orionldState.link == NULL) - contextP = kjString(orionldState.kjsonP, "@context", coreContextUrl); - else - contextP = kjString(orionldState.kjsonP, "@context", orionldState.link); + if (pCheckGeo(geoInfoP, orionldState.uriParams.geometry, orionldState.uriParams.georel, orionldState.uriParams.coordinates, orionldState.uriParams.geoproperty) == false) + return false; - kjChildAdd(apiEntityP, contextP); - orionldState.noLinkHeader = true; - } + QNode* qNode = NULL; + if (orionldState.uriParams.q != NULL) + { + qNode = qCheck(orionldState.uriParams.q); + if (qNode == NULL) + return false; } - // 5. Put the original entity type inside 'properties' - if (typeP != NULL) - kjChildPrepend(propertiesP, typeP); + *qNodeP = qNode; + + return true; +} - // 6. Put the original entity id inside the top level entity - if (idP != NULL) - kjChildAdd(apiEntityP, idP); - // 7. Create the new 'type' for the GEOJSON Entity and add it to the toplevel - typeP = kjString(orionldState.kjsonP, "type", "Feature"); - kjChildPrepend(apiEntityP, typeP); - // 8. Create the "geometry" (key-values) top-level item - KjNode* geometryP = NULL; - if ((geometryNodeP != NULL) && (geometryNodeP->type == KjObject)) +// ----------------------------------------------------------------------------- +// +// regMatchForEntitiesQuery - FIXME: Move to orionld/forwarding/regMatchForEntitiesQuery.cpp/h +// +DistOp* regMatchForEntitiesQuery +( + RegistrationMode regMode, + StringArray* idListP, + StringArray* typeListP, + StringArray* attrListP +) +{ + DistOp* distOpList = NULL; + + for (RegCacheItem* regP = orionldState.tenantP->regCache->regList; regP != NULL; regP = regP->next) { - geometryP = kjLookup(geometryNodeP, "value"); - if (geometryP == NULL) + if ((regP->mode & regMode) == 0) + continue; + + if (regMatchOperation(regP, DoQueryEntity) == false) + { + LM_T(LmtRegMatch, ("%s: No Reg Match due to Operation (operation == QueryEntity)", regP->regId)); + continue; + } + + // Loop detection + if (viaMatch(orionldState.in.via, regP->hostAlias) == true) { - // "value" not found ... can it be Simplified format? - geometryP = geometryNodeP; + LM_T(LmtRegMatch, ("%s: No Reg Match due to Loop (Via)", regP->regId)); + continue; } - if (geometryP != NULL) + if (xForwardedForMatch(orionldState.in.xForwardedFor, regP->ipAndPort) == true) { - if (geometryP->type == KjObject) // && hasChildren type+coordinates ... - { - geometryP = kjClone(orionldState.kjsonP, geometryP); - geometryP->name = (char*) "geometry"; - } - else - geometryP = NULL; + LM_T(LmtRegMatch, ("%s: No Reg Match due to Loop (X-Forwarded-For)", regP->regId)); + continue; } + + DistOp* distOpP = regMatchInformationArrayForQuery(regP, idListP, typeListP, attrListP); + if (distOpP == NULL) + { + LM_T(LmtRegMatch, ("%s: No Reg Match due to Information Array", regP->regId)); + continue; + } + + // + // Add distOpP to the linked list (distOpList) + // + LM_T(LmtRegMatch, ("%s: Reg Match !", regP->regId)); + + distOpList = distOpListsMerge(distOpList, distOpP); } - if (geometryP == NULL) - geometryP = kjNull(orionldState.kjsonP, "geometry"); - kjChildAdd(apiEntityP, geometryP); + return distOpList; +} + + + +// ---------------------------------------------------------------------------- +// +// distOpRequestsForEntitiesQuery - +// +DistOp* distOpRequestsForEntitiesQuery(char* idPattern, QNode* qNode) +{ + // FIXME: idPattern, qNode also need to be taken into account inside regMatchForEntitiesQuery + DistOp* auxiliarList = regMatchForEntitiesQuery(RegModeAuxiliary, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); + DistOp* exclusiveList = regMatchForEntitiesQuery(RegModeExclusive, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); + DistOp* redirectList = regMatchForEntitiesQuery(RegModeRedirect, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); + // FIXME: Strip off attrs, entityId, entityType, etc from URI params (regMatchForEntitiesQuery(RegModeExclusive) already does it for each match + + DistOp* inclusiveList = regMatchForEntitiesQuery(RegModeInclusive, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); + DistOp* distOpList; - // 9. Adding all the properties to top-level - propertiesP->next = NULL; - kjChildAdd(apiEntityP, propertiesP); + distOpList = distOpListsMerge(exclusiveList, redirectList); + distOpList = distOpListsMerge(distOpList, inclusiveList); + distOpList = distOpListsMerge(distOpList, auxiliarList); - return apiEntityP; + return distOpList; } @@ -266,143 +225,124 @@ KjNode* apiEntityToGeoJson(KjNode* apiEntityP, KjNode* geometryNodeP, bool geoPr // // Steal checks of URI params from legacyGetEntities // -// NOTE: -// What if an entity id list is given, but with one single entity id? -// In general these two requests should be equivalent: -// - GET /entities?id=urn:ngsi-ld:T:E1 -// - GET /entities/urn:ngsi-ld:T:E1 -// -// However, if more URI params are given, the similarities aren't that big anymore. -// E.g., if attrs is given: -// - GET /entities?id=urn:ngsi-ld:T:E1&attrs=P1 [1] -// - GET /entities/urn:ngsi-ld:T:E1&attrs=P1 [2] +// Three cases: +// * legacy +// * local query (either by setting "local=true", or by "no matching registrations +// * distributed query (best effort, without freezing time) // -// Imagine that the entity urn:ngsi-ld:T:E1 doesn't have an attribute P1. -// - The query (1) would give back an empty arry - no match -// - The retrieval (2) would give back the entity withoput any attributes -// -// So, perhaps better to play it safe way and NOT EVER "redirect" to GET /entities/{entityId} // bool orionldGetEntities(void) { if ((experimental == false) || (orionldState.in.legacy != NULL)) // If Legacy header - use old implementation return legacyGetEntities(); - // - // URI param validity check - // - char* id = orionldState.uriParams.id; - char* type = orionldState.uriParams.type; - char* idPattern = orionldState.uriParams.idPattern; - char* q = orionldState.uriParams.q; - char* attrs = orionldState.uriParams.attrs; - char* geometry = orionldState.uriParams.geometry; - bool local = orionldState.uriParams.local; - - if ((id == NULL) && (idPattern == NULL) && (type == NULL) && ((geometry == NULL) || (*geometry == 0)) && (attrs == NULL) && (q == NULL) && (local == false)) - { - orionldError(OrionldBadRequestData, - "Too broad query", - "Need at least one of: entity-id, entity-type, geo-location, attribute-list, Q-filter, local=true", - 400); - - return false; - } - - - // - // If ONE or ZERO types in URI param 'type', the prepared array isn't used, just a simple char-pointer (named "type") - // - if (orionldState.in.typeList.items == 0) type = (char*) ".*"; - else if (orionldState.in.typeList.items == 1) type = orionldState.in.typeList.array[0]; - - OrionldGeoInfo geoInfo; - if (geoCheck(&geoInfo) == false) - return false; + char* id = orionldState.uriParams.id; // Validity checked in orionldMhdConnectionTreat.cpp (pCheckEntityIdParam) + char* type = orionldState.uriParams.type; // Validity checked in orionldMhdConnectionTreat.cpp (pCheckEntityTypeParam) + char* idPattern = orionldState.uriParams.idPattern; // No check + char* q = orionldState.uriParams.q; + char* geometry = orionldState.uriParams.geometry; + char* attrs = orionldState.uriParams.attrs; // Validity checked in orionldMhdConnectionTreat.cpp (pCheckAttrsParam) + bool local = orionldState.uriParams.local; + QNode* qNode = NULL; - QNode* qNode = NULL; - if (orionldState.uriParams.q != NULL) - { - qNode = qCheck(orionldState.uriParams.q); - if (qNode == NULL) - return false; - } + LM_T(LmtEntityMap, ("onlyIds: %s", (orionldState.uriParams.onlyIds == true)? "TRUE" : "FALSE")); // According to the spec, id takes precedence over idPattern, so, if both are present, idPattern is NULLed out - if ((orionldState.in.idList.items > 0) && (idPattern != NULL)) + if ((orionldState.in.idList.items > 0) && (orionldState.uriParams.idPattern != NULL)) idPattern = NULL; - char* geojsonGeometryLongName = NULL; - if (orionldState.out.contentType == GEOJSON) - geojsonGeometryLongName = orionldState.in.geometryPropertyExpanded; - - int64_t count; - KjNode* dbEntityArray = mongocEntitiesQuery(&orionldState.in.typeList, - &orionldState.in.idList, - idPattern, - &orionldState.in.attrList, - qNode, - &geoInfo, - &count, - geojsonGeometryLongName); - - if (dbEntityArray == NULL) + OrionldGeoInfo geoInfo; + if (pCheckQueryParams(id, type, idPattern, q, geometry, attrs, local, orionldState.in.entityMap, &qNode, &geoInfo) == false) return false; - KjNode* apiEntityArray = kjArray(orionldState.kjsonP, NULL); - RenderFormat rf = RF_NORMALIZED; - - if (orionldState.uriParamOptions.concise == true) rf = RF_CONCISE; - else if (orionldState.uriParamOptions.keyValues == true) rf = RF_KEYVALUES; + // + // Distributed GET /entities, using the concept "entityMaps" is disabled by default. + // To turn it on, please start Orion-LD with the CLI option: + // -wip entityMaps + // Or, the env var: + // export ORIONLD_WIP=entityMaps + // + if (entityMapsEnabled == false) + orionldState.distributed = false; - if (orionldState.out.contentType == GEOJSON) + // + // Entity Maps can be turned on using the HTTP header ORIONLD-WIP + // + if (orionldState.in.wip != NULL) { - KjNode* geojsonToplevelP = kjObject(orionldState.kjsonP, NULL); - KjNode* featuresP = kjArray(orionldState.kjsonP, "features"); // this is where all entities go - KjNode* typeP = kjString(orionldState.kjsonP, "type", "FeatureCollection"); - - kjChildAdd(geojsonToplevelP, typeP); - kjChildAdd(geojsonToplevelP, featuresP); - - orionldState.responseTree = geojsonToplevelP; - - KjNode* dbEntityP = dbEntityArray->value.firstChildP; - KjNode* next; - - while (dbEntityP != NULL) - { - next = dbEntityP->next; - - // Must remove dbEntityP from dbEntityArray as dbEntityP gets transformed into apiEntityP and then inserted into featuresP - kjChildRemove(dbEntityArray, dbEntityP); - - KjNode* apiEntityP = dbModelToApiEntity2(dbEntityP, orionldState.uriParamOptions.sysAttrs, rf, orionldState.uriParams.lang, true, &orionldState.pd); - const char* geometryPropertyName = (orionldState.uriParams.geometryProperty == NULL)? "location" : orionldState.uriParams.geometryProperty; - KjNode* geometryNodeP = kjLookup(apiEntityP, geometryPropertyName); - - apiEntityP = apiEntityToGeoJson(apiEntityP, geometryNodeP, orionldState.geoPropertyFromProjection); - kjChildAdd(featuresP, apiEntityP); - - dbEntityP = next; - } + if (strcmp(orionldState.in.wip, "entityMaps") == 0) + orionldState.distributed = true; } - else - { - orionldState.responseTree = apiEntityArray; - for (KjNode* dbEntityP = dbEntityArray->value.firstChildP; dbEntityP != NULL; dbEntityP = dbEntityP->next) + if (orionldState.in.entityMap != NULL) + { + // No query params can be used when asking for pages in an entity map + if ((id != NULL) || (type != NULL) || (idPattern != NULL) || (q != NULL) || + (geometry != NULL) || (attrs != NULL) || (orionldState.uriParams.local == true)) { - KjNode* apiEntityP = dbModelToApiEntity2(dbEntityP, orionldState.uriParamOptions.sysAttrs, rf, orionldState.uriParams.lang, true, &orionldState.pd); - kjChildAdd(apiEntityArray, apiEntityP); + orionldError(OrionldBadRequestData, "Query parameters present", "not allowed when paginating using an entity map", 400); + return false; } } - if (orionldState.uriParams.count == true) - orionldHeaderAdd(&orionldState.out.headers, HttpResultsCount, NULL, count); + if (orionldState.distributed == false) + return orionldGetEntitiesLocal(&orionldState.in.typeList, + &orionldState.in.idList, + &orionldState.in.attrList, + idPattern, + qNode, + &geoInfo, + orionldState.uriParams.lang, + orionldState.uriParamOptions.sysAttrs, + orionldState.uriParams.geometryProperty, + orionldState.uriParams.onlyIds, + false); + + if (orionldState.in.entityMap == NULL) // No prior entity map is requested - must create a new one + { + // + // Find matching registrations + // + orionldState.distOpList = distOpRequestsForEntitiesQuery(idPattern, qNode); - // If empty result array, no Link header is needed - if (orionldState.responseTree->value.firstChildP == NULL) - orionldState.noLinkHeader = true; + // + // if there are no matching registrations, the request is treated as a local request + // + if (orionldState.distOpList == NULL) + return orionldGetEntitiesLocal(&orionldState.in.typeList, + &orionldState.in.idList, + &orionldState.in.attrList, + idPattern, + qNode, + &geoInfo, + orionldState.uriParams.lang, + orionldState.uriParamOptions.sysAttrs, + orionldState.uriParams.geometryProperty, + orionldState.uriParams.onlyIds, + true); + + // Create the "@none" DistOp + DistOp* local = distOpCreate(DoQueryEntity, NULL, &orionldState.in.idList, &orionldState.in.typeList, &orionldState.in.attrList); + + // Add lang, geometryProperty, qNode, ... to the "@none" DistOp + local->lang = (orionldState.uriParams.lang != NULL)? strdup(orionldState.uriParams.lang) : NULL; + local->geoInfo.geometry = geoInfo.geometry; + local->geoInfo.georel = geoInfo.georel; + local->geoInfo.coordinates = (geoInfo.coordinates != NULL)? kjClone(NULL, geoInfo.coordinates) : NULL; + local->geoInfo.minDistance = geoInfo.minDistance; + local->geoInfo.maxDistance = geoInfo.maxDistance; + local->geoInfo.geoProperty = (geoInfo.geoProperty != NULL)? strdup(geoInfo.geoProperty) : NULL; + local->geometryProperty = (orionldState.uriParams.geometryProperty != NULL)? strdup(orionldState.uriParams.geometryProperty) : NULL; + local->qNode = (qNode != NULL)? qClone(qNode) : NULL; + + // Add the "@none" DistOp to the linked list of DistOps + local->next = orionldState.distOpList; + orionldState.distOpList = local; + + // This is the DistOp list to be used over pagination - it is "malloqued" so it survives the current request + distOpListDebug2(orionldState.distOpList, "distOpList for Entity Query"); + LM_T(LmtDistOpList, ("--------------- ")); + } - return true; + return orionldGetEntitiesDistributed(orionldState.distOpList, idPattern, qNode, &geoInfo); } diff --git a/src/lib/orionld/serviceRoutines/orionldGetEntitiesDistributed.cpp b/src/lib/orionld/serviceRoutines/orionldGetEntitiesDistributed.cpp new file mode 100644 index 0000000000..4e9215bd45 --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldGetEntitiesDistributed.cpp @@ -0,0 +1,203 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjFree.h" // kjFree +#include "kjson/kjLookup.h" // kjLookup +#include "kjson/kjBuilder.h" // kjChildRemove, kjChildAdd, kjArray +} + +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo +#include "orionld/q/QNode.h" // QNode +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/forwarding/distOpListRelease.h" // distOpListRelease +#include "orionld/entityMaps/entityMapCreate.h" // entityMapCreate +#include "orionld/serviceRoutines/orionldGetEntitiesLocal.h" // orionldGetEntitiesLocal +#include "orionld/serviceRoutines/orionldGetEntitiesPage.h" // orionldGetEntitiesPage +#include "orionld/serviceRoutines/orionldGetEntitiesDistributed.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// Implementation details +// +// - To be able to do pagination, we need a list of all matching entities: +// +// "EntityMap": { +// "urn:E1": [ null ], # null indicates it was found locally +// "urn:E2": [ null ], +// "urn:E3": [ null ], +// "urn:E4": [ null ] +// } +// +// - As entities can be distributed, the ID of each distributed entity needs to be an array of matching registrations: +// +// "urn:E1": [ null, "urn:R1", "urn:R3", ... ] +// +// So, we need to ask every matching broker (URI params id,idPattern,type,attrs, ... VS registrations) for their list of +// entity ids. +// This is not part of the NGSI-LD API, right now. I'll try to push this for inclusion in the API. +// What is needed is for "GET /entities" to have a URL param '?entityIdArray' and just return an array of entity ids - all of them. +// Until this gets included in the API, this mechanism will only work in a federation of 100% Orion-LD brokers +// +// Mechanism: +// 1. GET a list of all matching registrations. +// 2. Query them all (locally as well) for their 'Entity Id Array' +// 3. Merge all the reponses into one single array (orionldState,in.EntityMap) +// 4. Now we have a list and we can do pagination. +// 5. Just pick the start index and end index of the EntityMap and: +// - Identify each registered endpoint to be queried (from start index to end index) +// - Gather all entity ids for each registered endpoint +// - query using (GET /entities?id=E1,E2,E3,...En) +// - Actually, if one endpoint has multiple registrations, the attrs param may differ between registrations +// - Will probably need one GET /entities per registration, unfortunately :( +// 6. Merge all results into a single entity array, and especially merge entities of the same id +// 7. Respond to the initial GET /entities +// + + + +// +// Entity Map +// +// Right now it looks like this: +// { +// "urn:entities:E1": [ "urn:registrations:R1", "urn:registrations:R2", ... "@none" ], +// "urn:entities:E1": [ "urn:registrations:R1", "urn:registrations:R2", ... ], +// } +// +// which is later turned into: +// { +// "urn:registrations:R1": ["urn:entities:E1", "urn:entities:E2" ], +// "urn:registrations:R2": ["urn:entities:E1", "urn:entities:E2" ], +// "@none": ["urn:entities:E1"] +// } +// +// BUT, I need also entity type and attrs ... +// +// New Entity Map: +// { +// "urn:entities:E1": { +// "regs": [ "urn:registrations:R1", "urn:registrations:R2", ... "@none" ] +// "type": "T", +// "attrs": [ "urn:attribute:P1", "urn:attribute:P2", ... ] +// } +// } +// +// And turn it into (not only the array of entity id, but also, type, attrs, ... ALL URL params): +// { +// "urn:registrations:R1": [ +// { +// "ids": "urn:entities:E1,urn:entities:E2", +// "type": "T", +// "attrs": "urn:attribute:P1,urn:attribute:P2,...,urn:attribute:P2", +// "q": "xxx", +// "geoQ": {} +// }, +// ] +// } +// +// BUT, I already store all that info in the DistOp structure ... +// Well, not all perhaps - I'd have to add a few things +// So, what if I give the DistOp items an ID and I add that ID (so I can find it later) +// +// New Entity Map II: +// { +// "urn:entities:E1": [ "DistOp0001", "DistOp0002", "DistOp0003", ... "@none" ], +// } +// +// And turn that into: +// { +// "DistOp0001": [ "urn:entities:E1", "urn:entities:E2" ], +// "DistOp0002": [ "urn:entities:E1", "urn:entities:E2" ], +// "@none": [ "urn:entities:E1", "urn:entities:E2" ], +// } +// +// Does local have a DistOp? +// It will need to - the URL params from the first request (when the Entity Map was created) must be saved +// + + +// ---------------------------------------------------------------------------- +// +// orionldGetEntitiesDistributed - +// +bool orionldGetEntitiesDistributed(DistOp* distOpList, char* idPattern, QNode* qNode, OrionldGeoInfo* geoInfoP) +{ + if (orionldState.in.entityMap != NULL) + return orionldGetEntitiesPage(); + + LM_T(LmtCount, ("--------------------------- Creating entity map")); + orionldState.in.entityMap = entityMapCreate(distOpList, idPattern, qNode, geoInfoP); + distOpListRelease(distOpList); + + LM_T(LmtCount, ("--------------------------- entity map at %p", orionldState.in.entityMap)); + if (orionldState.in.entityMap != NULL) + { + LM_T(LmtCount, ("--------------------------- Created entity map")); + kjTreeLog(orionldState.in.entityMap->map, "EntityMap excl local entities", LmtCount); + + // Add the new entity map to the global list of entity maps + // sem-take + orionldState.in.entityMap->next = entityMaps; + entityMaps = orionldState.in.entityMap; + // sem-give + + // Must return the ID of the entity map as an HTTP header + orionldHeaderAdd(&orionldState.out.headers, HttpEntityMap, orionldState.in.entityMap->id, 0); + + if (orionldState.uriParams.limit == 0) + { + orionldHeaderAdd(&orionldState.out.headers, HttpResultsCount, NULL, orionldState.in.entityMap->count); + orionldState.responseTree = kjArray(orionldState.kjsonP, NULL); + return true; + } + } + else + LM_E(("entityMapCreate returned NULL")); + + // + // if there are no entity hits to the matching registrations, the request is treated as a local request + // + if ((orionldState.in.entityMap == NULL) || (orionldState.in.entityMap->map->value.firstChildP == NULL)) + return orionldGetEntitiesLocal(&orionldState.in.typeList, + &orionldState.in.idList, + &orionldState.in.attrList, + idPattern, + qNode, + geoInfoP, + orionldState.uriParams.lang, + orionldState.uriParamOptions.sysAttrs, + orionldState.uriParams.geometryProperty, + orionldState.uriParams.onlyIds, + true); + + return orionldGetEntitiesPage(); +} diff --git a/src/lib/orionld/serviceRoutines/orionldGetEntitiesDistributed.h b/src/lib/orionld/serviceRoutines/orionldGetEntitiesDistributed.h new file mode 100644 index 0000000000..633051a358 --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldGetEntitiesDistributed.h @@ -0,0 +1,40 @@ +#ifndef SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDGETENTITIESDISTRIBUTED_H_ +#define SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDGETENTITIESDISTRIBUTED_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo +#include "orionld/q/QNode.h" // QNode +#include "orionld/forwarding/DistOp.h" // DistOp + + + +// ---------------------------------------------------------------------------- +// +// orionldGetEntitiesDistributed - +// +extern bool orionldGetEntitiesDistributed(DistOp* distOpList, char* idPattern, QNode* qNode, OrionldGeoInfo* geoInfoP); + +#endif // SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDGETENTITIESDISTRIBUTED_H_ diff --git a/src/lib/orionld/serviceRoutines/orionldGetEntitiesLocal.cpp b/src/lib/orionld/serviceRoutines/orionldGetEntitiesLocal.cpp new file mode 100644 index 0000000000..362cc655b2 --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldGetEntitiesLocal.cpp @@ -0,0 +1,262 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // NULL +#include // types: uint64_t, ... + +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjBuilder.h" // kjString, kjObject, kjChildAdd, ... +#include "kjson/kjLookup.h" // kjLookup +#include "kjson/kjClone.h" // kjClone +} + +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/types/OrionldHeader.h" // orionldHeaderAdd, HttpResultsCount +#include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo +#include "orionld/q/QNode.h" // QNode +#include "orionld/context/orionldContextItemExpand.h" // orionldContextItemExpand +#include "orionld/mongoc/mongocEntitiesQuery.h" // mongocEntitiesQuery +#include "orionld/kjTree/kjChildPrepend.h" // kjChildPrepend +#include "orionld/dbModel/dbModelToApiEntity.h" // dbModelToApiEntity2 +#include "orionld/dbModel/dbModelToEntityIdAndTypeObject.h" // dbModelToEntityIdAndTypeObject +#include "orionld/serviceRoutines/orionldGetEntitiesLocal.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// apiEntityToGeoJson - transform an API Entity into a GEOJSON Entity +// +KjNode* apiEntityToGeoJson(KjNode* apiEntityP, KjNode* geometryNodeP, bool geoPropertyFromProjection) +{ + KjNode* propertiesP = kjObject(orionldState.kjsonP, "properties"); + + // + // 1. Find 'type' in the original entity - remove it and wait until everything is moved to 'properties' before putting 'type' back + // + KjNode* typeP = kjLookup(apiEntityP, "type"); + if (typeP != NULL) // Can't really be NULL, can it? + kjChildRemove(apiEntityP, typeP); + + + // + // 2. Find the 'id' in the original entity and remove it temporarily - will be put back again once 'properties' has been filled + // + KjNode* idP = kjLookup(apiEntityP, "id"); + if (idP != NULL) // Can't really be NULL, can it? + kjChildRemove(apiEntityP, idP); + + // 3. In case the geometryProperty was added to the projection even though it was not part of the "attrs" URI param - just remove it + if ((geoPropertyFromProjection == true) && (geometryNodeP != NULL)) + kjChildRemove(apiEntityP, geometryNodeP); + + // 4. Move EVERYTHING from "apiEntityP" to "properties" + propertiesP->value.firstChildP = apiEntityP->value.firstChildP; + propertiesP->lastChild = apiEntityP->lastChild; + + // Clear out apiEntityP + apiEntityP->value.firstChildP = NULL; + apiEntityP->lastChild = NULL; + + // + // Should the @context be added to the payload body? + // + if (orionldState.linkHeaderAdded == false) + { + // Only if Prefer is not set to body=json + if ((orionldState.preferHeader == NULL) || (strcasecmp(orionldState.preferHeader, "body=json") != 0)) + { + KjNode* contextP; + + if (orionldState.link == NULL) + contextP = kjString(orionldState.kjsonP, "@context", coreContextUrl); + else + contextP = kjString(orionldState.kjsonP, "@context", orionldState.link); + + kjChildAdd(apiEntityP, contextP); + orionldState.noLinkHeader = true; + } + } + + // 5. Put the original entity type inside 'properties' + if (typeP != NULL) + kjChildPrepend(propertiesP, typeP); + + // 6. Put the original entity id inside the top level entity + if (idP != NULL) + kjChildAdd(apiEntityP, idP); + + // 7. Create the new 'type' for the GEOJSON Entity and add it to the toplevel + typeP = kjString(orionldState.kjsonP, "type", "Feature"); + kjChildPrepend(apiEntityP, typeP); + + // 8. Create the "geometry" (key-values) top-level item + KjNode* geometryP = NULL; + if ((geometryNodeP != NULL) && (geometryNodeP->type == KjObject)) + { + geometryP = kjLookup(geometryNodeP, "value"); + if (geometryP == NULL) + { + // "value" not found ... can it be Simplified format? + geometryP = geometryNodeP; + } + + if (geometryP != NULL) + { + if (geometryP->type == KjObject) // && hasChildren type+coordinates ... + { + geometryP = kjClone(orionldState.kjsonP, geometryP); + geometryP->name = (char*) "geometry"; + } + else + geometryP = NULL; + } + } + + if (geometryP == NULL) + geometryP = kjNull(orionldState.kjsonP, "geometry"); + + kjChildAdd(apiEntityP, geometryP); + + // 9. Adding all the properties to top-level + propertiesP->next = NULL; + kjChildAdd(apiEntityP, propertiesP); + + return apiEntityP; +} + + + +// ---------------------------------------------------------------------------- +// +// orionldGetEntitiesLocal - +// +bool orionldGetEntitiesLocal +( + StringArray* typeList, + StringArray* idList, + StringArray* attrList, + char* idPattern, + QNode* qNode, + OrionldGeoInfo* geoInfoP, + const char* lang, + bool sysAttrs, + const char* geometryProperty, + bool onlyIds, + bool countHeaderAlreadyAdded +) +{ + char* geojsonGeometryLongName = NULL; + + if (orionldState.out.contentType == GEOJSON) + { + if ((geometryProperty != NULL) && (strcmp(geometryProperty, "location") != 0)) + geojsonGeometryLongName = orionldContextItemExpand(orionldState.contextP, geometryProperty, true, NULL); + else + geojsonGeometryLongName = (char*) "location"; + } + + int64_t count = 0; + KjNode* dbEntityArray = mongocEntitiesQuery(typeList, + idList, + idPattern, + attrList, + qNode, + geoInfoP, + &count, + geojsonGeometryLongName, + onlyIds); + + if (dbEntityArray == NULL) + return false; + + if (onlyIds == true) + { + orionldState.responseTree = dbModelToEntityIdAndTypeObject(dbEntityArray); + orionldState.noLinkHeader = true; + } + else + { + KjNode* apiEntityArray = kjArray(orionldState.kjsonP, NULL); + RenderFormat rf = RF_NORMALIZED; + + if (orionldState.uriParamOptions.concise == true) rf = RF_CONCISE; + else if (orionldState.uriParamOptions.keyValues == true) rf = RF_KEYVALUES; + + if (orionldState.out.contentType == GEOJSON) + { + KjNode* geojsonToplevelP = kjObject(orionldState.kjsonP, NULL); + KjNode* featuresP = kjArray(orionldState.kjsonP, "features"); // this is where all entities go + KjNode* typeP = kjString(orionldState.kjsonP, "type", "FeatureCollection"); + + kjChildAdd(geojsonToplevelP, typeP); + kjChildAdd(geojsonToplevelP, featuresP); + + orionldState.responseTree = geojsonToplevelP; + + KjNode* dbEntityP = dbEntityArray->value.firstChildP; + KjNode* next; + + while (dbEntityP != NULL) + { + next = dbEntityP->next; + + // Must remove dbEntityP from dbEntityArray as dbEntityP gets transformed into apiEntityP and then inserted into featuresP + kjChildRemove(dbEntityArray, dbEntityP); + + KjNode* apiEntityP = dbModelToApiEntity2(dbEntityP, sysAttrs, rf, lang, true, &orionldState.pd); + const char* geometryPropertyName = (geometryProperty == NULL)? "location" : geometryProperty; + KjNode* geometryNodeP = kjLookup(apiEntityP, geometryPropertyName); + + apiEntityP = apiEntityToGeoJson(apiEntityP, geometryNodeP, orionldState.geoPropertyFromProjection); + kjChildAdd(featuresP, apiEntityP); + + dbEntityP = next; + } + } + else + { + orionldState.responseTree = apiEntityArray; + + for (KjNode* dbEntityP = dbEntityArray->value.firstChildP; dbEntityP != NULL; dbEntityP = dbEntityP->next) + { + KjNode* apiEntityP = dbModelToApiEntity2(dbEntityP, orionldState.uriParamOptions.sysAttrs, rf, lang, true, &orionldState.pd); + kjChildAdd(apiEntityArray, apiEntityP); + } + } + } + + if ((orionldState.uriParams.count == true) && (countHeaderAlreadyAdded == false)) + orionldHeaderAdd(&orionldState.out.headers, HttpResultsCount, NULL, count); + + // If empty result array, no Link header is needed + if (orionldState.responseTree->value.firstChildP == NULL) + orionldState.noLinkHeader = true; + + return true; +} diff --git a/src/lib/orionld/serviceRoutines/orionldGetEntitiesLocal.h b/src/lib/orionld/serviceRoutines/orionldGetEntitiesLocal.h new file mode 100644 index 0000000000..78e92437cb --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldGetEntitiesLocal.h @@ -0,0 +1,52 @@ +#ifndef SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDGETENTITIESLOCAL_H_ +#define SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDGETENTITIESLOCAL_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include "orionld/q/QNode.h" // QNode +#include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo +#include "orionld/types/StringArray.h" // StringArray + + +// ---------------------------------------------------------------------------- +// +// orionldGetEntitiesLocal - +// +extern bool orionldGetEntitiesLocal +( + StringArray* typeList, + StringArray* idList, + StringArray* attrList, + char* idPattern, + QNode* qNode, + OrionldGeoInfo* geoInfoP, + const char* lang, + bool sysAttrs, + const char* geometryProperty, + bool onlyIds, + bool countHeaderAlreadyAdded +); + +#endif // SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDGETENTITIESLOCAL_H_ diff --git a/src/lib/orionld/serviceRoutines/orionldGetEntitiesPage.cpp b/src/lib/orionld/serviceRoutines/orionldGetEntitiesPage.cpp new file mode 100644 index 0000000000..7c10669de4 --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldGetEntitiesPage.cpp @@ -0,0 +1,347 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kalloc/kaAlloc.h" // kaAlloc +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjRender.h" // kjFastRender (for debugging purposes - LM_T) +#include "kjson/kjBuilder.h" // kjArray, ... +#include "kjson/kjLookup.h" // kjLookup +} + +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/common/orionldState.h" // orionldState, entityMaps +#include "orionld/common/orionldError.h" // orionldError +#include "orionld/kjTree/kjChildCount.h" // kjChildCount +#include "orionld/forwarding/DistOp.h" // DistOp +#include "orionld/forwarding/distOpLookupByRegId.h" // distOpLookupByRegId +#include "orionld/forwarding/distOpListDebug.h" // distOpListDebug +#include "orionld/forwarding/distOpsSend.h" // distOpsSend2 +#include "orionld/forwarding/distOpItemListDebug.h" // distOpItemListDebug +#include "orionld/forwarding/distOpListItemAdd.h" // distOpListItemAdd +#include "orionld/forwarding/distOpResponseMergeIntoEntityArray.h" // distOpResponseMergeIntoEntityArray +#include "orionld/forwarding/distOpsSendAndReceive.h" // distOpsSendAndReceive +#include "orionld/serviceRoutines/orionldGetEntitiesLocal.h" // orionldGetEntitiesLocal +#include "orionld/serviceRoutines/orionldGetEntitiesPage.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// cleanupSysAttrs - +// +static void cleanupSysAttrs(void) +{ + LM_T(LmtSR, ("In cleanupSysAttrs: orionldState.responseTree: %p", orionldState.responseTree)); + + for (KjNode* entityP = orionldState.responseTree->value.firstChildP; entityP != NULL; entityP = entityP->next) + { + KjNode* attrP = entityP->value.firstChildP; + KjNode* nextAttrP; + + while (attrP != NULL) + { + nextAttrP = attrP->next; + + LM_T(LmtSR, ("attrP->name: '%s'", attrP->name)); + if (strcmp(attrP->name, "createdAt") == 0) kjChildRemove(entityP, attrP); + else if (strcmp(attrP->name, "modifiedAt") == 0) kjChildRemove(entityP, attrP); + else if (attrP->type == KjObject) + { + // It's an attribute + + KjNode* subAttrP = attrP->value.firstChildP; + KjNode* nextSubAttrP; + + while (subAttrP != NULL) + { + nextSubAttrP = subAttrP->next; + + if (strcmp(subAttrP->name, "createdAt") == 0) kjChildRemove(attrP, subAttrP); + else if (strcmp(subAttrP->name, "modifiedAt") == 0) kjChildRemove(attrP, subAttrP); + + subAttrP = nextSubAttrP; + } + } + + attrP = nextAttrP; + } + } +} + + + +// ----------------------------------------------------------------------------- +// +// idListFix - +// +static void idListFix(KjNode* entityIdArray) +{ + int entityIds = kjChildCount(entityIdArray); + + orionldState.in.idList.items = entityIds; + orionldState.in.idList.array = (char**) kaAlloc(&orionldState.kalloc, sizeof(char*) * entityIds); + + KjNode* entityIdP = entityIdArray->value.firstChildP; + for (int ix = 0; ix < entityIds; ix++) + { + orionldState.in.idList.array[ix] = entityIdP->value.s; + entityIdP = entityIdP->next; + } +} + + + +// ----------------------------------------------------------------------------- +// +// queryResponse - +// +static int queryResponse(DistOp* distOpP, void* callbackParam) +{ + KjNode* entityArray = (KjNode*) callbackParam; + + distOpResponseMergeIntoEntityArray(distOpP, entityArray); + return 0; +} + + + +// ---------------------------------------------------------------------------- +// +// orionldGetEntitiesPage - +// +bool orionldGetEntitiesPage(void) +{ + uint32_t offset = orionldState.uriParams.offset; + uint32_t limit = orionldState.uriParams.limit; + KjNode* entityArray = kjArray(orionldState.kjsonP, NULL); + + LM_T(LmtEntityMap, ("entity map: '%s'", orionldState.in.entityMap->id)); + LM_T(LmtEntityMap, ("items in entity map: %d", orionldState.in.entityMap->count)); + LM_T(LmtEntityMap, ("offset: %d", offset)); + LM_T(LmtEntityMap, ("limit: %d", limit)); + + // HTTP Status code and payload body + orionldState.responseTree = entityArray; + LM_T(LmtSR, ("orionldState.responseTree: %p", orionldState.responseTree)); + orionldState.httpStatusCode = 200; + + if (orionldState.uriParams.count == true) + { + LM_T(LmtEntityMap, ("%d entities match, in the entire federation", orionldState.in.entityMap->count)); + LM_T(LmtEntityMap, ("COUNT: Adding HttpResultsCount header: %d", orionldState.in.entityMap->count)); + orionldHeaderAdd(&orionldState.out.headers, HttpResultsCount, NULL, orionldState.in.entityMap->count); + } + + if (offset >= orionldState.in.entityMap->count) + { + LM_T(LmtEntityMap, ("offset (%d) >= orionldState.in.entityMap->count (%d)", offset, orionldState.in.entityMap->count)); + return true; + } + + // + // Fast forward to offset index in the KJNode array that is the entity map + // + KjNode* entityMap = orionldState.in.entityMap->map->value.firstChildP; + + for (uint32_t ix = 0; ix < offset; ix++) + { + entityMap = entityMap->next; + } + + // + // entityMap now points to the first entity to give back. + // Must extract all parts of the entities, according to their array inside the Entity Map, + // and merge them together (in case of distributed entities + // + + // + // What we have here is a number of "slots" in the entity map, each slot with the layout: + // + // "urn:cp3:entities:E30" [ "urn:Reg1", "urn:Reg2", null ] + // + // To avoid making a DB query, of forwarded request, for each and every entity in the slots, + // we must here group them into: + // + // { + // "urn:reg1": [ "urn:E1", ... "urn:En" ], + // "urn:reg2": [ "urn:E1", ... "urn:En" ], + // "@none": [ "urn:E1", ... "urn:En" ] + // } + // ] + // + // Once that array is ready, we can send forwarded requests or query the local DB + // + KjNode* sources = kjObject(orionldState.kjsonP, NULL); + + for (uint32_t ix = 0; ix < limit; ix++) + { + if (entityMap == NULL) // in case we have less than "limit" + break; + + char* entityId = entityMap->name; + + for (KjNode* regP = entityMap->value.firstChildP; regP != NULL; regP = regP->next) + { + const char* regId = (regP->type == KjString)? regP->value.s : "@none"; + KjNode* regArray = kjLookup(sources, regId); + + if (regArray == NULL) + { + regArray = kjArray(orionldState.kjsonP, regId); + kjChildAdd(sources, regArray); + } + + KjNode* entityIdNodeP = kjString(orionldState.kjsonP, NULL, entityId); + kjChildAdd(regArray, entityIdNodeP); + } + + entityMap = entityMap->next; + } + + DistOpListItem* distOpListItem = NULL; + for (KjNode* sourceP = sources->value.firstChildP; sourceP != NULL; sourceP = sourceP->next) + { + if (strcmp(sourceP->name, "@none") == 0) + { + LM_T(LmtDistOpList, ("orionldState.distOpList at %p", orionldState.distOpList)); + DistOp* distOpP = distOpLookupByRegId(orionldState.distOpList, "@none"); + + // Local query - set input params for orionldGetEntitiesLocal + + // We want the entire entity this time, not only the entity id + orionldState.uriParams.onlyIds = false; + + // The type doesn't matter, we have a list of entity ids + orionldState.in.typeList.items = 0; + +#if 0 + LM_T(LmtDistOpAttributes, ("------------ Local DB Query -------------")); + LM_T(LmtDistOpAttributes, ("orionldState.uriParams.attrs: %s", orionldState.uriParams.attrs)); + LM_T(LmtDistOpAttributes, ("orionldState.in.attrList.items: %d", orionldState.in.attrList.items)); + for (int ix = 0; ix < orionldState.in.attrList.items; ix++) + { + LM_T(LmtDistOpAttributes, ("orionldState.in.attrList.array[%d]: '%s'", ix, orionldState.in.attrList.array[ix])); + } + LM_T(LmtDistOpAttributes, ("distOpP->attrsParam: %s", distOpP->attrsParam)); + LM_T(LmtDistOpAttributes, ("distOpP->attrList: %p", distOpP->attrList)); + + if (distOpP->attrList != NULL) + { + LM_T(LmtDistOpAttributes, ("distOpP->attrList->items: %d", distOpP->attrList->items)); + for (int ix = 0; ix < distOpP->attrList->items; ix++) + { + LM_T(LmtDistOpAttributes, ("distOpP->attrList->array[%d]: '%s'", ix, distOpP->attrList->array[ix])); + } + } +#endif + + // Set orionldState.in.geometryPropertyExpanded according to the entityMap creation request (that's modifying the response) + orionldState.in.geometryPropertyExpanded = NULL; // FIXME: fix + + // Set orionldState.in.idList according to the entities in the entityMap + idListFix(sourceP); + + // Use orionldState.in.attrList and not distOpP->attrList for local requests + + // No paging here + orionldState.uriParams.offset = 0; + orionldState.uriParams.limit = orionldState.in.idList.items; + + LM_T(LmtEntityMap, ("Query local database for %d entities", orionldState.in.idList.items)); + orionldGetEntitiesLocal(distOpP->typeList, + distOpP->idList, + &orionldState.in.attrList, + NULL, + distOpP->qNode, + &distOpP->geoInfo, + distOpP->lang, + true, // sysAttrs needed, to help pick attributes in case more than one of the same + distOpP->geometryProperty, + false, + true); + + // Response comes in orionldState.responseTree - move those to entityArray + if ((orionldState.responseTree != NULL) && (orionldState.responseTree->value.firstChildP != NULL)) + { + LM_T(LmtDistOpResponseDetail, ("Adding a 'distop-response-entity' to the entityArray")); + + orionldState.responseTree->lastChild->next = entityArray->value.firstChildP; + entityArray->value.firstChildP = orionldState.responseTree->value.firstChildP; + if (entityArray->lastChild == NULL) + entityArray->lastChild = orionldState.responseTree->lastChild; + } + } + else + { + int idStringSize = 0; + + LM_T(LmtEntityMap, ("Query '%s' for:", sourceP->name)); + for (KjNode* entityNodeP = sourceP->value.firstChildP; entityNodeP != NULL; entityNodeP = entityNodeP->next) + { + LM_T(LmtEntityMap, (" o %s", entityNodeP->value.s)); + idStringSize += strlen(entityNodeP->value.s) + 1; // +1 for the comma + } + + char* idString = kaAlloc(&orionldState.kalloc, idStringSize); + int idStringIx = 0; + + bzero(idString, idStringSize); + for (KjNode* entityNodeP = sourceP->value.firstChildP; entityNodeP != NULL; entityNodeP = entityNodeP->next) + { + strcpy(&idString[idStringIx], entityNodeP->value.s); + idStringIx += strlen(entityNodeP->value.s); + + if (entityNodeP->next != NULL) // No comma if last item + { + idString[idStringIx] = ','; + idStringIx += 1; + } + } + + LM_T(LmtEntityMap, ("Query '%s' with entity ids=%s", sourceP->name, idString)); + distOpListItem = distOpListItemAdd(distOpListItem, sourceP->name, idString); + } + } + + if (distOpListItem != NULL) + { + distOpItemListDebug(distOpListItem, "To Forward for GET /entities"); + distOpsSendAndReceive(distOpListItem, queryResponse, entityArray); + } + + orionldState.responseTree = entityArray; + + // + // Time to cleanup ... + // + // 1. Remove all timestamps, unless requested + // + if (orionldState.uriParamOptions.sysAttrs == false) + cleanupSysAttrs(); + + return true; +} diff --git a/src/lib/orionld/serviceRoutines/orionldGetEntitiesPage.h b/src/lib/orionld/serviceRoutines/orionldGetEntitiesPage.h new file mode 100644 index 0000000000..41a8bdf093 --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldGetEntitiesPage.h @@ -0,0 +1,37 @@ +#ifndef SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDGETENTITIESPAGE_H_ +#define SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDGETENTITIESPAGE_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ + + + +// ---------------------------------------------------------------------------- +// +// orionldGetEntitiesPage - +// +extern bool orionldGetEntitiesPage(void); + +#endif // SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDGETENTITIESPAGE_H_ diff --git a/src/lib/orionld/serviceRoutines/orionldGetEntity.cpp b/src/lib/orionld/serviceRoutines/orionldGetEntity.cpp index 29d72a4a8c..bc965c5059 100644 --- a/src/lib/orionld/serviceRoutines/orionldGetEntity.cpp +++ b/src/lib/orionld/serviceRoutines/orionldGetEntity.cpp @@ -1,4 +1,3 @@ - /* * * Copyright 2022 FIWARE Foundation e.V. @@ -57,6 +56,7 @@ extern "C" #include "orionld/forwarding/distOpEntityMerge.h" // distOpEntityMerge #include "orionld/forwarding/distOpListRelease.h" // distOpListRelease #include "orionld/forwarding/xForwardedForCompose.h" // xForwardedForCompose +#include "orionld/forwarding/viaCompose.h" // viaCompose #include "orionld/serviceRoutines/orionldGetEntity.h" // Own interface @@ -184,13 +184,15 @@ bool orionldGetEntity(void) { // Now that we've found all matching registrations we can add ourselves to the X-forwarded-For header char* xff = xForwardedForCompose(orionldState.in.xForwardedFor, localIpAndPort); + char* via = viaCompose(orionldState.in.via, brokerId); for (DistOp* distOpP = distOpList; distOpP != NULL; distOpP = distOpP->next) { // Send the forwarded request and await all responses if (distOpP->regP != NULL) { - if (distOpSend(distOpP, dateHeader, xff) == 0) + LM_T(LmtDistOpAttributes, ("distOp::attrsParam: '%s'", distOpP->attrsParam)); + if (distOpSend(distOpP, dateHeader, xff, via, false, NULL) == 0) { ++forwards; distOpP->error = false; diff --git a/src/lib/orionld/serviceRoutines/orionldGetEntityMap.cpp b/src/lib/orionld/serviceRoutines/orionldGetEntityMap.cpp new file mode 100644 index 0000000000..fc591803f8 --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldGetEntityMap.cpp @@ -0,0 +1,61 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjLookup.h" // kjLookup +#include "kjson/kjClone.h" // kjClone +#include "kjson/kjBuilder.h" // kjObject, kjArray, kjChildAdd, ... +} + +#include "orionld/common/orionldState.h" // orionldState, entityMaps +#include "orionld/common/orionldError.h" // orionldError +#include "orionld/types/EntityMap.h" // EntityMap +#include "orionld/entityMaps/entityMapLookup.h" // entityMapLookup +#include "orionld/serviceRoutines/orionldGetEntityMap.h" // Own interface + + + +// ---------------------------------------------------------------------------- +// +// orionldGetEntityMap - +// +bool orionldGetEntityMap(void) +{ + const char* entityMapId = orionldState.wildcard[0]; + EntityMap* entityMap = entityMapLookup(entityMapId); + + if (entityMap == NULL) + { + orionldError(OrionldResourceNotFound, "EntityMap Not Found", entityMapId, 404); + return false; + } + + orionldState.responseTree = entityMap->map; + orionldState.httpStatusCode = 200; + orionldState.noLinkHeader = true; + + return true; +} diff --git a/src/lib/orionld/serviceRoutines/orionldGetEntityMap.h b/src/lib/orionld/serviceRoutines/orionldGetEntityMap.h new file mode 100644 index 0000000000..307346137c --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldGetEntityMap.h @@ -0,0 +1,37 @@ +#ifndef SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDGETENTITYMAP_H_ +#define SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDGETENTITYMAP_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ + + + +// ---------------------------------------------------------------------------- +// +// orionldGetEntityMap - +// +extern bool orionldGetEntityMap(void); + +#endif // SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDGETENTITYMAP_H_ diff --git a/src/lib/orionld/serviceRoutines/orionldPatchEntity.cpp b/src/lib/orionld/serviceRoutines/orionldPatchEntity.cpp index 8ab31b305e..965c8a3eaa 100644 --- a/src/lib/orionld/serviceRoutines/orionldPatchEntity.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPatchEntity.cpp @@ -177,6 +177,10 @@ char* pCheckEntityType2(KjNode* payloadTypeNode, KjNode* dbEntityP, char* entity +// ----------------------------------------------------------------------------- +// +// attributeLookup - +// static bool attributeLookup(KjNode* dbAttrsP, char* attrName) { dotForEq(attrName); @@ -193,17 +197,21 @@ static bool attributeLookup(KjNode* dbAttrsP, char* attrName) #if 0 +// ----------------------------------------------------------------------------- +// +// rawResponse - +// void rawResponse(DistOp* distOpList, const char* what) { - LM_T(LmtDistOpMsgs, ("=============== rawResponse: %s", what)); + LM_T(LmtSR, ("=============== rawResponse: %s", what)); for (DistOp* distOpP = distOpList; distOpP != NULL; distOpP = distOpP->next) { if (distOpP->rawResponse != NULL) - LM_T(LmtDistOpMsgs, ("%s: rawResponse: '%s'", distOpP->regP->regId, distOpP->rawResponse)); + LM_T(LmtSR, ("%s: rawResponse: '%s'", distOpP->regP->regId, distOpP->rawResponse)); else - LM_T(LmtDistOpMsgs, ("%s: rawResponse: NULL", distOpP->regP->regId)); + LM_T(LmtSR, ("%s: rawResponse: NULL", distOpP->regP->regId)); } - LM_T(LmtDistOpMsgs, ("====================================================================")); + LM_T(LmtSR, ("====================================================================")); } #endif @@ -376,7 +384,7 @@ bool orionldPatchEntity(void) bzero(&local, sizeof(local)); local.requestBody = orionldState.requestTree; - distOpSuccess(responseBody, &local, NULL); + distOpSuccess(responseBody, &local, entityId, NULL); } // diff --git a/src/lib/orionld/serviceRoutines/orionldPatchEntity2.cpp b/src/lib/orionld/serviceRoutines/orionldPatchEntity2.cpp index cf30b323a9..20ceb39bfb 100644 --- a/src/lib/orionld/serviceRoutines/orionldPatchEntity2.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPatchEntity2.cpp @@ -605,7 +605,7 @@ bool orionldPatchEntity2(void) { char body[1024]; kjFastRender(distOpP->requestBody, body); - LM_T(LmtDistOpMsgs, ("Registration '%s': %s", distOpP->regP->regId, body)); + LM_T(LmtSR, ("Registration '%s': %s", distOpP->regP->regId, body)); } #endif } @@ -729,7 +729,7 @@ bool orionldPatchEntity2(void) local.requestBody = requestForLocal; if (dbUpdateResult == true) - distOpSuccess(responseBody, &local, NULL); + distOpSuccess(responseBody, &local, entityId, NULL); } responseFix(responseBody, DoMergeEntity, 204, entityId); diff --git a/src/lib/orionld/serviceRoutines/orionldPatchRegistration.cpp b/src/lib/orionld/serviceRoutines/orionldPatchRegistration.cpp index ace9e099d7..a05ed19e1a 100644 --- a/src/lib/orionld/serviceRoutines/orionldPatchRegistration.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPatchRegistration.cpp @@ -35,10 +35,12 @@ extern "C" } #include "logMsg/logMsg.h" // LM_* +#include "logMsg/traceLevels.h" // LmtRegCache #include "orionld/common/orionldState.h" // orionldState #include "orionld/common/orionldError.h" // orionldError #include "orionld/common/CHECK.h" // STRING_CHECK, ... +#include "orionld/common/tenantList.h" // tenant0 #include "orionld/types/RegistrationMode.h" // registrationMode #include "orionld/payloadCheck/PCHECK.h" // PCHECK_URI #include "orionld/payloadCheck/pcheckRegistration.h" // pcheckRegistration @@ -48,6 +50,7 @@ extern "C" #include "orionld/regCache/regCacheItemLookup.h" // regCacheItemLookup #include "orionld/regCache/regCacheIdPatternRegexCompile.h" // regCacheIdPatternRegexCompile #include "orionld/regCache/regCacheItemRegexRelease.h" // regCacheItemRegexRelease +#include "orionld/regCache/regCachePresent.h" // regCachePresent #include "orionld/dbModel/dbModelFromApiRegistration.h" // dbModelFromApiRegistration #include "orionld/dbModel/dbModelToApiRegistration.h" // dbModelToApiRegistration #include "orionld/mongoc/mongocRegistrationGet.h" // mongocRegistrationGet @@ -769,8 +772,8 @@ bool orionldPatchRegistration(void) LM_X(1, ("Internal Error (if this happens it's a bug of Orion-LD - the idPattern was checked in pcheckEntityInfo and all OK")); } - // extern void regCachePresent(void); - // regCachePresent(); + if (lmTraceIsSet(LmtRegCache)) + regCachePresent(); // // Return 204 if all OK diff --git a/src/lib/orionld/serviceRoutines/orionldPostBatchCreate.cpp b/src/lib/orionld/serviceRoutines/orionldPostBatchCreate.cpp index e9e57cd84f..f02b0e49cc 100644 --- a/src/lib/orionld/serviceRoutines/orionldPostBatchCreate.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPostBatchCreate.cpp @@ -102,7 +102,7 @@ bool orionldPostBatchCreate(void) // // FIXME: Use simpler mongoc function - we only need to know whether the entities exist or not // - KjNode* dbEntityArray = mongocEntitiesQuery(NULL, &eIdArray, NULL, NULL, NULL, NULL, NULL, NULL); + KjNode* dbEntityArray = mongocEntitiesQuery(NULL, &eIdArray, NULL, NULL, NULL, NULL, NULL, NULL, false); // FIXME: Last param should be 'true' if (dbEntityArray == NULL) { orionldError(OrionldInternalError, "Database Error", "error querying the database for entities", 500); diff --git a/src/lib/orionld/serviceRoutines/orionldPostBatchDelete.cpp b/src/lib/orionld/serviceRoutines/orionldPostBatchDelete.cpp index f97470d551..60f0e4f064 100644 --- a/src/lib/orionld/serviceRoutines/orionldPostBatchDelete.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPostBatchDelete.cpp @@ -47,8 +47,10 @@ extern "C" #include "orionld/forwarding/distOpListsMerge.h" // distOpListsMerge #include "orionld/forwarding/distOpSend.h" // distOpSend #include "orionld/forwarding/distOpLookupByCurlHandle.h" // distOpLookupByCurlHandle +#include "orionld/forwarding/distOpListDebug.h" // distOpListDebug2 #include "orionld/forwarding/distOpListRelease.h" // distOpListRelease #include "orionld/forwarding/xForwardedForCompose.h" // xForwardedForCompose +#include "orionld/forwarding/viaCompose.h" // viaCompose #include "orionld/forwarding/regMatchForBatchDelete.h" // regMatchForBatchDelete #include "orionld/serviceRoutines/orionldPostBatchDelete.h" // Own interface @@ -194,7 +196,7 @@ void distReqsMergeForBatchDelete(DistOp* distOpList) } else { - // Remove drP from list (just skip over it + // Remove drP from list (just skip over it) prev->next = next; } @@ -467,8 +469,9 @@ bool orionldPostBatchDelete(void) // Enqueue all forwarded requests // Now that we've found all matching registrations we can add ourselves to the X-forwarded-For header char* xff = xForwardedForCompose(orionldState.in.xForwardedFor, localIpAndPort); + char* via = viaCompose(orionldState.in.via, brokerId); - int forwards = 0; + int forwards = 0; for (DistOp* distReqP = distOpList; distReqP != NULL; distReqP = distReqP->next) { // Send the forwarded request and await all responses @@ -477,7 +480,7 @@ bool orionldPostBatchDelete(void) char dateHeader[70]; snprintf(dateHeader, sizeof(dateHeader), "Date: %s", orionldState.requestTimeString); - if (distOpSend(distReqP, dateHeader, xff) == 0) + if (distOpSend(distReqP, dateHeader, xff, via, false, NULL) == 0) { ++forwards; distReqP->error = false; diff --git a/src/lib/orionld/serviceRoutines/orionldPostBatchUpdate.cpp b/src/lib/orionld/serviceRoutines/orionldPostBatchUpdate.cpp index 3a067431d0..b0ebde179c 100644 --- a/src/lib/orionld/serviceRoutines/orionldPostBatchUpdate.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPostBatchUpdate.cpp @@ -108,7 +108,7 @@ bool orionldPostBatchUpdate(void) // // The entity id array is ready - time to query mongo // - KjNode* dbEntityArray = mongocEntitiesQuery(NULL, &eIdArray, NULL, NULL, NULL, NULL, NULL, NULL); + KjNode* dbEntityArray = mongocEntitiesQuery(NULL, &eIdArray, NULL, NULL, NULL, NULL, NULL, NULL, false); if (dbEntityArray == NULL) { orionldError(OrionldInternalError, "Database Error", "error querying the database for entities", 500); diff --git a/src/lib/orionld/serviceRoutines/orionldPostBatchUpsert.cpp b/src/lib/orionld/serviceRoutines/orionldPostBatchUpsert.cpp index 52cce158d8..aec0e9e311 100644 --- a/src/lib/orionld/serviceRoutines/orionldPostBatchUpsert.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPostBatchUpsert.cpp @@ -172,7 +172,7 @@ bool orionldPostBatchUpsert(void) // orionldState.uriParams.limit = noOfEntities; orionldState.uriParams.offset = 0; - KjNode* dbEntityArray = mongocEntitiesQuery(NULL, &eIdArray, NULL, NULL, NULL, NULL, NULL, NULL); + KjNode* dbEntityArray = mongocEntitiesQuery(NULL, &eIdArray, NULL, NULL, NULL, NULL, NULL, NULL, false); if (dbEntityArray == NULL) { orionldError(OrionldInternalError, "Database Error", "error querying the database for entities", 500); diff --git a/src/lib/orionld/serviceRoutines/orionldPostEntities.cpp b/src/lib/orionld/serviceRoutines/orionldPostEntities.cpp index 5ec5ba91c6..c6c9ea506e 100644 --- a/src/lib/orionld/serviceRoutines/orionldPostEntities.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPostEntities.cpp @@ -234,7 +234,7 @@ bool orionldPostEntities(void) DistOp local; bzero(&local, sizeof(local)); local.requestBody = cloneForTroeP; - distOpSuccess(responseBody, &local, NULL); + distOpSuccess(responseBody, &local, NULL, NULL); } // diff --git a/src/lib/orionld/serviceRoutines/orionldPostEntity.cpp b/src/lib/orionld/serviceRoutines/orionldPostEntity.cpp index d4f12d1c7a..2a4611bbb2 100644 --- a/src/lib/orionld/serviceRoutines/orionldPostEntity.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPostEntity.cpp @@ -262,7 +262,7 @@ bool orionldPostEntity(void) kjChildRemove(orionldState.requestTree, attrP); } else - distOpSuccess(responseBody, NULL, attrP->name); + distOpSuccess(responseBody, NULL, entityId, attrP->name); } } else @@ -274,7 +274,7 @@ bool orionldPostEntity(void) kjChildRemove(orionldState.requestTree, attrP); } else - distOpSuccess(responseBody, NULL, attrP->name); + distOpSuccess(responseBody, NULL, entityId, attrP->name); } attrP = next; diff --git a/src/lib/orionld/serviceRoutines/orionldPostQuery.cpp b/src/lib/orionld/serviceRoutines/orionldPostQuery.cpp index 054ae30060..548a0b390a 100644 --- a/src/lib/orionld/serviceRoutines/orionldPostQuery.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPostQuery.cpp @@ -63,8 +63,6 @@ bool orionldPostQuery(void) if (treeNodeV == NULL) return false; - LM_T(LmtSR, ("QNode tree at %p", treeNodeV[3].output)); - // // pCheckQuery has left us everything checked and expanded. // Now we need to transform stuff so that we can call mongocEntitiesQuery diff --git a/src/lib/orionld/serviceRoutines/orionldPostRegistrations.cpp b/src/lib/orionld/serviceRoutines/orionldPostRegistrations.cpp index d65c5250ac..6a0761996e 100644 --- a/src/lib/orionld/serviceRoutines/orionldPostRegistrations.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPostRegistrations.cpp @@ -161,8 +161,7 @@ bool orionldPostRegistrations(void) if (regIdP == NULL) { // Invent a registration id - strncpy(registrationIdV, "urn:ngsi-ld:ContextSourceRegistration:", sizeof(registrationIdV) - 1); - uuidGenerate(®istrationIdV[38], sizeof(registrationIdV) - 38, false); + uuidGenerate(registrationIdV, sizeof(registrationIdV), "urn:ngsi-ld:ContextSourceRegistration:"); regIdP = kjString(orionldState.kjsonP, "id", registrationId); } else diff --git a/src/lib/orionld/serviceRoutines/orionldPostSubscriptions.cpp b/src/lib/orionld/serviceRoutines/orionldPostSubscriptions.cpp index 3d532f1c26..b25015eb31 100644 --- a/src/lib/orionld/serviceRoutines/orionldPostSubscriptions.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPostSubscriptions.cpp @@ -49,7 +49,7 @@ extern "C" #include "orionld/pernot/PernotSubscription.h" // PernotSubscription #include "orionld/pernot/PernotSubCache.h" // PernotSubCache #include "orionld/pernot/pernotSubCacheAdd.h" // pernotSubCacheAdd -#include "orionld/pernot/pernotSubCacheRemove.h" // pernotSubCacheRemove +#include "orionld/pernot/pernotItemRelease.h" // pernotItemRelease #include "orionld/pernot/pernotSubCacheLookup.h" // pernotSubCacheLookup #include "orionld/mqtt/mqttParse.h" // mqttParse #include "orionld/mqtt/mqttConnectionEstablish.h" // mqttConnectionEstablish @@ -134,7 +134,7 @@ bool orionldPostSubscriptions(void) // char* detail = NULL; if ((subCacheItemLookup(orionldState.tenantP->tenant, subId) != NULL) || - (pernotSubCacheLookup(orionldState.tenantP->tenant, subId) != NULL) || + (pernotSubCacheLookup(subId, orionldState.tenantP->tenant) != NULL) || (mongocSubscriptionExists(subId, &detail) == true)) { if (detail == NULL) @@ -152,9 +152,8 @@ bool orionldPostSubscriptions(void) } else { - strncpy(subscriptionId, "urn:ngsi-ld:subscription:", sizeof(subscriptionId) - 1); - uuidGenerate(&subscriptionId[25], sizeof(subscriptionId) - 25, false); - + char subscriptionId[80]; + uuidGenerate(subscriptionId, sizeof(subscriptionId), "urn:ngsi-ld:subscription:"); subIdP = kjString(orionldState.kjsonP, "id", subscriptionId); } @@ -318,7 +317,7 @@ bool orionldPostSubscriptions(void) if (cSubP != NULL) subCacheItemRemove(cSubP); else - pernotSubCacheRemove(pSubP); + pernotItemRelease(pSubP); if (qTree != NULL) qRelease(qTree); diff --git a/src/lib/orionld/socketService/socketServiceInit.cpp b/src/lib/orionld/socketService/socketServiceInit.cpp index b8885971c7..e71c9d8051 100644 --- a/src/lib/orionld/socketService/socketServiceInit.cpp +++ b/src/lib/orionld/socketService/socketServiceInit.cpp @@ -61,6 +61,6 @@ int socketServiceInit(unsigned short port) if (listen(listenFd, 10) == -1) LM_RP(-1, ("error listening to socket for socket service")); - LM_T(LmtSocketService, ("SS: socket service listens on fd %d", listenFd)); + LM_T(LmtSocketService, ("Socket Service listens on fd %d", listenFd)); return listenFd; } diff --git a/src/lib/orionld/troe/pgAttributeBuild.cpp b/src/lib/orionld/troe/pgAttributeBuild.cpp index bc1764f97f..de5726193e 100644 --- a/src/lib/orionld/troe/pgAttributeBuild.cpp +++ b/src/lib/orionld/troe/pgAttributeBuild.cpp @@ -67,7 +67,7 @@ bool pgAttributeBuild KjNode* valueNodeP = NULL; KjNode* subAttrV = kjArray(orionldState.kjsonP, NULL); - uuidGenerate(instanceId, sizeof(instanceId), true); + uuidGenerate(instanceId, sizeof(instanceId), "urn:ngsi-ld:attribute:instance:"); // Extract attribute info, call subAttributeBuild when necessary KjNode* nodeP = attributeNodeP->value.firstChildP; diff --git a/src/lib/orionld/troe/pgCommands.cpp b/src/lib/orionld/troe/pgCommands.cpp index 4787720a7e..c927516ba5 100644 --- a/src/lib/orionld/troe/pgCommands.cpp +++ b/src/lib/orionld/troe/pgCommands.cpp @@ -57,6 +57,7 @@ void pgCommands(char* sql[], int commands) for (int ix = 0; ix < commands; ix++) { LM_T(LmtSql, ("SQL: %s;", sql[ix])); + PGresult* res = PQexec(connectionP->connectionP, sql[ix]); if (res == NULL) { diff --git a/src/lib/orionld/troe/pgEntityBuild.cpp b/src/lib/orionld/troe/pgEntityBuild.cpp index e94bdc45f3..54dec65aa2 100644 --- a/src/lib/orionld/troe/pgEntityBuild.cpp +++ b/src/lib/orionld/troe/pgEntityBuild.cpp @@ -60,7 +60,7 @@ bool pgEntityBuild { char instanceId[80]; - uuidGenerate(instanceId, sizeof(instanceId), true); + uuidGenerate(instanceId, sizeof(instanceId), "urn:ngsi-ld:attribute:instance:"); // // If entity ID and TYPE still in there - remove them! diff --git a/src/lib/orionld/troe/pgSubAttributeBuild.cpp b/src/lib/orionld/troe/pgSubAttributeBuild.cpp index f7534a504b..57eb0633e5 100644 --- a/src/lib/orionld/troe/pgSubAttributeBuild.cpp +++ b/src/lib/orionld/troe/pgSubAttributeBuild.cpp @@ -60,7 +60,7 @@ bool pgSubAttributeBuild if (subAttributeNodeP->type != KjObject) return true; - uuidGenerate(instanceId, sizeof(instanceId), true); + uuidGenerate(instanceId, sizeof(instanceId), "urn:ngsi-ld:attribute:instance:"); // Extract sub-attribute info for (KjNode* nodeP = subAttributeNodeP->value.firstChildP; nodeP != NULL; nodeP = nodeP->next) diff --git a/src/lib/orionld/troe/troeDeleteAttribute.cpp b/src/lib/orionld/troe/troeDeleteAttribute.cpp index f3fc8247dd..facc4d12ec 100644 --- a/src/lib/orionld/troe/troeDeleteAttribute.cpp +++ b/src/lib/orionld/troe/troeDeleteAttribute.cpp @@ -55,7 +55,7 @@ bool troeDeleteAttribute(void) char* attributeNameEq; char instanceId[80]; - uuidGenerate(instanceId, sizeof(instanceId), true); + uuidGenerate(instanceId, sizeof(instanceId), "urn:ngsi-ld:attribute:instance:"); attributeNameEq = kaStrdup(&orionldState.kalloc, attributeName); dotForEq(attributeNameEq); diff --git a/src/lib/orionld/troe/troeDeleteEntity.cpp b/src/lib/orionld/troe/troeDeleteEntity.cpp index ea01720c85..e56949e107 100644 --- a/src/lib/orionld/troe/troeDeleteEntity.cpp +++ b/src/lib/orionld/troe/troeDeleteEntity.cpp @@ -47,7 +47,7 @@ bool troeDeleteEntity(void) char* entityId = orionldState.wildcard[0]; char instanceId[80]; - uuidGenerate(instanceId, sizeof(instanceId), true); + uuidGenerate(instanceId, sizeof(instanceId), "urn:ngsi-ld:attribute:instance:"); pgAppendInit(&entitiesBuffer, 512); // Enough for deletion of a single entity pgAppend(&entitiesBuffer, PG_ENTITY_INSERT_START, 0); diff --git a/src/lib/orionld/troe/troePatchEntity.cpp b/src/lib/orionld/troe/troePatchEntity.cpp index 2a16392368..44192b4d95 100644 --- a/src/lib/orionld/troe/troePatchEntity.cpp +++ b/src/lib/orionld/troe/troePatchEntity.cpp @@ -80,7 +80,7 @@ bool troePatchEntity(void) if (dotP == NULL) { char instanceId[80]; - uuidGenerate(instanceId, sizeof(instanceId), true); + uuidGenerate(instanceId, sizeof(instanceId), "urn:ngsi-ld:attribute:instance:"); pgAttributeAppend(&attributesBuffer, instanceId, attrName, "Delete", entityId, NULL, NULL, true, NULL, NULL, NULL, NULL); } else diff --git a/src/lib/orionld/troe/troePostBatchDelete.cpp b/src/lib/orionld/troe/troePostBatchDelete.cpp index 2eb1c8f866..0853c9cea9 100644 --- a/src/lib/orionld/troe/troePostBatchDelete.cpp +++ b/src/lib/orionld/troe/troePostBatchDelete.cpp @@ -53,7 +53,7 @@ bool troePostBatchDelete(void) char* entityId = entityIdP->value.s; char instanceId[80]; - uuidGenerate(instanceId, sizeof(instanceId), true); + uuidGenerate(instanceId, sizeof(instanceId), "urn:ngsi-ld:attribute:instance:"); pgEntityAppend(&entitiesBuffer, "Delete", entityId, "NULL", instanceId); } diff --git a/src/lib/orionld/types/CMakeLists.txt b/src/lib/orionld/types/CMakeLists.txt index d30a01bd6a..622d6e5760 100644 --- a/src/lib/orionld/types/CMakeLists.txt +++ b/src/lib/orionld/types/CMakeLists.txt @@ -31,6 +31,7 @@ SET (SOURCES OrionldResponseErrorType.cpp RegistrationMode.cpp Protocol.cpp + StringArray.cpp ) # Include directories diff --git a/src/lib/orionld/types/EntityMap.h b/src/lib/orionld/types/EntityMap.h new file mode 100644 index 0000000000..1b8b683d98 --- /dev/null +++ b/src/lib/orionld/types/EntityMap.h @@ -0,0 +1,49 @@ +#ifndef SRC_LIB_ORIONLD_TYPES_ENTITYMAP_H_ +#define SRC_LIB_ORIONLD_TYPES_ENTITYMAP_H_ + +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +#include // types: uint32_t, ... + +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +} + + + +// ----------------------------------------------------------------------------- +// +// EntityMap - +// +typedef struct EntityMap +{ + char id[64]; + KjNode* map; + uint32_t count; + struct EntityMap* next; +} EntityMap; + +#endif // SRC_LIB_ORIONLD_TYPES_ENTITYMAP_H_ diff --git a/src/lib/orionld/types/OrionldGeoInfo.h b/src/lib/orionld/types/OrionldGeoInfo.h index 9b91d84821..1cd4f553d0 100644 --- a/src/lib/orionld/types/OrionldGeoInfo.h +++ b/src/lib/orionld/types/OrionldGeoInfo.h @@ -25,6 +25,11 @@ * * Author: Ken Zangelin */ +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +} + #include "orionld/types/OrionldGeometry.h" // OrionldGeometry #include "orionld/types/OrionldGeorel.h" // OrionldGeorel diff --git a/src/lib/orionld/types/OrionldHeader.cpp b/src/lib/orionld/types/OrionldHeader.cpp index 9b3c9e3f11..e3502df758 100644 --- a/src/lib/orionld/types/OrionldHeader.cpp +++ b/src/lib/orionld/types/OrionldHeader.cpp @@ -71,7 +71,8 @@ const char* orionldHeaderName[] = { "Access-Control-Max-Age", "Access-Control-Expose-Headers", "Accept-Patch", - "Performance" + "Performance", + "NGSILD-EntityMap" }; diff --git a/src/lib/orionld/types/OrionldHeader.h b/src/lib/orionld/types/OrionldHeader.h index 9ea7ed1b78..f40d0ef8de 100644 --- a/src/lib/orionld/types/OrionldHeader.h +++ b/src/lib/orionld/types/OrionldHeader.h @@ -62,7 +62,8 @@ typedef enum OrionldHeaderType HttpMaxAge, // CORS HttpExposeHeaders, // CORS HttpAcceptPatch, - HttpPerformance + HttpPerformance, + HttpEntityMap // For distributed GET /entities } OrionldHeaderType; diff --git a/src/lib/orionld/types/StringArray.cpp b/src/lib/orionld/types/StringArray.cpp new file mode 100644 index 0000000000..53f70599de --- /dev/null +++ b/src/lib/orionld/types/StringArray.cpp @@ -0,0 +1,53 @@ +/* +* +* Copyright 2023 FIWARE Foundation e.V. +* +* This file is part of Orion-LD Context Broker. +* +* Orion-LD Context Broker is free software: you can redistribute it and/or +* modify it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* Orion-LD Context Broker is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +* General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +* +* For those usages not covered by this license please contact with +* orionld at fiware dot org +* +* Author: Ken Zangelin +*/ +extern "C" +{ +#include "kalloc/kaAlloc.h" // kaAlloc +#include "kalloc/kaStrdup.h" // kaStrdup +} + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/types/StringArray.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// stringArrayClone - +// +StringArray* stringArrayClone(StringArray* saP) +{ + StringArray* clone = (StringArray*) kaAlloc(&orionldState.kalloc, sizeof(StringArray)); + + clone->items = saP->items; + clone->array = (char**) kaAlloc(&orionldState.kalloc, saP->items * sizeof(char*)); + + for (int ix = 0; ix < clone->items; ix++) + { + clone->array[ix] = kaStrdup(&orionldState.kalloc, saP->array[ix]); + } + + return clone; +} diff --git a/src/lib/orionld/types/StringArray.h b/src/lib/orionld/types/StringArray.h index 932ac0a106..aaa87a3979 100644 --- a/src/lib/orionld/types/StringArray.h +++ b/src/lib/orionld/types/StringArray.h @@ -38,4 +38,13 @@ typedef struct StringArray char** array; } StringArray; + + + +// ----------------------------------------------------------------------------- +// +// stringArrayClone - +// +extern StringArray* stringArrayClone(StringArray* saP); + #endif // SRC_LIB_ORIONLD_TYPES_STRINGARRAY_H_ diff --git a/src/lib/rest/rest.cpp b/src/lib/rest/rest.cpp index d06d89c05c..0990d5fddf 100644 --- a/src/lib/rest/rest.cpp +++ b/src/lib/rest/rest.cpp @@ -70,6 +70,7 @@ extern "C" #include "orionld/rest/orionldMhdConnectionInit.h" // orionldMhdConnectionInit #include "orionld/rest/orionldMhdConnectionPayloadRead.h" // orionldMhdConnectionPayloadRead #include "orionld/rest/orionldMhdConnectionTreat.h" // orionldMhdConnectionTreat +#include "orionld/forwarding/distOpListRelease.h" // distOpListRelease #include "rest/HttpHeaders.h" // HTTP_* defines #include "rest/Verb.h" @@ -119,7 +120,6 @@ static unsigned int connMemory; static unsigned int maxConns; static unsigned int threadPoolSize; static unsigned int mhdConnectionTimeout = 0; -static int reqNo = 1; @@ -428,19 +428,14 @@ static void requestCompleted free(orionldState.curlHeadersV); } - // - // FIXME: - // Would be nice to do this here instead of in every service routine - // Just, I don't have the pointer (distOpList) in orionldState. - // At least I can do the call to curl_multi_cleanup - // if (orionldState.curlDoMultiP != NULL) { - // distOpListRelease(orionldState.distOpList); curl_multi_cleanup(orionldState.curlDoMultiP); orionldState.curlDoMultiP = NULL; } + if (orionldState.distOpList != NULL) + distOpListRelease(orionldState.distOpList); lmTransactionEnd(); // Incoming REST request ends @@ -526,10 +521,8 @@ static void requestCompleted *con_cls = NULL; - ++reqNo; - #ifdef REQUEST_PERFORMANCE - // if (reqNo % 100 == 0) + // if (requestNo % 100 == 0) { PERFORMANCE(reqEnd); diff --git a/test/functionalTest/cases/0000_cli/bool_option_with_value.test b/test/functionalTest/cases/0000_cli/bool_option_with_value.test index a7e6d4e6cd..5ff9b25792 100644 --- a/test/functionalTest/cases/0000_cli/bool_option_with_value.test +++ b/test/functionalTest/cases/0000_cli/bool_option_with_value.test @@ -46,7 +46,7 @@ Usage: orionld [option '-U' (extended usage)] [option '-logLevel' ] [option '-logAppend' (append to log-file)] - [option '-coreContext' ] + [option '-coreContext' ] [option '-fg' (don't start as daemon)] [option '-localIp' ] [option '-port' ] @@ -108,10 +108,11 @@ Usage: orionld [option '-U' (extended usage)] [option '-troeUser' ] [option '-troePwd' ] [option '-troePoolSize' ] - [option '-distributed' (turn on distributed operation)] [option '-noNotifyFalseUpdate' (turn off notifications on non-updates)] [option '-experimental' (enable experimental implementation - use at own risk - see release notes of Orion-LD v1.1.0)] [option '-mongocOnly' (enable experimental implementation + turn off mongo legacy driver)] [option '-cSubCounters' ] + [option '-distributed' (turn on distributed operation)] + [option '-brokerId' ] --TEARDOWN-- diff --git a/test/functionalTest/cases/0000_cli/command_line_options.test b/test/functionalTest/cases/0000_cli/command_line_options.test index 99bbd2d25f..f6e339162b 100644 --- a/test/functionalTest/cases/0000_cli/command_line_options.test +++ b/test/functionalTest/cases/0000_cli/command_line_options.test @@ -35,7 +35,7 @@ Usage: orionld [option '-U' (extended usage)] [option '-logLevel' ] [option '-logAppend' (append to log-file)] - [option '-coreContext' ] + [option '-coreContext' ] [option '-fg' (don't start as daemon)] [option '-localIp' ] [option '-port' ] @@ -97,10 +97,11 @@ Usage: orionld [option '-U' (extended usage)] [option '-troeUser' ] [option '-troePwd' ] [option '-troePoolSize' ] - [option '-distributed' (turn on distributed operation)] [option '-noNotifyFalseUpdate' (turn off notifications on non-updates)] [option '-experimental' (enable experimental implementation - use at own risk - see release notes of Orion-LD v1.1.0)] [option '-mongocOnly' (enable experimental implementation + turn off mongo legacy driver)] [option '-cSubCounters' ] + [option '-distributed' (turn on distributed operation)] + [option '-brokerId' ] --TEARDOWN-- diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_andreu-crash.test b/test/functionalTest/cases/0000_ngsild/ngsild_andreu-crash.test index 4ef8973e4f..aafef8da1f 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_andreu-crash.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_andreu-crash.test @@ -26,7 +26,7 @@ Subscription Deletion in HA with two brokers --SHELL-INIT-- dbInit CB dbInit CP1 -orionldStart CB -experimental -forwarding -t 0-255 +orionldStart CB -experimental -forwarding orionldStart CP1 -experimental --SHELL-- @@ -54,7 +54,6 @@ payload='{ ], "endpoint": "localhost:'$CP1_PORT'", "operations": [ "createEntity", "retrieveEntity","mergeEntity" ] - }' orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" echo diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_context_simultaneous_download.test b/test/functionalTest/cases/0000_ngsild/ngsild_context_simultaneous_download.test index db38c21ea3..5fee32d647 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_context_simultaneous_download.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_context_simultaneous_download.test @@ -31,7 +31,7 @@ orionldStart CB # # This test exercises the 'wait for context download feature', implemented in the end of February 2021. -# - Sending a request to create an entity E1 with context X, in the bacground, so another request can enter the broker simultaneously +# - Sending a request to create an entity E1 with context X, in the background, so another request can enter the broker simultaneously # - Sending another request, with the same context X, to create another entity (E2) # # As the first request is already downloading context X when the second request is received, the second request will await the download of @@ -156,4 +156,4 @@ Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" + +{ + "P1": { + "type": "Property", + "value": 1 + }, + "id": "urn:E1", + "type": "T" +} + + +04. Dump accumulator and see attrs=P1,P2 +======================================== +GET http://REGEX(.*)/ngsi-ld/v1/entities/urn:E1?attrs=P1,P2&options=sysAttrs&type=T +User-Agent: orionld/REGEX(.*) +Host: localhost:9997 +Accept: application/json +Date: REGEX(.*) +X-Forwarded-For: REGEX(.*) +Via: 1.1 REGEX(.*) +======================================= + + +--TEARDOWN-- +brokerStop CB +accumulatorStop +dbDrop CB diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_entityMap_get.test b/test/functionalTest/cases/0000_ngsild/ngsild_entityMap_get.test new file mode 100644 index 0000000000..70edcd3a86 --- /dev/null +++ b/test/functionalTest/cases/0000_ngsild/ngsild_entityMap_get.test @@ -0,0 +1,542 @@ +# Copyright 2023 FIWARE Foundation e.V. +# +# This file is part of Orion-LD Context Broker. +# +# Orion-LD Context Broker is free software: you can redistribute it and/or +# modify it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Orion-LD Context Broker is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +# General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +# +# For those usages not covered by this license please contact with +# orionld at fiware dot org + +# VALGRIND_READY - to mark the test ready for valgrindTestSuite.sh + +--NAME-- +Retrieval of an entityMap + +--SHELL-INIT-- +dbInit CB +dbInit CP1 +dbInit CP2 +dbInit CP3 +dbInit CP4 +orionldStart CB -experimental -forwarding -wip entityMaps +orionldStart CP1 -experimental +orionldStart CP2 -experimental +orionldStart CP3 -experimental +orionldStart CP4 -experimental + +--SHELL-- +# +# 01. In CB, create an entity urn:E1 with attribute P0 +# 02. In CP1, create an entity urn:E1 with attributes P1-P3+R1 +# 03. In CP2, Create an entity urn:E1 with attributes P1-P3+R1 +# 04. In CP3, Create an entity urn:E1 with attributes P1-P3+R1 +# 05. In CP4, Create an entity urn:E1 with attributes P1-P4+R1+R4 (only P4 and R4 will be used) +# 06. Create an exclusive registration R1 on urn:E1/P1 for CP1 (on urn:E1 only) +# 07. Create a redirect registration R2 on urn:E1/P2 for CP2 (on urn:E1 only) +# 08. Create an inclusive registration R3 on T/P3 for CP3 (on type:T) +# 09. Create an auxiliar registration R4 on T for CP4 (on type:T) +# 10. GET /entities, only to create the entityMap +# 11. GET /entityMaps/* - see the entityMap with urn:E1 and its 4 registrations + URLs +# 12. In CB, create an entity urn:E2 +# 13. In CP1, create an entity urn:E3 +# 14. In CP2, Create an entity urn:E4 +# 15. In CP3, Create an entity urn:E5 +# 16. In CP4, Create an entity urn:E6 +# 17. Delete the old entity map +# 18. GET /entities, only to create a new entityMap (finding urn:E1, urn:E5) +# 19. GET /entityMaps/* - see the entityMap with urn:E1 - urn:E5 +# + +echo "01. In CB, create an entity urn:E1 with attribute P0" +echo "=====================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P0": "In CB" +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "02. In CP1, create an entity urn:E1 with attributes P1-P3+R1" +echo "============================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P1": "P1 in CP1", + "P2": "P2 in CP1", + "P3": "P3 in CP1", + "R1": { "object": "urn:in:cp1" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "03. In CP2, Create an entity urn:E1 with attributes P1-P3+R1" +echo "============================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P1": "P1 in CP2", + "P2": "P2 in CP2", + "P3": "P3 in CP2", + "R1": { "object": "urn:in:cp2" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP2_PORT +echo +echo + + +echo "04. In CP3, Create an entity urn:E1 with attributes P1-P3+R1" +echo "============================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P1": "P1 in CP3", + "P2": "P2 in CP3", + "P3": "P3 in CP3", + "R1": { "object": "urn:in:cp3" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP3_PORT +echo +echo + + +echo "05. In CP4, Create an entity urn:E1 with attributes P1-P4+R1+R4 (only P4 and R4 will be used)" +echo "=============================================================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P1": "P1 in CP4", + "P2": "P2 in CP4", + "P3": "P3 in CP4", + "P4": "P4 in CP4", + "R1": { "object": "urn:in:cp4" }, + "R4": { "object": "urn:in:cp4" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP4_PORT +echo +echo + + +echo "06. Create an exclusive registration R1 on urn:E1/P1 for CP1" +echo "============================================================" +payload='{ + "id": "urn:Reg1", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "id": "urn:E1", + "type": "T" + } + ], + "propertyNames": [ "P1" ] + } + ], + "mode": "exclusive", + "operations": [ "queryEntity" ], + "endpoint": "http://localhost:'$CP1_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "07. Create a redirect registration R2 on urn:E1/P2 for CP2" +echo "============================================================" +payload='{ + "id": "urn:Reg2", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "id": "urn:E1", + "type": "T" + } + ], + "propertyNames": [ "P2" ] + } + ], + "mode": "redirect", + "operations": [ "queryEntity" ], + "endpoint": "http://localhost:'$CP2_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "08. Create an inclusive registration R3 on T/P3 for CP3" +echo "=======================================================" +payload='{ + "id": "urn:Reg3", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ], + "propertyNames": [ "P3" ] + } + ], + "mode": "inclusive", + "operations": [ "queryEntity" ], + "endpoint": "http://localhost:'$CP3_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "09. Create an auxiliar registration R4 on T/P4 for CP4" +echo "=======================================================" +payload='{ + "id": "urn:Reg4", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "auxiliary", + "operations": [ "queryEntity" ], + "endpoint": "http://localhost:'$CP4_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "10. GET /entities, only to create the entityMap" +echo "===============================================" +orionCurl --url "/ngsi-ld/v1/entities?type=T&limit=0&count=true" +entityMap=$(echo "$_responseHeaders" | grep NGSILD-EntityMap: | awk -F ': ' '{ print $2 }' | tr -d "\r\n") +echo +echo + + +echo "11. GET /entityMaps/* - see the entityMap with urn:E1 and its 4 registrations + URLs" +echo "====================================================================================" +orionCurl --url /ngsi-ld/v1/entityMaps/$entityMap +echo +echo + + +echo "12. In CB, create an entity urn:E2" +echo "===================================" +payload='{ + "id": "urn:E2", + "type": "T", + "P0": "In CB" +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "13. In CP1, create an entity urn:E3" +echo "===================================" +payload='{ + "id": "urn:E3", + "type": "T", + "P1": "P1 in CP1" +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "14. In CP2, Create an entity urn:E4" +echo "===================================" +payload='{ + "id": "urn:E4", + "type": "T", + "P1": "P1 in CP2" +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP2_PORT +echo +echo + + +echo "15. In CP3, Create an entity urn:E5" +echo "===================================" +payload='{ + "id": "urn:E5", + "type": "T", + "P1": "P1 in CP3" +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP3_PORT +echo +echo + + +echo "16. In CP4, Create an entity urn:E6" +echo "===================================" +payload='{ + "id": "urn:E6", + "type": "T", + "P1": "P1 in CP4" +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP4_PORT +echo +echo + + +echo "17. Delete the old entity map" +echo "=============================" +orionCurl --url /ngsi-ld/v1/entityMaps/$entityMap -X DELETE +echo +echo + + +echo "18. GET /entities, only to create a new entityMap" +echo "=================================================" +orionCurl --url "/ngsi-ld/v1/entities?type=T&count=true&limit=0" +entityMap=$(echo "$_responseHeaders" | grep NGSILD-EntityMap: | awk -F ': ' '{ print $2 }' | tr -d "\r\n") +echo +echo + + +echo "19. GET /entityMaps/* - see the entityMap with urn:E1 - urn:E5" +echo "==============================================================" +orionCurl --url /ngsi-ld/v1/entityMaps/$entityMap +echo +echo + + +--REGEXPECT-- +01. In CB, create an entity urn:E1 with attribute P0 +===================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +02. In CP1, create an entity urn:E1 with attributes P1-P3+R1 +============================================================ +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +03. In CP2, Create an entity urn:E1 with attributes P1-P3+R1 +============================================================ +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +04. In CP3, Create an entity urn:E1 with attributes P1-P3+R1 +============================================================ +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +05. In CP4, Create an entity urn:E1 with attributes P1-P4+R1+R4 (only P4 and R4 will be used) +============================================================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +06. Create an exclusive registration R1 on urn:E1/P1 for CP1 +============================================================ +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:Reg1 + + + +07. Create a redirect registration R2 on urn:E1/P2 for CP2 +============================================================ +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:Reg2 + + + +08. Create an inclusive registration R3 on T/P3 for CP3 +======================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:Reg3 + + + +09. Create an auxiliar registration R4 on T/P4 for CP4 +======================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:Reg4 + + + +10. GET /entities, only to create the entityMap +=============================================== +HTTP/1.1 200 OK +Content-Length: 2 +Content-Type: application/json +Date: REGEX(.*) +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" +NGSILD-EntityMap: urn:ngsi-ld:entity-map:REGEX(.*) +NGSILD-Results-Count: 1 + +[] + + +11. GET /entityMaps/* - see the entityMap with urn:E1 and its 4 registrations + URLs +==================================================================================== +HTTP/1.1 200 OK +Content-Length: 64 +Content-Type: application/json +Date: REGEX(.*) + +{ + "urn:E1": [ + "@none", + "urn:Reg1", + "urn:Reg2", + "urn:Reg3", + "urn:Reg4" + ] +} + + +12. In CB, create an entity urn:E2 +=================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E2 + + + +13. In CP1, create an entity urn:E3 +=================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E3 + + + +14. In CP2, Create an entity urn:E4 +=================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E4 + + + +15. In CP3, Create an entity urn:E5 +=================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E5 + + + +16. In CP4, Create an entity urn:E6 +=================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E6 + + + +17. Delete the old entity map +============================= +HTTP/1.1 200 OK +Content-Length: 0 +Date: REGEX(.*) + + + +18. GET /entities, only to create a new entityMap +================================================= +HTTP/1.1 200 OK +Content-Length: 2 +Content-Type: application/json +Date: REGEX(.*) +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" +NGSILD-EntityMap: urn:ngsi-ld:entity-map:REGEX(.*) +NGSILD-Results-Count: 3 + +[] + + +19. GET /entityMaps/* - see the entityMap with urn:E1 - urn:E5 +============================================================== +HTTP/1.1 200 OK +Content-Length: 105 +Content-Type: application/json +Date: REGEX(.*) + +{ + "urn:E1": [ + "@none", + "urn:Reg1", + "urn:Reg2", + "urn:Reg3", + "urn:Reg4" + ], + "urn:E2": [ + "@none" + ], + "urn:E6": [ + "urn:Reg4" + ] +} + + +--TEARDOWN--- +brokerStop CB +brokerStop CP1 +brokerStop CP2 +brokerStop CP3 +brokerStop CP4 +dbDrop CB +dbDrop CP1 +dbDrop CP2 +dbDrop CP3 +dbDrop CP4 diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_forward_get_entities-entity-split-over-many-brokers.test b/test/functionalTest/cases/0000_ngsild/ngsild_forward_get_entities-entity-split-over-many-brokers.test new file mode 100644 index 0000000000..0599aea9f1 --- /dev/null +++ b/test/functionalTest/cases/0000_ngsild/ngsild_forward_get_entities-entity-split-over-many-brokers.test @@ -0,0 +1,367 @@ +# Copyright 2023 FIWARE Foundation e.V. +# +# This file is part of Orion-LD Context Broker. +# +# Orion-LD Context Broker is free software: you can redistribute it and/or +# modify it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Orion-LD Context Broker is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +# General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +# +# For those usages not covered by this license please contact with +# orionld at fiware dot org + +# VALGRIND_READY - to mark the test ready for valgrindTestSuite.sh + +--NAME-- +NGSI-LD Forward of GET /entities - with an entity split over various brokers + +--SHELL-INIT-- +dbInit CB +dbInit CP1 +dbInit CP2 +dbInit CP3 +dbInit CP4 +orionldStart CB -experimental -forwarding -wip entityMaps +orionldStart CP1 -experimental +orionldStart CP2 -experimental +orionldStart CP3 -experimental +orionldStart CP4 -experimental + +--SHELL-- +# +# 01. In CB, create an entity urn:E1 with attribute P0 +# 02. In CP1, create an entity urn:E1 with attributes P1-P3+R1 +# 03. In CP2, Create an entity urn:E1 with attributes P1-P3+R1 +# 04. In CP3, Create an entity urn:E1 with attributes P1-P3+R1 +# 05. In CP4, Create an entity urn:E1 with attributes P1-P4+R1+R4 (only P4 and R4 will be used) +# 06. Create an exclusive registration R1 on urn:E1/P1 for CP1 +# 07. Create a redirect registration R2 on urn:E1/P2 for CP2 +# 08. Create an inclusive registration R3 on T/P3 for CP3 +# 09. Create an auxiliar registration R3 on T for CP4 +# 10. GET /entities - see all properties P1-P4, each from a different broker, and relationships R1+R4 from CP4 +# + +echo "01. In CB, create an entity urn:E1 with attribute P0" +echo "=====================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P0": "In CB" +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + +echo "02. In CP1, create an entity urn:E1 with attributes P1-P3+R1" +echo "============================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P1": "P1 in CP1", + "P2": "P2 in CP1", + "P3": "P3 in CP1", + "R1": { "object": "urn:in:cp1" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "03. In CP2, Create an entity urn:E1 with attributes P1-P3+R1" +echo "============================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P1": "P1 in CP2", + "P2": "P2 in CP2", + "P3": "P3 in CP2", + "R1": { "object": "urn:in:cp2" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP2_PORT +echo +echo + + +echo "04. In CP3, Create an entity urn:E1 with attributes P1-P3+R1" +echo "============================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P1": "P1 in CP3", + "P2": "P2 in CP3", + "P3": "P3 in CP3", + "R1": { "object": "urn:in:cp3" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP3_PORT +echo +echo + + +echo "05. In CP4, Create an entity urn:E1 with attributes P1-P4+R1+R4 (only P4 and R4 will be used)" +echo "=============================================================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P1": "P1 in CP4", + "P2": "P2 in CP4", + "P3": "P3 in CP4", + "P4": "P4 in CP4", + "R1": { "object": "urn:in:cp4" }, + "R4": { "object": "urn:in:cp4" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP4_PORT +echo +echo + + +echo "06. Create an exclusive registration R1 on urn:E1/P1 for CP1" +echo "============================================================" +payload='{ + "id": "urn:Reg1", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "id": "urn:E1", + "type": "T" + } + ], + "propertyNames": [ "P1" ] + } + ], + "mode": "exclusive", + "operations": [ "queryEntity" ], + "endpoint": "http://localhost:'$CP1_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "07. Create a redirect registration R2 on urn:E1/P2 for CP2" +echo "============================================================" +payload='{ + "id": "urn:Reg2", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "id": "urn:E1", + "type": "T" + } + ], + "propertyNames": [ "P2" ] + } + ], + "mode": "redirect", + "operations": [ "queryEntity" ], + "endpoint": "http://localhost:'$CP2_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "08. Create an inclusive registration R3 on T/P3 for CP3" +echo "=======================================================" +payload='{ + "id": "urn:Reg3", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ], + "propertyNames": [ "P3" ] + } + ], + "mode": "inclusive", + "operations": [ "queryEntity" ], + "endpoint": "http://localhost:'$CP3_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "09. Create an auxiliar registration R3 on T/P4 for CP4" +echo "=======================================================" +payload='{ + "id": "urn:Reg4", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "auxiliary", + "operations": [ "queryEntity" ], + "endpoint": "http://localhost:'$CP4_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "10. GET /entities - see all properties P1-P4, each from a different broker, and relationships R1+R4 from CP4" +echo "============================================================================================================" +orionCurl --url '/ngsi-ld/v1/entities?type=T&count=true' +echo +echo + + +--REGEXPECT-- +01. In CB, create an entity urn:E1 with attribute P0 +===================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +02. In CP1, create an entity urn:E1 with attributes P1-P3+R1 +============================================================ +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +03. In CP2, Create an entity urn:E1 with attributes P1-P3+R1 +============================================================ +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +04. In CP3, Create an entity urn:E1 with attributes P1-P3+R1 +============================================================ +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +05. In CP4, Create an entity urn:E1 with attributes P1-P4+R1+R4 (only P4 and R4 will be used) +============================================================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +06. Create an exclusive registration R1 on urn:E1/P1 for CP1 +============================================================ +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:Reg1 + + + +07. Create a redirect registration R2 on urn:E1/P2 for CP2 +============================================================ +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:Reg2 + + + +08. Create an inclusive registration R3 on T/P3 for CP3 +======================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:Reg3 + + + +09. Create an auxiliar registration R3 on T/P4 for CP4 +======================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:Reg4 + + + +10. GET /entities - see all properties P1-P4, each from a different broker, and relationships R1+R4 from CP4 +============================================================================================================ +HTTP/1.1 200 OK +Content-Length: 351 +Content-Type: application/json +Date: REGEX(.*) +Link: no hits and no attrs are returned" +echo "06. Do the same GET, but with a Link HTTP header, with a @context that redefines P1 and R2 => no hits and no attrs are returned" echo "===============================================================================================================================" orionCurl --url /ngsi-ld/v1/entities/urn:ngsi-ld:entities:E1?attrs=P1,R2 -H "Link: ;"' rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"' echo @@ -213,7 +213,7 @@ Link: no hits and no attrs are returned +06. Do the same GET, but with a Link HTTP header, with a @context that redefines P1 and R2 => no hits and no attrs are returned =============================================================================================================================== HTTP/1.1 200 OK Content-Length: 43 diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_forward_with_content-type_context_and_tenant.test b/test/functionalTest/cases/0000_ngsild/ngsild_forward_with_content-type_context_and_tenant.test index 731e4c7a48..78ef9aaa1c 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_forward_with_content-type_context_and_tenant.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_forward_with_content-type_context_and_tenant.test @@ -331,6 +331,7 @@ Accept: application/json Content-Type: application/json Date: REGEX(.*) X-Forwarded-For: REGEX(.*) +Via: 1.1 REGEX(.*) { "P1": { @@ -367,6 +368,7 @@ Host: REGEX(.*) Accept: application/json Date: REGEX(.*) X-Forwarded-For: REGEX(.*) +Via: 1.1 REGEX(.*) ======================================= @@ -404,6 +406,7 @@ Ngsild-Tenant: do_t1 Content-Type: application/json Date: REGEX(.*) X-Forwarded-For: REGEX(.*) +Via: 1.1 REGEX(.*) { "P1": { @@ -441,6 +444,7 @@ Accept: application/json Ngsild-Tenant: do_t1 Date: REGEX(.*) X-Forwarded-For: REGEX(.*) +Via: 1.1 REGEX(.*) ======================================= @@ -462,6 +466,7 @@ Ngsild-Tenant: do_t1 Content-Type: application/ld+json Date: REGEX(.*) X-Forwarded-For: REGEX(.*) +Via: 1.1 REGEX(.*) { "@context": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld", @@ -507,6 +512,7 @@ Content-Type: application/json Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" Date: REGEX(.*) X-Forwarded-For: REGEX(.*) +Via: 1.1 REGEX(.*) { "P1": { @@ -544,6 +550,7 @@ Accept: application/ld+json Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" Date: REGEX(.*) X-Forwarded-For: REGEX(.*) +Via: 1.1 REGEX(.*) ======================================= diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_four_brokers_and_forwarding_loop_ALL-CONNECTED.test b/test/functionalTest/cases/0000_ngsild/ngsild_four_brokers_and_forwarding_loop_ALL-CONNECTED.test new file mode 100644 index 0000000000..65ecba2ee7 --- /dev/null +++ b/test/functionalTest/cases/0000_ngsild/ngsild_four_brokers_and_forwarding_loop_ALL-CONNECTED.test @@ -0,0 +1,751 @@ +# Copyright 2023 FIWARE Foundation e.V. +# +# This file is part of Orion-LD Context Broker. +# +# Orion-LD Context Broker is free software: you can redistribute it and/or +# modify it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Orion-LD Context Broker is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +# General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +# +# For those usages not covered by this license please contact with +# orionld at fiware dot org + +# VALGRIND_READY - to mark the test ready for valgrindTestSuite.sh + +--NAME-- +GET /entities with four brokers all connected via registrations + +--SHELL-INIT-- +dbInit CB +dbInit CP1 +dbInit CP2 +dbInit CP3 +orionldStart CB -experimental -forwarding -wip entityMaps +orionldStart CP1 -experimental -forwarding -wip entityMaps +orionldStart CP2 -experimental -forwarding -wip entityMaps +orionldStart CP3 -experimental -forwarding -wip entityMaps + +--SHELL-- + +# +# 01. In CB, create an entity urn:E1 with attributes P1+R1 +# 02. In CB, create an entity urn:E2 with attributes P1+R1 +# 03. In CP1, create an entity urn:E3 with attributes P1+R1 +# 04. In CP1, create an entity urn:E4 with attributes P1+R1 +# 05. In CP2, create an entity urn:E5 with attributes P1+R1 +# 06. In CP2, create an entity urn:E6 with attributes P1+R1 +# 07. In CP3, create an entity urn:E7 with attributes P1+R1 +# 08. In CP3, create an entity urn:E8 with attributes P1+R1 +# 09. Create an inclusive registration R01 in CB on entities of type T pointing to CP1 +# 10. Create an inclusive registration R02 in CB on entities of type T pointing to CP2 +# 12. Create an inclusive registration R03 in CB on entities of type T pointing to CP3 +# 12. Create an inclusive registration R04 in CP1 on entities of type T pointing to CB +# 13. Create an inclusive registration R05 in CP1 on entities of type T pointing to CP2 +# 14. Create an inclusive registration R06 in CP1 on entities of type T pointing to CP3 +# 15. Create an inclusive registration R07 in CP2 on entities of type T pointing to CB +# 16. Create an inclusive registration R08 in CP2 on entities of type T pointing to CP1 +# 17. Create an inclusive registration R09 in CP2 on entities of type T pointing to CP3 +# 18. Create an inclusive registration R10 in CP3 on entities of type T pointing to CB +# 19. Create an inclusive registration R11 in CP3 on entities of type T pointing to CP1 +# 20. Create an inclusive registration R12 in CP3 on entities of type T pointing to CP2 +# 21. GET /entities?type=T in CB - see 8 entities +# + +echo "01. In CB, create an entity urn:E1 with attributes P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P1": "Entity E1 in Orion A", + "R1": { "object": "urn:E0" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "02. In CB, create an entity urn:E2 with attributes P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E2", + "type": "T", + "P1": "Entity E2 in Orion A", + "R1": { "object": "urn:E1" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "03. In CP1, create an entity urn:E3 with attributes P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E3", + "type": "T", + "P1": "Entity E3 in Orion B", + "R1": { "object": "urn:E2" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "04. In CP1, create an entity urn:E4 with attributes P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E4", + "type": "T", + "P1": "Entity E4 in Orion B", + "R1": { "object": "urn:E3" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "05. In CP2, create an entity urn:E5 with attributes P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E5", + "type": "T", + "P1": "Entity E5 in Orion C", + "R1": { "object": "urn:E4" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP2_PORT +echo +echo + + +echo "06. In CP2, create an entity urn:E6 with attributes P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E6", + "type": "T", + "P1": "Entity E6 in Orion C", + "R1": { "object": "urn:E5" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP2_PORT +echo +echo + + +echo "07. In CP3, create an entity urn:E7 with attributes P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E7", + "type": "T", + "P1": "Entity E7 in Orion D", + "R1": { "object": "urn:E6" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP3_PORT +echo +echo + + +echo "08. In CP3, create an entity urn:E8 with attributes P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E8", + "type": "T", + "P1": "Entity E8 in Orion D", + "R1": { "object": "urn:E7" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP3_PORT +echo +echo + + +echo "09. Create an inclusive registration R01 in CB on entities of type T pointing to CP1" +echo "====================================================================================" +payload='{ + "id": "urn:R01", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CP1_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "10. Create an inclusive registration R02 in CB on entities of type T pointing to CP2" +echo "====================================================================================" +payload='{ + "id": "urn:R02", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CP2_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "11. Create an inclusive registration R03 in CB on entities of type T pointing to CP3" +echo "====================================================================================" +payload='{ + "id": "urn:R03", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CP3_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "12. Create an inclusive registration R04 in CP1 on entities of type T pointing to CB" +echo "====================================================================================" +payload='{ + "id": "urn:R04", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CB_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "13. Create an inclusive registration R05 in CP1 on entities of type T pointing to CP2" +echo "=====================================================================================" +payload='{ + "id": "urn:R05", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CP2_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "14. Create an inclusive registration R06 in CP1 on entities of type T pointing to CP3" +echo "=====================================================================================" +payload='{ + "id": "urn:R06", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CP3_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "15. Create an inclusive registration R07 in CP2 on entities of type T pointing to CB" +echo "====================================================================================" +payload='{ + "id": "urn:R07", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CB_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" --port $CP2_PORT +echo +echo + + +echo "16. Create an inclusive registration R08 in CP2 on entities of type T pointing to CP1" +echo "=====================================================================================" +payload='{ + "id": "urn:R08", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CP1_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" --port $CP2_PORT +echo +echo + + +echo "17. Create an inclusive registration R09 in CP2 on entities of type T pointing to CP3" +echo "=====================================================================================" +payload='{ + "id": "urn:R09", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CP3_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" --port $CP2_PORT +echo +echo + + +echo "18. Create an inclusive registration R10 in CP3 on entities of type T pointing to CB" +echo "====================================================================================" +payload='{ + "id": "urn:R10", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CB_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" --port $CP3_PORT +echo +echo + + +echo "19. Create an inclusive registration R11 in CP3 on entities of type T pointing to CP1" +echo "=====================================================================================" +payload='{ + "id": "urn:R11", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CP1_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" --port $CP3_PORT +echo +echo + + +echo "20. Create an inclusive registration R12 in CP3 on entities of type T pointing to CP2" +echo "=====================================================================================" +payload='{ + "id": "urn:R12", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CP2_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" --port $CP3_PORT +echo +echo + + +echo "21. GET /entities?type=T in CB - see 8 entities" +echo "===============================================" +orionCurl --url '/ngsi-ld/v1/entities?type=T&count=true' -H "aerOS: true" +entityMap=$(echo "$_responseHeaders" | grep NGSILD-EntityMap: | awk -F ': ' '{ print $2 }' | tr -d "\r\n") +echo +echo + + +--REGEXPECT-- +01. In CB, create an entity urn:E1 with attributes P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +02. In CB, create an entity urn:E2 with attributes P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E2 + + + +03. In CP1, create an entity urn:E3 with attributes P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E3 + + + +04. In CP1, create an entity urn:E4 with attributes P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E4 + + + +05. In CP2, create an entity urn:E5 with attributes P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E5 + + + +06. In CP2, create an entity urn:E6 with attributes P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E6 + + + +07. In CP3, create an entity urn:E7 with attributes P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E7 + + + +08. In CP3, create an entity urn:E8 with attributes P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E8 + + + +09. Create an inclusive registration R01 in CB on entities of type T pointing to CP1 +==================================================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R01 + + + +10. Create an inclusive registration R02 in CB on entities of type T pointing to CP2 +==================================================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R02 + + + +11. Create an inclusive registration R03 in CB on entities of type T pointing to CP3 +==================================================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R03 + + + +12. Create an inclusive registration R04 in CP1 on entities of type T pointing to CB +==================================================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R04 + + + +13. Create an inclusive registration R05 in CP1 on entities of type T pointing to CP2 +===================================================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R05 + + + +14. Create an inclusive registration R06 in CP1 on entities of type T pointing to CP3 +===================================================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R06 + + + +15. Create an inclusive registration R07 in CP2 on entities of type T pointing to CB +==================================================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R07 + + + +16. Create an inclusive registration R08 in CP2 on entities of type T pointing to CP1 +===================================================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R08 + + + +17. Create an inclusive registration R09 in CP2 on entities of type T pointing to CP3 +===================================================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R09 + + + +18. Create an inclusive registration R10 in CP3 on entities of type T pointing to CB +==================================================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R10 + + + +19. Create an inclusive registration R11 in CP3 on entities of type T pointing to CP1 +===================================================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R11 + + + +20. Create an inclusive registration R12 in CP3 on entities of type T pointing to CP2 +===================================================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R12 + + + +21. GET /entities?type=T in CB - see 8 entities +=============================================== +HTTP/1.1 200 OK +Content-Length: REGEX(.*) +Content-Type: application/json +Date: REGEX(.*) +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" + +{ + "P1": { + "type": "Property", + "value": "updated P1 attr of E4" + }, + "R1": { + "object": "urn:E3", + "type": "Relationship" + }, + "id": "urn:E4", + "type": "T" +} + + +06. GET /entities/urn:E4 from CP1 - see the update +================================================== +HTTP/1.1 200 OK +Content-Length: 130 +Content-Type: application/json +Date: REGEX(.*) +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" + +{ + "P1": { + "type": "Property", + "value": "updated P1 attr of E4" + }, + "R1": { + "object": "urn:E3", + "type": "Relationship" + }, + "id": "urn:E4", + "type": "T" +} + + +--TEARDOWN-- +brokerStop CB +brokerStop CP1 +dbDrop CB +dbDrop CP1 diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_issue_1468.test b/test/functionalTest/cases/0000_ngsild/ngsild_issue_1468.test new file mode 100644 index 0000000000..80bfefcc9b --- /dev/null +++ b/test/functionalTest/cases/0000_ngsild/ngsild_issue_1468.test @@ -0,0 +1,199 @@ +# Copyright 2023 FIWARE Foundation e.V. +# +# This file is part of Orion-LD Context Broker. +# +# Orion-LD Context Broker is free software: you can redistribute it and/or +# modify it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Orion-LD Context Broker is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +# General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +# +# For those usages not covered by this license please contact with +# orionld at fiware dot org + +# VALGRIND_READY - to mark the test ready for valgrindTestSuite.sh + +--NAME-- +Issue #1468 - forward 'q' + +--SHELL-INIT-- +dbInit CB +dbInit CP1 +orionldStart CB -experimental -forwarding +orionldStart CP1 -experimental + +--SHELL-- + +# +# 01. Create an entity urn:E1, type T in CP1 +# 02. Create an entity urn:E2, type T in CP1 +# 03. Create an entity urn:E2, type T in CP1 +# 04. Create an entity urn:E2, type T in CP1 +# 05. GET /entities/urn:E4 from CB - with q= +# + +echo "01. Create an entity urn:E3, type T in CP1" +echo "==========================================" +payload='{ + "id": "urn:E3", + "type": "T", + "P1": "Entity E3 in Orion B", + "R1": { "object": "urn:E4" } +}' +orionCurl --url /ngsi-ld/v1/entities?local=true --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "02. Create an entity urn:E4, type T in CP1" +echo "==========================================" +payload='{ + "id": "urn:E4", + "type": "T", + "P1": "Entity E4 in Orion B", + "R1": { "object": "urn:E3" } +}' +orionCurl --url /ngsi-ld/v1/entities?local=true --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "03. Create inclusive registration of entities of type T in CB, to forward to CP1" +echo "================================================================================" +payload='{ + "id": "urn:R1", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps", "updateOps", "deleteEntity" ], + "endpoint": "'http://localhost:$CP1_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "04. PATCH /entities/urn:E4/attrs/P1" +echo "===================================" +payload='{ + "type": "Property", + "value": "updated P1 attr of E4" +}' +orionCurl --url /ngsi-ld/v1/entities/urn:E4/attrs/P1 --payload "$payload" -X PATCH +echo +echo + + +echo "05. GET /entities/urn:E4 from CB - see the update (from CP1)" +echo "============================================================" +orionCurl --url /ngsi-ld/v1/entities/urn:E4?type=T +echo +echo + + +echo "06. GET /entities/urn:E4 from CP1 - see the update" +echo "==================================================" +orionCurl --url /ngsi-ld/v1/entities/urn:E4?type=T --port $CP1_PORT +echo +echo + + +--REGEXPECT-- +01. Create an entity urn:E3, type T in CP1 +========================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E3 + + + +02. Create an entity urn:E4, type T in CP1 +========================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E4 + + + +03. Create inclusive registration of entities of type T in CB, to forward to CP1 +================================================================================ +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R1 + + + +04. PATCH /entities/urn:E4/attrs/P1 +=================================== +HTTP/1.1 204 No Content +Date: REGEX(.*) + + + +05. GET /entities/urn:E4 from CB - see the update (from CP1) +============================================================ +HTTP/1.1 200 OK +Content-Length: 130 +Content-Type: application/json +Date: REGEX(.*) +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" + +{ + "P1": { + "type": "Property", + "value": "updated P1 attr of E4" + }, + "R1": { + "object": "urn:E3", + "type": "Relationship" + }, + "id": "urn:E4", + "type": "T" +} + + +06. GET /entities/urn:E4 from CP1 - see the update +================================================== +HTTP/1.1 200 OK +Content-Length: 130 +Content-Type: application/json +Date: REGEX(.*) +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" + +{ + "P1": { + "type": "Property", + "value": "updated P1 attr of E4" + }, + "R1": { + "object": "urn:E3", + "type": "Relationship" + }, + "id": "urn:E4", + "type": "T" +} + + +--TEARDOWN-- +brokerStop CB +brokerStop CP1 +dbDrop CB +dbDrop CP1 diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_new_GET_entities-with-entityMap.test b/test/functionalTest/cases/0000_ngsild/ngsild_new_GET_entities-with-entityMap.test new file mode 100644 index 0000000000..41cc5cb38d --- /dev/null +++ b/test/functionalTest/cases/0000_ngsild/ngsild_new_GET_entities-with-entityMap.test @@ -0,0 +1,309 @@ +# Copyright 2023 FIWARE Foundation e.V. +# +# This file is part of Orion-LD Context Broker. +# +# Orion-LD Context Broker is free software: you can redistribute it and/or +# modify it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Orion-LD Context Broker is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +# General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +# +# For those usages not covered by this license please contact with +# orionld at fiware dot org + +# VALGRIND_READY - to mark the test ready for valgrindTestSuite.sh + +--NAME-- +GET /entities with Entity Map + +--SHELL-INIT-- +dbInit CB +dbInit CP1 +export CB_TRACELEVELS=${CB_TRACELEVELS:-31,70-79,80} +orionldStart CB -experimental -forwarding -wip entityMaps +orionldStart CP1 -experimental + +--SHELL-- + +# +# 01. In CB, create an entity urn:E1 with attribute P1+R1 +# 02. In CB, create an entity urn:E2 with attribute P2+R2 +# 03. In CP1, create an entity urn:E1 with attributes P1+P2+R1 +# 04. In CP1, create an entity urn:E2 with attributes P1+P2+R1 +# 05. Create an inclusive registration R1 on urn:E1/P1+R1 for CP1 +# 06. Create an inclusive registration R2 on urn:E2/P1+R1 for CP1 +# 07. Query the CB with type=T, to get urn:E1+urn:E2 and the entity map +# 08. Query the CB with attrs=P1,P2 - see only P1, as P2 isn't registered +# + +echo "01. In CB, create an entity urn:E1 with attribute P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P1": "In CB", + "P3": "In CB", + "R1": { "object": "urn:E2" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "02. In CB, create an entity urn:E2 with attribute P2+R2" +echo "=========================================================" +payload='{ + "id": "urn:E2", + "type": "T", + "P2": "In CB", + "R2": { "object": "urn:E1" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "03. In CP1, create an entity urn:E1 with attributes P1+P2+R1" +echo "============================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P1": "P1 in CP1", + "P2": "P2 in CP1", + "R1": { "object": "urn:in:cp1" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "04. In CP1, create an entity urn:E2 with attributes P1+P2+R1" +echo "============================================================" +payload='{ + "id": "urn:E2", + "type": "T", + "P1": "P1 in CP1", + "P2": "P2 in CP1", + "R1": { "object": "urn:in:cp1" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "05. Create an inclusive registration R1 on urn:E1/P1+R1 for CP1" +echo "===============================================================" +payload='{ + "id": "urn:R1", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "id": "urn:E1", + "type": "T" + } + ], + "propertyNames": [ "P1" ], + "relationshipNames": [ "R1" ] + } + ], + "mode": "inclusive", + "operations": [ "queryEntity" ], + "endpoint": "http://localhost:'$CP1_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "06. Create an inclusive registration R2 on urn:E1/P1+R1 for CP1" +echo "===============================================================" +payload='{ + "id": "urn:R2", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "id": "urn:E2", + "type": "T" + } + ], + "propertyNames": [ "P1" ], + "relationshipNames": [ "R1" ] + } + ], + "mode": "inclusive", + "operations": [ "queryEntity" ], + "endpoint": "http://localhost:'$CP1_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "07. Query the CB with type=T, to get urn:E1+urn:E2 and the entity map" +echo "=====================================================================" +orionCurl --url '/ngsi-ld/v1/entities?type=T' +entityMap=$(echo "$_responseHeaders" | grep NGSILD-EntityMap: | awk -F ': ' '{ print $2 }' | tr -d "\r\n") +echo +echo + + +echo "08. Query the CB with attrs=P1,P2 - see only P1, as P2 isn't registered" +echo "=======================================================================" +orionCurl --url '/ngsi-ld/v1/entities?type=T&reset=true&attrs=P1,P2' +echo +echo + + +--REGEXPECT-- +01. In CB, create an entity urn:E1 with attribute P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +02. In CB, create an entity urn:E2 with attribute P2+R2 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E2 + + + +03. In CP1, create an entity urn:E1 with attributes P1+P2+R1 +============================================================ +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +04. In CP1, create an entity urn:E2 with attributes P1+P2+R1 +============================================================ +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E2 + + + +05. Create an inclusive registration R1 on urn:E1/P1+R1 for CP1 +=============================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R1 + + + +06. Create an inclusive registration R2 on urn:E1/P1+R1 for CP1 +=============================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R2 + + + +07. Query the CB with type=T, to get urn:E1+urn:E2 and the entity map +===================================================================== +HTTP/1.1 200 OK +Content-Length: 376 +Content-Type: application/json +Date: REGEX(.*) +Link: + +--SHELL-INIT-- +dbInit CB +orionldStart CB -forwarding -experimental +accumulatorStart --url "/ngsi-ld/v1/entities/urn:ngsi-ld:entities:E1" + +--SHELL-- + +# +# 01. Create entity urn:ngsi-ld:entities:E1 in CB +# 02. Create a registration in CB for ACC over entity urn:ngsi-ld:entities:E1, all attributes +# 03. Ask CB to GET entity urn:ngsi-ld:entities:E1, with HTTP header Authorization - provoke a forwarded request to ACC +# 04. Dump accumulator and see the Authorization HTTP header + +echo "01. Create entity urn:ngsi-ld:entities:E1 in CB" +echo "===============================================" +payload='{ + "id": "urn:ngsi-ld:entities:E1", + "type": "T", + "P1": { + "type": "Property", + "value": 1 + }, + "R1": { + "type": "Relationship", + "object": "urn:ngsi-ld:relationships:r1" + } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "02. Create a registration in CB for ACC over entity urn:ngsi-ld:entities:E1, all attributes" +echo "===========================================================================================" +payload='{ + "id": "urn:ngsi-ld:ContextSourceRegistration:csr01", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "id": "urn:ngsi-ld:entities:E1", + "type": "T" + } + ] + } + ], + "contextSourceInfo": [ + { + "key": "Authorization", + "value": "urn:ngsi-ld:request" + } + ], + "endpoint": "http://localhost:'$LISTENER_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "03. Ask CB to GET entity urn:ngsi-ld:entities:E1, with HTTP header Authorization - provoke a forwarded request to ACC" +echo "=====================================================================================================================" +orionCurl --url /ngsi-ld/v1/entities/urn:ngsi-ld:entities:E1 -H "Authorization: 123" +echo +echo + + +echo "04. Dump accumulator and see the Authorization HTTP header" +echo "==========================================================" +accumulatorDump +echo +echo + + +--REGEXPECT-- +01. Create entity urn:ngsi-ld:entities:E1 in CB +=============================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:ngsi-ld:entities:E1 + + + +02. Create a registration in CB for ACC over entity urn:ngsi-ld:entities:E1, all attributes +=========================================================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:ngsi-ld:ContextSourceRegistration:csr01 + + + +03. Ask CB to GET entity urn:ngsi-ld:entities:E1, with HTTP header Authorization - provoke a forwarded request to ACC +===================================================================================================================== +HTTP/1.1 200 OK +Content-Length: 147 +Content-Type: application/json +Date: REGEX(.*) +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" +NGSILD-EntityMap: urn:ngsi-ld:entity-map:REGEX(.*) + +[ + { + "CPU": { + "type": "Property", + "value": 1 + }, + "id": "urn:E1", + "type": "IE" + }, + { + "CPU": { + "type": "Property", + "value": 2 + }, + "id": "urn:E2", + "type": "IE" + }, + { + "CPU": { + "type": "Property", + "value": 3 + }, + "id": "urn:E3", + "type": "IE" + } +] + + +08. DELETE urn:E2 via CB (forwarded message to both CP1 and CP2 - success from CP1, 404 from CP2) +================================================================================================= +HTTP/1.1 207 Multi-Status +Content-Length: 184 +Content-Type: application/json +Date: REGEX(.*) +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" + +{ + "notUpdated": [ + { + "detail": "urn:E2", + "statusCode": 404, + "title": "Not Found" + }, + { + "detail": "urn:E2", + "registrationId": "urn:R1", + "statusCode": 404, + "title": "Entity not found" + } + ], + "updated": [ + "urn:E2" + ] +} + + +09. DELETE urn:E1 via CB (forwarded message to both CP1 and CP2 - 404 from both of them) +======================================================================================== +HTTP/1.1 207 Multi-Status +Content-Length: 210 +Content-Type: application/json +Date: REGEX(.*) +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" + +{ + "notUpdated": [ + { + "detail": "urn:E1", + "registrationId": "urn:R1", + "statusCode": 404, + "title": "Not Found" + }, + { + "detail": "urn:E1", + "registrationId": "urn:R1", + "statusCode": 404, + "title": "Entity not found" + } + ], + "updated": [ + "urn:E1" + ] +} + + +--TEARDOWN-- +brokerStop CB +brokerStop CP1 +brokerStop CP2 +dbDrop CB +dbDrop CP1 +dbDrop CP2 diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_three_brokers_and_forwarding_loop_ALL-CONNECTED.test b/test/functionalTest/cases/0000_ngsild/ngsild_three_brokers_and_forwarding_loop_ALL-CONNECTED.test new file mode 100644 index 0000000000..a7564cafdc --- /dev/null +++ b/test/functionalTest/cases/0000_ngsild/ngsild_three_brokers_and_forwarding_loop_ALL-CONNECTED.test @@ -0,0 +1,504 @@ +# Copyright 2023 FIWARE Foundation e.V. +# +# This file is part of Orion-LD Context Broker. +# +# Orion-LD Context Broker is free software: you can redistribute it and/or +# modify it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Orion-LD Context Broker is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +# General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Orion-LD Context Broker. If not, see http://www.gnu.org/licenses/. +# +# For those usages not covered by this license please contact with +# orionld at fiware dot org + +# VALGRIND_READY - to mark the test ready for valgrindTestSuite.sh + +--NAME-- +GET /entities with three brokers all connected via registrations + +--SHELL-INIT-- +dbInit CB +dbInit CP1 +dbInit CP2 +orionldStart CB -experimental -forwarding -wip entityMaps +orionldStart CP1 -experimental -forwarding -wip entityMaps +orionldStart CP2 -experimental -forwarding -wip entityMaps + +--SHELL-- + +# +# 01. In CB, create an entity urn:E1 with attributes P1+R1 +# 02. In CB, create an entity urn:E2 with attributes P1+R1 +# 03. In CP1, create an entity urn:E3 with attributes P1+R1 +# 04. In CP1, create an entity urn:E4 with attributes P1+R1 +# 05. In CP2, create an entity urn:E5 with attributes P1+R1 +# 06. In CP2, create an entity urn:E6 with attributes P1+R1 +# 07. Create an inclusive registration R1 in CB on entities of type T pointing to CP1 +# 08. Create an inclusive registration R2 in CB on entities of type T pointing to CP2 +# 09. Create an inclusive registration R1 in CP1 on entities of type T pointing to CB +# 10. Create an inclusive registration R2 in CP1 on entities of type T pointing to CP2 +# 11. Create an inclusive registration R1 in CP2 on entities of type T pointing to CB +# 12. Create an inclusive registration R2 in CP2 on entities of type T pointing to CP1 +# 13. GET /entities?type=T in CB +# + +# +# Flow: +# Client -> CB GET /entities +# CB -> CP1 GET /entities?idsOly=true +# CP1 - CB LOOP +# CP1 - CP2 GET /entities?idsOly=true +# CP2 - CB LOOP +# CP2 - CP1 LOOP + +# CB -> CP2 GET /entities?idsOly=true +# CP2 - CB LOOP +# CP2 - CP1 GET /entities?idsOly=true +# CP1 - CB LOOP +# CP1 - CP2 LOOP + +# CB -> CP1 GET /entities +# CP1 - CB LOOP +# CP1 - CP2 GET /entities +# CP2 - CB LOOP +# CP2 - CP1 LOOP + +# CB -> CP2 GET /entities +# CP2 - CB LOOP +# CP2 - CP1 GET /entities +# CP1 - CB LOOP +# CP1 - CP2 LOOP + +echo "01. In CB, create an entity urn:E1 with attributes P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "P1": "Entity E1 in Orion A", + "R1": { "object": "urn:E2" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "02. In CB, create an entity urn:E2 with attributes P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E2", + "type": "T", + "P1": "Entity E2 in Orion A", + "R1": { "object": "urn:E2" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "03. In CP1, create an entity urn:E3 with attributes P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E3", + "type": "T", + "P1": "Entity E3 in Orion B", + "R1": { "object": "urn:E4" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "04. In CP1, create an entity urn:E4 with attributes P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E4", + "type": "T", + "P1": "Entity E4 in Orion B", + "R1": { "object": "urn:E3" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "05. In CP2, create an entity urn:E5 with attributes P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E5", + "type": "T", + "P1": "Entity E5 in Orion C", + "R1": { "object": "urn:E6" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP2_PORT +echo +echo + + +echo "06. In CP2, create an entity urn:E6 with attributes P1+R1" +echo "=========================================================" +payload='{ + "id": "urn:E6", + "type": "T", + "P1": "Entity E6 in Orion C", + "R1": { "object": "urn:E5" } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" --port $CP2_PORT +echo +echo + + +echo "07. Create an inclusive registration R1 in CB on entities of type T pointing to CP1" +echo "=========================================================" +payload='{ + "id": "urn:R1", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CP1_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "08. Create an inclusive registration R2 in CB on entities of type T pointing to CP2" +echo "=========================================================" +payload='{ + "id": "urn:R2", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CP2_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" +echo +echo + + +echo "09. Create an inclusive registration R1 in CP1 on entities of type T pointing to CB" +echo "=========================================================" +payload='{ + "id": "urn:R1", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CB_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "10. Create an inclusive registration R2 in CP1 on entities of type T pointing to CP2" +echo "=========================================================" +payload='{ + "id": "urn:R2", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CP2_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" --port $CP1_PORT +echo +echo + + +echo "11. Create an inclusive registration R1 in CP2 on entities of type T pointing to CB" +echo "=========================================================" +payload='{ + "id": "urn:R1", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CB_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" --port $CP2_PORT +echo +echo + + +echo "12. Create an inclusive registration R2 in CP2 on entities of type T pointing to CP1" +echo "=========================================================" +payload='{ + "id": "urn:R2", + "type": "ContextSourceRegistration", + "information": [ + { + "entities": [ + { + "type": "T" + } + ] + } + ], + "mode": "inclusive", + "operations": [ "retrieveOps" ], + "endpoint": "http://'$(hostname)':'$CP1_PORT'" +}' +orionCurl --url /ngsi-ld/v1/csourceRegistrations --payload "$payload" --port $CP2_PORT +echo +echo + + +echo "13. GET /entities?type=T in CB" +echo "==============================" +orionCurl --url '/ngsi-ld/v1/entities?type=T&count=true' -H "aerOS: true" +entityMap=$(echo "$_responseHeaders" | grep NGSILD-EntityMap: | awk -F ': ' '{ print $2 }' | tr -d "\r\n") +echo +echo + + +--REGEXPECT-- +01. In CB, create an entity urn:E1 with attributes P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +02. In CB, create an entity urn:E2 with attributes P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E2 + + + +03. In CP1, create an entity urn:E3 with attributes P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E3 + + + +04. In CP1, create an entity urn:E4 with attributes P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E4 + + + +05. In CP2, create an entity urn:E5 with attributes P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E5 + + + +06. In CP2, create an entity urn:E6 with attributes P1+R1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E6 + + + +07. Create an inclusive registration R1 in CB on entities of type T pointing to CP1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R1 + + + +08. Create an inclusive registration R2 in CB on entities of type T pointing to CP2 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R2 + + + +09. Create an inclusive registration R1 in CP1 on entities of type T pointing to CB +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R1 + + + +10. Create an inclusive registration R2 in CP1 on entities of type T pointing to CP2 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R2 + + + +11. Create an inclusive registration R1 in CP2 on entities of type T pointing to CB +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R1 + + + +12. Create an inclusive registration R2 in CP2 on entities of type T pointing to CP1 +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/csourceRegistrations/urn:R2 + + + +13. GET /entities?type=T in CB +============================== +HTTP/1.1 200 OK +Content-Length: REGEX(.*) +Content-Type: application/json +Date: REGEX(.*) +Link: /tmp/valgrind.out 2>&1 & valgrind -v --leak-check=full --show-leak-kinds=definite,indirect --track-origins=yes --trace-children=yes --suppressions=$REPO_HOME/test/valgrind/suppressions.supp $CB_START_CMD > /tmp/valgrind.out 2>&1 & fi @@ -650,7 +648,9 @@ function orionldStart then $BROKER_START_CMD else - valgrind -v --leak-check=full --show-leak-kinds=definite,indirect --track-origins=yes --trace-children=yes --suppressions=$REPO_HOME/test/valgrind/suppressions.supp $BROKER_START_CMD > /tmp/valgrind.out 2>&1 & + # Use the CLI --gen-suppressions=all for valgrind to get suppressions (to put in suppressions.supp) + # valgrind -v --leak-check=full --show-leak-kinds=definite,indirect --track-origins=yes --trace-children=yes --suppressions=$REPO_HOME/test/valgrind/suppressions.supp --gen-suppressions=all $BROKER_START_CMD > /tmp/valgrind.out 2>&1 & + valgrind -v --leak-check=full --show-leak-kinds=definite,indirect --track-origins=yes --trace-children=yes --suppressions=$REPO_HOME/test/valgrind/suppressions.supp $BROKER_START_CMD > /tmp/valgrind.out 2>&1 & fi # Waiting for broker/valgrind to start @@ -1837,11 +1837,11 @@ function cServerCurl r=0 if [ "$hasPayload" == "yes" ] then - curlCommand='curl -s http://localhost:7080$url -X $verb --dump-header /tmp/cServerHeaders -d "$_payload" -H "Content-Type: application/ld+json"' + curlCommand="curl -s http://localhost:7080$url -X $verb --dump-header /tmp/cServerHeaders "'-d "$_payload" -H "Content-Type: application/ld+json"' curl -s http://localhost:7080$url -X $verb --dump-header /tmp/cServerHeaders -d "$_payload" -H "Content-Type: application/ld+json" > /tmp/cServerOut r=$? else - curlCommand='curl -s http://localhost:7080$url -X $verb --dump-header /tmp/cServerHeaders' + curlCommand="curl -s http://localhost:7080$url -X $verb --dump-header /tmp/cServerHeaders" curl -s http://localhost:7080$url -X $verb --dump-header /tmp/cServerHeaders > /tmp/cServerOut r=$? fi diff --git a/test/functionalTest/testHarness.sh b/test/functionalTest/testHarness.sh index 82f00ecba2..6b68739bdf 100755 --- a/test/functionalTest/testHarness.sh +++ b/test/functionalTest/testHarness.sh @@ -237,7 +237,7 @@ errors=0 function exitFunction() { errors=$errors+1 - + exitCode=$1 errorText=$2 testFile=$3 @@ -1054,6 +1054,20 @@ function partExecute() eCode=$? fi + # + # If still error - remove all SORT_START and SORT_END and sort the entire output/regexpect file and diff again + # + if [ "$eCode" != "0" ] + then + vMsg "$dirname/$filename: Error on normal diff - trying a sorted diff" + cat $dirname/$filename.regexpect | egrep -v '^#SORT_START$|^#SORT_END$' | sort > $dirname/$filename.regexpect.sorted + cat $dirname/$filename.out | sort > $dirname/$filename.out.sorted + $DIFF -r $dirname/$filename.regexpect.sorted -i $dirname/$filename.out.sorted > $dirname/$filename.diff + eCode=$? + vMsg "$dirname/$filename: Error code $eCode on sorted diff" + rm -f $dirname/$filename.regexpect.sorted $dirname/$filename.out.sorted + fi + if [ "$eCode" != "0" ] then logMsg "$what $dirname/$filename: eCode=$eCode" @@ -1432,7 +1446,7 @@ do echo $xsecs seconds else echo "$xsecs seconds - ERROR: $errorText" - fi + fi else echo "SUCCESS" fi diff --git a/test/unittests/main_UnitTest.cpp b/test/unittests/main_UnitTest.cpp index 3c27ace71b..eb03e7e5f4 100644 --- a/test/unittests/main_UnitTest.cpp +++ b/test/unittests/main_UnitTest.cpp @@ -107,6 +107,7 @@ bool mongocOnly = false; bool debugCurl = false; uint32_t cSubCounters = 0; char localIpAndPort[135]; +char brokerId[136]; unsigned long long inReqPayloadMaxSize; unsigned long long outReqMsgMaxSize; bool triggerOperation = false; diff --git a/test/valgrind/suppressions.supp b/test/valgrind/suppressions.supp index 65e3a55b8e..6d39974181 100644 --- a/test/valgrind/suppressions.supp +++ b/test/valgrind/suppressions.supp @@ -140,3 +140,26 @@ fun:curl_multi_perform fun:curl_easy_perform } +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:__libc_alloc_buffer_allocate + fun:alloc_buffer_allocate + fun:__resolv_conf_allocate + fun:__resolv_conf_load + fun:__resolv_conf_get_current + fun:__res_vinit + fun:maybe_init + fun:context_get + fun:context_get + fun:__resolv_context_get + fun:get_nss_addresses + fun:gaih_inet + fun:getaddrinfo + fun:mongoc_client_connect_tcp + fun:mongoc_client_connect + fun:_server_monitor_setup_connection + fun:_server_monitor_check_server +}