diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 90b6bb92f3..cfb9d98f61 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -5,4 +5,9 @@ Fixed issues: * #280 - Changed all error codes from InvalidRequest to BadRequestData, except for JSON Parse Error that is still an InvalidRequest * #280 - Giving errors for expiresAt in the past (for registrations and subscriptions) * #280 - Fixed a bug in error detection of downloading errors in arrays of contexts, at lower nesting levels + * #280 - Better 501 handling of temporal operations + * #280 - Highly experimental feature for subscriptions: allowing a wildcard as value for entity type, to not filter on entity type + * #280 - Implemented Periodic Notifications (subscriptions with a 'timeInterval') + * #280 - Fixed a crash in the subscription cache for subscriptions with empty URL paths in the notification::endpoint::uri field + * #280 - New CLI (hidden) for extra field in notifications (trigger: "VERB URL PATH"): -triggerOperation * #1427 - Disabling the keep-alive as it seems to be missing in libpaho (this is just a part of the issue) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa986db3af..217c8c80f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -208,6 +208,7 @@ SET (ORION_LIBS orionld_troe orionld_dbModel orionld_apiModel + orionld_pernot serviceRoutines serviceRoutinesV2 ngsiNotify @@ -366,6 +367,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/pernot) ADD_SUBDIRECTORY(src/lib/orionld/socketService) ADD_SUBDIRECTORY(src/lib/orionld/notifications) ADD_SUBDIRECTORY(src/lib/orionld/forwarding) diff --git a/docker/Dockerfile-test b/docker/Dockerfile-test index c0db1ce2bb..695442baf9 100644 --- a/docker/Dockerfile-test +++ b/docker/Dockerfile-test @@ -14,6 +14,8 @@ WORKDIR lcov-1.14 RUN make install WORKDIR ${PATH_TO_SRC} +RUN yum-config-manager --disable pgdg10 + RUN yum install -y gnupg RUN yum install -y https://repo.mongodb.org/yum/redhat/8Server/mongodb-org/4.4/x86_64/RPMS/mongodb-database-tools-100.4.1.x86_64.rpm diff --git a/docker/build-ubi/install-gnutls.sh b/docker/build-ubi/install-gnutls.sh index 6023353aa6..9b65d612df 100755 --- a/docker/build-ubi/install-gnutls.sh +++ b/docker/build-ubi/install-gnutls.sh @@ -35,8 +35,8 @@ yum -y install https://rpmfind.net/linux/centos/8-stream/BaseOS/x86_64/os/Packag yum -y install https://rpmfind.net/linux/centos/8-stream/AppStream/x86_64/os/Packages/nettle-devel-3.4.1-7.el8.x86_64.rpm yum -y install https://rpmfind.net/linux/centos/8-stream/AppStream/x86_64/os/Packages/libidn2-devel-2.2.0-1.el8.x86_64.rpm yum -y install https://rpmfind.net/linux/centos/8-stream/AppStream/x86_64/os/Packages/libtasn1-devel-4.13-3.el8.x86_64.rpm -yum -y install https://dl.rockylinux.org/pub/rocky/8/AppStream/x86_64/os/Packages/g/gnutls-c++-3.6.16-5.el8_6.x86_64.rpm -yum -y install https://dl.rockylinux.org/pub/rocky/8/AppStream/x86_64/os/Packages/g/gnutls-devel-3.6.16-5.el8_6.x86_64.rpm +yum -y install https://dl.rockylinux.org/pub/rocky/8/AppStream/x86_64/os/Packages/g/gnutls-c++-3.6.16-6.el8_7.x86_64.rpm +yum -y install https://dl.rockylinux.org/pub/rocky/8/AppStream/x86_64/os/Packages/g/gnutls-devel-3.6.16-6.el8_7.x86_64.rpm #yum -y install http://mirror.centos.org/centos/8/BaseOS/x86_64/os/Packages/gmp-devel-6.1.2-10.el8.x86_64.rpm #yum -y install http://mirror.centos.org/centos/8/BaseOS/x86_64/os/Packages/pkgconf-pkg-config-1.4.2-1.el8.x86_64.rpm diff --git a/docker/build-ubi/install-paho-client.sh b/docker/build-ubi/install-paho-client.sh index 8e03ff1314..949b6ca90d 100755 --- a/docker/build-ubi/install-paho-client.sh +++ b/docker/build-ubi/install-paho-client.sh @@ -25,7 +25,7 @@ set -e echo echo -e "\e[1;32m Debian Builder: installing Paho MQTT C library \e[0m" -wget https://mirror.centos.org/centos/7/os/x86_64/Packages/doxygen-1.8.5-4.el7.x86_64.rpm +wget http://mirror.centos.org/centos/7/os/x86_64/Packages/doxygen-1.8.5-4.el7.x86_64.rpm yum install -y doxygen-1.8.5-4.el7.x86_64.rpm diff --git a/docker/build-ubi/install-postgres-client.sh b/docker/build-ubi/install-postgres-client.sh index 0380cd8243..d98d813eda 100755 --- a/docker/build-ubi/install-postgres-client.sh +++ b/docker/build-ubi/install-postgres-client.sh @@ -34,7 +34,7 @@ yum -y install https://rpmfind.net/linux/centos/8-stream/PowerTools/x86_64/os/Pa yum -y install https://rpmfind.net/linux/centos/8-stream/AppStream/x86_64/os/Packages/blas-3.8.0-8.el8.x86_64.rpm yum -y install https://rpmfind.net/linux/centos/8-stream/AppStream/x86_64/os/Packages/lapack-3.8.0-8.el8.x86_64.rpm #yum -y install https://rpmfind.net/linux/epel/8/Everything/x86_64/Packages/a/armadillo-10.8.2-1.el8.x86_64.rpm -yum -y install http://yum.stanford.edu/mrepo/epel-EL8-x86_64/RPMS.all/armadillo-9.700.2-1.el8.x86_64.rpm +yum -y install https://archives.fedoraproject.org/pub/archive/epel/8.1/Everything/x86_64/Packages/a/armadillo-9.700.2-1.el8.x86_64.rpm echo "Add repos" yum update -y --nogpgcheck diff --git a/src/app/orionld/orionld.cpp b/src/app/orionld/orionld.cpp index 95af52b08c..b8c80c408d 100644 --- a/src/app/orionld/orionld.cpp +++ b/src/app/orionld/orionld.cpp @@ -130,6 +130,8 @@ extern "C" #include "orionld/regCache/regCacheInit.h" // regCacheInit #include "orionld/regCache/regCacheCreate.h" // regCacheCreate #include "orionld/regCache/regCacheRelease.h" // regCacheRelease +#include "orionld/pernot/pernotSubCacheInit.h" // pernotSubCacheInit +#include "orionld/pernot/pernotLoop.h" // pernotLoopStart #include "orionld/version.h" #include "orionld/orionRestServices.h" @@ -208,6 +210,7 @@ char reqMutexPolicy[16]; int writeConcern; unsigned int cprForwardLimit; int subCacheInterval; +int subCacheFlushInterval; char notificationMode[64]; int notificationQueueSize; int notificationThreadNum; @@ -236,6 +239,7 @@ bool ngsiv1Autocast; int contextDownloadAttempts; int contextDownloadTimeout; bool troe; +bool pernot; bool disableFileLog; bool lmtmp; char troeHost[256]; @@ -252,8 +256,9 @@ bool noswap; bool experimental = false; bool mongocOnly = false; bool debugCurl = false; -int cSubCounters; +uint32_t cSubCounters; char coreContextVersion[64]; +bool triggerOperation = false; @@ -294,6 +299,7 @@ char coreContextVersion[64]; #define WRITE_CONCERN_DESC "db write concern (0:unacknowledged, 1:acknowledged)" #define CPR_FORWARD_LIMIT_DESC "maximum number of distributed requests to Context Providers for a single client request" #define SUB_CACHE_IVAL_DESC "interval in seconds between calls to Subscription Cache refresh (0: no refresh)" +#define SUB_CACHE_FLUSH_IVAL_DESC "interval in seconds between calls to Pernot Subscription Cache Flush to DB (0: no flush)" #define NOTIFICATION_MODE_DESC "notification mode (persistent|transient|threadpool:q:n)" #define NO_CACHE "disable subscription cache for lookups" #define CONN_MEMORY_DESC "maximum memory size per connection (in kilobytes)" @@ -317,6 +323,7 @@ char coreContextVersion[64]; #define INSECURE_NOTIF "allow HTTPS notifications to peers which certificate cannot be authenticated with known CA certificates" #define NGSIV1_AUTOCAST "automatic cast for number, booleans and dates in NGSIv1 update/create attribute operations" #define TROE_DESC "enable TRoE - temporal representation of entities" +#define PERNOT_DESC "enable Pernot - Periodic Notifications" #define DISABLE_FILE_LOG "disable logging into file" #define TMPTRACES_DESC "disable traces" #define TROE_HOST_DESC "host for troe database db server" @@ -331,6 +338,7 @@ char coreContextVersion[64]; #define ID_INDEX_DESC "automatic mongo index on _id.id" #define NOSWAP_DESC "no swapping - for testing only!!!" #define NO_NOTIFY_FALSE_UPDATE_DESC "turn off notifications on non-updates" +#define TRIGGER_OPERATION_DESC "include the operation that triggered the notification" #define EXPERIMENTAL_DESC "enable experimental implementation - use at own risk - see release notes of Orion-LD v1.1.0" #define MONGOCONLY_DESC "enable experimental implementation + turn off mongo legacy driver" #define DBAUTHDB_DESC "database used for authentication" @@ -365,7 +373,6 @@ PaArgument paArgs[] = { { "-coreContext", coreContextVersion, "CORE_CONTEXT", PaString, PaOpt, _i ORIONLD_CORE_CONTEXT_URL_DEFAULT, PaNL, PaNL, CORE_CONTEXT_DESC }, - { "-noswap", &noswap, "NOSWAP", PaBool, PaHid, false, false, true, NOSWAP_DESC }, { "-fg", &fg, "FOREGROUND", PaBool, PaOpt, false, false, true, FG_DESC }, { "-localIp", bindAddress, "LOCALIP", PaString, PaOpt, IP_ALL, PaNL, PaNL, LOCALIP_DESC }, { "-port", &port, "PORT", PaInt, PaOpt, 1026, 1024, 65535, PORT_DESC }, @@ -383,10 +390,8 @@ PaArgument paArgs[] = { "-dbTimeout", &dbTimeout, "MONGO_TIMEOUT", PaDouble, PaOpt, 10000, PaNL, PaNL, DB_TMO_DESC }, { "-dbPoolSize", &dbPoolSize, "MONGO_POOL_SIZE", PaInt, PaOpt, 10, 1, 10000, DBPS_DESC }, { "-writeConcern", &writeConcern, "MONGO_WRITE_CONCERN", PaInt, PaOpt, 1, 0, 1, WRITE_CONCERN_DESC }, - { "-idIndex", &idIndex, "MONGO_ID_INDEX", PaBool, PaHid, false, false, true, ID_INDEX_DESC }, { "-ipv4", &useOnlyIPv4, "USEIPV4", PaBool, PaOpt, false, false, true, USEIPV4_DESC }, { "-ipv6", &useOnlyIPv6, "USEIPV6", PaBool, PaOpt, false, false, true, USEIPV6_DESC }, - { "-harakiri", &harakiri, "HARAKIRI", PaBool, PaHid, false, false, true, HARAKIRI_DESC }, { "-https", &https, "HTTPS", PaBool, PaOpt, false, false, true, HTTPS_DESC }, { "-key", httpsKeyFile, "HTTPS_KEYFILE", PaString, PaOpt, _i "", PaNL, PaNL, HTTPSKEYFILE_DESC }, { "-cert", httpsCertFile, "HTTPS_CERTFILE", PaString, PaOpt, _i "", PaNL, PaNL, HTTPSCERTFILE_DESC }, @@ -398,6 +403,7 @@ PaArgument paArgs[] = { "-corsMaxAge", &maxAge, "CORS_MAX_AGE", PaInt, PaOpt, 86400, -1, 86400, CORS_MAX_AGE_DESC }, { "-cprForwardLimit", &cprForwardLimit, "CPR_FORWARD_LIMIT", PaUInt, PaOpt, 1000, 0, UINT_MAX, CPR_FORWARD_LIMIT_DESC }, { "-subCacheIval", &subCacheInterval, "SUBCACHE_IVAL", PaInt, PaOpt, 0, 0, 3600, SUB_CACHE_IVAL_DESC }, + { "-subCacheFlushIval", &subCacheFlushInterval, "SUBCACHE_FLUSH_IVAL", PaInt, PaOpt, 10, 0, 3600, SUB_CACHE_FLUSH_IVAL_DESC }, { "-noCache", &noCache, "NOCACHE", PaBool, PaOpt, false, false, true, NO_CACHE }, { "-connectionMemory", &connectionMemory, "CONN_MEMORY", PaUInt, PaOpt, 64, 0, 1024, CONN_MEMORY_DESC }, { "-maxConnections", &maxConnections, "MAX_CONN", PaUInt, PaOpt, 1020, 1, PaNL, MAX_CONN_DESC }, @@ -422,22 +428,28 @@ PaArgument paArgs[] = { "-ngsiv1Autocast", &ngsiv1Autocast, "NGSIV1_AUTOCAST", PaBool, PaOpt, false, false, true, NGSIV1_AUTOCAST }, { "-ctxTimeout", &contextDownloadTimeout, "CONTEXT_DOWNLOAD_TIMEOUT", PaInt, PaOpt, 5000, 0, 20000, CTX_TMO_DESC }, { "-ctxAttempts", &contextDownloadAttempts, "CONTEXT_DOWNLOAD_ATTEMPTS", PaInt, PaOpt, 3, 0, 100, CTX_ATT_DESC }, + { "-pernot", &pernot, "PERNOT", PaBool, PaOpt, false, false, true, PERNOT_DESC }, { "-troe", &troe, "TROE", PaBool, PaOpt, false, false, true, TROE_DESC }, - { "-lmtmp", &lmtmp, "TMP_TRACES", PaBool, PaHid, true, false, true, TMPTRACES_DESC }, - { "-socketService", &socketService, "SOCKET_SERVICE", PaBool, PaHid, false, false, true, SOCKET_SERVICE_DESC }, { "-troeHost", troeHost, "TROE_HOST", PaString, PaOpt, _i "localhost", PaNL, PaNL, TROE_HOST_DESC }, { "-troePort", &troePort, "TROE_PORT", PaInt, PaOpt, 5432, PaNL, PaNL, TROE_PORT_DESC }, { "-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 }, - { "-ssPort", &socketServicePort, "SOCKET_SERVICE_PORT", PaUShort, PaHid, 1027, PaNL, PaNL, SOCKET_SERVICE_PORT_DESC }, - { "-forwarding", &distributed, "FORWARDING", PaBool, PaHid, false, false, true, FORWARDING_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 }, + { "-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 }, + { "-harakiri", &harakiri, "HARAKIRI", PaBool, PaHid, false, false, true, HARAKIRI_DESC }, + { "-idIndex", &idIndex, "MONGO_ID_INDEX", PaBool, PaHid, false, false, true, ID_INDEX_DESC }, + { "-noswap", &noswap, "NOSWAP", PaBool, PaHid, false, false, true, NOSWAP_DESC }, + { "-lmtmp", &lmtmp, "TMP_TRACES", PaBool, PaHid, true, false, true, TMPTRACES_DESC }, { "-debugCurl", &debugCurl, "DEBUG_CURL", PaBool, PaHid, false, false, true, DEBUG_CURL_DESC }, + { "-lmtmp", &lmtmp, "TMP_TRACES", PaBool, PaHid, true, false, true, TMPTRACES_DESC }, PA_END_OF_ARGS }; @@ -1193,6 +1205,9 @@ int main(int argC, char* argV[]) // regCacheInit(); + if (pernot == true) + pernotSubCacheInit(); + orionldServiceInit(restServiceVV, 9); if (mongocOnly == false) @@ -1201,7 +1216,7 @@ int main(int argC, char* argV[]) mongoInit(dbHost, rplSet, dbName, dbUser, dbPwd, multitenancy, dbTimeout, writeConcern, dbPoolSize, statSemWait); } - // Initialize orion libs + // Initialize libs alarmMgr.init(relogAlarms); metricsMgr.init(!disableMetrics, statSemWait); logSummaryInit(&lsPeriod); @@ -1342,6 +1357,10 @@ int main(int argC, char* argV[]) kaBufferReset(&orionldState.kalloc, KFALSE); + // Start the thread for periodic notifications + if (pernot == true) + pernotLoopStart(); + if (socketService == true) { int fd; diff --git a/src/app/orionld/orionldRestServices.cpp b/src/app/orionld/orionldRestServices.cpp index 4c4bdb3ccc..2c518b0e41 100644 --- a/src/app/orionld/orionldRestServices.cpp +++ b/src/app/orionld/orionldRestServices.cpp @@ -50,7 +50,6 @@ #include "orionld/serviceRoutines/orionldGetContexts.h" #include "orionld/serviceRoutines/orionldGetVersion.h" #include "orionld/serviceRoutines/orionldGetPing.h" -#include "orionld/serviceRoutines/orionldNotImplemented.h" #include "orionld/serviceRoutines/orionldPostBatchUpsert.h" #include "orionld/serviceRoutines/orionldPostBatchCreate.h" #include "orionld/serviceRoutines/orionldPostBatchUpdate.h" @@ -61,16 +60,22 @@ #include "orionld/serviceRoutines/orionldGetTenants.h" #include "orionld/serviceRoutines/orionldGetDbIndexes.h" #include "orionld/serviceRoutines/orionldPostQuery.h" -#include "orionld/serviceRoutines/orionldGetTemporalEntities.h" -#include "orionld/serviceRoutines/orionldGetTemporalEntity.h" -#include "orionld/serviceRoutines/orionldPostTemporalQuery.h" -#include "orionld/serviceRoutines/orionldPostTemporalEntities.h" #include "orionld/serviceRoutines/orionldPostContexts.h" #include "orionld/serviceRoutines/orionldDeleteContext.h" #include "orionld/serviceRoutines/orionldOptions.h" #include "orionld/serviceRoutines/orionldPutEntity.h" #include "orionld/serviceRoutines/orionldPutAttribute.h" +#include "orionld/serviceRoutines/orionldGetTemporalEntities.h" +#include "orionld/serviceRoutines/orionldGetTemporalEntity.h" +#include "orionld/serviceRoutines/orionldPostTemporalQuery.h" +#include "orionld/serviceRoutines/orionldPostTemporalEntities.h" +#include "orionld/serviceRoutines/orionldDeleteTemporalAttribute.h" // orionldDeleteTemporalAttribute +#include "orionld/serviceRoutines/orionldDeleteTemporalAttributeInstance.h" // orionldDeleteTemporalAttributeInstance +#include "orionld/serviceRoutines/orionldDeleteTemporalEntity.h" // orionldDeleteTemporalEntity +#include "orionld/serviceRoutines/orionldPatchTemporalAttributeInstance.h" // orionldPatchTemporalAttributeInstance +#include "orionld/serviceRoutines/orionldPostTemporalAttributes.h" // orionldPostTemporalAttributes + #include "orionld/rest/OrionLdRestService.h" // OrionLdRestServiceSimplified #include "orionld/orionldRestServices.h" // Own Interface @@ -111,20 +116,20 @@ static const int getServices = (sizeof(getServiceV) / sizeof(getServiceV[0])); // static OrionLdRestServiceSimplified postServiceV[] = { - { "/ngsi-ld/v1/entities/*/attrs", orionldPostEntity }, - { "/ngsi-ld/v1/entities", orionldPostEntities }, - { "/ngsi-ld/ex/v1/notify", orionldPostNotify }, - { "/ngsi-ld/v1/entityOperations/create", orionldPostBatchCreate }, - { "/ngsi-ld/v1/entityOperations/upsert", orionldPostBatchUpsert }, - { "/ngsi-ld/v1/entityOperations/update", orionldPostBatchUpdate }, - { "/ngsi-ld/v1/entityOperations/delete", orionldPostBatchDelete }, - { "/ngsi-ld/v1/entityOperations/query", orionldPostQuery }, - { "/ngsi-ld/v1/subscriptions", orionldPostSubscriptions }, - { "/ngsi-ld/v1/csourceRegistrations", orionldPostRegistrations }, - { "/ngsi-ld/v1/temporal/entities/*/attrs", orionldNotImplemented }, - { "/ngsi-ld/v1/temporal/entities", orionldPostTemporalEntities }, - { "/ngsi-ld/v1/temporal/entityOperations/query", orionldPostTemporalQuery }, - { "/ngsi-ld/v1/jsonldContexts", orionldPostContexts } + { "/ngsi-ld/v1/entities/*/attrs", orionldPostEntity }, + { "/ngsi-ld/v1/entities", orionldPostEntities }, + { "/ngsi-ld/ex/v1/notify", orionldPostNotify }, + { "/ngsi-ld/v1/entityOperations/create", orionldPostBatchCreate }, + { "/ngsi-ld/v1/entityOperations/upsert", orionldPostBatchUpsert }, + { "/ngsi-ld/v1/entityOperations/update", orionldPostBatchUpdate }, + { "/ngsi-ld/v1/entityOperations/delete", orionldPostBatchDelete }, + { "/ngsi-ld/v1/entityOperations/query", orionldPostQuery }, + { "/ngsi-ld/v1/subscriptions", orionldPostSubscriptions }, + { "/ngsi-ld/v1/csourceRegistrations", orionldPostRegistrations }, + { "/ngsi-ld/v1/temporal/entities/*/attrs", orionldPostTemporalAttributes }, + { "/ngsi-ld/v1/temporal/entities", orionldPostTemporalEntities }, + { "/ngsi-ld/v1/temporal/entityOperations/query", orionldPostTemporalQuery }, + { "/ngsi-ld/v1/jsonldContexts", orionldPostContexts } }; static const int postServices = (sizeof(postServiceV) / sizeof(postServiceV[0])); @@ -136,12 +141,12 @@ static const int postServices = (sizeof(postServiceV) / sizeof(postServiceV[0])) // static OrionLdRestServiceSimplified patchServiceV[] = { - { "/ngsi-ld/v1/entities/*/attrs/*", orionldPatchAttribute }, - { "/ngsi-ld/v1/entities/*/attrs", orionldPatchEntity }, - { "/ngsi-ld/v1/entities/*", orionldPatchEntity2 }, - { "/ngsi-ld/v1/subscriptions/*", orionldPatchSubscription }, - { "/ngsi-ld/v1/csourceRegistrations/*", orionldPatchRegistration }, - { "/ngsi-ld/v1/temporal/entities/*/attrs/*/*", orionldNotImplemented } + { "/ngsi-ld/v1/entities/*/attrs/*", orionldPatchAttribute }, + { "/ngsi-ld/v1/entities/*/attrs", orionldPatchEntity }, + { "/ngsi-ld/v1/entities/*", orionldPatchEntity2 }, + { "/ngsi-ld/v1/subscriptions/*", orionldPatchSubscription }, + { "/ngsi-ld/v1/csourceRegistrations/*", orionldPatchRegistration }, + { "/ngsi-ld/v1/temporal/entities/*/attrs/*/*", orionldPatchTemporalAttributeInstance } }; static const int patchServices = (sizeof(patchServiceV) / sizeof(patchServiceV[0])); @@ -166,13 +171,13 @@ static const int putServices = (sizeof(putServiceV) / sizeof(putServiceV[0])); // static OrionLdRestServiceSimplified deleteServiceV[] = { - { "/ngsi-ld/v1/entities/*/attrs/*", orionldDeleteAttribute }, - { "/ngsi-ld/v1/entities/*", orionldDeleteEntity }, - { "/ngsi-ld/v1/subscriptions/*", orionldDeleteSubscription }, - { "/ngsi-ld/v1/csourceRegistrations/*", orionldDeleteRegistration }, - { "/ngsi-ld/v1/jsonldContexts/*", orionldDeleteContext }, - { "/ngsi-ld/v1/temporal/entities/*/attrs/*", orionldNotImplemented }, - { "/ngsi-ld/v1/temporal/entities/*", orionldNotImplemented } + { "/ngsi-ld/v1/entities/*/attrs/*", orionldDeleteAttribute }, + { "/ngsi-ld/v1/entities/*", orionldDeleteEntity }, + { "/ngsi-ld/v1/subscriptions/*", orionldDeleteSubscription }, + { "/ngsi-ld/v1/csourceRegistrations/*", orionldDeleteRegistration }, + { "/ngsi-ld/v1/jsonldContexts/*", orionldDeleteContext }, + { "/ngsi-ld/v1/temporal/entities/*/attrs/*", orionldDeleteTemporalAttribute }, + { "/ngsi-ld/v1/temporal/entities/*", orionldDeleteTemporalEntity }, }; static const int deleteServices = (sizeof(deleteServiceV) / sizeof(deleteServiceV[0])); diff --git a/src/lib/cache/CachedSubscription.h b/src/lib/cache/CachedSubscription.h index 8c9c820daa..17c38abe0a 100644 --- a/src/lib/cache/CachedSubscription.h +++ b/src/lib/cache/CachedSubscription.h @@ -36,6 +36,7 @@ #include "orionld/context/OrionldContext.h" // OrionldContext #include "orionld/types/Protocol.h" // Protocol #include "orionld/types/OrionldAlteration.h" // OrionldAlterationTypes +#include "orionld/types/OrionldTenant.h" // OrionldTenant @@ -95,6 +96,7 @@ struct CachedSubscription std::vector metadata; std::vector notifyConditionV; char* tenant; + OrionldTenant* tenantP; char* servicePath; bool triggers[OrionldAlterationTypes]; double throttling; @@ -116,12 +118,15 @@ struct CachedSubscription std::string status; int64_t count; // delta count - since last sub cache refresh int64_t dbCount; // count taken from the database + int64_t failures; + int64_t dbFailures; + int consecutiveErrors; // Not in DB, no need + double lastNotificationTime; // timestamp of last notification attempt double lastFailure; // timestamp of last notification failure double lastSuccess; // timestamp of last successful notification - int consecutiveErrors; // Not in DB char lastErrorReason[128]; // Not in DB - int dirty; + uint32_t dirty; double createdAt; double modifiedAt; diff --git a/src/lib/cache/subCache.cpp b/src/lib/cache/subCache.cpp index ad94518713..06364f11df 100644 --- a/src/lib/cache/subCache.cpp +++ b/src/lib/cache/subCache.cpp @@ -52,6 +52,7 @@ extern "C" #include "orionld/types/OrionldTenant.h" // OrionldTenant #include "orionld/common/orionldState.h" // orionldState #include "orionld/common/tenantList.h" // tenantList +#include "orionld/common/orionldTenantLookup.h" // orionldTenantLookup #include "orionld/common/urlParse.h" // urlParse #include "orionld/q/qBuild.h" // qBuild #include "orionld/q/qRelease.h" // qRelease @@ -882,6 +883,7 @@ bool subCacheItemInsert // First the non-complex values // cSubP->tenant = (tenant[0] == 0)? NULL : strdup(tenant); + cSubP->tenantP = orionldTenantLookup(tenant); cSubP->expirationTime = expirationTime; cSubP->throttling = throttling; cSubP->lastNotificationTime = lastNotificationTime; @@ -889,7 +891,10 @@ bool subCacheItemInsert cSubP->lastSuccess = lastNotificationSuccessTime; cSubP->renderFormat = renderFormat; cSubP->next = NULL; + cSubP->dbCount = 0; cSubP->count = 0; + cSubP->failures = 0; + cSubP->dbFailures = 0; cSubP->status = status; cSubP->url = NULL; cSubP->ip = NULL; @@ -953,7 +958,7 @@ bool subCacheItemInsert // FIXME: Instead of calling qBuild here, I should pass the pointer from pCheckSubscription if (cSubP->qP != NULL) qRelease(cSubP->qP); - cSubP->qP = qBuild(q.c_str(), &cSubP->qText, &validForV2, &isMq, true); // cSubP->qText needs real allocation + cSubP->qP = qBuild(q.c_str(), &cSubP->qText, &validForV2, &isMq, true, false); // cSubP->qText needs real allocation if (cSubP->qText != NULL) cSubP->qText = strdup(cSubP->qText); @@ -1275,6 +1280,7 @@ typedef struct CachedSubSaved { double lastNotificationTime; int64_t count; + int64_t failures; double lastFailure; double lastSuccess; bool ngsild; @@ -1338,6 +1344,7 @@ void subCacheSync(void) cssP->lastNotificationTime = cSubP->lastNotificationTime; cssP->count = cSubP->count; // This count is later pushed ($inc) to DB - needs to go to cache as well + cssP->failures = cSubP->failures; cssP->lastFailure = cSubP->lastFailure; cssP->lastSuccess = cSubP->lastSuccess; cssP->ngsild = (cSubP->ldContext != "")? true : false; @@ -1401,12 +1408,22 @@ void subCacheSync(void) { const char* tenant = (cSubP->tenant == NULL)? "" : cSubP->tenant; if (experimental == true) - mongocSubCountersUpdate(tenant, cSubP, cssP->count, cssP->lastNotificationTime, cssP->lastFailure, cssP->lastSuccess, false, cssP->ngsild); + mongocSubCountersUpdate(cSubP->tenantP, + cSubP->subscriptionId, + cssP->ngsild, + cssP->count, + cssP->failures, + 0, // noMatch - not here - only for PerNot + cssP->lastNotificationTime, + cssP->lastSuccess, + cssP->lastFailure, + false); else { mongoSubCountersUpdate(tenant, cSubP->subscriptionId, cssP->count, + cssP->failures, cssP->lastNotificationTime, cssP->lastFailure, cssP->lastSuccess, @@ -1419,8 +1436,10 @@ void subCacheSync(void) if (cssP->lastNotificationTime != 0) cSubP->lastNotificationTime = cssP->lastNotificationTime; // Here the delta (just $inc'ed to DB) is also inc'ed to subCache - cSubP->dbCount += cssP->count; - cSubP->count = 0; + cSubP->dbCount += cssP->count; + cSubP->count = 0; + cSubP->dbFailures += cssP->failures; + cSubP->failures = 0; } cSubP = cSubP->next; @@ -1486,6 +1505,7 @@ void subCacheStart(void) LM_E(("Runtime Error (error creating thread: %d)", ret)); return; } + pthread_detach(tid); } @@ -1516,9 +1536,9 @@ void subCacheItemNotificationErrorStatus(const std::string& tenant, const std::s // The field 'count' has already been taken care of. Set to 0 in the calls to mongoSubCountersUpdate() if (errors == 0) - mongoSubCountersUpdate(tenant, subscriptionId, 0, kNow, -1, kNow, ngsild); // lastFailure == -1 + mongoSubCountersUpdate(tenant, subscriptionId, 0, 0, kNow, -1, kNow, ngsild); // lastFailure == -1 else - mongoSubCountersUpdate(tenant, subscriptionId, 0, kNow, kNow, -1, ngsild); // lastSuccess == -1, count == 0 + mongoSubCountersUpdate(tenant, subscriptionId, 0, 1, kNow, kNow, -1, ngsild); // lastSuccess == -1, count == 0 return; } diff --git a/src/lib/logMsg/traceLevels.h b/src/lib/logMsg/traceLevels.h index d772ecd717..f8abd8ce76 100644 --- a/src/lib/logMsg/traceLevels.h +++ b/src/lib/logMsg/traceLevels.h @@ -62,8 +62,9 @@ typedef enum TraceLevels LmtSubCache = 50, // Subscription Cache LmtSubCacheMatch, // Subscription Cache Matches LmtSubCacheDebug, // Subscription Cache Debug + LmtSubCacheStats, // Subscription Cache Counters and Timestamps LmtSubCacheSync, // Subscription Cache Refresh - LmtSubCacheStats, // Errors and timestamps in sub-cache + LmtSubCacheFlush, // Subscription Cache Flush // // Registration Cache @@ -95,6 +96,15 @@ typedef enum TraceLevels // GeoJSON LmtGeoJSON = 90, // GeoJSON ... everything (for now) + // + // Pernot sub-cache + // + LmtPernot = 100, // 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 + // // Misc // diff --git a/src/lib/mongoBackend/MongoCommonUpdate.cpp b/src/lib/mongoBackend/MongoCommonUpdate.cpp index c25c34d764..39522bccf6 100644 --- a/src/lib/mongoBackend/MongoCommonUpdate.cpp +++ b/src/lib/mongoBackend/MongoCommonUpdate.cpp @@ -688,7 +688,7 @@ static bool mergeAttrInfo(const BSONObj& attr, ContextAttribute* caP, BSONObj* m /* 4. Add creation date */ if (attr.hasField(ENT_ATTRS_CREATION_DATE)) { - ab.append(ENT_ATTRS_CREATION_DATE, getNumberFieldAsDoubleF(&attr, ENT_ATTRS_CREATION_DATE)); + ab.append(ENT_ATTRS_CREATION_DATE, getNumberFieldAsDoubleF(&attr, ENT_ATTRS_CREATION_DATE, false)); } /* Was it an actual update? */ @@ -774,7 +774,7 @@ static bool mergeAttrInfo(const BSONObj& attr, ContextAttribute* caP, BSONObj* m * in database by a CB instance previous to the support of creation and modification dates */ if (attr.hasField(ENT_ATTRS_MODIFICATION_DATE)) { - ab.append(ENT_ATTRS_MODIFICATION_DATE, getNumberFieldAsDoubleF(&attr, ENT_ATTRS_MODIFICATION_DATE)); + ab.append(ENT_ATTRS_MODIFICATION_DATE, getNumberFieldAsDoubleF(&attr, ENT_ATTRS_MODIFICATION_DATE, false)); } } @@ -1684,8 +1684,8 @@ static bool addTriggeredSubscriptions_noCache // // NOTE: renderFormatString: NGSIv1 JSON is 'default' (for old db-content) // - double throttling = getNumberFieldAsDoubleF(&sub, CSUB_THROTTLING); - double lastNotification = getNumberFieldAsDoubleF(&sub, CSUB_LASTNOTIFICATION); + double throttling = getNumberFieldAsDoubleF(&sub, CSUB_THROTTLING, true); + double lastNotification = getNumberFieldAsDoubleF(&sub, CSUB_LASTNOTIFICATION, true); const char* renderFmt = getStringFieldF(&sub, CSUB_FORMAT); const char* renderFormatString = (renderFmt[0] == 0)? "legacy" : renderFmt; RenderFormat renderFormat = stringToRenderFormat(renderFormatString); @@ -3546,8 +3546,8 @@ static void updateEntity // The hasField() check is needed as the entity could have been created with very old Orion version not // supporting modification/creation dates - notifyCerP->contextElement.entityId.creDate = getNumberFieldAsDoubleF(bobP, ENT_CREATION_DATE); - notifyCerP->contextElement.entityId.modDate = getNumberFieldAsDoubleF(bobP, ENT_MODIFICATION_DATE); + notifyCerP->contextElement.entityId.creDate = getNumberFieldAsDoubleF(bobP, ENT_CREATION_DATE, false); + notifyCerP->contextElement.entityId.modDate = getNumberFieldAsDoubleF(bobP, ENT_MODIFICATION_DATE, false); // The logic to detect notification loops is to check that the correlator in the request differs from the last one seen for the entity and, // in addition, the request was sent due to a custom notification diff --git a/src/lib/mongoBackend/dbConstants.h b/src/lib/mongoBackend/dbConstants.h index 1ad107bd7a..fe1ebf6f06 100644 --- a/src/lib/mongoBackend/dbConstants.h +++ b/src/lib/mongoBackend/dbConstants.h @@ -114,6 +114,7 @@ #define CSUB_ENTITY_ISPATTERN "isPattern" #define CSUB_ENTITY_ISTYPEPATTERN "isTypePattern" #define CSUB_COUNT "count" +#define CSUB_FAILURES "failures" #define CSUB_FORMAT "format" #define CSUB_STATUS "status" #define CSUB_SERVICE_PATH "servicePath" diff --git a/src/lib/mongoBackend/mongoGetSubscriptions.cpp b/src/lib/mongoBackend/mongoGetSubscriptions.cpp index b14a0e1bd6..3e4920ede8 100644 --- a/src/lib/mongoBackend/mongoGetSubscriptions.cpp +++ b/src/lib/mongoBackend/mongoGetSubscriptions.cpp @@ -177,12 +177,12 @@ static void extractNotification(Subscription* subP, const BSONObj* rP, OrionldTe ngsiv2::Notification* nP = &subP->notification; - subP->throttling = rP->hasField(CSUB_THROTTLING)? getNumberFieldAsDoubleF(rP, CSUB_THROTTLING) : -1; - nP->lastNotification = rP->hasField(CSUB_LASTNOTIFICATION)? getNumberFieldAsDoubleF(rP, CSUB_LASTNOTIFICATION) : -1; - nP->timesSent = rP->hasField(CSUB_COUNT)? getIntOrLongFieldAsLongF(rP, CSUB_COUNT) : 0; - nP->blacklist = rP->hasField(CSUB_BLACKLIST)? getBoolFieldF(rP, CSUB_BLACKLIST) : false; - nP->lastFailure = rP->hasField(CSUB_LASTFAILURE)? getNumberFieldAsDoubleF(rP, CSUB_LASTFAILURE) : -1; - nP->lastSuccess = rP->hasField(CSUB_LASTSUCCESS)? getNumberFieldAsDoubleF(rP, CSUB_LASTSUCCESS) : -1; + subP->throttling = rP->hasField(CSUB_THROTTLING)? getNumberFieldAsDoubleF(rP, CSUB_THROTTLING, true) : -1; + nP->lastNotification = rP->hasField(CSUB_LASTNOTIFICATION)? getNumberFieldAsDoubleF(rP, CSUB_LASTNOTIFICATION, true) : -1; + nP->timesSent = rP->hasField(CSUB_COUNT)? getIntOrLongFieldAsLongF(rP, CSUB_COUNT) : 0; + nP->blacklist = rP->hasField(CSUB_BLACKLIST)? getBoolFieldF(rP, CSUB_BLACKLIST) : false; + nP->lastFailure = rP->hasField(CSUB_LASTFAILURE)? getNumberFieldAsDoubleF(rP, CSUB_LASTFAILURE, true) : -1; + nP->lastSuccess = rP->hasField(CSUB_LASTSUCCESS)? getNumberFieldAsDoubleF(rP, CSUB_LASTSUCCESS, true) : -1; // Attributes format subP->attrsFormat = rP->hasField(CSUB_FORMAT)? stringToRenderFormat(getStringFieldF(rP, CSUB_FORMAT)) : RF_LEGACY; @@ -222,7 +222,7 @@ static void extractNotification(Subscription* subP, const BSONObj* rP, OrionldTe */ static void extractStatus(Subscription* s, const BSONObj& r) { - s->expires = r.hasField(CSUB_EXPIRATION)? getNumberFieldAsDoubleF(&r, CSUB_EXPIRATION) : -1; + s->expires = r.hasField(CSUB_EXPIRATION)? getNumberFieldAsDoubleF(&r, CSUB_EXPIRATION, true) : -1; // // Status diff --git a/src/lib/mongoBackend/mongoRegistrationAux.cpp b/src/lib/mongoBackend/mongoRegistrationAux.cpp index 59c209a2cd..5769355e84 100644 --- a/src/lib/mongoBackend/mongoRegistrationAux.cpp +++ b/src/lib/mongoBackend/mongoRegistrationAux.cpp @@ -253,7 +253,7 @@ bool mongoSetDataProvided(ngsiv2::Registration* regP, const mongo::BSONObj* rP, */ void mongoSetExpires(ngsiv2::Registration* regP, const mongo::BSONObj& r) { - regP->expires = (r.hasField(REG_EXPIRATION))? getNumberFieldAsDoubleF(&r, REG_EXPIRATION) : -1; + regP->expires = (r.hasField(REG_EXPIRATION))? getNumberFieldAsDoubleF(&r, REG_EXPIRATION, true) : -1; } diff --git a/src/lib/mongoBackend/mongoSubCache.cpp b/src/lib/mongoBackend/mongoSubCache.cpp index c9d8f99f70..182c385c17 100644 --- a/src/lib/mongoBackend/mongoSubCache.cpp +++ b/src/lib/mongoBackend/mongoSubCache.cpp @@ -150,13 +150,13 @@ int mongoSubCacheItemInsert(const char* tenant, const BSONObj& sub) cSubP->tenant = (tenant == NULL || tenant[0] == 0)? strdup("") : strdup(tenant); // FIXME: strdup("") ... really? cSubP->servicePath = strdup(sub.hasField(CSUB_SERVICE_PATH)? getStringFieldF(&sub, CSUB_SERVICE_PATH) : "/"); cSubP->renderFormat = renderFormat; - cSubP->throttling = sub.hasField(CSUB_THROTTLING)? getNumberFieldAsDoubleF(&sub, CSUB_THROTTLING) : -1; - cSubP->expirationTime = sub.hasField(CSUB_EXPIRATION)? getNumberFieldAsDoubleF(&sub, CSUB_EXPIRATION) : 0; - cSubP->lastNotificationTime = sub.hasField(CSUB_LASTNOTIFICATION)? getNumberFieldAsDoubleF(&sub, CSUB_LASTNOTIFICATION) : -1; - cSubP->status = sub.hasField(CSUB_STATUS)? getStringFieldF(&sub, CSUB_STATUS) : "active"; - cSubP->blacklist = sub.hasField(CSUB_BLACKLIST)? getBoolFieldF(&sub, CSUB_BLACKLIST) : false; - cSubP->lastFailure = sub.hasField(CSUB_LASTFAILURE)? getNumberFieldAsDoubleF(&sub, CSUB_LASTFAILURE) : -1; - cSubP->lastSuccess = sub.hasField(CSUB_LASTSUCCESS)? getNumberFieldAsDoubleF(&sub, CSUB_LASTSUCCESS) : -1; + cSubP->throttling = sub.hasField(CSUB_THROTTLING)? getNumberFieldAsDoubleF(&sub, CSUB_THROTTLING, true) : -1; + cSubP->expirationTime = sub.hasField(CSUB_EXPIRATION)? getNumberFieldAsDoubleF(&sub, CSUB_EXPIRATION, true) : 0; + cSubP->lastNotificationTime = sub.hasField(CSUB_LASTNOTIFICATION)? getNumberFieldAsDoubleF(&sub, CSUB_LASTNOTIFICATION, true) : -1; + cSubP->status = sub.hasField(CSUB_STATUS)? getStringFieldF(&sub, CSUB_STATUS) : "active"; + cSubP->blacklist = sub.hasField(CSUB_BLACKLIST)? getBoolFieldF(&sub, CSUB_BLACKLIST) : false; + cSubP->lastFailure = sub.hasField(CSUB_LASTFAILURE)? getNumberFieldAsDoubleF(&sub, CSUB_LASTFAILURE, true) : -1; + cSubP->lastSuccess = sub.hasField(CSUB_LASTSUCCESS)? getNumberFieldAsDoubleF(&sub, CSUB_LASTSUCCESS, true) : -1; cSubP->count = 0; cSubP->next = NULL; @@ -452,14 +452,14 @@ int mongoSubCacheItemInsert // if the database objuect contains lastNotificationTime, // then use the value from the database // - lastNotificationTime = getNumberFieldAsDoubleF(&sub, CSUB_LASTNOTIFICATION); + lastNotificationTime = getNumberFieldAsDoubleF(&sub, CSUB_LASTNOTIFICATION, true); } cSubP->tenant = (tenant == NULL || tenant[0] == 0)? NULL : strdup(tenant); cSubP->subscriptionId = strdup(subscriptionId); cSubP->servicePath = strdup(servicePath); cSubP->renderFormat = renderFormat; - cSubP->throttling = sub.hasField(CSUB_THROTTLING)? getNumberFieldAsDoubleF(&sub, CSUB_THROTTLING) : -1; + cSubP->throttling = sub.hasField(CSUB_THROTTLING)? getNumberFieldAsDoubleF(&sub, CSUB_THROTTLING, true) : -1; cSubP->expirationTime = expirationTime; cSubP->lastNotificationTime = lastNotificationTime; cSubP->count = 0; @@ -637,6 +637,35 @@ static void mongoSubCountersUpdateCount +/* **************************************************************************** +* +* mongoSubCountersUpdateFailures - +*/ +static void mongoSubCountersUpdateFailures +( + const std::string& collection, + const std::string& subId, + long long failures, + bool ngsild +) +{ + BSONObj condition; + BSONObj update; + std::string err; + + if (ngsild == true) + condition = BSON("_id" << subId); + else + condition = BSON("_id" << OID(subId)); + + update = BSON("$inc" << BSON(CSUB_FAILURES << failures)); + + if (collectionUpdate(collection.c_str(), condition, update, false, &err) != true) + LM_E(("Internal Error (error updating 'failures' for a subscription)")); +} + + + /* **************************************************************************** * * mongoSubCountersUpdateLastNotificationTime - @@ -764,6 +793,7 @@ void mongoSubCountersUpdate const std::string& tenant, const std::string& subId, long long count, + long long failures, double lastNotificationTime, double lastFailure, double lastSuccess, @@ -783,12 +813,13 @@ void mongoSubCountersUpdate else snprintf(collectionPath, sizeof(collectionPath), "%s.csubs", dbName); - LM_T(LmtLegacy, ("count: %d", count)); - LM_T(LmtLegacy, ("lastNotificationTime: %f", lastNotificationTime)); - LM_T(LmtLegacy, ("lastFailure: %f", lastFailure)); - LM_T(LmtLegacy, ("lastSuccess: %f", lastSuccess)); + LM_T(LmtSubCacheStats, ("Updating sub::count to %ll", count)); + LM_T(LmtSubCacheStats, ("lastNotificationTime: %f", lastNotificationTime)); + LM_T(LmtSubCacheStats, ("lastFailure: %f", lastFailure)); + LM_T(LmtSubCacheStats, ("lastSuccess: %f", lastSuccess)); if (count > 0) mongoSubCountersUpdateCount(collectionPath, subId, count, ngsild); + if (failures > 0) mongoSubCountersUpdateFailures(collectionPath, subId, failures, ngsild); if (lastNotificationTime > 0) mongoSubCountersUpdateLastNotificationTime(collectionPath, subId, lastNotificationTime, ngsild); if (lastFailure > 0) mongoSubCountersUpdateLastFailure(collectionPath, subId, lastFailure, ngsild); if (lastSuccess > 0) mongoSubCountersUpdateLastSuccess(collectionPath, subId, lastSuccess, ngsild); diff --git a/src/lib/mongoBackend/mongoSubCache.h b/src/lib/mongoBackend/mongoSubCache.h index 936d62b51e..77db960081 100644 --- a/src/lib/mongoBackend/mongoSubCache.h +++ b/src/lib/mongoBackend/mongoSubCache.h @@ -88,6 +88,7 @@ extern void mongoSubCountersUpdate const std::string& tenant, const std::string& subId, long long count, + long long failures, double lastNotificationTime, double lastFailure, double lastSuccess, diff --git a/src/lib/mongoBackend/mongoUpdateSubscription.cpp b/src/lib/mongoBackend/mongoUpdateSubscription.cpp index e62124f7a1..b620ea6ea8 100644 --- a/src/lib/mongoBackend/mongoUpdateSubscription.cpp +++ b/src/lib/mongoBackend/mongoUpdateSubscription.cpp @@ -78,7 +78,7 @@ static void setExpiration(const SubscriptionUpdate& subUp, const BSONObj& subOri { if (subOrig.hasField(CSUB_EXPIRATION)) { - double expires = getNumberFieldAsDoubleF(&subOrig, CSUB_EXPIRATION); + double expires = getNumberFieldAsDoubleF(&subOrig, CSUB_EXPIRATION, true); b->append(CSUB_EXPIRATION, expires); LM_T(LmtLegacy, ("Subscription expiration: %f", expires)); @@ -162,7 +162,7 @@ static void setThrottling(const SubscriptionUpdate& subUp, const BSONObj& subOri { if (subOrig.hasField(CSUB_THROTTLING)) { - double throttling = getNumberFieldAsDoubleF(&subOrig, CSUB_THROTTLING); + double throttling = getNumberFieldAsDoubleF(&subOrig, CSUB_THROTTLING, true); b->append(CSUB_THROTTLING, throttling); LM_T(LmtLegacy, ("Subscription throttling: %f", throttling)); @@ -528,7 +528,7 @@ static void setLastNotification(const BSONObj& subOrig, CachedSubscription* subC return; } - double lastNotification = getNumberFieldAsDoubleF(&subOrig, CSUB_LASTNOTIFICATION); + double lastNotification = getNumberFieldAsDoubleF(&subOrig, CSUB_LASTNOTIFICATION, true); // // Compare with 'lastNotificationTime', that might come from the sub-cache. @@ -550,7 +550,7 @@ static void setLastNotification(const BSONObj& subOrig, CachedSubscription* subC */ static double setLastFailure(const BSONObj& subOrig, CachedSubscription* subCacheP, BSONObjBuilder* b) { - double lastFailure = subOrig.hasField(CSUB_LASTFAILURE)? getNumberFieldAsDoubleF(&subOrig, CSUB_LASTFAILURE) : 0; + double lastFailure = subOrig.hasField(CSUB_LASTFAILURE)? getNumberFieldAsDoubleF(&subOrig, CSUB_LASTFAILURE, true) : 0; // // Compare with 'lastFailure' from the sub-cache. @@ -574,7 +574,7 @@ static double setLastFailure(const BSONObj& subOrig, CachedSubscription* subCach */ static double setLastSuccess(const BSONObj& subOrig, CachedSubscription* subCacheP, BSONObjBuilder* b) { - double lastSuccess = getNumberFieldAsDoubleF(&subOrig, CSUB_LASTSUCCESS); + double lastSuccess = getNumberFieldAsDoubleF(&subOrig, CSUB_LASTSUCCESS, true); // // Compare with 'lastSuccess' from the sub-cache. @@ -805,7 +805,7 @@ void updateInCache lastNotification, lastFailure, lastSuccess, - doc.hasField(CSUB_EXPIRATION)? getNumberFieldAsDoubleF(&doc, CSUB_EXPIRATION) : 0, + doc.hasField(CSUB_EXPIRATION)? getNumberFieldAsDoubleF(&doc, CSUB_EXPIRATION, true) : 0, doc.hasField(CSUB_STATUS)? getStringFieldF(&doc, CSUB_STATUS) : STATUS_ACTIVE, q, mq, diff --git a/src/lib/mongoBackend/safeMongo.cpp b/src/lib/mongoBackend/safeMongo.cpp index 1d2af7f9d9..0863012a20 100644 --- a/src/lib/mongoBackend/safeMongo.cpp +++ b/src/lib/mongoBackend/safeMongo.cpp @@ -280,7 +280,7 @@ bool getBoolField(const BSONObj* bP, const char* field, const char* caller, int * * getNumberFieldAsDouble - */ -double getNumberFieldAsDouble(const BSONObj* bP, const char* field, const char* caller, int line) +double getNumberFieldAsDouble(const BSONObj* bP, const char* field, bool okToNotExist, const char* caller, int line) { mongo::BSONType type = mongo::EOO; @@ -296,6 +296,9 @@ double getNumberFieldAsDouble(const BSONObj* bP, const char* field, const char* } else { + if (okToNotExist == true) + return 0; + LM_E(("Runtime Error (double/int/long field '%s' is missing in BSONObj <%s> from caller %s:%d)", field, bP->toString().c_str(), diff --git a/src/lib/mongoBackend/safeMongo.h b/src/lib/mongoBackend/safeMongo.h index 1e0799f67c..0813774832 100644 --- a/src/lib/mongoBackend/safeMongo.h +++ b/src/lib/mongoBackend/safeMongo.h @@ -46,7 +46,7 @@ #define getNumberFieldF(b, field) getNumberField(b, field, __FUNCTION__, __LINE__) #define getIntFieldF(b, field) getIntField(b, field, __FUNCTION__, __LINE__) #define getIntOrLongFieldAsLongF(b, field) getIntOrLongFieldAsLong(b, field, __FUNCTION__, __LINE__) -#define getNumberFieldAsDoubleF(b, field) getNumberFieldAsDouble(b, field, __FUNCTION__, __LINE__) +#define getNumberFieldAsDoubleF(b, field, okToNotExist) getNumberFieldAsDouble(b, field, okToNotExist, __FUNCTION__, __LINE__) #define getBoolFieldF(b, field) getBoolField(b, field, __FUNCTION__, __LINE__) #define getFieldF(b, field) getField(b, field, __FUNCTION__, __LINE__) #define setStringVectorF(b, field, v) setStringVector(b, field, v, __FUNCTION__, __LINE__) @@ -148,6 +148,7 @@ extern double getNumberFieldAsDouble ( const mongo::BSONObj* bP, const char* field, + bool okToNotExist, const char* caller = "", int line = 0 ); diff --git a/src/lib/ngsi/ContextElementResponse.cpp b/src/lib/ngsi/ContextElementResponse.cpp index 6e66206bd2..73a167ce11 100644 --- a/src/lib/ngsi/ContextElementResponse.cpp +++ b/src/lib/ngsi/ContextElementResponse.cpp @@ -306,12 +306,12 @@ ContextElementResponse::ContextElementResponse /* Set creDate and modDate at attribute level */ if (attr.hasField(ENT_ATTRS_CREATION_DATE)) { - caP->creDate = getNumberFieldAsDoubleF(&attr, ENT_ATTRS_CREATION_DATE); + caP->creDate = getNumberFieldAsDoubleF(&attr, ENT_ATTRS_CREATION_DATE, false); } if (attr.hasField(ENT_ATTRS_MODIFICATION_DATE)) { - caP->modDate = getNumberFieldAsDoubleF(&attr, ENT_ATTRS_MODIFICATION_DATE); + caP->modDate = getNumberFieldAsDoubleF(&attr, ENT_ATTRS_MODIFICATION_DATE, false); } contextElement.contextAttributeVector.push_back(caP); @@ -320,12 +320,12 @@ ContextElementResponse::ContextElementResponse /* Set creDate and modDate at entity level */ if (entityDocP->hasField(ENT_CREATION_DATE)) { - contextElement.entityId.creDate = getNumberFieldAsDoubleF(entityDocP, ENT_CREATION_DATE); + contextElement.entityId.creDate = getNumberFieldAsDoubleF(entityDocP, ENT_CREATION_DATE, false); } if (entityDocP->hasField(ENT_MODIFICATION_DATE)) { - contextElement.entityId.modDate = getNumberFieldAsDoubleF(entityDocP, ENT_MODIFICATION_DATE); + contextElement.entityId.modDate = getNumberFieldAsDoubleF(entityDocP, ENT_MODIFICATION_DATE, false); } } diff --git a/src/lib/ngsiNotify/QueueWorkers.cpp b/src/lib/ngsiNotify/QueueWorkers.cpp index 1a76c0a363..66463e0421 100644 --- a/src/lib/ngsiNotify/QueueWorkers.cpp +++ b/src/lib/ngsiNotify/QueueWorkers.cpp @@ -203,12 +203,7 @@ static void* workerFunc(void* pSyncQ) alarmMgr.notificationErrorReset(url); if (params->registration == false) - { - LM_T(LmtNotificationStats, ("Calling subCacheItemNotificationErrorStatus with OK")); subCacheItemNotificationErrorStatus(params->tenant, params->subscriptionId, 0, ngsildSubscription); - } - else - LM_T(LmtNotificationStats, ("NOT calling subCacheItemNotificationErrorStatus with ERROR")); } else { @@ -216,12 +211,7 @@ static void* workerFunc(void* pSyncQ) alarmMgr.notificationError(url, "notification failure for queue worker"); if (params->registration == false) - { - LM_T(LmtNotificationStats, ("Calling subCacheItemNotificationErrorStatus with ERROR")); subCacheItemNotificationErrorStatus(params->tenant, params->subscriptionId, 1, ngsildSubscription); - } - else - LM_T(LmtNotificationStats, ("NOT calling subCacheItemNotificationErrorStatus with ERROR")); } } diff --git a/src/lib/orionld/common/orionldServerConnect.cpp b/src/lib/orionld/common/orionldServerConnect.cpp index b3efe18cf7..668bf86e7f 100644 --- a/src/lib/orionld/common/orionldServerConnect.cpp +++ b/src/lib/orionld/common/orionldServerConnect.cpp @@ -39,7 +39,7 @@ // // orionldServerConnect - // -int orionldServerConnect(char* ip, uint16_t portNo) +int orionldServerConnect(const char* ip, uint16_t portNo) { int fd; struct hostent* heP; diff --git a/src/lib/orionld/common/orionldServerConnect.h b/src/lib/orionld/common/orionldServerConnect.h index fa1d092f81..1d9bf8aadd 100644 --- a/src/lib/orionld/common/orionldServerConnect.h +++ b/src/lib/orionld/common/orionldServerConnect.h @@ -32,6 +32,6 @@ // // orionldServerConnect - // -extern int orionldServerConnect(char* ip, uint16_t portNo); +extern int orionldServerConnect(const char* ip, uint16_t portNo); #endif // SRC_LIB_ORIONLD_COMMON_ORIONLDSERVERCONNECT_H_ diff --git a/src/lib/orionld/common/orionldState.cpp b/src/lib/orionld/common/orionldState.cpp index 309cce6d38..1db09926bf 100644 --- a/src/lib/orionld/common/orionldState.cpp +++ b/src/lib/orionld/common/orionldState.cpp @@ -44,6 +44,7 @@ extern "C" #include "orionld/common/numberToDate.h" // numberToDate #include "orionld/q/QNode.h" // QNode #include "orionld/common/performance.h" // REQUEST_PERFORMANCE +#include "orionld/pernot/PernotSubCache.h" // PernotSubCache #include "orionld/common/orionldState.h" // Own interface @@ -103,13 +104,15 @@ bool orionldStartup = true; 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 +size_t hostHeaderLen; +PernotSubCache pernotSubCache; + // // Variables for Mongo C Driver // -mongoc_collection_t* mongoEntitiesCollectionP = NULL; // Deprecated -mongoc_collection_t* mongoRegistrationsCollectionP = NULL; // Deprecated - mongoc_uri_t* mongocUri; mongoc_client_pool_t* mongocPool; sem_t mongocContextsSem; diff --git a/src/lib/orionld/common/orionldState.h b/src/lib/orionld/common/orionldState.h index 16f326513d..4ae458cf5b 100644 --- a/src/lib/orionld/common/orionldState.h +++ b/src/lib/orionld/common/orionldState.h @@ -61,6 +61,7 @@ extern "C" #include "orionld/types/OrionldAlteration.h" // OrionldAlteration #include "orionld/types/StringArray.h" // StringArray #include "orionld/troe/troe.h" // TroeMode +#include "orionld/pernot/PernotSubCache.h" // PernotSubCache #include "orionld/context/OrionldContext.h" // OrionldContext @@ -565,6 +566,8 @@ extern char dbURI[]; // From orionld.cpp extern bool multitenancy; // From orionld.cpp extern int contextDownloadAttempts; // From orionld.cpp extern int contextDownloadTimeout; // From orionld.cpp +extern int subCacheInterval; // From orionld.cpp +extern int subCacheFlushInterval; // From orionld.cpp extern bool troe; // From orionld.cpp extern char troeHost[256]; // From orionld.cpp extern unsigned short troePort; // From orionld.cpp @@ -579,6 +582,7 @@ extern OrionldPhase orionldPhase; extern bool orionldStartup; // For now, only used inside sub-cache routines extern bool idIndex; // From orionld.cpp extern bool noNotifyFalseUpdate; // From orionld.cpp +extern bool triggerOperation; // From orionld.cpp extern char mongoServerVersion[32]; extern bool experimental; // From orionld.cpp extern bool mongocOnly; // From orionld.cpp @@ -589,7 +593,8 @@ extern size_t userAgentHeaderLen; // From notificationSend.cpp extern char userAgentHeaderNoLF[64]; // move to orionld.cpp? extern bool debugCurl; // From orionld.cpp extern bool noCache; // From orionld.cpp -extern int cSubCounters; // Number of subscription counter updates before flush from sub-cache to DB +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 unsigned long long inReqPayloadMaxSize; extern unsigned long long outReqMsgMaxSize; diff --git a/src/lib/orionld/common/subCacheApiSubscriptionInsert.cpp b/src/lib/orionld/common/subCacheApiSubscriptionInsert.cpp index bac131b512..d92d2d5fa6 100644 --- a/src/lib/orionld/common/subCacheApiSubscriptionInsert.cpp +++ b/src/lib/orionld/common/subCacheApiSubscriptionInsert.cpp @@ -70,7 +70,7 @@ static void subCacheItemFill RenderFormat renderFormat ) { - kjTreeLog(apiSubscriptionP, "apiSubscriptionP", LmtSR); + kjTreeLog(apiSubscriptionP, "apiSubscriptionP", LmtSubCacheSync); cSubP->tenant = (tenant == NULL || *tenant == 0)? NULL : strdup(tenant); cSubP->servicePath = strdup("/#"); @@ -100,16 +100,11 @@ static void subCacheItemFill KjNode* langP = kjLookup(apiSubscriptionP, "lang"); KjNode* createdAtP = kjLookup(apiSubscriptionP, "createdAt"); KjNode* modifiedAtP = kjLookup(apiSubscriptionP, "modifiedAt"); - KjNode* dbCountP = kjLookup(apiSubscriptionP, "count"); - KjNode* lastNotificationP = kjLookup(apiSubscriptionP, "lastNotification"); - KjNode* lastSuccessP = kjLookup(apiSubscriptionP, "lastSuccess"); - KjNode* lastFailureP = kjLookup(apiSubscriptionP, "lastFailure"); - - // Fixing false alarms for q and mq - if ((qP != NULL) && (qP->value.s != NULL) && (qP->value.s[0] == 0)) - qP = NULL; - if ((mqP != NULL) && (mqP->value.s != NULL) && (mqP->value.s[0] == 0)) - mqP = NULL; + KjNode* dbCountP = kjLookup(notificationP, "timesSent"); + KjNode* dbFailuresP = kjLookup(notificationP, "timesFailed"); + KjNode* lastNotificationP = kjLookup(notificationP, "lastNotification"); + KjNode* lastSuccessP = kjLookup(notificationP, "lastSuccess"); + KjNode* lastFailureP = kjLookup(notificationP, "lastFailure"); if (subscriptionIdP == NULL) subscriptionIdP = kjLookup(apiSubscriptionP, "id"); @@ -123,22 +118,34 @@ static void subCacheItemFill if (subscriptionNameP != NULL) cSubP->name = subscriptionNameP->value.s; - if (dbCountP != NULL) - cSubP->dbCount = dbCountP->value.i; - else - cSubP->dbCount = 0; + if (descriptionP != NULL) + cSubP->description = strdup(descriptionP->value.s); - if (lastNotificationP != NULL) - cSubP->lastNotificationTime = lastNotificationP->value.f; - if (lastSuccessP != NULL) - cSubP->lastSuccess = lastSuccessP->value.f; + // + // DB Counters + // + cSubP->dbCount = (dbCountP != NULL)? dbCountP->value.i : 0; + cSubP->dbFailures = (dbFailuresP != NULL)? dbFailuresP->value.i : 0; - if (lastFailureP != NULL) - cSubP->lastFailure = lastFailureP->value.f; + LM_T(LmtSubCacheSync, ("%s: dbFailures == %d", subscriptionIdP->value.s, cSubP->dbFailures)); + LM_T(LmtSubCacheSync, ("%s: count == %d", subscriptionIdP->value.s, cSubP->count)); + LM_T(LmtSubCacheSync, ("%s: dbCount == %d", subscriptionIdP->value.s, cSubP->dbCount)); - if (descriptionP != NULL) - cSubP->description = strdup(descriptionP->value.s); + + // + // timestamps + // + if (lastNotificationP != NULL) cSubP->lastNotificationTime = lastNotificationP->value.f; + if (lastSuccessP != NULL) cSubP->lastSuccess = lastSuccessP->value.f; + if (lastFailureP != NULL) cSubP->lastFailure = lastFailureP->value.f; + + + // Fixing false alarms for q and mq + if ((qP != NULL) && (qP->value.s != NULL) && (qP->value.s[0] == 0)) + qP = NULL; + if ((mqP != NULL) && (mqP->value.s != NULL) && (mqP->value.s[0] == 0)) + mqP = NULL; if (qP != NULL) cSubP->expression.q = qP->value.s; @@ -149,6 +156,7 @@ static void subCacheItemFill if (ldqP != NULL) cSubP->qText = strdup(ldqP->value.s); + if (isActiveP != NULL) { cSubP->isActive = isActiveP->value.b; @@ -227,20 +235,36 @@ static void subCacheItemFill { for (KjNode* eSelectorP = entitiesP->value.firstChildP; eSelectorP != NULL; eSelectorP = eSelectorP->next) { - KjNode* idP = kjLookup(eSelectorP, "id"); - KjNode* idPatternP = kjLookup(eSelectorP, "idPattern"); - KjNode* typeP = kjLookup(eSelectorP, "type"); - char* id = (idP != NULL)? idP->value.s : (char*) ""; - const char* idPattern = (idPatternP != NULL)? idPatternP->value.s : ""; - const char* type = (typeP != NULL)? typeP->value.s : ""; - const char* isPattern = "false"; - - if (idP == NULL) + KjNode* idP = kjLookup(eSelectorP, "id"); + KjNode* idPatternP = kjLookup(eSelectorP, "idPattern"); + KjNode* typeP = kjLookup(eSelectorP, "type"); + char* id = (idP != NULL)? idP->value.s : NULL; + char* idPattern = (idPatternP != NULL)? idPatternP->value.s : NULL; + char* type = (typeP != NULL)? typeP->value.s : NULL; + char* isPattern = (char*) "false"; + + LM_T(LmtSR, ("id: '%s'", id)); + LM_T(LmtSR, ("idPattern: '%s'", idPattern)); + LM_T(LmtSR, ("type: '%s'", type)); + + if ((idP == NULL) && (idPattern == NULL)) { - id = (char*) ((idPatternP != NULL)? idPattern : ".*"); + id = (char*) ".*"; isPattern = (char*) "true"; } + else + { + if ((idP != NULL) && (idPatternP != NULL)) + idPatternP = NULL; + + if (idPattern != NULL) + { + id = idPatternP->value.s; + isPattern = (char*) "true"; + } + } + LM_T(LmtSR, ("Creating a new EntityInfo (id: '%s', idPattern: '%s', type: '%s')", id, idPattern, type)); EntityInfo* eP = new EntityInfo(id, type, isPattern, false); cSubP->entityIdInfos.push_back(eP); } @@ -291,7 +315,7 @@ static void subCacheItemFill cSubP->rest)); cSubP->protocolString = strdup(cSubP->protocolString); cSubP->ip = strdup(cSubP->ip); - cSubP->rest = strdup(cSubP->rest); + cSubP->rest = (cSubP->rest != NULL)? strdup(cSubP->rest) : NULL; } if (cSubP->protocol == MQTT) @@ -458,7 +482,8 @@ static CachedSubscription* subCacheApiSubscriptionUpdate cSubP->modifiedAt = modifiedAtNode->value.f; // The new value is in the DB } - cSubP->count = 0; + cSubP->count = 0; + cSubP->failures = 0; subCacheItemStrip(cSubP); subCacheItemFill(cSubP, apiSubscriptionP, qTree, geoCoordinatesP, contextP, tenant, showChangesP, sysAttrsP, renderFormat); diff --git a/src/lib/orionld/dbModel/dbModelFromApiSubscription.cpp b/src/lib/orionld/dbModel/dbModelFromApiSubscription.cpp index 1e23193a85..26302825fd 100644 --- a/src/lib/orionld/dbModel/dbModelFromApiSubscription.cpp +++ b/src/lib/orionld/dbModel/dbModelFromApiSubscription.cpp @@ -348,6 +348,7 @@ bool dbModelFromApiSubscription(KjNode* apiSubscriptionP, bool patch) // it's much better to not do this inside the loop. Especially as the tree must be modified and a for-loop // would no longer be possible // + kjTreeLog(notificationP, "notificationP", LmtSR); if (notificationP != NULL) { KjNode* nItemP = notificationP->value.firstChildP; diff --git a/src/lib/orionld/dbModel/dbModelToApiEntity.cpp b/src/lib/orionld/dbModel/dbModelToApiEntity.cpp index cafdd01330..166347a077 100644 --- a/src/lib/orionld/dbModel/dbModelToApiEntity.cpp +++ b/src/lib/orionld/dbModel/dbModelToApiEntity.cpp @@ -57,7 +57,7 @@ extern "C" // orionldAlterationsTreat() ? // orionldPostBatchUpsert() // -KjNode* dbModelToApiEntity(KjNode* dbEntityP, bool sysAttrs, const char* entityId) +KjNode* dbModelToApiEntity(KjNode* dbEntityP, bool sysAttrs, char* entityId) { KjNode* apiEntityP = kjObject(orionldState.kjsonP, NULL); KjNode* dbIdObjectP = kjLookup(dbEntityP, "_id"); diff --git a/src/lib/orionld/dbModel/dbModelToApiEntity.h b/src/lib/orionld/dbModel/dbModelToApiEntity.h index 6dc15549b5..a7cd3e07bf 100644 --- a/src/lib/orionld/dbModel/dbModelToApiEntity.h +++ b/src/lib/orionld/dbModel/dbModelToApiEntity.h @@ -49,7 +49,7 @@ extern "C" // USED BY // - orionldAlterationsTreat (for notifications) // -extern KjNode* dbModelToApiEntity(KjNode* attrP, bool sysAttrs, const char* entityId); +extern KjNode* dbModelToApiEntity(KjNode* attrP, bool sysAttrs, char* entityId); diff --git a/src/lib/orionld/dbModel/dbModelToApiSubscription.cpp b/src/lib/orionld/dbModel/dbModelToApiSubscription.cpp index fe130faffb..4a5b31740b 100644 --- a/src/lib/orionld/dbModel/dbModelToApiSubscription.cpp +++ b/src/lib/orionld/dbModel/dbModelToApiSubscription.cpp @@ -134,7 +134,7 @@ static bool notificationStatus(KjNode* dbLastSuccessP, KjNode* dbLastFailureP) // "custom": false, // "servicePath": "/#", // "blacklist": false, -// "ldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testFullContext.jsonld" +// "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testFullContext.jsonld" // } // // An API Subscription looks like this: @@ -181,7 +181,8 @@ KjNode* dbModelToApiSubscription KjNode** contextNodePP, KjNode** showChangesP, KjNode** sysAttrsP, - RenderFormat* renderFormatP + RenderFormat* renderFormatP, + double* timeIntervalP ) { KjNode* dbSubIdP = kjLookup(dbSubP, "_id"); DB_ITEM_NOT_FOUND(dbSubIdP, "id", tenant); @@ -203,6 +204,8 @@ KjNode* dbModelToApiSubscription KjNode* dbExpirationP = kjLookup(dbSubP, "expiration"); KjNode* dbLdContextP = kjLookup(dbSubP, "ldContext"); KjNode* dbCountP = kjLookup(dbSubP, "count"); + KjNode* timesFailedP = kjLookup(dbSubP, "timesFailed"); + KjNode* noMatchP = kjLookup(dbSubP, "noMatch"); KjNode* dbLastNotificationP = kjLookup(dbSubP, "lastNotification"); KjNode* dbLastSuccessP = kjLookup(dbSubP, "lastSuccess"); KjNode* dbLastFailureP = kjLookup(dbSubP, "lastFailure"); @@ -211,6 +214,7 @@ KjNode* dbModelToApiSubscription KjNode* dbLangP = kjLookup(dbSubP, "lang"); KjNode* dbCreatedAtP = NULL; KjNode* dbModifiedAtP = NULL; + KjNode* timeIntervalNodeP = kjLookup(dbSubP, "timeInterval"); if ((orionldState.uriParamOptions.sysAttrs == true) || (forSubCache == true)) { @@ -236,7 +240,7 @@ KjNode* dbModelToApiSubscription // sysAttrs if ((dbSysAttrsP != NULL) && (sysAttrsP != NULL)) - *sysAttrsP = dbShowChangesP; + *sysAttrsP = dbSysAttrsP; // // If dbSubIdP is a JSON Object, it's an NGSIv2 subscription and its "id" looks like this: @@ -282,7 +286,6 @@ KjNode* dbModelToApiSubscription if (dbLangP != NULL) kjChildAdd(apiSubP, dbLangP); - // // "entities" // @@ -292,39 +295,31 @@ KjNode* dbModelToApiSubscription // for (KjNode* entityP = dbEntitiesP->value.firstChildP; entityP != NULL; entityP = entityP->next) { + KjNode* idP = kjLookup(entityP, "id"); KjNode* isPatternP = kjLookup(entityP, "isPattern"); - KjNode* isTypePatternP = kjLookup(entityP, "isTypePattern"); KjNode* typeP = kjLookup(entityP, "type"); + KjNode* isTypePatternP = kjLookup(entityP, "isTypePattern"); + // There is no "Type Pattern" in NGSI-LD + kjChildRemove(entityP, isTypePatternP); - // bool ngsild = (dbLdContextP != NULL); - // if (ngsild) - { - if (isTypePatternP != NULL) - kjChildRemove(entityP, isTypePatternP); + // There is no "isPattern" in NGSI-LD + kjChildRemove(entityP, isPatternP); - if (isPatternP != NULL) - { - if (strcmp(isPatternP->value.s, "true") == 0) - { - KjNode* idP = kjLookup(entityP, "id"); - if (strcmp(idP->value.s, ".*") == 0) - kjChildRemove(entityP, idP); - else - idP->name = (char*) "idPattern"; - } - - kjChildRemove(entityP, isPatternP); - - // type must be compacted - if (typeP != NULL) // Can't really be NULL though ... - { - // But, for sub-cache we need the long names - if (forSubCache == false) - typeP->value.s = orionldContextItemAliasLookup(orionldState.contextP, typeP->value.s, NULL, NULL); - } - } + if (strcmp(isPatternP->value.s, "true") == 0) + { + if (strcmp(idP->value.s, ".*") == 0) + kjChildRemove(entityP, idP); + else + idP->name = (char*) "idPattern"; } + + kjChildRemove(entityP, isPatternP); + + // type must be compacted + // However, for sub-cache we need the long names + if (forSubCache == false) + typeP->value.s = orionldContextItemAliasLookup(orionldState.contextP, typeP->value.s, NULL, NULL); } kjChildAdd(apiSubP, dbEntitiesP); @@ -344,6 +339,13 @@ KjNode* dbModelToApiSubscription } } + // timeInterval + if (timeIntervalNodeP != NULL) + { + kjChildAdd(apiSubP, timeIntervalNodeP); + *timeIntervalP = (timeIntervalNodeP->type == KjInt)? timeIntervalNodeP->value.i : timeIntervalNodeP->value.f; + } + // "q" for NGSI-LD if ((dbLdqP != NULL) && (dbLdqP->value.s[0] != 0)) { @@ -489,6 +491,49 @@ KjNode* dbModelToApiSubscription kjChildAdd(notificationP, nStatusNodeP); + // lastNotification + if (dbLastNotificationP != NULL) + kjChildAdd(notificationP, dbLastNotificationP); + + // lastSuccess + if (dbLastSuccessP != NULL) + kjChildAdd(notificationP, dbLastSuccessP); + + // lastFailure + if (dbLastFailureP != NULL) + kjChildAdd(notificationP, dbLastFailureP); + + // timesSent + if (dbCountP != NULL) + { + int timesSent = 0; + + if (dbCountP->type == KjInt) + timesSent = dbCountP->value.i; + else if (dbCountP->type == KjObject) + LM_W(("Subscription::count: find the integer inside the object!")); + + // + // Transform to KjInt named "timesSent" + // + dbCountP->name = (char*) "timesSent"; + dbCountP->type = KjInt; + dbCountP->value.i = timesSent; + + kjChildAdd(notificationP, dbCountP); + LM_T(LmtSubCacheStats, ("count/timesSent: %d", timesSent)); + } + + // timesFailed + if (timesFailedP != NULL) + kjChildAdd(notificationP, timesFailedP); + + // noMatch + if (noMatchP != NULL) + kjChildAdd(notificationP, noMatchP); + + // Add "notification" to top level + kjChildAdd(apiSubP, notificationP); if (dbReferenceP != NULL) { @@ -502,7 +547,6 @@ KjNode* dbModelToApiSubscription dbMimeTypeP->name = (char*) "accept"; } - kjChildAdd(apiSubP, notificationP); // @@ -643,27 +687,13 @@ KjNode* dbModelToApiSubscription kjChildAdd(apiSubP, modifiedAtNodeP); } -#if 0 - // ldContext ... not yet implemented in the API SPEC for subscriptions to have their own @context + // jsonldContext if (dbLdContextP != NULL) + { kjChildAdd(apiSubP, dbLdContextP); -#endif - - // count - take sub-cache delta into consideration - if (dbCountP != NULL) - kjChildAdd(apiSubP, dbCountP); - - // lastNotification - take from sub-cache - if (dbLastNotificationP != NULL) - kjChildAdd(apiSubP, dbLastNotificationP); - - // lastSuccess - take from sub-cache - if (dbLastSuccessP != NULL) - kjChildAdd(apiSubP, dbLastSuccessP); + dbLdContextP->name = (char*) "jsonldContext"; + } - // lastFailure - take from sub-cache - if (dbLastFailureP != NULL) - kjChildAdd(apiSubP, dbLastFailureP); if (qNodePP != NULL) // FIXME: This is more than a bit weird ... *qNodePP = NULL; diff --git a/src/lib/orionld/dbModel/dbModelToApiSubscription.h b/src/lib/orionld/dbModel/dbModelToApiSubscription.h index 256ecbf0c9..fa43666f0b 100644 --- a/src/lib/orionld/dbModel/dbModelToApiSubscription.h +++ b/src/lib/orionld/dbModel/dbModelToApiSubscription.h @@ -50,7 +50,8 @@ extern KjNode* dbModelToApiSubscription KjNode** contextNodePP, KjNode** showChangesP, KjNode** sysAttrsP, - RenderFormat* renderFormatP + RenderFormat* renderFormatP, + double* timeIntervalP ); #endif // SRC_LIB_ORIONLD_DBMODEL_DBMODELTOAPISUBSCRIPTION_H_ diff --git a/src/lib/orionld/kjTree/CMakeLists.txt b/src/lib/orionld/kjTree/CMakeLists.txt index 7f253b1c74..b30da0dc3f 100644 --- a/src/lib/orionld/kjTree/CMakeLists.txt +++ b/src/lib/orionld/kjTree/CMakeLists.txt @@ -60,6 +60,7 @@ SET (SOURCES kjNodeDecouple.cpp kjNavigate.cpp kjTreeFromCachedSubscription.cpp + kjTreeFromPernotSubscription.cpp kjAttributeNormalizedToSimplified.cpp kjAttributeNormalizedToConcise.cpp kjSysAttrsRemove.cpp diff --git a/src/lib/orionld/kjTree/kjTreeFromCachedSubscription.cpp b/src/lib/orionld/kjTree/kjTreeFromCachedSubscription.cpp index 9537f3b001..0b343e52c5 100644 --- a/src/lib/orionld/kjTree/kjTreeFromCachedSubscription.cpp +++ b/src/lib/orionld/kjTree/kjTreeFromCachedSubscription.cpp @@ -132,14 +132,21 @@ KjNode* kjTreeFromCachedSubscription(CachedSubscription* cSubP, bool sysAttrs, b KjNode* eObjectP = kjObject(orionldState.kjsonP, NULL); NULL_CHECK(eObjectP); + LM_T(LmtSR, ("Got an entity")); // // id/idPattern // // SPECIAL CASE: // If it's a .* pattern, then it is omitted // - const char* entityId = eiP->entityId.c_str(); - bool noIdNeeded = (eiP->isPattern == true) && (strcmp(entityId, ".*") == 0); + const char* entityId = eiP->entityId.c_str(); + const char* entityType = eiP->entityType.c_str(); + bool noIdNeeded = (eiP->isPattern == true) && (strcmp(entityId, ".*") == 0); + + LM_T(LmtSR, ("entityId: %s", entityId)); + LM_T(LmtSR, ("isPattern: %s", (eiP->isPattern == true)? "true" : "false")); + LM_T(LmtSR, ("entityType: %s", entityType)); + LM_T(LmtSR, ("noIdNeeded: %s", (noIdNeeded == false)? "FALSE" : "TRUE")); if ((noIdNeeded == false) && (*entityId != 0)) { @@ -377,6 +384,9 @@ KjNode* kjTreeFromCachedSubscription(CachedSubscription* cSubP, bool sysAttrs, b // if (cSubP->count + cSubP->dbCount > 0) { + LM_T(LmtSubCacheStats, ("count: %d", cSubP->count)); + LM_T(LmtSubCacheStats, ("dbCount: %d", cSubP->dbCount)); + nodeP = kjInteger(orionldState.kjsonP, "timesSent", cSubP->count + cSubP->dbCount); NULL_CHECK(nodeP); kjChildAdd(notificationNodeP, nodeP); @@ -385,7 +395,7 @@ KjNode* kjTreeFromCachedSubscription(CachedSubscription* cSubP, bool sysAttrs, b // // notification::lastNotification // - if (cSubP->lastNotificationTime > 0) + if (cSubP->lastNotificationTime > 0.1) { numberToDate(cSubP->lastNotificationTime, dateTime, sizeof(dateTime)); nodeP = kjString(orionldState.kjsonP, "lastNotification", dateTime); @@ -396,7 +406,7 @@ KjNode* kjTreeFromCachedSubscription(CachedSubscription* cSubP, bool sysAttrs, b // // notification::lastFailure // - if (cSubP->lastFailure > 0) + if (cSubP->lastFailure > 0.1) { numberToDate(cSubP->lastFailure, dateTime, sizeof(dateTime)); nodeP = kjString(orionldState.kjsonP, "lastFailure", dateTime); @@ -407,7 +417,7 @@ KjNode* kjTreeFromCachedSubscription(CachedSubscription* cSubP, bool sysAttrs, b // // notification::lastSuccess // - if (cSubP->lastSuccess > 0) + if (cSubP->lastSuccess > 0.1) { numberToDate(cSubP->lastSuccess, dateTime, sizeof(dateTime)); nodeP = kjString(orionldState.kjsonP, "lastSuccess", dateTime); @@ -503,6 +513,16 @@ KjNode* kjTreeFromCachedSubscription(CachedSubscription* cSubP, bool sysAttrs, b kjChildAdd(sP, nodeP); } + // + // jsonldCcontext + // + if (cSubP->ldContext != "") + { + nodeP = kjString(orionldState.kjsonP, "jsonldContext", cSubP->ldContext.c_str()); + NULL_CHECK(nodeP); + kjChildAdd(sP, nodeP); + } + // // @context // diff --git a/src/lib/orionld/kjTree/kjTreeFromPernotSubscription.cpp b/src/lib/orionld/kjTree/kjTreeFromPernotSubscription.cpp new file mode 100644 index 0000000000..dc584c2164 --- /dev/null +++ b/src/lib/orionld/kjTree/kjTreeFromPernotSubscription.cpp @@ -0,0 +1,225 @@ +/* +* +* 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/kjClone.h" // kjClone +#include "kjson/kjLookup.h" // kjLookup +#include "kjson/kjBuilder.h" // kjString, kjChildAdd, ... +} + +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/common/orionldError.h" // orionldError +#include "orionld/common/numberToDate.h" // numberToDate +#include "orionld/context/orionldContextItemAliasLookup.h" // orionldContextItemAliasLookup +#include "orionld/dbModel/dbModelValueStrip.h" // dbModelValueStrip +#include "orionld/q/qAliasCompact.h" // qAliasCompact +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription + + + +// ----------------------------------------------------------------------------- +// +// NULL_CHECK - +// +#define NULL_CHECK(pointer) if (pointer == NULL) return outOfMemory() + + + +// ----------------------------------------------------------------------------- +// +// outOfMemory - +// +static KjNode* outOfMemory(void) +{ + LM_E(("Internal Error (out of memory creating a KjNode-tree for a PernotSubscription)")); + return NULL; +} + + + +// ----------------------------------------------------------------------------- +// +// timestampAdd - +// +static void timestampAdd(KjNode* containerP, const char* fieldName, double ts) +{ + if (ts <= 0) + return; + + char buf[64]; + if (numberToDate(ts, buf, sizeof(buf) - 1) == true) + { + KjNode* nodeP = kjString(orionldState.kjsonP, fieldName, buf); + kjChildAdd(containerP, nodeP); + } +} + + + +// ----------------------------------------------------------------------------- +// +// counterAdd - +// +static void counterAdd(KjNode* containerP, const char* fieldName, uint32_t count) +{ + if (count == 0) + return; + + KjNode* nodeP = kjInteger(orionldState.kjsonP, fieldName, count); + kjChildAdd(containerP, nodeP); +} + + + +// ----------------------------------------------------------------------------- +// +// kjTreeFromPernotSubscription - in NGSI-LD API format +// +KjNode* kjTreeFromPernotSubscription(PernotSubscription* pSubP, bool sysAttrs, bool contextInBody) +{ + KjNode* nodeP; + + // + // Top level object - the Subscription + // + // 1. We start with what we already get from the Pernot sub-cache. + // + // 2. And, we remove the sysAttrs, unless asked for, + // + // 3. We add the "volatile" fields: + // "isActive", + // "status" + // "notification::status" + // + // 4. We add counters and timestamps: + // - timesSent + // - timesFailed + // - lastNotification + // - lastFailure + // - lastSuccess + // + // 5. And we compact all fields that need it, according with the current @context + // + // 6. Compact attribute names in q + // + KjNode* sP = kjClone(orionldState.kjsonP, pSubP->kjSubP); + NULL_CHECK(sP); + + KjNode* notificationP = kjLookup(sP, "notification"); + if (notificationP == NULL) + { + orionldError(OrionldInternalError, "Database Error (subscription without notification field)", pSubP->subscriptionId, 500); + return NULL; + } + + // 2. Remove modifiedAt, createdAt? + if (sysAttrs == false) + { + nodeP = kjLookup(sP, "modifiedAt"); + if (nodeP != NULL) + kjChildRemove(sP, nodeP); + + nodeP = kjLookup(sP, "createdAt"); + if (nodeP != NULL) + kjChildRemove(sP, nodeP); + } + + // 3. Add the "volatile" fields, but, first decide the states + bool isActive = true; + char* status = (char*) "active"; + char* notificationStatus = (char*) "ok"; + // + // Lookup and compare with: + // * expiresAt + // * any error + // * paused, ... + // + KjNode* isActiveP = kjBoolean(orionldState.kjsonP, "isActive", isActive); + kjChildAdd(sP, isActiveP); + KjNode* statusP = kjString(orionldState.kjsonP, "status", status); + kjChildAdd(sP, statusP); + KjNode* notificationStatusP = kjString(orionldState.kjsonP, "status", notificationStatus); + kjChildAdd(notificationP, notificationStatusP); + + // 4. counters and timestamps + counterAdd(notificationP, "timesSent", pSubP->notificationAttemptsDb + pSubP->notificationAttempts); + counterAdd(notificationP, "timesFailed", pSubP->notificationErrorsDb + pSubP->notificationErrors); + timestampAdd(notificationP, "lastNotification", pSubP->lastNotificationTime); + timestampAdd(notificationP, "lastSuccess", pSubP->lastSuccessTime); + timestampAdd(notificationP, "lastFailure", pSubP->lastFailureTime); + + // 5.1. Compact entity types + KjNode* entitiesP = kjLookup(sP, "entities"); + if (entitiesP != NULL) + { + for (KjNode* entitiesItemP = entitiesP->value.firstChildP; entitiesItemP != NULL; entitiesItemP = entitiesItemP->next) + { + KjNode* typeP = kjLookup(entitiesItemP, "type"); + + if (typeP != NULL) + typeP->value.s = orionldContextItemAliasLookup(orionldState.contextP, typeP->value.s, NULL, NULL); + } + } + + // 5.2. Compact notification::attributes + KjNode* attributesP = kjLookup(notificationP, "attributes"); + if (attributesP != NULL) + { + for (KjNode* attributeP = attributesP->value.firstChildP; attributeP != NULL; attributeP = attributeP->next) + { + attributeP->value.s = orionldContextItemAliasLookup(orionldState.contextP, attributeP->value.s, NULL, NULL); + } + } + + // + // 6. Compact attribute names in q + // + KjNode* qP = kjLookup(sP, "q"); + if (qP != NULL) + { + LM_T(LmtPernot, ("Found 'q' (%s)- fixing it", qP->value.s)); + dbModelValueStrip(qP); + LM_T(LmtPernot, ("'q' after dbModelValueStrip: '%s'", qP->value.s)); + qAliasCompact(qP, true); // qAliasCompact uses orionldState.contextP - which is what we want + qP->name = (char*) "q"; + LM_T(LmtPernot, ("'q' final: '%s'", qP->value.s)); + } + else + LM_T(LmtPernot, ("No 'q'")); + + // + // name => subscriptionName + // FIXME: don't do this every time! + // do it in pernotSubCacheAdd! + // + KjNode* nameP = kjLookup(sP, "name"); + if (nameP != NULL) + nameP->name = (char*) "subscriptionName"; + + return sP; +} diff --git a/src/lib/orionld/kjTree/kjTreeFromPernotSubscription.h b/src/lib/orionld/kjTree/kjTreeFromPernotSubscription.h new file mode 100644 index 0000000000..515e0a901a --- /dev/null +++ b/src/lib/orionld/kjTree/kjTreeFromPernotSubscription.h @@ -0,0 +1,43 @@ +#ifndef SRC_LIB_ORIONLD_KJTREE_KJTREEFROMPERNOTSUBSCRIPTION_H_ +#define SRC_LIB_ORIONLD_KJTREE_KJTREEFROMPERNOTSUBSCRIPTION_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/pernot/PernotSubscription.h" // PernotSubscription + + + +// ----------------------------------------------------------------------------- +// +// kjTreeFromPernotSubscription - in NGSI-LD API format +// +extern KjNode* kjTreeFromPernotSubscription(PernotSubscription* cSubP, bool sysAttrs, bool contextInBody); + +#endif // SRC_LIB_ORIONLD_KJTREE_KJTREEFROMPERNOTSUBSCRIPTION_H_ diff --git a/src/lib/orionld/kjTree/kjTreeLog.cpp b/src/lib/orionld/kjTree/kjTreeLog.cpp index 5ea655c755..ad696b7e96 100644 --- a/src/lib/orionld/kjTree/kjTreeLog.cpp +++ b/src/lib/orionld/kjTree/kjTreeLog.cpp @@ -50,13 +50,15 @@ void kjTreeLogFunction(KjNode* tree, const char* msg, const char* path, int line if (tree == NULL) { - lmOut((char*) "NULL Tree", 'T', fileNameOnly, lineNo, functionName, traceLevel, NULL); + char error[256]; + snprintf(error, sizeof(error) - 1, "%s: NULL", msg); + lmOut(error, 'T', fileNameOnly, lineNo, functionName, traceLevel, NULL); return; } int bufSize = kjFastRenderSize(tree); - // Too big trees will not be rendered - this is just tracing + // Too big trees will not be rendered - this is just logging if (bufSize < 10 * 1024) { char* treeBuf = kaAlloc(&orionldState.kalloc, bufSize + 512); diff --git a/src/lib/orionld/kjTree/kjTreeToSubscription.cpp b/src/lib/orionld/kjTree/kjTreeToSubscription.cpp index 6ca3dacc26..7df8a44135 100644 --- a/src/lib/orionld/kjTree/kjTreeToSubscription.cpp +++ b/src/lib/orionld/kjTree/kjTreeToSubscription.cpp @@ -260,7 +260,7 @@ bool kjTreeToSubscription(ngsiv2::Subscription* subP, char** subIdPP, KjNode** e } else if (strcmp(kNodeP->name, "timeInterval") == 0) { - orionldError(OrionldBadRequestData, "Not Implemented", "Subscription::timeInterval is not implemented", 501); + orionldError(OrionldOperationNotSupported, "Not Implemented", "Periodic Notification Subscriptions are not implemented", 501); return false; } else if ((kNodeP->name[0] == 'q') && (kNodeP->name[1] == 0)) @@ -362,7 +362,7 @@ bool kjTreeToSubscription(ngsiv2::Subscription* subP, char** subIdPP, KjNode** e if ((timeIntervalP != NULL) && (watchedAttributesPresent == true)) { - orionldError(OrionldBadRequestData, "Both 'timeInterval' and 'watchedAttributes' present", NULL, 400); + orionldError(OrionldBadRequestData, "Inconsistent subscription", "Both 'timeInterval' and 'watchedAttributes' present", 400); return false; } diff --git a/src/lib/orionld/mongoBackend/mongoLdRegistrationAux.cpp b/src/lib/orionld/mongoBackend/mongoLdRegistrationAux.cpp index 5d7855d250..896cac6c92 100644 --- a/src/lib/orionld/mongoBackend/mongoLdRegistrationAux.cpp +++ b/src/lib/orionld/mongoBackend/mongoLdRegistrationAux.cpp @@ -132,7 +132,7 @@ void mongoSetLdName(ngsiv2::Registration* reg, mongo::BSONObj* bobjP) */ void mongoSetExpiration(ngsiv2::Registration* regP, const mongo::BSONObj& r) { - regP->expires = (r.hasField(REG_EXPIRATION))? getNumberFieldAsDoubleF(&r, REG_EXPIRATION) : -1; + regP->expires = (r.hasField(REG_EXPIRATION))? getNumberFieldAsDoubleF(&r, REG_EXPIRATION, true) : -1; } @@ -148,8 +148,8 @@ void mongoSetLdObservationInterval(ngsiv2::Registration* reg, const mongo::BSONO mongo::BSONObj obj; getObjectFieldF(&obj, &r, REG_OBSERVATION_INTERVAL); - reg->observationInterval.start = getNumberFieldAsDoubleF(&obj, "startAt"); - reg->observationInterval.end = obj.hasField("endAt") ? getNumberFieldAsDoubleF(&obj, "endAt") : -1; + reg->observationInterval.start = getNumberFieldAsDoubleF(&obj, "startAt", true); + reg->observationInterval.end = obj.hasField("endAt") ? getNumberFieldAsDoubleF(&obj, "endAt", true) : -1; } else { @@ -171,8 +171,8 @@ void mongoSetLdManagementInterval(ngsiv2::Registration* reg, const mongo::BSONOb mongo::BSONObj obj; getObjectFieldF(&obj, &r, REG_MANAGEMENT_INTERVAL); - reg->managementInterval.start = getNumberFieldAsDoubleF(&obj, "startAt"); - reg->managementInterval.end = obj.hasField("endAt") ? getNumberFieldAsDoubleF(&obj, "endAt") : -1; + reg->managementInterval.start = getNumberFieldAsDoubleF(&obj, "startAt", true); + reg->managementInterval.end = obj.hasField("endAt") ? getNumberFieldAsDoubleF(&obj, "endAt", true) : -1; } else { @@ -190,7 +190,7 @@ void mongoSetLdManagementInterval(ngsiv2::Registration* reg, const mongo::BSONOb void mongoSetLdTimestamp(double* timestampP, const char* name, const mongo::BSONObj& bobj) { if (bobj.hasField(name)) - *timestampP = getNumberFieldAsDoubleF(&bobj, name); + *timestampP = getNumberFieldAsDoubleF(&bobj, name, true); else *timestampP = -1; } diff --git a/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntitiesDelete.cpp b/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntitiesDelete.cpp index ccd775ce71..9c9d88b3ce 100644 --- a/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntitiesDelete.cpp +++ b/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntitiesDelete.cpp @@ -33,7 +33,7 @@ extern "C" #include "logMsg/logMsg.h" // LM_* #include "logMsg/traceLevels.h" // Lmt* -#include "orionld/common/orionldState.h" // orionldState, dbName, mongoEntitiesCollectionP +#include "orionld/common/orionldState.h" // orionldState, dbName #include "mongoBackend/MongoGlobal.h" // getMongoConnection, releaseMongoConnection, ... #include "orionld/mongoCppLegacy/mongoCppLegacyEntitiesDelete.h" // Own interface diff --git a/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityFieldDelete.cpp b/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityFieldDelete.cpp index 9318e67679..b636d1d7f3 100644 --- a/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityFieldDelete.cpp +++ b/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityFieldDelete.cpp @@ -32,7 +32,7 @@ extern "C" #include "logMsg/logMsg.h" // LM_* #include "logMsg/traceLevels.h" // Lmt* -#include "orionld/common/orionldState.h" // orionldState, dbName, mongoEntitiesCollectionP +#include "orionld/common/orionldState.h" // orionldState, dbName #include "mongoBackend/MongoGlobal.h" // getMongoConnection, releaseMongoConnection, ... #include "orionld/mongoCppLegacy/mongoCppLegacyEntityFieldDelete.h" // Own interface diff --git a/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityFieldReplace.cpp b/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityFieldReplace.cpp index c3f92e359c..0fc699a78e 100644 --- a/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityFieldReplace.cpp +++ b/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityFieldReplace.cpp @@ -34,7 +34,7 @@ extern "C" #include "mongoBackend/MongoGlobal.h" // getMongoConnection, releaseMongoConnection, ... -#include "orionld/common/orionldState.h" // orionldState, dbName, mongoEntitiesCollectionP +#include "orionld/common/orionldState.h" // orionldState, dbName #include "orionld/mongoCppLegacy/mongoCppLegacyKjTreeToBsonObj.h" // mongoCppLegacyKjTreeToBsonObj #include "orionld/mongoCppLegacy/mongoCppLegacyEntityFieldReplace.h" // Own interface diff --git a/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityListLookupWithIdTypeCreDate.cpp b/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityListLookupWithIdTypeCreDate.cpp index 1f3a54c1c5..20ef6208dd 100644 --- a/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityListLookupWithIdTypeCreDate.cpp +++ b/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityListLookupWithIdTypeCreDate.cpp @@ -39,7 +39,7 @@ extern "C" #include "mongoBackend/MongoGlobal.h" // getMongoConnection, releaseMongoConnection, ... #include "mongoBackend/safeMongo.h" // getStringFieldF, ... -#include "orionld/common/orionldState.h" // orionldState, dbName, mongoEntitiesCollectionP +#include "orionld/common/orionldState.h" // orionldState, dbName #include "orionld/mongoCppLegacy/mongoCppLegacyDbNumberFieldGet.h" // mongoCppLegacyDbNumberFieldGet #include "orionld/mongoCppLegacy/mongoCppLegacyDbStringFieldGet.h" // mongoCppLegacyDbStringFieldGet #include "orionld/mongoCppLegacy/mongoCppLegacyDbObjectFieldGet.h" // mongoCppLegacyDbObjectFieldGet diff --git a/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityUpdate.cpp b/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityUpdate.cpp index e8bd60d76f..1d92744a70 100644 --- a/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityUpdate.cpp +++ b/src/lib/orionld/mongoCppLegacy/mongoCppLegacyEntityUpdate.cpp @@ -32,7 +32,7 @@ extern "C" #include "logMsg/logMsg.h" // LM_* #include "logMsg/traceLevels.h" // Lmt* -#include "orionld/common/orionldState.h" // orionldState, dbName, mongoEntitiesCollectionP +#include "orionld/common/orionldState.h" // orionldState, dbName #include "mongoBackend/MongoGlobal.h" // getMongoConnection, releaseMongoConnection, ... #include "orionld/mongoCppLegacy/mongoCppLegacyDataToKjTree.h" // mongoCppLegacyDataToKjTree diff --git a/src/lib/orionld/mongoCppLegacy/mongoCppLegacySubscriptionMatchEntityIdAndAttributes.cpp b/src/lib/orionld/mongoCppLegacy/mongoCppLegacySubscriptionMatchEntityIdAndAttributes.cpp index 11e57d70c8..03eb047fab 100644 --- a/src/lib/orionld/mongoCppLegacy/mongoCppLegacySubscriptionMatchEntityIdAndAttributes.cpp +++ b/src/lib/orionld/mongoCppLegacy/mongoCppLegacySubscriptionMatchEntityIdAndAttributes.cpp @@ -34,7 +34,7 @@ extern "C" #include "logMsg/traceLevels.h" // Lmt* #include "mongoBackend/MongoGlobal.h" // getMongoConnection, releaseMongoConnection, ... -#include "orionld/common/orionldState.h" // orionldState, dbName, mongoEntitiesCollectionP +#include "orionld/common/orionldState.h" // orionldState, dbName #include "orionld/mongoCppLegacy/mongoCppLegacyDataToKjTree.h" // mongoCppLegacyDataToKjTree #include "orionld/mongoCppLegacy/mongoCppLegacySubscriptionMatchEntityIdAndAttributes.h" // Own interface diff --git a/src/lib/orionld/mongoc/mongocEntitiesQuery.cpp b/src/lib/orionld/mongoc/mongocEntitiesQuery.cpp index 23f5c0ee4a..867ec76660 100644 --- a/src/lib/orionld/mongoc/mongocEntitiesQuery.cpp +++ b/src/lib/orionld/mongoc/mongocEntitiesQuery.cpp @@ -40,6 +40,7 @@ extern "C" #include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo #include "orionld/q/QNode.h" // QNode #include "orionld/q/qTreeToBson.h" // qTreeToBson +#include "orionld/mongoc/mongocWriteLog.h" // MONGOC_RLOG - FIXME: change name to mongocLog.h #include "orionld/mongoc/mongocConnectionGet.h" // mongocConnectionGet #include "orionld/mongoc/mongocKjTreeToBson.h" // mongocKjTreeToBson #include "orionld/mongoc/mongocKjTreeFromBson.h" // mongocKjTreeFromBson @@ -254,7 +255,9 @@ static bool geoNearFilter(bson_t* mongoFilterP, OrionldGeoInfo* geoInfoP) char geoPropertyPath[512]; int geoPropertyPathLen; if (geoPropertyDbPath(geoPropertyPath, sizeof(geoPropertyPath), geoInfoP->geoProperty, &geoPropertyPathLen) == false) - return false; + LM_RE(false, ("Failed to aeemble the geoProperty path")); + + LM_T(LmtMongoc, ("geoPropertyPath: '%s'", geoPropertyPath)); bson_append_document(mongoFilterP, geoPropertyPath, geoPropertyPathLen, &location); @@ -309,6 +312,7 @@ static bool geoWithinFilter(bson_t* mongoFilterP, OrionldGeoInfo* geoInfoP) bson_init(&within); bson_init(&coordinates); + kjTreeLog(geoInfoP->coordinates, "coordinates", LmtMongoc); mongocKjTreeToBson(geoInfoP->coordinates, &coordinates); bson_append_array(&geometry, "coordinates", 11, &coordinates); @@ -788,6 +792,7 @@ KjNode* mongocEntitiesQuery if (limit != 0) { + MONGOC_RLOG("Lookup Entities", orionldState.tenantP->mongoDbName, "entities", &mongoFilter, LmtMongoc); mongoCursorP = mongoc_collection_find_with_opts(orionldState.mongoc.entitiesP, &mongoFilter, &options, readPrefs); bson_destroy(&options); diff --git a/src/lib/orionld/mongoc/mongocEntitiesQuery2.cpp b/src/lib/orionld/mongoc/mongocEntitiesQuery2.cpp index 27961b29f5..93ef62336c 100644 --- a/src/lib/orionld/mongoc/mongocEntitiesQuery2.cpp +++ b/src/lib/orionld/mongoc/mongocEntitiesQuery2.cpp @@ -39,6 +39,7 @@ extern "C" #include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo #include "orionld/types/OrionldGeometry.h" // orionldGeometryFromString #include "orionld/q/QNode.h" // QNode +#include "orionld/mongoc/mongocWriteLog.h" // MONGOC_RLOG - FIXME: change name to mongocLog.h #include "orionld/mongoc/mongocConnectionGet.h" // mongocConnectionGet #include "orionld/mongoc/mongocKjTreeToBson.h" // mongocKjTreeToBson #include "orionld/mongoc/mongocKjTreeFromBson.h" // mongocKjTreeFromBson @@ -319,7 +320,7 @@ KjNode* mongocEntitiesQuery2 if (geoInfoP != NULL) { if (geoFilter(&mongoFilter, geoInfoP) == false) - return NULL; + LM_RE(NULL, ("geoFilter flagged an error")); geojsonGeometry = geoInfoP->geoProperty; } @@ -379,6 +380,7 @@ KjNode* mongocEntitiesQuery2 bson_free(optionsString); #endif + MONGOC_RLOG("Lookup Entities", orionldState.tenantP->mongoDbName, "entities", &mongoFilter, LmtMongoc); mongoCursorP = mongoc_collection_find_with_opts(orionldState.mongoc.entitiesP, &mongoFilter, &options, readPrefs); bson_destroy(&options); @@ -400,6 +402,7 @@ KjNode* mongocEntitiesQuery2 #endif int hits = 0; + mongoDocP = NULL; while (mongoc_cursor_next(mongoCursorP, &mongoDocP)) { entityNodeP = mongocKjTreeFromBson(mongoDocP, &title, &detail); diff --git a/src/lib/orionld/mongoc/mongocEntityUpdate.cpp b/src/lib/orionld/mongoc/mongocEntityUpdate.cpp index 8c2f0f6388..5a5696af53 100644 --- a/src/lib/orionld/mongoc/mongocEntityUpdate.cpp +++ b/src/lib/orionld/mongoc/mongocEntityUpdate.cpp @@ -36,6 +36,7 @@ extern "C" #include "orionld/common/orionldState.h" // orionldState #include "orionld/common/dotForEq.h" // dotForEq +#include "orionld/mongoc/mongocWriteLog.h" // MONGOC_WLOG #include "orionld/mongoc/mongocConnectionGet.h" // mongocConnectionGet #include "orionld/mongoc/mongocKjTreeToBson.h" // mongocKjTreeToBson #include "orionld/mongoc/mongocIndexString.h" // mongocIndexString @@ -100,6 +101,7 @@ static void mongocKjTreeToUpdateBson if (isValue) { bson_t compound; + LM_T(LmtMongoc, ("Member '%s' has a value that is a compound object - calling mongocKjTreeToBson", subAttrP->name)); mongocKjTreeToBson(subAttrP, &compound); bson_append_document(setP, path, pathLen, &compound); } @@ -108,6 +110,7 @@ static void mongocKjTreeToUpdateBson int bsonPathLen = strlen(bsonPath) + 1 + strlen(attrP->name) + 1; char* bsonPath2 = kaAlloc(&orionldState.kalloc, bsonPathLen); + LM_T(LmtMongoc, ("Member '%s' has a value that is a simple value - calling mongocKjTreeToUpdateBson", subAttrP->name)); snprintf(bsonPath2, bsonPathLen, "%s.%s", bsonPath, attrP->name); mongocKjTreeToUpdateBson(subAttrP, setP, unsetP, bsonPath2, NULL, NULL, NULL, level + 1); // Perhaps another function for sub-attributes ...? } @@ -118,9 +121,12 @@ static void mongocKjTreeToUpdateBson { bson_t compound; + LM_T(LmtMongoc, ("Array member '%s' is a compound object - calling mongocKjTreeToBson", subAttrP->name)); mongocKjTreeToBson(subAttrP, &compound); bson_append_array(setP, path, pathLen, &compound); } + else + LM_T(LmtMongoc, ("Wait, what???")); } ++jx; @@ -210,6 +216,7 @@ static bool patchApply char buf[16]; int bufLen = mongocIndexString(ix, buf); + LM_T(LmtMongoc, ("Array Index: '%s'", buf)); bson_append_utf8(&commaArrayBson, buf, bufLen, itemNameP->value.s, -1); ++ix; } @@ -235,12 +242,15 @@ static bool patchApply else if (tree->type == KjBoolean) bson_append_bool(setP, path, -1, tree->value.b); else if (tree->type == KjArray) { + LM_T(LmtMongoc, ("'%s' is an Array - calling mongocKjTreeToBson", tree->name)); + kjTreeLog(tree, "TREE", LmtMongoc); mongocKjTreeToBson(tree, &compound); bson_append_array(setP, path, -1, &compound); bson_destroy(&compound); } else if (tree->type == KjObject) { + LM_T(LmtMongoc, ("'%s' is an Object - calling mongocKjTreeToBson", tree->name)); mongocKjTreeToBson(tree, &compound); bson_append_document(setP, path, -1, &compound); bson_destroy(&compound); @@ -294,6 +304,7 @@ bool mongocEntityUpdate(const char* entityId, KjNode* patchTree) if (pulls > 0) bson_append_document(&request, "$pull", 5, &pull); if (pushes > 0) bson_append_document(&request, "$push", 5, &push); + MONGOC_WLOG("PATCH Entity", orionldState.tenantP->mongoDbName, "entities", &selector, &request, LmtMongoc); bool b = mongoc_collection_update_one(orionldState.mongoc.entitiesP, &selector, &request, NULL, &reply, &orionldState.mongoc.error); if (b == false) { diff --git a/src/lib/orionld/mongoc/mongocKjTreeFromBson.cpp b/src/lib/orionld/mongoc/mongocKjTreeFromBson.cpp index 995ca6430a..4395d17d47 100644 --- a/src/lib/orionld/mongoc/mongocKjTreeFromBson.cpp +++ b/src/lib/orionld/mongoc/mongocKjTreeFromBson.cpp @@ -59,7 +59,7 @@ 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 361142aea2..bfe1f7f71f 100644 --- a/src/lib/orionld/mongoc/mongocKjTreeToBson.cpp +++ b/src/lib/orionld/mongoc/mongocKjTreeToBson.cpp @@ -54,11 +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)); } else { name = nodeP->name; slen = strlen(name); + LM_T(LmtMongoc, ("Name: '%s'", name)); } if (nodeP->type == KjString) bson_append_utf8(parentP, name, slen, nodeP->value.s, -1); @@ -75,6 +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)); kjTreeToBson(itemP, &bObject, false); } bson_append_document_end(parentP, &bObject); @@ -88,6 +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)); kjTreeToBson(itemP, &bArray, true, arrayIndex); arrayIndex += 1; } @@ -108,8 +112,13 @@ void mongocKjTreeToBson(KjNode* treeP, bson_t* bsonP) bson_init(bsonP); + int arrayIx = 0; for (KjNode* itemP = treeP->value.firstChildP; itemP != NULL; itemP = itemP->next) { - kjTreeToBson(itemP, bsonP, inArray); + LM_T(LmtMongoc, ("Calling kjTreeToBson for '%s' of '%s'", itemP->name, treeP->name)); + kjTreeToBson(itemP, bsonP, inArray, arrayIx); + + if (inArray == true) + ++arrayIx; } } diff --git a/src/lib/orionld/mongoc/mongocSubCachePopulateByTenant.cpp b/src/lib/orionld/mongoc/mongocSubCachePopulateByTenant.cpp index eec4759446..f808d7fe4c 100644 --- a/src/lib/orionld/mongoc/mongocSubCachePopulateByTenant.cpp +++ b/src/lib/orionld/mongoc/mongocSubCachePopulateByTenant.cpp @@ -34,6 +34,7 @@ extern "C" #include "orionld/common/orionldState.h" // mongocPool #include "orionld/common/subCacheApiSubscriptionInsert.h" // subCacheApiSubscriptionInsert +#include "orionld/pernot/pernotSubCacheAdd.h" // pernotSubCacheAdd #include "orionld/dbModel/dbModelToApiSubscription.h" // dbModelToApiSubscription #include "orionld/types/OrionldTenant.h" // OrionldTenant #include "orionld/q/QNode.h" // QNode @@ -124,6 +125,9 @@ bool mongocSubCachePopulateByTenant(OrionldTenant* tenantP, bool refresh) KjNode* showChangesP = NULL; KjNode* sysAttrsP = NULL; RenderFormat renderFormat = RF_NORMALIZED; + double timeInterval = 0; + + kjTreeLog(dbSubP, "dbSubP", LmtPernot); KjNode* apiSubP = dbModelToApiSubscription(dbSubP, tenantP->tenant, true, @@ -132,17 +136,25 @@ bool mongocSubCachePopulateByTenant(OrionldTenant* tenantP, bool refresh) &contextNodeP, &showChangesP, &sysAttrsP, - &renderFormat); + &renderFormat, + &timeInterval); if (apiSubP == NULL) continue; + kjTreeLog(apiSubP, "apiSubP", LmtPernot); + OrionldContext* contextP = NULL; if (contextNodeP != NULL) contextP = orionldContextFromUrl(contextNodeP->value.s, NULL); - CachedSubscription* cSubP = subCacheApiSubscriptionInsert(apiSubP, qTree, coordinatesP, contextP, tenantP->tenant, showChangesP, sysAttrsP, renderFormat); - cSubP->inDB = true; + if (timeInterval == 0) + { + CachedSubscription* cSubP = subCacheApiSubscriptionInsert(apiSubP, qTree, coordinatesP, contextP, tenantP->tenant, showChangesP, sysAttrsP, renderFormat); + cSubP->inDB = true; + } + else + pernotSubCacheAdd(NULL, apiSubP, NULL, qTree, coordinatesP, contextP, tenantP, showChangesP, sysAttrsP, renderFormat, timeInterval); } if (refresh == true) diff --git a/src/lib/orionld/mongoc/mongocSubCountersUpdate.cpp b/src/lib/orionld/mongoc/mongocSubCountersUpdate.cpp index b3b40783ba..a723d75cbe 100644 --- a/src/lib/orionld/mongoc/mongocSubCountersUpdate.cpp +++ b/src/lib/orionld/mongoc/mongocSubCountersUpdate.cpp @@ -31,6 +31,7 @@ #include "orionld/common/orionldState.h" // orionldState #include "orionld/common/tenantList.h" // tenant0 #include "orionld/common/orionldTenantLookup.h" // orionldTenantLookup +#include "orionld/types/OrionldTenant.h" // OrionldTenant #include "orionld/mongoc/mongocWriteLog.h" // MONGOC_WLOG #include "orionld/mongoc/mongocSubCountersUpdate.h" // Own interface @@ -59,58 +60,74 @@ // void mongocSubCountersUpdate ( - const char* tenant, - CachedSubscription* cSubP, - int deltaCount, + OrionldTenant* tenantP, + const char* subscriptionId, + bool ngsild, + int32_t deltaAttempts, + int32_t deltaFailures, + int32_t deltaNoMatch, double lastNotificationTime, - double lastFailure, double lastSuccess, - bool forcedToPause, - bool ngsild + double lastFailure, + bool forcedToPause ) { - OrionldTenant* tenantP = orionldTenantLookup(tenant); + if (tenantP == NULL) + tenantP = &tenant0; + mongoc_client_t* connectionP = mongoc_client_pool_pop(mongocPool); mongoc_collection_t* subscriptionsP = mongoc_client_get_collection(connectionP, tenantP->mongoDbName, "csubs"); - bson_t request; // Entire request with count and timestamps to be updated + bson_t request; // Entire request with count and timestamps to be updated bson_t reply; - bson_t count; bson_t max; bson_t selector; + bson_t inc; bson_init(&reply); bson_init(&selector); - bson_init(&count); + bson_init(&inc); bson_init(&max); bson_init(&request); // Selector - The _id is an OID if not NGSI-LD - if (cSubP->ldContext != "") - bson_append_utf8(&selector, "_id", 3, cSubP->subscriptionId, -1); + if (ngsild == true) + bson_append_utf8(&selector, "_id", 3, subscriptionId, -1); else { bson_oid_t oid; - bson_oid_init_from_string(&oid, cSubP->subscriptionId); + bson_oid_init_from_string(&oid, subscriptionId); bson_append_oid(&selector, "_id", 3, &oid); } - // Count - if (deltaCount > 0) - bson_append_int64(&count, "count", 5, deltaCount); + // Attempts (timesSent) + if (deltaAttempts > 0) + bson_append_int32(&inc, "count", 5, deltaAttempts); + + // deltaFailures (timesFailed) + if (deltaFailures > 0) + bson_append_int32(&inc, "timesFailed", 11, deltaFailures); + + // deltaNoMatch (noMatch) + if (deltaNoMatch > 0) + bson_append_int32(&inc, "noMatch", 7, deltaNoMatch); + + if (deltaAttempts + deltaFailures + deltaNoMatch > 0) + bson_append_document(&request, "$inc", 4, &inc); + // Timestamps if (lastNotificationTime > 0) bson_append_double(&max, "lastNotification", 16, lastNotificationTime); if (lastSuccess > 0) bson_append_double(&max, "lastSuccess", 11, lastSuccess); if (lastFailure > 0) bson_append_double(&max, "lastFailure", 11, lastFailure); - if (deltaCount > 0) - bson_append_document(&request, "$inc", 4, &count); - LM_T(LmtNotificationStats, ("%s: lastNotificationTime: %f", cSubP->subscriptionId, lastNotificationTime)); - LM_T(LmtNotificationStats, ("%s: lastSuccess: %f", cSubP->subscriptionId, lastSuccess)); - LM_T(LmtNotificationStats, ("%s: lastFailure: %f", cSubP->subscriptionId, lastFailure)); - LM_T(LmtNotificationStats, ("%s: deltaCount: %d", cSubP->subscriptionId, deltaCount)); - LM_T(LmtNotificationStats, ("%s: forcedToPause: %s", cSubP->subscriptionId, (forcedToPause == true)? "TRUE" : "FALSE")); + LM_T(LmtNotificationStats, ("%s: lastNotificationTime: %f", subscriptionId, lastNotificationTime)); + LM_T(LmtNotificationStats, ("%s: lastSuccess: %f", subscriptionId, lastSuccess)); + LM_T(LmtNotificationStats, ("%s: lastFailure: %f", subscriptionId, lastFailure)); + LM_T(LmtNotificationStats, ("%s: deltaAttempts: %d", subscriptionId, deltaAttempts)); + LM_T(LmtNotificationStats, ("%s: deltaFailures: %d", subscriptionId, deltaFailures)); + LM_T(LmtNotificationStats, ("%s: deltaNoMatch: %d", subscriptionId, deltaNoMatch)); + LM_T(LmtNotificationStats, ("%s: forcedToPause: %s", subscriptionId, (forcedToPause == true)? "TRUE" : "FALSE")); if (forcedToPause == true) { @@ -128,16 +145,16 @@ void mongocSubCountersUpdate bool b = mongoc_collection_update_one(subscriptionsP, &selector, &request, NULL, &reply, &bError); if (b == false) - LM_E(("mongoc error updating subscription counters/timestamps for '%s': [%d.%d]: %s", cSubP->subscriptionId, bError.domain, bError.code, bError.message)); + LM_E(("mongoc error updating subscription counters/timestamps for '%s': [%d.%d]: %s", subscriptionId, bError.domain, bError.code, bError.message)); else - LM_T(LmtNotificationStats, ("%s: Successfully updated sub-counters/timestamps", cSubP->subscriptionId)); + LM_T(LmtNotificationStats, ("%s: Successfully updated sub-counters/timestamps", subscriptionId)); mongoc_client_pool_push(mongocPool, connectionP); mongoc_collection_destroy(subscriptionsP); bson_destroy(&reply); bson_destroy(&selector); - bson_destroy(&count); + bson_destroy(&inc); bson_destroy(&max); bson_destroy(&request); } diff --git a/src/lib/orionld/mongoc/mongocSubCountersUpdate.h b/src/lib/orionld/mongoc/mongocSubCountersUpdate.h index 952b581098..ef76e604db 100644 --- a/src/lib/orionld/mongoc/mongocSubCountersUpdate.h +++ b/src/lib/orionld/mongoc/mongocSubCountersUpdate.h @@ -25,7 +25,7 @@ * * Author: Ken Zangelin */ -#include "cache/subCache.h" // CachedSubscription +#include "orionld/types/OrionldTenant.h" // OrionldTenant @@ -35,14 +35,16 @@ // extern void mongocSubCountersUpdate ( - const char* tenant, - CachedSubscription* cSubP, - int deltaCount, + OrionldTenant* tenantP, + const char* subscriptionId, + bool ngsild, + int32_t deltaAttempts, + int32_t deltaFailures, + int32_t deltaNoMatch, double lastNotificationTime, - double lastFailure, double lastSuccess, - bool forcedToPause, - bool ngsild + double lastFailure, + bool forcedToPause ); #endif // SRC_LIB_ORIONLD_MONGOC_MONGOCSUBCOUNTERSUPDATE_H_ diff --git a/src/lib/orionld/notifications/httpNotify.cpp b/src/lib/orionld/notifications/httpNotify.cpp index 325a59cc9e..2cc90da274 100644 --- a/src/lib/orionld/notifications/httpNotify.cpp +++ b/src/lib/orionld/notifications/httpNotify.cpp @@ -37,30 +37,41 @@ // // httpNotify - send a notification over http // -int httpNotify(CachedSubscription* cSubP, struct iovec* ioVec, int ioVecLen, double notificationTime) +int httpNotify +( + CachedSubscription* cSubP, + PernotSubscription* pSubP, + const char* subscriptionId, + const char* ip, + unsigned short port, + const char* path, + struct iovec* ioVec, + int ioVecLen, + double notificationTime +) { // Connect - LM_T(LmtNotificationSend, ("%s: Connecting to notification '%s:%d' receptor for '%s' notification", cSubP->subscriptionId, cSubP->ip, cSubP->port, cSubP->protocolString)); - int fd = orionldServerConnect(cSubP->ip, cSubP->port); + LM_T(LmtNotificationSend, ("%s: Connecting to notification '%s:%d' receptor for HTTP notification", subscriptionId, ip, port)); + int fd = orionldServerConnect(ip, port); if (fd == -1) { - LM_E(("Internal Error (unable to connect to server for notification for subscription '%s': %s)", cSubP->subscriptionId, strerror(errno))); - notificationFailure(cSubP, "Unable to connect to notification endpoint", notificationTime); + LM_E(("Internal Error (unable to connect to server for notification for subscription '%s': %s)", subscriptionId, strerror(errno))); + notificationFailure(cSubP, pSubP, "Unable to connect to notification endpoint", notificationTime); return -1; } - LM_T(LmtNotificationSend, ("%s: Connected to notification receptor '%s:%d' on fd %d", cSubP->subscriptionId, cSubP->ip, cSubP->port, fd)); + LM_T(LmtNotificationSend, ("%s: Connected to notification receptor '%s:%d' on fd %d", subscriptionId, ip, port, fd)); if (lmTraceIsSet(LmtNotificationHeaders) == true) { for (int ix = 0; ix < ioVecLen - 1; ix++) { - LM_T(LmtNotificationHeaders, ("%s: Notification Request Header: '%s'", cSubP->subscriptionId, ioVec[ix].iov_base)); + LM_T(LmtNotificationHeaders, ("%s: Notification Request Header: '%s'", subscriptionId, ioVec[ix].iov_base)); } } - LM_T(LmtNotificationBody, ("%s: Notification Request Body: %s", cSubP->subscriptionId, ioVec[ioVecLen - 1].iov_base)); + LM_T(LmtNotificationBody, ("%s: Notification Request Body: %s", subscriptionId, ioVec[ioVecLen - 1].iov_base)); // Send int nb; @@ -68,12 +79,12 @@ int httpNotify(CachedSubscription* cSubP, struct iovec* ioVec, int ioVecLen, dou { close(fd); - LM_E(("Internal Error (unable to send to server for notification for subscription '%s' (fd: %d): %s", cSubP->subscriptionId, fd, strerror(errno))); - notificationFailure(cSubP, "Unable to write to notification endpoint", notificationTime); + LM_E(("Internal Error (unable to send to server for notification for subscription '%s' (fd: %d): %s", subscriptionId, fd, strerror(errno))); + notificationFailure(cSubP, pSubP, "Unable to write to notification endpoint", notificationTime); return -1; } - LM_T(LmtNotificationSend, ("%s: Written %d bytes to fd %d of %s:%d", cSubP->subscriptionId, nb, fd, cSubP->ip, cSubP->port)); + LM_T(LmtNotificationSend, ("%s: Written %d bytes to fd %d of %s:%d", subscriptionId, nb, fd, ip, port)); return fd; } diff --git a/src/lib/orionld/notifications/httpNotify.h b/src/lib/orionld/notifications/httpNotify.h index 30b88f4d5f..f5aa0ba51d 100644 --- a/src/lib/orionld/notifications/httpNotify.h +++ b/src/lib/orionld/notifications/httpNotify.h @@ -28,6 +28,7 @@ #include // iovec #include "cache/CachedSubscription.h" // CachedSubscription +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription @@ -35,6 +36,17 @@ // // httpNotify - send a notification over http // -extern int httpNotify(CachedSubscription* cSubP, struct iovec* ioVec, int ioVecLen, double timestamp); +extern int httpNotify +( + CachedSubscription* cSubP, + PernotSubscription* pSubP, + const char* subscriptionId, + const char* ip, + unsigned short port, + const char* path, + struct iovec* ioVec, + int ioVecLen, + double notificationTime +); #endif // SRC_LIB_ORIONLD_NOTIFICATIONS_HTTPNOTIFY_H_ diff --git a/src/lib/orionld/notifications/notificationFailure.cpp b/src/lib/orionld/notifications/notificationFailure.cpp index 3256a0100a..ab350d6e7b 100644 --- a/src/lib/orionld/notifications/notificationFailure.cpp +++ b/src/lib/orionld/notifications/notificationFailure.cpp @@ -48,6 +48,7 @@ void notificationFailure(CachedSubscription* subP, const char* errorReason, doub subP->lastFailure = notificationTime; subP->consecutiveErrors += 1; subP->count += 1; + subP->failures += 1; subP->dirty += 1; strncpy(subP->lastErrorReason, errorReason, sizeof(subP->lastErrorReason) - 1); @@ -74,16 +75,106 @@ void notificationFailure(CachedSubscription* subP, const char* errorReason, doub // if (((cSubCounters != 0) && (subP->dirty >= cSubCounters)) || (forcedToPause == true)) { - LM_T(LmtNotificationStats, ("%s: Calling mongocSubCountersUpdate", subP->subscriptionId)); - mongocSubCountersUpdate(subP->tenant, subP, subP->count, subP->lastNotificationTime, subP->lastFailure, subP->lastSuccess, forcedToPause, true); - subP->dirty = 0; - subP->dbCount += subP->count; - subP->count = 0; + mongocSubCountersUpdate(subP->tenantP, + subP->subscriptionId, + (subP->ldContext != ""), + subP->count, + subP->failures, + 0, + subP->lastNotificationTime, + subP->lastSuccess, + subP->lastFailure, + forcedToPause); + subP->dirty = 0; + subP->dbCount += subP->count; + subP->count = 0; + subP->dbFailures += subP->failures; + subP->failures = 0; + } +} + + + +// ----------------------------------------------------------------------------- +// +// notificationFailure - +// +void notificationFailure(PernotSubscription* pSubP, const char* errorReason, double notificationTime) +{ + LM_T(LmtNotificationStats, ("%s: notification failure (timestamp: %f)", pSubP->subscriptionId, notificationTime)); + bool forcedToPause = false; + + pSubP->lastNotificationTime = notificationTime; + pSubP->lastFailureTime = notificationTime; + pSubP->consecutiveErrors += 1; + pSubP->notificationAttempts += 1; + pSubP->dirty += 1; + + strncpy(pSubP->lastErrorReason, errorReason, sizeof(pSubP->lastErrorReason) - 1); + + // Force the subscription into "paused" due to too many consecutive errors + if (pSubP->consecutiveErrors >= 3) + { + LM_T(LmtNotificationStats, ("%s: force the subscription into PAUSE due to 3 consecutive errors", pSubP->subscriptionId)); + pSubP->isActive = false; + pSubP->state = SubPaused; + forcedToPause = true; + } + + promCounterIncrease(promNotifications); + promCounterIncrease(promNotificationsFailed); + + LM_T(LmtNotificationStats, ("%s: dirty: %d, cSubCounters: %d", pSubP->subscriptionId, pSubP->dirty, cSubCounters)); + + // + // Flush to DB? + // - If forcedToPause + // - If pSubP->dirty (number of counter updates since last flush) >= cSubCounters + // - AND cSubCounters != 0 + // + if (((cSubCounters != 0) && (pSubP->dirty >= cSubCounters)) || (forcedToPause == true)) + { + LM_T(LmtNotificationStats, ("%s: Calling mongocSubCountersUpdate", pSubP->subscriptionId)); + + // Save to database + mongocSubCountersUpdate(pSubP->tenantP, + pSubP->subscriptionId, + true, + pSubP->notificationAttempts, + pSubP->notificationErrors, + pSubP->noMatch, + pSubP->lastNotificationTime, + pSubP->lastSuccessTime, + pSubP->lastFailureTime, + forcedToPause); + + // Reset counters + pSubP->dirty = 0; + pSubP->notificationAttemptsDb += pSubP->notificationAttempts; + pSubP->notificationAttempts = 0; + pSubP->notificationErrorsDb += pSubP->notificationErrors; + pSubP->notificationErrors = 0; + pSubP->noMatchDb += pSubP->noMatch; + pSubP->noMatch = 0; } else LM_T(LmtNotificationStats, ("%s: Not calling mongocSubCountersUpdate (cSubCounters: %d, dirty: %d, forcedToPause: %s)", - subP->subscriptionId, + pSubP->subscriptionId, cSubCounters, - subP->dirty, + pSubP->dirty, (forcedToPause == true)? "true" : "false")); } + + + +// ----------------------------------------------------------------------------- +// +// notificationFailure - +// +void notificationFailure(CachedSubscription* cSubP, PernotSubscription* pSubP, const char* errorReason, double notificationTime) +{ + if (cSubP != NULL) + notificationFailure(cSubP, errorReason, notificationTime); + else + notificationFailure(pSubP, errorReason, notificationTime); +} diff --git a/src/lib/orionld/notifications/notificationFailure.h b/src/lib/orionld/notifications/notificationFailure.h index 462707e579..2a5935634c 100644 --- a/src/lib/orionld/notifications/notificationFailure.h +++ b/src/lib/orionld/notifications/notificationFailure.h @@ -26,6 +26,7 @@ * Author: Ken Zangelin */ #include "cache/CachedSubscription.h" // CachedSubscription +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription @@ -33,6 +34,8 @@ // // notificationFailure - // -extern void notificationFailure(CachedSubscription* subP, const char* errorReason, double timestamp); +extern void notificationFailure(CachedSubscription* cSubP, const char* errorReason, double timestamp); +extern void notificationFailure(PernotSubscription* pSubP, const char* errorReason, double timestamp); +extern void notificationFailure(CachedSubscription* cSubP, PernotSubscription* pSubP, const char* errorReason, double timestamp); #endif // SRC_LIB_ORIONLD_NOTIFICATIONS_NOTIFICATIONFAILURE_H_ diff --git a/src/lib/orionld/notifications/notificationSend.cpp b/src/lib/orionld/notifications/notificationSend.cpp index f75f10fa60..65d0216700 100644 --- a/src/lib/orionld/notifications/notificationSend.cpp +++ b/src/lib/orionld/notifications/notificationSend.cpp @@ -66,19 +66,19 @@ extern "C" // ----------------------------------------------------------------------------- // -// Fixed value headers +// Fixed value headers - mocve to separate file - also used in pernot/pernotSend.cpp // -static const char* contentTypeHeaderJson = (char*) "Content-Type: application/json\r\n"; -static const char* contentTypeHeaderJsonLd = (char*) "Content-Type: application/ld+json\r\n"; -static const char* contentTypeHeaderGeoJson = (char*) "Content-Type: application/geo+json\r\n"; -static const char* acceptHeader = (char*) "Accept: application/json\r\n"; +const char* contentTypeHeaderJson = (char*) "Content-Type: application/json\r\n"; +const char* contentTypeHeaderJsonLd = (char*) "Content-Type: application/ld+json\r\n"; +const char* contentTypeHeaderGeoJson = (char*) "Content-Type: application/geo+json\r\n"; +const char* acceptHeader = (char*) "Accept: application/json\r\n"; -static const char* normalizedHeader = (char*) "Ngsild-Attribute-Format: Normalized\r\n"; -static const char* conciseHeader = (char*) "Ngsild-Attribute-Format: Concise\r\n"; -static const char* simplifiedHeader = (char*) "Ngsild-Attribute-Format: Simplified\r\n"; +const char* normalizedHeader = (char*) "Ngsild-Attribute-Format: Normalized\r\n"; +const char* conciseHeader = (char*) "Ngsild-Attribute-Format: Concise\r\n"; +const char* simplifiedHeader = (char*) "Ngsild-Attribute-Format: Simplified\r\n"; -static const char* normalizedHeaderNgsiV2 = (char*) "Ngsiv2-Attrsformat: normalized\r\n"; -static const char* keyValuesHeaderNgsiV2 = (char*) "Ngsiv2-Attrsformat: keyValues\r\n"; +const char* normalizedHeaderNgsiV2 = (char*) "Ngsiv2-Attrsformat: normalized\r\n"; +const char* keyValuesHeaderNgsiV2 = (char*) "Ngsiv2-Attrsformat: keyValues\r\n"; char userAgentHeader[64]; // "User-Agent: orionld/" + ORIONLD_VERSION + \r\n" - initialized in orionldServiceInit() size_t userAgentHeaderLen = 0; // Set in orionldServiceInit() @@ -495,7 +495,7 @@ static KjNode* attributeFilter(KjNode* apiEntityP, OrionldAlterationMatch* mAltP // // notificationTreeForNgsiV2 - // -KjNode* notificationTreeForNgsiV2(OrionldAlterationMatch* matchP) +static KjNode* notificationTreeForNgsiV2(OrionldAlterationMatch* matchP) { CachedSubscription* subP = matchP->subP; KjNode* notificationP = kjObject(orionldState.kjsonP, NULL); @@ -509,7 +509,7 @@ KjNode* notificationTreeForNgsiV2(OrionldAlterationMatch* matchP) // KjNode* apiEntityP = matchP->altP->finalApiEntityP; // This is not correct - can be more than one entity - if (matchP->subP->attributes.size() > 0) + if (subP->attributes.size() > 0) apiEntityP = attributeFilter(apiEntityP, matchP); if ((subP->renderFormat == RF_CROSS_APIS_KEYVALUES) || (subP->renderFormat == RF_CROSS_APIS_KEYVALUES_COMPACT)) @@ -533,7 +533,7 @@ KjNode* notificationTreeForNgsiV2(OrionldAlterationMatch* matchP) // // notificationTree - // -KjNode* notificationTree(OrionldAlterationMatch* matchList) +static KjNode* notificationTree(OrionldAlterationMatch* matchList) { CachedSubscription* subP = matchList->subP; KjNode* notificationP = kjObject(orionldState.kjsonP, NULL); @@ -554,6 +554,15 @@ KjNode* notificationTree(OrionldAlterationMatch* matchList) kjChildAdd(notificationP, notifiedAtNodeP); kjChildAdd(notificationP, dataNodeP); + // Reason for the notification + if (triggerOperation == true) + { + char trigger[128]; + snprintf(trigger, sizeof(trigger) - 1, "%s %s", orionldState.verbString, orionldState.urlPath); + KjNode* triggerP = kjString(orionldState.kjsonP, "trigger", trigger); + kjChildAdd(notificationP, triggerP); + } + for (OrionldAlterationMatch* matchP = matchList; matchP != NULL; matchP = matchP->next) { KjNode* apiEntityP = (subP->sysAttrs == false)? matchP->altP->finalApiEntityP : matchP->altP->finalApiEntityWithSysAttrsP; @@ -612,6 +621,19 @@ KjNode* notificationTree(OrionldAlterationMatch* matchList) // size_t iov_len; /* Number of bytes to transfer */ // }; // +// To adapt notificationSend to pernot, I need: +// - mAltP->subP->renderFormat (easy) +// - mAltP->subP->subscriptionId (notificationTreeForNgsiV2 - easy) +// - finalApiEntityP (notificationTreeForNgsiV2 - easy - that's the output of the query for Pernot) +// - subP->attributes (notificationTreeForNgsiV2 - easy) +// - subP->contextP (notificationTreeForNgsiV2 - easy) +// - finalApiEntityWithSysAttrsP (notificationTree - no probs, must add sysAttrs to the query if Pern ot sub has sysAttrs set) +// - subP->httpInfo.mimeType (notificationTree - easy) +// - subP->ldContext (notificationSend) +// - subP->httpInfo.notifierInfo (notificationSend) +// - mAltP->subP->rest +// +// int notificationSend(OrionldAlterationMatch* mAltP, double timestamp, CURL** curlHandlePP) { bool ngsiv2 = (mAltP->subP->renderFormat >= RF_CROSS_APIS_NORMALIZED); @@ -884,7 +906,16 @@ int notificationSend(OrionldAlterationMatch* mAltP, double timestamp, CURL** cur // // The message is ready - just need to be sent // - if (mAltP->subP->protocol == HTTP) return httpNotify(mAltP->subP, ioVec, ioVecLen, timestamp); + if (mAltP->subP->protocol == HTTP) + return httpNotify(mAltP->subP, + NULL, + mAltP->subP->subscriptionId, + mAltP->subP->ip, + mAltP->subP->port, + mAltP->subP->rest, + ioVec, + ioVecLen, + timestamp); else if (mAltP->subP->protocol == HTTPS) return httpsNotify(mAltP->subP, ioVec, ioVecLen, timestamp, curlHandlePP); else if (mAltP->subP->protocol == MQTT) return mqttNotify(mAltP->subP, ioVec, ioVecLen, timestamp); diff --git a/src/lib/orionld/notifications/notificationSuccess.cpp b/src/lib/orionld/notifications/notificationSuccess.cpp index f03e57fd7e..4ba002f3d6 100644 --- a/src/lib/orionld/notifications/notificationSuccess.cpp +++ b/src/lib/orionld/notifications/notificationSuccess.cpp @@ -60,10 +60,13 @@ void notificationSuccess(CachedSubscription* subP, const double timestamp) if ((cSubCounters != 0) && (subP->dirty >= cSubCounters)) { LM_T(LmtNotificationStats, ("%s: Calling mongocSubCountersUpdate", subP->subscriptionId)); - mongocSubCountersUpdate(subP->tenant, subP, subP->count, subP->lastNotificationTime, subP->lastFailure, subP->lastSuccess, false, true); - subP->dirty = 0; - subP->dbCount += subP->count; - subP->count = 0; + + mongocSubCountersUpdate(subP->tenantP, subP->subscriptionId, (subP->ldContext != ""), subP->count, subP->failures, 0, subP->lastNotificationTime, subP->lastSuccess, subP->lastFailure, false); + subP->dirty = 0; + subP->dbCount += subP->count; + subP->count = 0; + subP->dbFailures += subP->failures; + subP->failures = 0; } else LM_T(LmtNotificationStats, ("%s: Not calling mongocSubCountersUpdate (cSubCounters: %d, dirty: %d)", diff --git a/src/lib/orionld/notifications/subCacheAlterationMatch.cpp b/src/lib/orionld/notifications/subCacheAlterationMatch.cpp index 2677ccab6f..87a067c6e8 100644 --- a/src/lib/orionld/notifications/subCacheAlterationMatch.cpp +++ b/src/lib/orionld/notifications/subCacheAlterationMatch.cpp @@ -91,9 +91,13 @@ static bool entityTypeMatch(CachedSubscription* subP, const char* entityType, in { for (int ix = 0; ix < eItems; ++ix) { - EntityInfo* eiP = subP->entityIdInfos[ix]; + EntityInfo* eiP = subP->entityIdInfos[ix]; + const char* eType = eiP->entityType.c_str(); + + if (strcmp(entityType, eType) == 0) + return true; - if (strcmp(entityType, eiP->entityType.c_str()) == 0) + if ((eType[0] == '*') && (eType[1] == 0)) return true; } @@ -1157,7 +1161,7 @@ OrionldAlterationMatch* subCacheAlterationMatch(OrionldAlteration* alterationLis // Only done if its an NGSI-LD operation AND if it's an NGSI-LD Subscription (ldContext has a value != "") // if ((subP->qP == NULL) && (subP->ldContext != "") && (subP->qText != NULL)) - subP->qP = qBuild(subP->qText, NULL, NULL, NULL, false); + subP->qP = qBuild(subP->qText, NULL, NULL, NULL, false, false); if (subP->qP != NULL) { diff --git a/src/lib/orionld/payloadCheck/PCHECK.h b/src/lib/orionld/payloadCheck/PCHECK.h index 6a04fe984e..3607a3c374 100644 --- a/src/lib/orionld/payloadCheck/PCHECK.h +++ b/src/lib/orionld/payloadCheck/PCHECK.h @@ -96,6 +96,25 @@ do +// ----------------------------------------------------------------------------- +// +// PCHECK_INTEGER - +// +#define PCHECK_INTEGER(kNodeP, _type, _title, detail, status) \ +do \ +{ \ + if (kNodeP->type != KjInt) \ + { \ + int type = (_type == 0)? OrionldBadRequestData : _type; \ + const char* title = (_title == NULL)? "Not a JSON Integer" : _title; \ + \ + orionldError((OrionldResponseErrorType) type, title, detail, status); \ + return false; \ + } \ +} while (0) + + + // ----------------------------------------------------------------------------- // // PCHECK_STRING_OR_ARRAY - @@ -168,6 +187,34 @@ do +// ----------------------------------------------------------------------------- +// +// PCHECK_NUMBER_GT - +// +#define PCHECK_NUMBER_GT(kNodeP, _type, _title, detail, status, minValue) \ +do \ +{ \ + if ((kNodeP->type == KjInt) && (kNodeP->value.i <= minValue)) \ + { \ + int type = (_type == 0)? OrionldBadRequestData : _type; \ + const char* title = (_title == NULL)? "Too small a Number" : _title; \ + \ + orionldError((OrionldResponseErrorType) type, title, detail, status); \ + return false; \ + } \ + \ + if ((kNodeP->type == KjFloat) && (kNodeP->value.f <= minValue)) \ + { \ + int type = (_type == 0)? OrionldBadRequestData : _type; \ + const char* title = (_title == NULL)? "Too small a Number" : _title; \ + \ + orionldError((OrionldResponseErrorType) type, title, detail, status); \ + return false; \ + } \ +} while (0) + + + // ----------------------------------------------------------------------------- // // PCHECK_OBJECT - diff --git a/src/lib/orionld/payloadCheck/fieldPaths.cpp b/src/lib/orionld/payloadCheck/fieldPaths.cpp index 98d0771f24..a9849a278b 100644 --- a/src/lib/orionld/payloadCheck/fieldPaths.cpp +++ b/src/lib/orionld/payloadCheck/fieldPaths.cpp @@ -40,6 +40,7 @@ const char* SubscriptionEntitiesIdPatternPath = "Subscription::entities const char* SubscriptionEntitiesTypePath = "Subscription::entities::type"; const char* SubscriptionWatchedAttributesPath = "Subscription::watchedAttributes"; const char* SubscriptionWatchedAttributesItemPath = "Subscription::watchedAttributes[X]"; +const char* SubscriptionTimeIntervalPath = "Subscription::timeInterval"; const char* SubscriptionNotificationPath = "Subscription::notification"; const char* SubscriptionNotificationFormatPath = "Subscription::notification::format"; const char* SubscriptionNotificationAttributesPath = "Subscription::notification::attributes"; diff --git a/src/lib/orionld/payloadCheck/fieldPaths.h b/src/lib/orionld/payloadCheck/fieldPaths.h index 274502887a..777f7f9e5b 100644 --- a/src/lib/orionld/payloadCheck/fieldPaths.h +++ b/src/lib/orionld/payloadCheck/fieldPaths.h @@ -47,6 +47,7 @@ extern const char* SubscriptionNotificationShowChangesPath; extern const char* SubscriptionNotificationSysAttrsPath; extern const char* SubscriptionWatchedAttributesPath; extern const char* SubscriptionWatchedAttributesItemPath; +extern const char* SubscriptionTimeIntervalPath; extern const char* SubscriptionQPath; extern const char* SubscriptionGeoqPath; extern const char* SubscriptionIsActivePath; diff --git a/src/lib/orionld/payloadCheck/pCheckNotification.cpp b/src/lib/orionld/payloadCheck/pCheckNotification.cpp index f5bf56aa52..a258fdcdd8 100644 --- a/src/lib/orionld/payloadCheck/pCheckNotification.cpp +++ b/src/lib/orionld/payloadCheck/pCheckNotification.cpp @@ -25,6 +25,7 @@ extern "C" { #include "kjson/KjNode.h" // KjNode +#include "kjson/kjBuilder.h" // kjChildRemove } #include "logMsg/logMsg.h" // LM_* @@ -65,8 +66,12 @@ bool pCheckNotification PCHECK_OBJECT(notificationP, 0, NULL, SubscriptionNotificationPath, 400); PCHECK_OBJECT_EMPTY(notificationP, 0, NULL, SubscriptionNotificationPath, 400); - for (KjNode* nItemP = notificationP->value.firstChildP; nItemP != NULL; nItemP = nItemP->next) + KjNode* nItemP = notificationP->value.firstChildP; + KjNode* next; + while (nItemP != NULL) { + next = nItemP->next; + if (strcmp(nItemP->name, "attributes") == 0) { PCHECK_DUPLICATE(attributesP, nItemP, 0, NULL, SubscriptionNotificationAttributesPath, 400); @@ -115,16 +120,22 @@ bool pCheckNotification *sysAttrsOutP = sysAttrsP; LM_T(LmtSysAttrs, ("Found a 'sysAttrs' in Subscription::notification (%s)", (sysAttrsP->value.b == true)? "true" : "false")); } - else if (strcmp(nItemP->name, "status") == 0) {} // Ignored - else if (strcmp(nItemP->name, "timesSent") == 0) {} // Ignored - else if (strcmp(nItemP->name, "lastNotification") == 0) {} // Ignored - else if (strcmp(nItemP->name, "lastSuccess") == 0) {} // Ignored - else if (strcmp(nItemP->name, "lastFailure") == 0) {} // Ignored + else if ((strcmp(nItemP->name, "status") == 0) || + (strcmp(nItemP->name, "timesSent") == 0) || + (strcmp(nItemP->name, "timesFailed") == 0) || + (strcmp(nItemP->name, "lastNotification") == 0) || + (strcmp(nItemP->name, "lastSuccess") == 0) || + (strcmp(nItemP->name, "lastFailure") == 0)) + { + kjChildRemove(notificationP, nItemP); + } else { orionldError(OrionldBadRequestData, "Invalid field for Subscription::notification", nItemP->name, 400); return false; } + + nItemP = next; } if ((endpointP == NULL) && (patch == false)) diff --git a/src/lib/orionld/payloadCheck/pCheckQuery.cpp b/src/lib/orionld/payloadCheck/pCheckQuery.cpp index a3fd3ff411..894601cff7 100644 --- a/src/lib/orionld/payloadCheck/pCheckQuery.cpp +++ b/src/lib/orionld/payloadCheck/pCheckQuery.cpp @@ -269,7 +269,7 @@ TreeNode* pCheckQuery(KjNode* queryP) if ((treeNodeV[1].nodeP != NULL) && (pCheckEntities(treeNodeV[1].nodeP) == false)) return NULL; if ((treeNodeV[2].nodeP != NULL) && (pCheckAttrs(treeNodeV[2].nodeP) == false)) return NULL; if ((treeNodeV[3].nodeP != NULL) && ((treeNodeV[3].output = pcheckQ(treeNodeV[3].nodeP->value.s)) == NULL)) return NULL; - if ((treeNodeV[4].nodeP != NULL) && ((treeNodeV[4].output = pcheckGeoQ(treeNodeV[4].nodeP, false)) == NULL)) return NULL; + if ((treeNodeV[4].nodeP != NULL) && ((treeNodeV[4].output = pcheckGeoQ(&orionldState.kalloc, treeNodeV[4].nodeP, false)) == NULL)) return NULL; // // Now, for the query to not be too wide, we need at least one of: diff --git a/src/lib/orionld/payloadCheck/pCheckSubscription.cpp b/src/lib/orionld/payloadCheck/pCheckSubscription.cpp index e8809db8f5..ba10286057 100644 --- a/src/lib/orionld/payloadCheck/pCheckSubscription.cpp +++ b/src/lib/orionld/payloadCheck/pCheckSubscription.cpp @@ -27,6 +27,7 @@ extern "C" #include "kbase/kMacros.h" // K_FT #include "kjson/KjNode.h" // KjNode #include "kjson/kjBuilder.h" // kjChildRemove +#include "kjson/kjLookup.h" // kjLookup } #include "logMsg/logMsg.h" // LM_* @@ -114,6 +115,7 @@ bool pCheckSubscription bool* mqttChangeP, KjNode** showChangesP, KjNode** sysAttrsP, + double* timeInterval, RenderFormat* renderFormatP ) { @@ -130,11 +132,13 @@ bool pCheckSubscription KjNode* qP = NULL; KjNode* geoqP = NULL; KjNode* langP = NULL; + KjNode* timeIntervalP = NULL; double expiresAt; *mqttChangeP = NULL; *showChangesP = NULL; *sysAttrsP = NULL; + *timeInterval = 0; if (idP != NULL) { @@ -161,6 +165,27 @@ bool pCheckSubscription } } + // + // First we need to know whether it is a n ormal subscription or a periodic notification subscription (timeInterval) + // + bool pernot = false; + + KjNode* timeIntervalNodeP = kjLookup(subP, "timeInterval"); + if (timeIntervalNodeP != NULL) + { + PCHECK_NUMBER(timeIntervalNodeP, 0, NULL, SubscriptionTimeIntervalPath, 400); + PCHECK_NUMBER_GT(timeIntervalNodeP, 0, "Non-supported timeInterval (must be greater than zero)", SubscriptionTimeIntervalPath, 400, 0); + + pernot = true; + *timeInterval = (timeIntervalNodeP->type == KjInt)? timeIntervalNodeP->value.i : timeIntervalNodeP->value.f; + } + else + *timeInterval = 0; + + + // + // Now loop over the entiry payload + // KjNode* subItemP = subP->value.firstChildP; KjNode* next; while (subItemP != NULL) @@ -198,16 +223,13 @@ bool pCheckSubscription return false; } else if (strcmp(subItemP->name, "timeInterval") == 0) - { - orionldError(OrionldOperationNotSupported, "Not Implemented", "Time-Interval for Subscriptions", 501); - return false; - } + PCHECK_DUPLICATE(timeIntervalP, subItemP, 0, NULL, SubscriptionTimeIntervalPath, 400); else if (strcmp(subItemP->name, "q") == 0) { PCHECK_DUPLICATE(qP, subItemP, 0, NULL, SubscriptionQPath, 400); PCHECK_STRING(qP, 0, NULL, SubscriptionQPath, 400); - *qTreeP = qBuild(qP->value.s, qRenderedForDbP, qValidForV2P, qIsMqP, true); // 5th parameter: qToDbModel == true + *qTreeP = qBuild(qP->value.s, qRenderedForDbP, qValidForV2P, qIsMqP, true, pernot); // 5th parameter: qToDbModel == true *qNodeP = qP; if (*qTreeP == NULL) @@ -215,11 +237,17 @@ bool pCheckSubscription } else if (strcmp(subItemP->name, "geoQ") == 0) { + if (*timeInterval > 0) + { + orionldError(OrionldOperationNotSupported, "Not Implemented", "Geo-Query is not yet implemented for Periodic Notification Subscriptions", 501); + return false; + } + PCHECK_OBJECT(subItemP, 0, NULL, SubscriptionGeoqPath, 400); PCHECK_DUPLICATE(geoqP, subItemP, 0, NULL, SubscriptionGeoqPath, 400); OrionldGeoInfo* geoInfoP; - if ((geoInfoP = pcheckGeoQ(geoqP, true)) == NULL) + if ((geoInfoP = pcheckGeoQ(&orionldState.kalloc, geoqP, true)) == NULL) return false; *geoCoordinatesPP = geoInfoP->coordinates; @@ -252,20 +280,7 @@ bool pCheckSubscription { PCHECK_DUPLICATE(throttlingP, subItemP, 0, NULL, SubscriptionThrottlingPath, 400); PCHECK_NUMBER(throttlingP, 0, NULL, SubscriptionThrottlingPath, 400); - - // - // Can't be negative - // - if ((throttlingP->type == KjInt) && (throttlingP->value.i < 0)) - { - orionldError(OrionldBadRequestData, "Negative Number not allowed in this position", SubscriptionThrottlingPath, 400); - return false; - } - else if ((throttlingP->type == KjFloat) && (throttlingP->value.f < 0)) - { - orionldError(OrionldBadRequestData, "Negative Number not allowed in this position", SubscriptionThrottlingPath, 400); - return false; - } + PCHECK_NUMBER_GT(throttlingP, 0, "Negative Number not allowed in this position", SubscriptionThrottlingPath, 400, 0); } else if (strcmp(subItemP->name, "lang") == 0) { @@ -282,9 +297,14 @@ bool pCheckSubscription orionldError(OrionldOperationNotSupported, "Not Implemented", SubscriptionScopePath, 501); return false; } - else if (strcmp(subItemP->name, "status") == 0) { kjChildRemove(subP, subItemP); } // Silently REMOVED - else if (strcmp(subItemP->name, "createdAt") == 0) { kjChildRemove(subP, subItemP); } // Silently REMOVED - else if (strcmp(subItemP->name, "modifiedAt") == 0) { kjChildRemove(subP, subItemP); } // Silently REMOVED + else if (strcmp(subItemP->name, "status") == 0) { kjChildRemove(subP, subItemP); } // Silently REMOVED + else if (strcmp(subItemP->name, "createdAt") == 0) { kjChildRemove(subP, subItemP); } // Silently REMOVED + else if (strcmp(subItemP->name, "modifiedAt") == 0) { kjChildRemove(subP, subItemP); } // Silently REMOVED + else if (strcmp(subItemP->name, "notificationTrigger") == 0) + { + orionldError(OrionldOperationNotSupported, "Not Implemented", subItemP->name, 501); + return false; + } else { orionldError(OrionldBadRequestData, "Unknown field for subscription", subItemP->name, 400); @@ -308,10 +328,34 @@ bool pCheckSubscription return false; } - if ((entitiesP == NULL) && (watchedAttributesP == NULL)) + if (*timeInterval != 0) { - orionldError(OrionldBadRequestData, "Mandatory field missing", "At least one of 'entities' and 'watchedAttributes' must be present" , 400); - return false; + if (entitiesP == NULL) + { + orionldError(OrionldBadRequestData, "Mandatory field missing", "'entities' is mandatory for timeInterval subscriptions" , 400); + return false; + } + + // Make sure it is consistent + if (watchedAttributesP != NULL) + { + orionldError(OrionldBadRequestData, "Inconsistent subscription", "Both 'timeInterval' and 'watchedAttributes' are present", 400); + return false; + } + + if (throttlingP != NULL) + { + orionldError(OrionldBadRequestData, "Inconsistent subscription", "Both 'timeInterval' and 'throttling' are present", 400); + return false; + } + } + else + { + if ((entitiesP == NULL) && (watchedAttributesP == NULL)) + { + orionldError(OrionldBadRequestData, "Mandatory field missing", "At least one of 'entities' and 'watchedAttributes' must be present" , 400); + return false; + } } } diff --git a/src/lib/orionld/payloadCheck/pCheckSubscription.h b/src/lib/orionld/payloadCheck/pCheckSubscription.h index b8c3087640..c6c5f79329 100644 --- a/src/lib/orionld/payloadCheck/pCheckSubscription.h +++ b/src/lib/orionld/payloadCheck/pCheckSubscription.h @@ -58,6 +58,7 @@ extern bool pCheckSubscription bool* mqttChangeP, KjNode** showChangesP, KjNode** sysAttrsP, + double* timeInterval, RenderFormat* renderFormatP ); diff --git a/src/lib/orionld/payloadCheck/pcheckEntityInfo.cpp b/src/lib/orionld/payloadCheck/pcheckEntityInfo.cpp index f2345651e0..4276b7a372 100644 --- a/src/lib/orionld/payloadCheck/pcheckEntityInfo.cpp +++ b/src/lib/orionld/payloadCheck/pcheckEntityInfo.cpp @@ -115,8 +115,13 @@ bool pcheckEntityInfo(KjNode* entityInfoP, bool typeMandatory, bool idMandatory, // Expand, unless already expanded // If a ':' is found inside the first 10 chars, the value is assumed to be expanded ... // - if (orionldContextItemAlreadyExpanded(entityItemP->value.s) == false) - entityItemP->value.s = orionldContextItemExpand(orionldState.contextP, entityItemP->value.s, true, NULL); // entity type + // No expansion if the type is '*' - meaning "ALL ENTITY TYPES" + // + if ((entityItemP->value.s[0] != '*') || (entityItemP->value.s[1] != 0)) + { + if (orionldContextItemAlreadyExpanded(entityItemP->value.s) == false) + entityItemP->value.s = orionldContextItemExpand(orionldState.contextP, entityItemP->value.s, true, NULL); // entity type + } } else { diff --git a/src/lib/orionld/payloadCheck/pcheckGeoQ.cpp b/src/lib/orionld/payloadCheck/pcheckGeoQ.cpp index adb1ca6886..1db98f6aeb 100644 --- a/src/lib/orionld/payloadCheck/pcheckGeoQ.cpp +++ b/src/lib/orionld/payloadCheck/pcheckGeoQ.cpp @@ -25,6 +25,7 @@ #include // strcmp extern "C" { +#include "kalloc/KAlloc.h" // KAlloc #include "kjson/KjNode.h" // KjNode #include "kjson/kjParse.h" // kjParse } @@ -52,13 +53,13 @@ extern "C" // // pcheckGeoQ - // -OrionldGeoInfo* pcheckGeoQ(KjNode* geoqNodeP, bool isSubscription) +OrionldGeoInfo* pcheckGeoQ(KAlloc* kallocP, KjNode* geoqNodeP, bool isSubscription) { KjNode* geometryP = NULL; KjNode* coordinatesP = NULL; KjNode* georelP = NULL; KjNode* geopropertyP = NULL; - OrionldGeoInfo* geoInfoP = (OrionldGeoInfo*) kaAlloc(&orionldState.kalloc, sizeof(OrionldGeoInfo)); + OrionldGeoInfo* geoInfoP = (OrionldGeoInfo*) ((kallocP != NULL)? kaAlloc(kallocP, sizeof(OrionldGeoInfo)) : malloc(sizeof(OrionldGeoInfo))); if (geoInfoP == NULL) { diff --git a/src/lib/orionld/payloadCheck/pcheckGeoQ.h b/src/lib/orionld/payloadCheck/pcheckGeoQ.h index df282211ef..7f0b3dc89e 100644 --- a/src/lib/orionld/payloadCheck/pcheckGeoQ.h +++ b/src/lib/orionld/payloadCheck/pcheckGeoQ.h @@ -27,6 +27,7 @@ */ extern "C" { +#include "kalloc/KAlloc.h" // KAlloc #include "kjson/KjNode.h" // KjNode } @@ -38,6 +39,6 @@ extern "C" // // pcheckGeoQ - // -extern OrionldGeoInfo* pcheckGeoQ(KjNode* geoqNodeP, bool pCheckGeoGeometry); +extern OrionldGeoInfo* pcheckGeoQ(KAlloc* kallocP, KjNode* geoqNodeP, bool pCheckGeoGeometry); #endif // SRC_LIB_ORIONLD_PAYLOADCHECK_PCHECKGEOQ_H_ diff --git a/src/lib/orionld/payloadCheck/pcheckQ.cpp b/src/lib/orionld/payloadCheck/pcheckQ.cpp index 82678f5412..488bc71414 100644 --- a/src/lib/orionld/payloadCheck/pcheckQ.cpp +++ b/src/lib/orionld/payloadCheck/pcheckQ.cpp @@ -65,6 +65,6 @@ QNode* pcheckQ(char* qString) return NULL; } - qPresent(qTree, "QP", "pcheckQ"); + qPresent(qTree, "QP", "pcheckQ", LmtQ); return qTree; } diff --git a/src/lib/orionld/payloadCheck/pcheckQuery.cpp b/src/lib/orionld/payloadCheck/pcheckQuery.cpp index 893b42bff8..28e221c60f 100644 --- a/src/lib/orionld/payloadCheck/pcheckQuery.cpp +++ b/src/lib/orionld/payloadCheck/pcheckQuery.cpp @@ -200,7 +200,7 @@ bool pcheckQuery(KjNode* tree, KjNode** entitiesPP, KjNode** attrsPP, QNode** qT return false; if ((qP != NULL) && ((*qTreePP = pcheckQ(qP->value.s)) == NULL)) return false; - if ((geoqP != NULL) && (pcheckGeoQ(geoqP, false) == NULL)) + if ((geoqP != NULL) && (pcheckGeoQ(&orionldState.kalloc, geoqP, false) == NULL)) return false; return true; diff --git a/src/lib/orionld/payloadCheck/pcheckSubscription.cpp b/src/lib/orionld/payloadCheck/pcheckSubscription.cpp index 1197f01879..d34706d293 100644 --- a/src/lib/orionld/payloadCheck/pcheckSubscription.cpp +++ b/src/lib/orionld/payloadCheck/pcheckSubscription.cpp @@ -161,7 +161,7 @@ bool pcheckSubscription DUPLICATE_CHECK(geoqP, "geoQ", nodeP); OBJECT_CHECK(nodeP, "geoQ"); EMPTY_OBJECT_CHECK(nodeP, "geoQ"); - OrionldGeoInfo* geoInfoP = pcheckGeoQ(nodeP, true); + OrionldGeoInfo* geoInfoP = pcheckGeoQ(&orionldState.kalloc, nodeP, true); if (geoInfoP == NULL) return false; *geoCoordinatesPP = geoInfoP->coordinates; diff --git a/src/lib/orionld/pernot/CMakeLists.txt b/src/lib/orionld/pernot/CMakeLists.txt new file mode 100644 index 0000000000..f27a44126a --- /dev/null +++ b/src/lib/orionld/pernot/CMakeLists.txt @@ -0,0 +1,40 @@ +# 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 + pernotSubCacheInit.cpp + pernotSubCacheAdd.cpp + pernotSubCacheRemove.cpp + pernotSubCacheLookup.cpp + pernotLoop.cpp + pernotTreat.cpp + pernotSend.cpp +) + +# Include directories +# ----------------------------------------------------------------- +include_directories("${PROJECT_SOURCE_DIR}/src/lib") + + +# Library declaration +# ----------------------------------------------------------------- +ADD_LIBRARY(orionld_pernot STATIC ${SOURCES}) diff --git a/src/lib/orionld/pernot/PernotSubCache.cpp b/src/lib/orionld/pernot/PernotSubCache.cpp new file mode 100644 index 0000000000..6305c02276 --- /dev/null +++ b/src/lib/orionld/pernot/PernotSubCache.cpp @@ -0,0 +1,40 @@ +/* +* +* 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 "common/RenderFormat.h" // RenderFormat + +#include "orionld/q/QNode.h" // QNode +#include "orionld/context/OrionldContext.h" // OrionldContext +#include "orionld/pernot/PernotSubCache.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// pernotSubCacheRemove - +// +bool pernotSubCacheRemove(PernotSubscription* pSubP) +{ + return true; +} diff --git a/src/lib/orionld/pernot/PernotSubCache.h b/src/lib/orionld/pernot/PernotSubCache.h new file mode 100644 index 0000000000..ac359ca959 --- /dev/null +++ b/src/lib/orionld/pernot/PernotSubCache.h @@ -0,0 +1,45 @@ +#ifndef SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHE_H_ +#define SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHE_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 // sem_t + +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription + + + +// ----------------------------------------------------------------------------- +// +// PernotSubCache - +// +typedef struct PernotSubCache +{ + PernotSubscription* head; + PernotSubscription* tail; + int newSubs; +} PernotSubCache; + +#endif // SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHE_H_ diff --git a/src/lib/orionld/pernot/PernotSubscription.h b/src/lib/orionld/pernot/PernotSubscription.h new file mode 100644 index 0000000000..ee9410db5b --- /dev/null +++ b/src/lib/orionld/pernot/PernotSubscription.h @@ -0,0 +1,126 @@ +#ifndef SRC_LIB_ORIONLD_PERNOT_PERNOTSUBSCRIPTION_H_ +#define SRC_LIB_ORIONLD_PERNOT_PERNOTSUBSCRIPTION_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: uint64_t, ... +#include // CURL + +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +} + +#include "common/RenderFormat.h" // RenderFormat +#include "common/MimeType.h" // MimeType + +#include "orionld/types/OrionldTenant.h" // OrionldTenant +#include "orionld/types/Protocol.h" // Protocol +#include "orionld/types/StringArray.h" // StringArray +#include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo +#include "orionld/q/QNode.h" // QNode + + + +// ----------------------------------------------------------------------------- +// +// PernotState - +// +typedef enum PernotState +{ + SubActive = 1, + SubPaused = 2, + SubErroneous = 3, + SubExpired = 4 +} PernotState; + + + +// ----------------------------------------------------------------------------- +// +// PernotSubscription - Periodic Notification Subscription +// +typedef struct PernotSubscription +{ + char* subscriptionId; + PernotState state; + double timeInterval; // In seconds + KjNode* kjSubP; // OR, I split the entire tree into binary fields inside PernotSubscription ... + OrionldTenant* tenantP; + + // Cached from KjSubP + char* lang; + char* context; + bool isActive; + + // Timestamps + double lastNotificationTime; // In seconds + double lastSuccessTime; + double lastFailureTime; + double expiresAt; + + // For the Query + KjNode* eSelector; + KjNode* attrsSelector; + QNode* qSelector; + OrionldGeoInfo* geoSelector; + + // Counters + uint32_t notificationAttemptsDb; // Total number of notification attempts, in DB + uint32_t notificationAttempts; // Total number of notification attempts, in cache (to be added to dbCount) + uint32_t notificationErrorsDb; // Total number of FAILED notification attempts, in DB + uint32_t notificationErrors; // Total number of FAILED notification attempts, in cache (to be added to dbNotificationErrors) + uint32_t noMatchDb; // Total number of attempts without any matching results of the entities query, in DB + uint32_t noMatch; // Total number of attempts without any matching results of the entities query, in cache (added to dbNoMatch) + uint32_t dirty; // Counter of number of cache counters/timestamps updates since last flush to DB + + // URL + char url[512]; // parsed and destroyed - can't be used (protocolString, ip, and rest points inside this buffer) + char* protocolString; // pointing to 'protocol' part of 'url' + char* ip; // pointing to 'ip' part of 'url' + unsigned short port; // port, as parsed from 'url' + char* rest; // pointing to 'rest' part of 'url' + Protocol protocol; + + // HTTP headers for the notification + StringArray headers; + + // Notification Format/Details + bool ngsiv2; + bool sysAttrs; + RenderFormat renderFormat; + MimeType mimeType; + + // Errors + uint32_t consecutiveErrors; + uint32_t cooldown; + char* lastErrorReason; + + CURL* curlHandle; + + struct PernotSubscription* next; +} PernotSubscription; + +#endif // SRC_LIB_ORIONLD_PERNOT_PERNOTSUBSCRIPTION_H_ diff --git a/src/lib/orionld/pernot/pernotLoop.cpp b/src/lib/orionld/pernot/pernotLoop.cpp new file mode 100644 index 0000000000..4d00a26bbb --- /dev/null +++ b/src/lib/orionld/pernot/pernotLoop.cpp @@ -0,0 +1,208 @@ +/* +* +* 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 // strerror +#include // gettimeofday +#include // pthread_create +#include // usleep + +extern "C" +{ +#include "kbase/kMacros.h" // K_MIN +} + +#include "logMsg/logMsg.h" // LM_x + +#include "orionld/common/orionldState.h" // orionldState, pernotSubCache +#include "orionld/mongoc/mongocSubCountersUpdate.h" // mongocSubCountersUpdate +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription +#include "orionld/pernot/PernotSubCache.h" // PernotSubCache +#include "orionld/pernot/pernotTreat.h" // pernotTreat +#include "orionld/pernot/pernotLoop.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// currentTime - +// +double currentTime(void) +{ + // int gettimeofday(struct timeval *tv, struct timezone *tz); + struct timeval tv; + + if (gettimeofday(&tv, NULL) != 0) + LM_RE(0, ("gettimeofday error: %s", strerror(errno))); + + return tv.tv_sec + tv.tv_usec / 1000000.0; +} + + + +// ----------------------------------------------------------------------------- +// +// pernotSubCacheFlushToDb - +// +void pernotSubCacheFlushToDb(void) +{ + for (PernotSubscription* subP = pernotSubCache.head; subP != NULL; subP = subP->next) + { + if (subP->dirty == false) + continue; + + LM_T(LmtPernotFlush, ("%s: flushing to db (noMatch=%d, notificationAttempts=%d)", subP->subscriptionId, subP->noMatch, subP->notificationAttempts)); + mongocSubCountersUpdate(subP->tenantP, + subP->subscriptionId, + true, + subP->notificationAttempts, + subP->notificationErrors, + subP->noMatch, + subP->lastNotificationTime, + subP->lastSuccessTime, + subP->lastFailureTime, + false); + + // Reset counters + subP->dirty = 0; + subP->notificationAttemptsDb += subP->notificationAttempts; + subP->notificationAttempts = 0; + subP->notificationErrorsDb += subP->notificationErrors; + subP->notificationErrors = 0; + subP->noMatchDb += subP->noMatch; + subP->noMatch = 0; + } +} + + + +// ----------------------------------------------------------------------------- +// +// pernotSubCacheRefresh - +// +void pernotSubCacheRefresh(void) +{ +} + + + +// ----------------------------------------------------------------------------- +// +// pernotLoop - +// +static void* pernotLoop(void* vP) +{ + double nextFlushAt = currentTime() + subCacheFlushInterval; + double nextCacheRefreshAt = currentTime() + subCacheInterval; + double now = 0; + + while (1) + { + for (PernotSubscription* subP = pernotSubCache.head; subP != NULL; subP = subP->next) + { + now = currentTime(); + + if (subP->state == SubPaused) + { + LM_T(LmtPernotLoop, ("%s: Paused", subP->subscriptionId)); + continue; + } + + if (subP->isActive == false) + { + LM_T(LmtPernotLoop, ("%s: Inactive", subP->subscriptionId)); + continue; + } + + if ((subP->expiresAt > 0) && (subP->expiresAt <= now)) + subP->state = SubExpired; // Should it be removed? + + if (subP->state == SubExpired) + { + LM_T(LmtPernotLoop, ("%s: Expired", subP->subscriptionId)); + continue; + } + + if (subP->state == SubErroneous) + { + // Check for cooldown - take it out of error if cooldown time has passwd + if (subP->lastFailureTime + subP->cooldown < now) + subP->state = SubActive; + else + { + LM_T(LmtPernotLoop, ("%s: Erroneous", subP->subscriptionId)); + continue; + } + } + + double diffTime = subP->lastNotificationTime + subP->timeInterval - now; + LM_T(LmtPernotLoopTimes, ("%s: lastNotificationTime: %f", subP->subscriptionId, subP->lastNotificationTime)); + LM_T(LmtPernotLoopTimes, ("%s: now: %f", subP->subscriptionId, now)); + LM_T(LmtPernotLoopTimes, ("%s: diffTime: %f", subP->subscriptionId, diffTime)); + LM_T(LmtPernotLoopTimes, ("%s: noMatch: %d", subP->subscriptionId, subP->noMatch)); + LM_T(LmtPernotLoopTimes, ("%s: noMatchDb: %d", subP->subscriptionId, subP->noMatchDb)); + LM_T(LmtPernotLoopTimes, ("%s: notificationAttempts: %d", subP->subscriptionId, subP->notificationAttempts)); + LM_T(LmtPernotLoopTimes, ("%s: notificationAttemptsDb: %d", subP->subscriptionId, subP->notificationAttemptsDb)); + + if (diffTime <= 0) + { + LM_T(LmtPernotLoop, ("%s: ---------- Sending notification at %f", subP->subscriptionId, now)); + subP->lastNotificationTime = now; // Either it works or fails, the timestamp needs to be updated (it's part of the loop) + pernotTreatStart(subP); // Query runs in a new thread, loop continues + } + } + + if ((subCacheFlushInterval > 0) && (now > nextFlushAt)) + { + LM_T(LmtPernotLoop, ("Flushing Pernot SubCache contents to DB")); + pernotSubCacheFlushToDb(); + nextFlushAt += subCacheFlushInterval; + } + else if ((subCacheInterval > 0) && (now > nextCacheRefreshAt)) + { + LM_T(LmtPernotLoop, ("Refreshing Pernot SubCache contents from DB")); + pernotSubCacheRefresh(); + nextCacheRefreshAt += subCacheInterval; + } + else + { + // LM_T(LmtPernotLoop, ("Sleeping 50ms")); + usleep(50000); // We always end up here if there are no subscriptions in the cache + } + } + LM_T(LmtPernotLoop, ("End of loop")); + return NULL; +} + + + +pthread_t pernotThreadID; +// ----------------------------------------------------------------------------- +// +// pernotLoopStart - +// +void pernotLoopStart(void) +{ + LM_T(LmtPernot, ("Starting thread for the Periodic Notification Loop")); + pthread_create(&pernotThreadID, NULL, pernotLoop, NULL); +} diff --git a/src/lib/orionld/pernot/pernotLoop.h b/src/lib/orionld/pernot/pernotLoop.h new file mode 100644 index 0000000000..9587f07583 --- /dev/null +++ b/src/lib/orionld/pernot/pernotLoop.h @@ -0,0 +1,37 @@ +#ifndef SRC_LIB_ORIONLD_PERNOT_PERNOTLOOP_H_ +#define SRC_LIB_ORIONLD_PERNOT_PERNOTLOOP_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 +*/ + + + +// ----------------------------------------------------------------------------- +// +// pernotLoopStart - +// +extern void pernotLoopStart(void); + +#endif // SRC_LIB_ORIONLD_PERNOT_PERNOTLOOP_H_ diff --git a/src/lib/orionld/pernot/pernotSend.cpp b/src/lib/orionld/pernot/pernotSend.cpp new file mode 100644 index 0000000000..8b42cc8e28 --- /dev/null +++ b/src/lib/orionld/pernot/pernotSend.cpp @@ -0,0 +1,237 @@ +/* +* +* 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/kjRenderSize.h" // kjFastRenderSize +#include "kjson/kjRender.h" // kjFastRender +#include "kjson/kjBuilder.h" // kjObject, kjArray, kjString, kjChildAdd, ... +} + +#include "logMsg/logMsg.h" // LM_* + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/common/numberToDate.h" // numberToDate +#include "orionld/common/uuidGenerate.h" // uuidGenerate +#include "orionld/common/tenantList.h" // tenant0 +#include "orionld/context/orionldCoreContext.h" // ORIONLD_CORE_CONTEXT_URL_V1_0 +#include "orionld/notifications/httpNotify.h" // httpNotify +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription +#include "orionld/pernot/pernotSend.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// Fixed value headers - from notifications/notificationSend.cpp +// +extern const char* contentTypeHeaderJson; +extern const char* contentTypeHeaderJsonLd; +extern const char* contentTypeHeaderGeoJson; +extern const char* acceptHeader; +extern const char* normalizedHeader; +extern const char* conciseHeader; +extern const char* simplifiedHeader; +extern const char* normalizedHeaderNgsiV2; +extern const char* keyValuesHeaderNgsiV2; + + + +// ----------------------------------------------------------------------------- +// +// notificationTree - +// +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); + + char date[64]; + numberToDate(subP->lastNotificationTime, date, sizeof(date)); + KjNode* idNodeP = kjString(orionldState.kjsonP, "id", notificationId); + KjNode* typeNodeP = kjString(orionldState.kjsonP, "type", "Notification"); + KjNode* subscriptionIdNodeP = kjString(orionldState.kjsonP, "subscriptionId", subP->subscriptionId); + KjNode* notifiedAtNodeP = kjString(orionldState.kjsonP, "notifiedAt", date); + + entityArray->name = (char*) "data"; + + kjChildAdd(notificationP, idNodeP); + kjChildAdd(notificationP, typeNodeP); + kjChildAdd(notificationP, subscriptionIdNodeP); + kjChildAdd(notificationP, notifiedAtNodeP); + kjChildAdd(notificationP, entityArray); + + return notificationP; +} + + + +// ----------------------------------------------------------------------------- +// +// notificationTreeForNgsiV2 - +// +static KjNode* notificationTreeForNgsiV2(PernotSubscription* subP, KjNode* entityArray) +{ + LM_X(1, ("Pernot subs in NGSIv2 format is not implemented, how did we get here???")); + return NULL; +} + + + +// ----------------------------------------------------------------------------- +// +// pernotSend - +// +bool pernotSend(PernotSubscription* subP, KjNode* entityArray) +{ + // + // Outgoing Payload Body + // + char body[2 * 1024]; + KjNode* notificationP = (subP->ngsiv2 == false)? notificationTree(subP, entityArray) : notificationTreeForNgsiV2(subP, entityArray); + long unsigned int payloadBodySize = kjFastRenderSize(notificationP); + char* payloadBody = (payloadBodySize < sizeof(body))? body : kaAlloc(&orionldState.kalloc, payloadBodySize); + + kjFastRender(notificationP, payloadBody); + + // Assuming HTTP for now + + // + // Outgoing Header + // + char requestHeader[512]; + size_t requestHeaderLen = 0; + + if (subP->renderFormat < RF_CROSS_APIS_NORMALIZED) + requestHeaderLen = snprintf(requestHeader, sizeof(requestHeader), "POST /%s?subscriptionId=%s HTTP/1.1\r\n", + subP->rest, + subP->subscriptionId); + + // + // Content-Length + // + char contentLenHeader[48]; + size_t contentLenHeaderLen; + int contentLen = strlen(payloadBody); + + contentLenHeaderLen = snprintf(contentLenHeader, sizeof(contentLenHeader) - 1, "Content-Length: %d\r\n", contentLen); + + + int headers = 7; // the minimum number of request headers + + // + // Headers from Subscription::notification::endpoint::receiverInfo+headers (or custom notification in NGSIv2 ...) + // + headers += subP->headers.items; + + char hostHeader[128]; + size_t hostHeaderLen = snprintf(hostHeader, sizeof(hostHeader) - 1, "Host: %s\r\n", subP->ip); + + int ioVecLen = headers + 3; // Request line + X headers + empty line + payload body + struct iovec ioVec[53] = { + { requestHeader, requestHeaderLen }, + { contentLenHeader, contentLenHeaderLen }, + { (void*) contentTypeHeaderJson, 32 }, // Index 2 + { (void*) userAgentHeader, userAgentHeaderLen }, + { (void*) hostHeader, hostHeaderLen }, + { (void*) acceptHeader, 26 }, + { (void*) normalizedHeader, 37 } // Index 6 + }; + int headerIx = 7; + bool addLinkHeader = true; + + if (subP->mimeType == JSONLD) // If Content-Type is application/ld+json, modify slot 2 of ioVec + { + ioVec[2].iov_base = (void*) contentTypeHeaderJsonLd; // REPLACE "application/json" with "application/ld+json" + ioVec[2].iov_len = 35; + addLinkHeader = false; + } + + if ((addLinkHeader == true) && (subP->ngsiv2 == false)) // Add Link header - but not if NGSIv2 Cross Notification + { + char linkHeader[512]; + const char* link = (subP->context == NULL)? ORIONLD_CORE_CONTEXT_URL_V1_0 : subP->context; + + snprintf(linkHeader, sizeof(linkHeader), "Link: <%s>; rel=\"http://www.w3.org/ns/json-ld#context\"; type=\"application/ld+json\"\r\n", link); + + ioVec[headerIx].iov_base = linkHeader; + ioVec[headerIx].iov_len = strlen(linkHeader); + ++headerIx; + } + + // FIXME: Loop over subP->headers.array, and add those to ioVec + for (int ix = 0; ix < subP->headers.items; ix++) + { + ioVec[headerIx].iov_base = subP->headers.array[ix]; + ioVec[headerIx].iov_len = strlen(subP->headers.array[ix]); + ++headerIx; + } + + // + // Ngsild-Tenant + // + if ((subP->tenantP != NULL) && (subP->tenantP != &tenant0) && (subP->ngsiv2 == false)) + { + int len = strlen(subP->tenantP->tenant) + 20; // Ngsild-Tenant: xxx\r\n0 - '\r' seems to not count for strlen ... + char* buf = kaAlloc(&orionldState.kalloc, len); + + ioVec[headerIx].iov_len = snprintf(buf, len, "Ngsild-Tenant: %s\r\n", subP->tenantP->tenant); + ioVec[headerIx].iov_base = buf; + + ++headerIx; + } + + // Empty line delimiting HTTP Headers and Payload Body + ioVec[headerIx].iov_base = (void*) "\r\n"; + ioVec[headerIx].iov_len = 2; + ++headerIx; + + + // Payload Body + ioVec[headerIx].iov_base = payloadBody; + ioVec[headerIx].iov_len = contentLen; + + ioVecLen = headerIx + 1; + + // + // The message is ready - just need to be sent + // + if (subP->protocol == HTTP) + { + LM_T(LmtPernot, ("Sending a Periodic Notification to %s:%d%s", subP->ip, subP->port, subP->rest)); + return httpNotify(NULL, subP, subP->subscriptionId, subP->ip, subP->port, subP->rest, ioVec, ioVecLen, subP->lastNotificationTime); + } +#if 0 + else if (subP->protocol == HTTPS) return httpsNotify(subP, ioVec, ioVecLen, now, curlHandlePP); + else if (subP->protocol == MQTT) return mqttNotify(subP, ioVec, ioVecLen, now); +#endif + + LM_W(("%s: Unsupported protocol for notifications: '%s'", subP->subscriptionId, subP->protocol)); + + return false; +} diff --git a/src/lib/orionld/pernot/pernotSend.h b/src/lib/orionld/pernot/pernotSend.h new file mode 100644 index 0000000000..ecf8922cbb --- /dev/null +++ b/src/lib/orionld/pernot/pernotSend.h @@ -0,0 +1,43 @@ +#ifndef SRC_LIB_ORIONLD_PERNOT_PERNOTSEND_H_ +#define SRC_LIB_ORIONLD_PERNOT_PERNOTSEND_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/pernot/PernotSubscription.h" // PernotSubscription + + + +// ----------------------------------------------------------------------------- +// +// pernotSend - +// +extern bool pernotSend(PernotSubscription* subP, KjNode* entityArray); + +#endif // SRC_LIB_ORIONLD_PERNOT_PERNOTSEND_H_ diff --git a/src/lib/orionld/pernot/pernotSubCacheAdd.cpp b/src/lib/orionld/pernot/pernotSubCacheAdd.cpp new file mode 100644 index 0000000000..f4d799396a --- /dev/null +++ b/src/lib/orionld/pernot/pernotSubCacheAdd.cpp @@ -0,0 +1,326 @@ +/* +* +* 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 +#include // sem_post + +extern "C" +{ +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjLookup.h" // kjLookup +#include "kjson/kjClone.h" // kjClone +#include "kjson/kjBuilder.h" // kjString, kjChildAdd +} + +#include "logMsg/logMsg.h" // LM_* + +#include "common/RenderFormat.h" // RenderFormat + +#include "orionld/common/orionldState.h" // orionldState, pernotSubCache +#include "orionld/common/urlParse.h" // urlParse +#include "orionld/types/Protocol.h" // Protocol, protocolFromString +#include "orionld/types/OrionldTenant.h" // OrionldTenant +#include "orionld/q/QNode.h" // QNode +#include "orionld/payloadCheck/pcheckGeoQ.h" // pcheckGeoQ +#include "orionld/kjTree/kjChildCount.h" // kjChildCount +#include "orionld/context/OrionldContext.h" // OrionldContext +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription +#include "orionld/pernot/PernotSubCache.h" // PernotSubCache + + + +// ----------------------------------------------------------------------------- +// +// receiverInfo - +// +// As there is no incoming info for the HTTP headers of Pernot subscriptions, we can +// safely create the entire string of 'key: value' for all the headers. +// +static void receiverInfo(PernotSubscription* pSubP, KjNode* endpointP) +{ + KjNode* receiverInfoP = kjLookup(endpointP, "receiverInfo"); + + pSubP->headers.items = 0; + + if (receiverInfoP != NULL) + { + pSubP->headers.items = kjChildCount(receiverInfoP); + pSubP->headers.array = (char**) malloc(pSubP->headers.items * sizeof(char*)); + + int ix = 0; + for (KjNode* kvPairP = receiverInfoP->value.firstChildP; kvPairP != NULL; kvPairP = kvPairP->next) + { + KjNode* keyP = kjLookup(kvPairP, "key"); + KjNode* valueP = kjLookup(kvPairP, "value"); + int sLen = strlen(keyP->value.s) + strlen(valueP->value.s) + 4; + char* s = (char*) malloc(sLen); + + snprintf(s, sLen - 1, "%s:%s\r\n", keyP->value.s, valueP->value.s); + pSubP->headers.array[ix] = s; + ++ix; + } + } +} + + + +// ----------------------------------------------------------------------------- +// +// counterFromDb - +// +static void counterFromDb(KjNode* subP, uint32_t* counterP, const char* fieldName) +{ + LM_T(LmtPernot, ("Getting counter '%s' from db", fieldName)); + KjNode* counterNodeP = kjLookup(subP, fieldName); + + if (counterNodeP != NULL) + { + LM_T(LmtPernot, ("Found counter '%s' in db: %d", fieldName, counterNodeP->value.i)); + *counterP = counterNodeP->value.i; + } + else + { + *counterP = 0; + LM_T(LmtPernot, ("Counter '%s' NOT found in db", fieldName)); + } +} + + + +// ----------------------------------------------------------------------------- +// +// timestampFromDb - +// +static void timestampFromDb(KjNode* apiSubP, double* tsP, const char* fieldName) +{ + KjNode* tsNodeP = kjLookup(apiSubP, fieldName); + if (tsNodeP != NULL) + { + // Need to remove it from apiSubP and save it in cache + kjChildRemove(apiSubP, tsNodeP); + *tsP = tsNodeP->value.f; + } + else + *tsP = 0; +} + + + +// ----------------------------------------------------------------------------- +// +// pernotSubCacheAdd - +// +PernotSubscription* pernotSubCacheAdd +( + char* subscriptionId, + KjNode* apiSubP, + KjNode* endpointP, + QNode* qTree, + KjNode* geoCoordinatesP, + OrionldContext* contextP, + OrionldTenant* tenantP, + KjNode* showChangesP, + KjNode* sysAttrsP, + RenderFormat renderFormat, + double timeInterval +) +{ + PernotSubscription* pSubP = (PernotSubscription*) malloc(sizeof(PernotSubscription)); + bzero(pSubP, sizeof(PernotSubscription)); + + if (subscriptionId == NULL) + { + KjNode* idP = kjLookup(apiSubP, "id"); + if (idP == NULL) + LM_RE(NULL, ("No subscription id")); + subscriptionId = idP->value.s; + } + + LM_T(LmtPernot, ("Adding pernot subscription '%s' to cache", subscriptionId)); + + pSubP->subscriptionId = strdup(subscriptionId); + pSubP->timeInterval = timeInterval; + pSubP->kjSubP = kjClone(NULL, apiSubP); + pSubP->tenantP = tenantP; + pSubP->renderFormat = renderFormat; + pSubP->sysAttrs = (sysAttrsP == NULL)? false : sysAttrsP->value.b; + pSubP->ngsiv2 = (renderFormat >= RF_CROSS_APIS_NORMALIZED); + pSubP->context = (contextP == NULL)? NULL : contextP->url; + pSubP->isActive = true; // Active by default, then we'll see ... + + KjNode* notificationP = kjLookup(pSubP->kjSubP, "notification"); + + // Query parameters + pSubP->eSelector = kjLookup(pSubP->kjSubP, "entities"); + pSubP->attrsSelector = kjLookup(notificationP, "attributes"); + pSubP->qSelector = qTree; + pSubP->geoSelector = NULL; + + KjNode* geoqP = kjLookup(apiSubP, "geoQ"); + if (geoqP != NULL) + { + pSubP->geoSelector = pcheckGeoQ(NULL, geoqP, true); // FIXME: Already done in pcheckSubscription() + if (pSubP->geoSelector != NULL) + { + if (pSubP->geoSelector->geoProperty == NULL) + pSubP->geoSelector->geoProperty = (char*) "location"; + + LM_T(LmtPernotQuery, ("geometry: %d", pSubP->geoSelector->geometry)); + LM_T(LmtPernotQuery, ("georel: %d", pSubP->geoSelector->georel)); + LM_T(LmtPernotQuery, ("minDistance: %d", pSubP->geoSelector->minDistance)); + LM_T(LmtPernotQuery, ("maxDistance: %d", pSubP->geoSelector->maxDistance)); + LM_T(LmtPernotQuery, ("geoProperty: '%s'", pSubP->geoSelector->geoProperty)); + pSubP->geoSelector->coordinates = kjClone(NULL, pSubP->geoSelector->coordinates); + kjTreeLog(pSubP->geoSelector->coordinates, "coordinates", LmtPernotQuery); + + } + } + + // notification::endpoint + if (endpointP == NULL) + endpointP = kjLookup(notificationP, "endpoint"); + + // URL + KjNode* uriP = kjLookup(endpointP, "uri"); + strncpy(pSubP->url, uriP->value.s, sizeof(pSubP->url) - 1); + urlParse(pSubP->url, &pSubP->protocolString, &pSubP->ip, &pSubP->port, &pSubP->rest); + pSubP->protocol = protocolFromString(pSubP->protocolString); + + // Mime Type for notifications + KjNode* acceptP = kjLookup(endpointP, "accept"); + pSubP->mimeType = JSON; // Default setting + if (acceptP != NULL) + { + if (strcmp(acceptP->value.s, "application/json") == 0) pSubP->mimeType = JSON; + else if (strcmp(acceptP->value.s, "application/ld+json") == 0) pSubP->mimeType = JSONLD; + else if (strcmp(acceptP->value.s, "application/geo+json") == 0) pSubP->mimeType = GEOJSON; + } + + // HTTP headers from receiverInfo + receiverInfo(pSubP, endpointP); + + // State - check also expiresAt+status + KjNode* isActiveNodeP = kjLookup(apiSubP, "isActive"); + if (isActiveNodeP != NULL) + pSubP->state = (isActiveNodeP->value.b == true)? SubActive : SubPaused; + else + pSubP->state = SubActive; + + // Counters + counterFromDb(notificationP, &pSubP->notificationAttemptsDb, "timesSent"); + counterFromDb(notificationP, &pSubP->notificationErrorsDb, "timesFailed"); + counterFromDb(notificationP, &pSubP->noMatchDb, "noMatch"); + + pSubP->notificationAttempts = 0; // cached - added to notificationAttemptsDb + pSubP->notificationErrors = 0; // cached - added to notificationErrorsDb + pSubP->noMatch = 0; // cached - added to noMatchDb + pSubP->consecutiveErrors = 0; // only cached, not saved in DB + + // Timestamps + timestampFromDb(apiSubP, &pSubP->lastNotificationTime, "lastNotification"); + timestampFromDb(apiSubP, &pSubP->lastSuccessTime, "lastSuccess"); + timestampFromDb(apiSubP, &pSubP->lastFailureTime, "lastFailure"); + + pSubP->cooldown = 0; // FIXME: get info from apiSubP + pSubP->curlHandle = NULL; + + // + // Add the subscription to the cache + // + pSubP->next = NULL; + + // Take semaphore ... + if (pernotSubCache.head == NULL) + { + pernotSubCache.head = pSubP; + pernotSubCache.tail = pSubP; + } + else + { + pernotSubCache.tail->next = pSubP; + pernotSubCache.tail = pSubP; + } + + // + // Prepare the kjSubP for future GET requests + // Must add the "type", and, perhaps also the "jsonldContext" + // Also, remove "q" and "mq" (that's NGSIv2), + // and then rename "ldQ" to "q" + // AND: + // isActive + // notification::endpoint::accept + // notification::format + // notification::status + // origin (cache) + // status + // + KjNode* typeP = kjString(NULL, "type", "Subscription"); + kjChildAdd(pSubP->kjSubP, typeP); + + KjNode* jsonldContextP = kjLookup(pSubP->kjSubP, "jsonldContext"); + if (jsonldContextP == NULL) + { + jsonldContextP = kjString(NULL, "jsonldContext", orionldState.contextP->url); + kjChildAdd(pSubP->kjSubP, jsonldContextP); + } + + KjNode* qP = kjLookup(pSubP->kjSubP, "q"); + if (qP != NULL) + kjChildRemove(pSubP->kjSubP, qP); + + KjNode* mqP = kjLookup(pSubP->kjSubP, "mq"); + if (mqP != NULL) + kjChildRemove(pSubP->kjSubP, mqP); + + qP = kjLookup(pSubP->kjSubP, "ldQ"); + if (qP != NULL) + qP->name = (char*) "q"; + + // origin + KjNode* originP = kjString(NULL, "origin", "cache"); + kjChildAdd(pSubP->kjSubP, originP); + + // notification::format + KjNode* formatP = kjLookup(notificationP, "format"); + if (formatP == NULL) + { + formatP = kjString(NULL, "format", "normalized"); + kjChildAdd(notificationP, formatP); + } + + // notification::endpoint::accept + if (acceptP == NULL) + { + acceptP = kjString(NULL, "accept", "application/json"); + kjChildAdd(endpointP, acceptP); + } + + // + // Caching stuff from the KjNode tree + // + KjNode* langP = kjLookup(pSubP->kjSubP, "lang"); + pSubP->lang = (langP != NULL)? langP->value.s : NULL; + + return NULL; +} diff --git a/src/lib/orionld/pernot/pernotSubCacheAdd.h b/src/lib/orionld/pernot/pernotSubCacheAdd.h new file mode 100644 index 0000000000..f073ef7567 --- /dev/null +++ b/src/lib/orionld/pernot/pernotSubCacheAdd.h @@ -0,0 +1,63 @@ +#ifndef SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHEADD_H_ +#define SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHEADD_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 "common/RenderFormat.h" // RenderFormat + +#include "orionld/types/OrionldTenant.h" // OrionldTenant +#include "orionld/context/OrionldContext.h" // OrionldContext +#include "orionld/q/QNode.h" // QNode +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription +#include "orionld/pernot/PernotSubCache.h" // PernotSubCache +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription + + + +// ----------------------------------------------------------------------------- +// +// pernotSubCacheAdd - +// +extern PernotSubscription* pernotSubCacheAdd +( + char* subscriptionId, + KjNode* subP, + KjNode* endpointP, + QNode* qTree, + KjNode* geoCoordinatesP, + OrionldContext* contextP, + OrionldTenant* tenantP, + KjNode* showChangesP, + KjNode* sysAttrsP, + RenderFormat renderFormat, + double timeInterval +); + +#endif // SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHEADD_H_ diff --git a/src/lib/orionld/pernot/pernotSubCacheInit.cpp b/src/lib/orionld/pernot/pernotSubCacheInit.cpp new file mode 100644 index 0000000000..0422def956 --- /dev/null +++ b/src/lib/orionld/pernot/pernotSubCacheInit.cpp @@ -0,0 +1,38 @@ +/* +* +* 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 "orionld/common/orionldState.h" // pernotSubCache + + + +// ----------------------------------------------------------------------------- +// +// pernotSubCacheInit - +// +void pernotSubCacheInit(void) +{ + bzero(&pernotSubCache, sizeof(pernotSubCache)); +} diff --git a/src/lib/orionld/pernot/pernotSubCacheInit.h b/src/lib/orionld/pernot/pernotSubCacheInit.h new file mode 100644 index 0000000000..7a371619c4 --- /dev/null +++ b/src/lib/orionld/pernot/pernotSubCacheInit.h @@ -0,0 +1,37 @@ +#ifndef SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHEINIT_H_ +#define SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHEINIT_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 +*/ + + + +// ----------------------------------------------------------------------------- +// +// pernotSubCacheInit - +// +extern void pernotSubCacheInit(void); + +#endif // SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHEINIT_H_ diff --git a/src/lib/orionld/pernot/pernotSubCacheLookup.cpp b/src/lib/orionld/pernot/pernotSubCacheLookup.cpp new file mode 100644 index 0000000000..abe0f67ca8 --- /dev/null +++ b/src/lib/orionld/pernot/pernotSubCacheLookup.cpp @@ -0,0 +1,49 @@ +/* +* +* 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/common/orionldState.h" // pernotSubCache +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription +#include "orionld/pernot/PernotSubCache.h" // PernotSubCache +#include "orionld/pernot/pernotSubCacheLookup.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// pernotSubCacheLookup - +// +PernotSubscription* pernotSubCacheLookup +( + const char* subscriptionId, + const char* tenant +) +{ + for (PernotSubscription* pSubP = pernotSubCache.head; pSubP != NULL; pSubP = pSubP->next) + { + if ((strcmp(pSubP->subscriptionId, subscriptionId) == 0) && (strcmp(pSubP->tenantP->tenant, tenant) == 0)) + return pSubP; + } + + return NULL; +} diff --git a/src/lib/orionld/pernot/pernotSubCacheLookup.h b/src/lib/orionld/pernot/pernotSubCacheLookup.h new file mode 100644 index 0000000000..b4d7620e30 --- /dev/null +++ b/src/lib/orionld/pernot/pernotSubCacheLookup.h @@ -0,0 +1,42 @@ +#ifndef SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHELOOKUP_H_ +#define SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHELOOKUP_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/pernot/PernotSubscription.h" // PernotSubscription + + + +// ----------------------------------------------------------------------------- +// +// pernotSubCacheLookup - +// +extern PernotSubscription* pernotSubCacheLookup +( + const char* subscriptionId, + const char* tenant +); + +#endif // SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHELOOKUP_H_ diff --git a/src/lib/orionld/pernot/pernotSubCacheRemove.cpp b/src/lib/orionld/pernot/pernotSubCacheRemove.cpp new file mode 100644 index 0000000000..357b970c71 --- /dev/null +++ b/src/lib/orionld/pernot/pernotSubCacheRemove.cpp @@ -0,0 +1,37 @@ +/* +* +* 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/pernot/PernotSubscription.h" // PernotSubscription +#include "orionld/pernot/pernotSubCacheRemove.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// pernotSubCacheRemove - +// +bool pernotSubCacheRemove(PernotSubscription* pSubP) +{ + return true; +} diff --git a/src/lib/orionld/pernot/pernotSubCacheRemove.h b/src/lib/orionld/pernot/pernotSubCacheRemove.h new file mode 100644 index 0000000000..b40898124d --- /dev/null +++ b/src/lib/orionld/pernot/pernotSubCacheRemove.h @@ -0,0 +1,45 @@ +#ifndef SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHEREMOVE_H_ +#define SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHEREMOVE_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 "common/RenderFormat.h" // RenderFormat + +#include "orionld/common/orionldState.h" // pernotSubCache +#include "orionld/q/QNode.h" // QNode +#include "orionld/context/OrionldContext.h" // OrionldContext +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription +#include "orionld/pernot/PernotSubCache.h" // PernotSubCache +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription + + + +// ----------------------------------------------------------------------------- +// +// pernotSubCacheRemove - +// +extern bool pernotSubCacheRemove(PernotSubscription* pSubP); + +#endif // SRC_LIB_ORIONLD_PERNOT_PERNOTSUBCACHEREMOVE_H_ diff --git a/src/lib/orionld/pernot/pernotTreat.cpp b/src/lib/orionld/pernot/pernotTreat.cpp new file mode 100644 index 0000000000..8fa6f1f962 --- /dev/null +++ b/src/lib/orionld/pernot/pernotTreat.cpp @@ -0,0 +1,215 @@ +/* +* +* 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 // pthread_exit + +extern "C" +{ +#include "kalloc/kaBufferInit.h" // kaBufferInit +#include "kjson/kjBufferCreate.h" // kjBufferCreate +#include "kjson/KjNode.h" // KjNode +#include "kjson/kjBuilder.h" // kjArray, ... +} + +#include "logMsg/logMsg.h" // LM_x + +#include "common/RenderFormat.h" // RenderFormat +#include "orionld/common/orionldState.h" // orionldState, pernotSubCache +#include "orionld/types/OrionldGeoInfo.h" // OrionldGeoInfo +#include "orionld/mongoc/mongocEntitiesQuery2.h" // mongocEntitiesQuery2 +#include "orionld/dbModel/dbModelToApiEntity.h" // dbModelToApiEntity2 +#include "orionld/pernot/PernotSubscription.h" // PernotSubscription +#include "orionld/pernot/pernotSend.h" // pernotSend +#include "orionld/pernot/pernotTreat.h" // Own interface + + + +// ----------------------------------------------------------------------------- +// +// dbModelToApiEntities - FIXME: Move to orionld/dbModel +// +KjNode* dbModelToApiEntities(KjNode* dbEntityArray, bool sysAttrs, RenderFormat renderFormat, char* lang) +{ + KjNode* apiEntityArray = kjArray(orionldState.kjsonP, NULL); + + for (KjNode* dbEntityP = dbEntityArray->value.firstChildP; dbEntityP != NULL; dbEntityP = dbEntityP->next) + { + OrionldProblemDetails pd; + KjNode* apiEntityP = dbModelToApiEntity2(dbEntityP, sysAttrs, renderFormat, lang, true, &pd); + + if (apiEntityP == NULL) + LM_E(("dbModelToApiEntity: %s: %s", pd.title, pd.detail)); + else + kjChildAdd(apiEntityArray, apiEntityP); + } + + return apiEntityArray; +} + + + +// ----------------------------------------------------------------------------- +// +// pernotTreat - +// +// New Fields for Pernot Subscription: +// - pageSize for pagination - when too many matches +// - maxPages - to cap the total number of paginated requests +// - local=true - only local entities (might be the default setting) +// - totalNotifationMsgsSent - stats: how many notifications (including paginated messages) have been sent +// - timesNoMatch - stats: how many times did the query yield no results? +// +static void* pernotTreat(void* vP) +{ + PernotSubscription* subP = (PernotSubscription*) vP; + bool ok = true; + char kallocBuffer[2048]; + + LM_T(LmtPernotFlush, ("In thread for one Periodic Notification Subscription (%s, %f)", subP->subscriptionId, subP->lastNotificationTime)); + + // 01. Initialize kalloc/kjson + // 02. Initialize orionldState + // 03. Prepare orionldState for the call to mongocEntitiesQuery2(): + // - orionldState.uriParams.offset (URL parameter) + // - orionldState.uriParams.limit (URL parameter) + // - orionldState.uriParams.count (URL parameter) + // - orionldState.tenantP (HTTP header) + // 04. KjNode* entitySelectorP (should be created when sub added to cache) + // 05. KjNode* attrsArray (should be created when sub added to cache) + // 06. QNode* qTree (should be created when sub added to cache) + // 07. OrionldGeoInfo* geoInfoP (should be created when sub added to cache) + // 08. char* lang (get it from subP->kjTreeP - cache pointer in PernotSubscription) + // 09. int64_t* countP + // 10. Perform the query/queries + // - First query with count + // - After that, a loop for pagination, if need be + // + + LM_T(LmtPernotLoop, ("%s: lastNotificationTime: %f", subP->subscriptionId, subP->lastNotificationTime)); + + orionldStateInit(NULL); + bzero(kallocBuffer, sizeof(kallocBuffer)); + kaBufferInit(&kalloc, kallocBuffer, sizeof(kallocBuffer), 8 * 1024, NULL, "Pernot KAlloc buffer"); + orionldState.kjsonP = kjBufferCreate(&kjson, &kalloc); + + LM_T(LmtPernot, ("Creating the query for pernot-subscription %s", subP->subscriptionId)); + int64_t count = 0; + KjNode* dbEntityArray; + KjNode* apiEntityArray; + + orionldState.uriParams.offset = 0; + orionldState.uriParams.limit = 20; // Or: set in subscription + 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)); + subP->noMatch += 1; + subP->dirty += 1; + goto done; + } + + apiEntityArray = dbModelToApiEntities(dbEntityArray, subP->sysAttrs, subP->renderFormat, subP->lang); + kjTreeLog(apiEntityArray, "apiEntityArray", LmtPernot); + + if (pernotSend(subP, apiEntityArray) == false) + ok = false; + else + { + int entitiesSent = MIN(count, orionldState.uriParams.limit); + if (entitiesSent < count) + { + orionldState.uriParams.count = false; + while (entitiesSent < count) + { + orionldState.uriParams.offset = entitiesSent; + + dbEntityArray = mongocEntitiesQuery2(subP->eSelector, subP->attrsSelector, subP->qSelector, NULL, subP->lang, NULL); + apiEntityArray = dbModelToApiEntities(dbEntityArray, subP->sysAttrs, subP->renderFormat, subP->lang); + + if (pernotSend(subP, apiEntityArray) == false) + { + ok = false; + break; + } + + entitiesSent += orionldState.uriParams.limit; + } + } + } + + // + // Timestamps and Status + // + if (ok == true) + { + LM_T(LmtPernot, ("Successful Periodic Notification")); + subP->lastSuccessTime = subP->lastNotificationTime; + subP->consecutiveErrors = 0; + } + else + { + LM_T(LmtPernot, ("Failed Periodic Notification")); + subP->lastFailureTime = subP->lastNotificationTime; + subP->notificationErrors += 1; + subP->consecutiveErrors += 1; + + if (subP->consecutiveErrors >= 3) + { + subP->state = SubErroneous; + LM_W(("%s: 3 consecutive errors - setting the subscription in Error state", subP->subscriptionId)); + } + } + +done: + subP->notificationAttempts += 1; // timesSent + pthread_exit(0); + return NULL; +} + + + +// ----------------------------------------------------------------------------- +// +// pernotTreatStart - +// +void pernotTreatStart(PernotSubscription* subP) +{ + pthread_t tid; + + LM_T(LmtPernotLoop, ("Starting thread for one Periodic Notification Subscription (%s, %f)", subP->subscriptionId, subP->lastNotificationTime)); + + pthread_create(&tid, NULL, pernotTreat, subP); + + // It's OK to lose the thread ID ... I think ... +} diff --git a/src/lib/orionld/pernot/pernotTreat.h b/src/lib/orionld/pernot/pernotTreat.h new file mode 100644 index 0000000000..984c5d1c97 --- /dev/null +++ b/src/lib/orionld/pernot/pernotTreat.h @@ -0,0 +1,38 @@ +#ifndef SRC_LIB_ORIONLD_PERNOT_PERNOTTREAT_H_ +#define SRC_LIB_ORIONLD_PERNOT_PERNOTTREAT_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/pernot/PernotSubscription.h" // PernotSubscription + + + +// ----------------------------------------------------------------------------- +// +// pernotTreatStart - +// +extern void pernotTreatStart(PernotSubscription* subP); + +#endif // SRC_LIB_ORIONLD_PERNOT_PERNOTTREAT_H_ diff --git a/src/lib/orionld/q/qAliasCompact.cpp b/src/lib/orionld/q/qAliasCompact.cpp index b6ab959861..08478933d4 100644 --- a/src/lib/orionld/q/qAliasCompact.cpp +++ b/src/lib/orionld/q/qAliasCompact.cpp @@ -71,6 +71,17 @@ bool qAliasCompact(KjNode* qP, bool compact) char c0 = cP[0]; char c1 = cP[1]; + if ((c0 == '(') || (c0 == ')')) + { + LM_T(LmtQ, ("Found a parenthesis - skipping it")); + ++cP; + varStart = cP; + out[outIx] = c0; + ++outIx; + + continue; + } + if (c0 == '!') { varStart = &cP[1]; @@ -103,6 +114,7 @@ bool qAliasCompact(KjNode* qP, bool compact) ++eqP; } + LM_T(LmtQ, ("Compacting '%s'", varStart)); alias = orionldContextItemAliasLookup(orionldState.contextP, varStart, NULL, NULL); } else diff --git a/src/lib/orionld/q/qBuild.cpp b/src/lib/orionld/q/qBuild.cpp index ba45e0235f..0c3b9f7bed 100644 --- a/src/lib/orionld/q/qBuild.cpp +++ b/src/lib/orionld/q/qBuild.cpp @@ -67,7 +67,7 @@ // isMqP output parameter indicating whether the q para meter corresponds to 'mq' in NGSIv2 // qToDbModel transform the variables in 'q' to look like they look in the q in the database // -QNode* qBuild(const char* q, char** qRenderP, bool* v2ValidP, bool* isMqP, bool qToDbModel) +QNode* qBuild(const char* q, char** qRenderP, bool* v2ValidP, bool* isMqP, bool qToDbModel, bool pernot) { QNode* qP = NULL; char* title; @@ -104,7 +104,7 @@ QNode* qBuild(const char* q, char** qRenderP, bool* v2ValidP, bool* isMqP, bool } } - qP = qParse(qList, NULL, false, qToDbModel, &title, &detail); // 3rd parameter: forDb=false + qP = qParse(qList, NULL, pernot, qToDbModel, &title, &detail); // 3rd parameter: 'forDb' false unless pernot subscription if (qP == NULL) { orionldError(OrionldBadRequestData, "Invalid Q-Filter", detail, 400); diff --git a/src/lib/orionld/q/qBuild.h b/src/lib/orionld/q/qBuild.h index effd155bea..42eb2f607a 100644 --- a/src/lib/orionld/q/qBuild.h +++ b/src/lib/orionld/q/qBuild.h @@ -55,6 +55,6 @@ // v2ValidP output parameter indicating whether the q string id valid for NGSIv2 // isMdP output parameter indicating whether the q para meter corresponds to 'mq' in NGSIv2 // -extern QNode* qBuild(const char* q, char** qRenderP, bool* v2ValidP, bool* isMdP, bool qToDbModel); +extern QNode* qBuild(const char* q, char** qRenderP, bool* v2ValidP, bool* isMdP, bool qToDbModel, bool pernot); #endif // SRC_LIB_ORIONLD_Q_QBUILD_H_ diff --git a/src/lib/orionld/q/qPresent.cpp b/src/lib/orionld/q/qPresent.cpp index 7c18e27e4c..a91e2c9f50 100644 --- a/src/lib/orionld/q/qPresent.cpp +++ b/src/lib/orionld/q/qPresent.cpp @@ -24,7 +24,7 @@ */ #include // memset -#include "logMsg/logMsg.h" // LM_* +#include "logMsg/logMsg.h" // TraceLevels, LM_* #include "orionld/q/QNode.h" // QNode #include "orionld/q/qPresent.h" // Own interface @@ -35,7 +35,7 @@ // // qTreePresent - // -static void qTreePresent(QNode* qP, int indent, const char* prefix) +static void qTreePresent(QNode* qP, int indent, const char* prefix, TraceLevels tLevel) { char indentV[100]; @@ -44,78 +44,78 @@ static void qTreePresent(QNode* qP, int indent, const char* prefix) if (qP->type == QNodeEQ) { - LM_T(LmtQ, ("%s:%sEQ:", prefix, indentV)); - qTreePresent(qP->value.children, indent+2, prefix); - qTreePresent(qP->value.children->next, indent+2, prefix); + LM_T(tLevel, ("%s:%sEQ:", prefix, indentV)); + qTreePresent(qP->value.children, indent+2, prefix, tLevel); + qTreePresent(qP->value.children->next, indent+2, prefix, tLevel); } else if (qP->type == QNodeNE) { - LM_T(LmtQ, ("%s:%sNE:", prefix, indentV)); - qTreePresent(qP->value.children, indent+2, prefix); - qTreePresent(qP->value.children->next, indent+2, prefix); + LM_T(tLevel, ("%s:%sNE:", prefix, indentV)); + qTreePresent(qP->value.children, indent+2, prefix, tLevel); + qTreePresent(qP->value.children->next, indent+2, prefix, tLevel); } else if (qP->type == QNodeLT) { - LM_T(LmtQ, ("%s:%sLT:", prefix, indentV)); - qTreePresent(qP->value.children, indent+2, prefix); - qTreePresent(qP->value.children->next, indent+2, prefix); + LM_T(tLevel, ("%s:%sLT:", prefix, indentV)); + qTreePresent(qP->value.children, indent+2, prefix, tLevel); + qTreePresent(qP->value.children->next, indent+2, prefix, tLevel); } else if (qP->type == QNodeLE) { - LM_T(LmtQ, ("%s:%sLE:", prefix, indentV)); - qTreePresent(qP->value.children, indent+2, prefix); - qTreePresent(qP->value.children->next, indent+2, prefix); + LM_T(tLevel, ("%s:%sLE:", prefix, indentV)); + qTreePresent(qP->value.children, indent+2, prefix, tLevel); + qTreePresent(qP->value.children->next, indent+2, prefix, tLevel); } else if (qP->type == QNodeGT) { - LM_T(LmtQ, ("%s:%sGT:", prefix, indentV)); - qTreePresent(qP->value.children, indent+2, prefix); - qTreePresent(qP->value.children->next, indent+2, prefix); + LM_T(tLevel, ("%s:%sGT:", prefix, indentV)); + qTreePresent(qP->value.children, indent+2, prefix, tLevel); + qTreePresent(qP->value.children->next, indent+2, prefix, tLevel); } else if (qP->type == QNodeGE) { - LM_T(LmtQ, ("%s:%sGE:", prefix, indentV)); - qTreePresent(qP->value.children, indent+2, prefix); - qTreePresent(qP->value.children->next, indent+2, prefix); + LM_T(tLevel, ("%s:%sGE:", prefix, indentV)); + qTreePresent(qP->value.children, indent+2, prefix, tLevel); + qTreePresent(qP->value.children->next, indent+2, prefix, tLevel); } else if (qP->type == QNodeVariable) - LM_T(LmtQ, ("%s:%s%s (Variable) (v at %p, qP at %p)", prefix, indentV, qP->value.v, qP->value.v, qP)); + LM_T(tLevel, ("%s:%s%s (Variable) (v at %p, qP at %p)", prefix, indentV, qP->value.v, qP->value.v, qP)); else if (qP->type == QNodeIntegerValue) - LM_T(LmtQ, ("%s:%s%d (Int)", prefix, indentV, qP->value.i)); + LM_T(tLevel, ("%s:%s%d (Int)", prefix, indentV, qP->value.i)); else if (qP->type == QNodeFloatValue) - LM_T(LmtQ, ("%s:%s%f (Float)", prefix, indentV, qP->value.f)); + LM_T(tLevel, ("%s:%s%f (Float)", prefix, indentV, qP->value.f)); else if (qP->type == QNodeStringValue) - LM_T(LmtQ, ("%s:%s%s (String) at %p (String at %p)", prefix, indentV, qP->value.s, qP, qP->value.s)); + LM_T(tLevel, ("%s:%s%s (String) at %p (String at %p)", prefix, indentV, qP->value.s, qP, qP->value.s)); else if (qP->type == QNodeTrueValue) - LM_T(LmtQ, ("%s:%sTRUE (Bool)", prefix, indentV)); + LM_T(tLevel, ("%s:%sTRUE (Bool)", prefix, indentV)); else if (qP->type == QNodeFalseValue) - LM_T(LmtQ, ("%s:%sFALSE (Bool)", prefix, indentV)); + LM_T(tLevel, ("%s:%sFALSE (Bool)", prefix, indentV)); else if (qP->type == QNodeExists) { - LM_T(LmtQ, ("%s:%s Exists (at %p):", prefix, indentV, qP)); - qTreePresent(qP->value.children, indent+2, prefix); + LM_T(tLevel, ("%s:%s Exists (at %p):", prefix, indentV, qP)); + qTreePresent(qP->value.children, indent+2, prefix, tLevel); } else if (qP->type == QNodeNotExists) { - LM_T(LmtQ, ("%s:%s Not Exists:", prefix, indentV)); - qTreePresent(qP->value.children, indent+2, prefix); + LM_T(tLevel, ("%s:%s Not Exists:", prefix, indentV)); + qTreePresent(qP->value.children, indent+2, prefix, tLevel); } else if (qP->type == QNodeOr) { - LM_T(LmtQ, ("%s:%sOR:", prefix, indentV)); + LM_T(tLevel, ("%s:%sOR:", prefix, indentV)); indent+=2; for (QNode* childP = qP->value.children; childP != NULL; childP = childP->next) - qTreePresent(childP, indent, prefix); + qTreePresent(childP, indent, prefix, tLevel); } else if (qP->type == QNodeAnd) { - LM_T(LmtQ, ("%s:%sAND:", prefix, indentV)); + LM_T(tLevel, ("%s:%sAND:", prefix, indentV)); indent+=2; for (QNode* childP = qP->value.children; childP != NULL; childP = childP->next) - qTreePresent(childP, indent, prefix); + qTreePresent(childP, indent, prefix, tLevel); } else - LM_T(LmtQ, ("%s:%s%s (presentation TBI)", prefix, indentV, qNodeType(qP->type))); + LM_T(tLevel, ("%s:%s%s (presentation TBI)", prefix, indentV, qNodeType(qP->type))); } @@ -124,11 +124,11 @@ static void qTreePresent(QNode* qP, int indent, const char* prefix) // // qPresent - // -void qPresent(QNode* qP, const char* prefix, const char* what) +void qPresent(QNode* qP, const char* prefix, const char* what, TraceLevels tLevel) { - LM_T(LmtQ, ("%s: --------------------- %s -----------------------------------", prefix, what)); - qTreePresent(qP, 0, prefix); - LM_T(LmtQ, ("%s: --------------------------------------------------------", prefix)); + LM_T(tLevel, ("%s: --------------------- %s -----------------------------------", prefix, what)); + qTreePresent(qP, 0, prefix, tLevel); + LM_T(tLevel, ("%s: --------------------------------------------------------", prefix)); } diff --git a/src/lib/orionld/q/qPresent.h b/src/lib/orionld/q/qPresent.h index 53f7d82081..1a2f847bf2 100644 --- a/src/lib/orionld/q/qPresent.h +++ b/src/lib/orionld/q/qPresent.h @@ -25,6 +25,8 @@ * * Author: Ken Zangelin */ +#include "logMsg/logMsg.h" // TraceLevels + #include "orionld/q/QNode.h" // QNode @@ -33,7 +35,7 @@ // // qPresent - // -extern void qPresent(QNode* qP, const char* prefix, const char* what); +extern void qPresent(QNode* qP, const char* prefix, const char* what, TraceLevels tLevel); diff --git a/src/lib/orionld/rest/OrionLdRestService.h b/src/lib/orionld/rest/OrionLdRestService.h index c8b762d0c0..358ec62459 100644 --- a/src/lib/orionld/rest/OrionLdRestService.h +++ b/src/lib/orionld/rest/OrionLdRestService.h @@ -179,6 +179,8 @@ typedef struct OrionLdRestService uint32_t options; // Peculiarities of this type of requests (bitmask) uint64_t uriParams; // Supported URI parameters (bitmask) bool isBatchOp; // true for BATCH operations + bool notImplemented; // Flags that the service hasn't been implemented + bool mintaka; // Flags that the service is not for Orion-LD but for Mintaka } OrionLdRestService; diff --git a/src/lib/orionld/rest/orionldMhdConnectionInit.cpp b/src/lib/orionld/rest/orionldMhdConnectionInit.cpp index a76d361295..737b1b663d 100644 --- a/src/lib/orionld/rest/orionldMhdConnectionInit.cpp +++ b/src/lib/orionld/rest/orionldMhdConnectionInit.cpp @@ -1049,6 +1049,13 @@ MHD_Result orionldMhdConnectionInit if (orionldState.serviceP == NULL) // 405 or 404 - no need to continue - prettyPrint not possible here return MHD_YES; + if (orionldState.serviceP->mintaka == true) + return MHD_YES; + + if (orionldState.serviceP->notImplemented == true) + return MHD_YES; + + // // 5. GET URI params // diff --git a/src/lib/orionld/rest/orionldMhdConnectionTreat.cpp b/src/lib/orionld/rest/orionldMhdConnectionTreat.cpp index b898eded56..135efc628e 100644 --- a/src/lib/orionld/rest/orionldMhdConnectionTreat.cpp +++ b/src/lib/orionld/rest/orionldMhdConnectionTreat.cpp @@ -940,6 +940,21 @@ MHD_Result orionldMhdConnectionTreat(void) promCounterIncrease(promNgsildRequests); + if (orionldState.serviceP == NULL) + goto respond; + + if (orionldState.serviceP->mintaka == true) + { + serviceRoutineResult = orionldState.serviceP->serviceRoutine(); + goto respond; + } + + if (orionldState.serviceP->notImplemented == true) + { + serviceRoutineResult = orionldState.serviceP->serviceRoutine(); + goto respond; + } + // If OPTIONS verb, we skip all checks, go straight to the service routine if (orionldState.verb == OPTIONS) goto serviceRoutine; diff --git a/src/lib/orionld/rest/orionldServiceInit.cpp b/src/lib/orionld/rest/orionldServiceInit.cpp index 3b03e0654e..002329331f 100644 --- a/src/lib/orionld/rest/orionldServiceInit.cpp +++ b/src/lib/orionld/rest/orionldServiceInit.cpp @@ -73,13 +73,23 @@ #include "orionld/serviceRoutines/orionldGetEntityType.h" // orionldGetEntityType #include "orionld/serviceRoutines/orionldGetEntityAttributes.h" // orionldGetEntityAttributes #include "orionld/serviceRoutines/orionldGetEntityAttribute.h" // orionldGetEntityAttribute -#include "orionld/serviceRoutines/orionldPostTemporalEntities.h" // orionldPostTemporalEntities + #include "orionld/serviceRoutines/orionldGetContexts.h" // orionldGetContexts #include "orionld/serviceRoutines/orionldGetContext.h" // orionldGetContext #include "orionld/serviceRoutines/orionldPostContexts.h" // orionldPostContexts #include "orionld/serviceRoutines/orionldDeleteContext.h" // orionldDeleteContext #include "orionld/serviceRoutines/orionldPostNotify.h" // orionldPostNotify +#include "orionld/serviceRoutines/orionldGetTemporalEntities.h" // orionldGetTemporalEntities +#include "orionld/serviceRoutines/orionldGetTemporalEntity.h" // orionldGetTemporalEntity +#include "orionld/serviceRoutines/orionldPostTemporalQuery.h" // orionldPostTemporalQuery +#include "orionld/serviceRoutines/orionldPostTemporalEntities.h" // orionldPostTemporalEntities +#include "orionld/serviceRoutines/orionldDeleteTemporalAttribute.h" // orionldDeleteTemporalAttribute +#include "orionld/serviceRoutines/orionldDeleteTemporalAttributeInstance.h" // orionldDeleteTemporalAttributeInstance +#include "orionld/serviceRoutines/orionldDeleteTemporalEntity.h" // orionldDeleteTemporalEntity +#include "orionld/serviceRoutines/orionldPatchTemporalAttributeInstance.h" // orionldPatchTemporalAttributeInstance +#include "orionld/serviceRoutines/orionldPostTemporalAttributes.h" // orionldPostTemporalAttributes + #include "orionld/troe/troePostEntities.h" // troePostEntities #include "orionld/troe/troePostBatchDelete.h" // troePostBatchDelete #include "orionld/troe/troeDeleteAttribute.h" // troeDeleteAttribute @@ -403,6 +413,24 @@ static void restServicePrepare(OrionLdRestService* serviceP, OrionLdRestServiceS serviceP->options |= ORIONLD_SERVICE_OPTION_PREFETCH_ID_AND_TYPE; serviceP->options |= ORIONLD_SERVICE_OPTION_EXPAND_TYPE; } + else if (serviceP->serviceRoutine == orionldGetTemporalEntities) + serviceP->mintaka = true; + else if (serviceP->serviceRoutine == orionldGetTemporalEntity) + serviceP->mintaka = true; + else if (serviceP->serviceRoutine == orionldPostTemporalQuery) + serviceP->mintaka = true; + else if (serviceP->serviceRoutine == orionldDeleteTemporalAttribute) + serviceP->notImplemented = true; + else if (serviceP->serviceRoutine == orionldDeleteTemporalAttributeInstance) + serviceP->notImplemented = true; + else if (serviceP->serviceRoutine == orionldDeleteTemporalEntity) + serviceP->notImplemented = true; + else if (serviceP->serviceRoutine == orionldPatchTemporalAttributeInstance) + serviceP->notImplemented = true; + else if (serviceP->serviceRoutine == orionldPostTemporalAttributes) + serviceP->notImplemented = true; + + else if (serviceP->serviceRoutine == orionldGetVersion) { serviceP->options = 0; // Tenant is Ignored diff --git a/src/lib/orionld/serviceRoutines/CMakeLists.txt b/src/lib/orionld/serviceRoutines/CMakeLists.txt index e31ce26b03..2d0b103cba 100644 --- a/src/lib/orionld/serviceRoutines/CMakeLists.txt +++ b/src/lib/orionld/serviceRoutines/CMakeLists.txt @@ -44,7 +44,6 @@ SET (SOURCES orionldGetContext.cpp orionldGetContexts.cpp orionldGetVersion.cpp - orionldNotImplemented.cpp orionldPostBatchCreate.cpp orionldPostBatchUpsert.cpp orionldPostBatchUpdate.cpp @@ -67,6 +66,11 @@ SET (SOURCES orionldOptions.cpp orionldPutEntity.cpp orionldPutAttribute.cpp + orionldDeleteTemporalAttribute.cpp + orionldDeleteTemporalAttributeInstance.cpp + orionldDeleteTemporalEntity.cpp + orionldPatchTemporalAttributeInstance.cpp + orionldPostTemporalAttributes.cpp ) # Include directories diff --git a/src/lib/orionld/serviceRoutines/orionldDeleteTemporalAttribute.cpp b/src/lib/orionld/serviceRoutines/orionldDeleteTemporalAttribute.cpp new file mode 100644 index 0000000000..e0c67c5f25 --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldDeleteTemporalAttribute.cpp @@ -0,0 +1,43 @@ +/* +* +* 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" + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/common/orionldError.h" // orionldError +#include "orionld/rest/OrionLdRestService.h" // OrionLdRestService +#include "orionld/serviceRoutines/orionldDeleteTemporalAttribute.h" // Own Interface + + + +// ---------------------------------------------------------------------------- +// +// orionldDeleteTemporalAttribute - +// +bool orionldDeleteTemporalAttribute(void) +{ + orionldError(OrionldOperationNotSupported, "Not Implemented", orionldState.serviceP->url, 501); + orionldState.noLinkHeader = true; // We don't want the Link header for non-implemented requests + return false; +} diff --git a/src/lib/orionld/serviceRoutines/orionldDeleteTemporalAttribute.h b/src/lib/orionld/serviceRoutines/orionldDeleteTemporalAttribute.h new file mode 100644 index 0000000000..08e475ee29 --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldDeleteTemporalAttribute.h @@ -0,0 +1,37 @@ +#ifndef SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDDELETETEMPORALATTRIBUTE_H_ +#define SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDDELETETEMPORALATTRIBUTE_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 +*/ + + + +// ---------------------------------------------------------------------------- +// +// orionldDeleteTemporalAttribute - +// +extern bool orionldDeleteTemporalAttribute(void); + +#endif // SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDDELETETEMPORALATTRIBUTE_H_ diff --git a/src/lib/orionld/serviceRoutines/orionldDeleteTemporalAttributeInstance.cpp b/src/lib/orionld/serviceRoutines/orionldDeleteTemporalAttributeInstance.cpp new file mode 100644 index 0000000000..e586049d2c --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldDeleteTemporalAttributeInstance.cpp @@ -0,0 +1,43 @@ +/* +* +* 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" + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/common/orionldError.h" // orionldError +#include "orionld/rest/OrionLdRestService.h" // OrionLdRestService +#include "orionld/serviceRoutines/orionldDeleteTemporalAttributeInstance.h" // Own Interface + + + +// ---------------------------------------------------------------------------- +// +// orionldDeleteTemporalAttributeInstance - +// +bool orionldDeleteTemporalAttributeInstance(void) +{ + orionldError(OrionldOperationNotSupported, "Not Implemented", orionldState.serviceP->url, 501); + orionldState.noLinkHeader = true; // We don't want the Link header for non-implemented requests + return false; +} diff --git a/src/lib/orionld/serviceRoutines/orionldDeleteTemporalAttributeInstance.h b/src/lib/orionld/serviceRoutines/orionldDeleteTemporalAttributeInstance.h new file mode 100644 index 0000000000..4c81de9e71 --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldDeleteTemporalAttributeInstance.h @@ -0,0 +1,37 @@ +#ifndef SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDDELETETEMPORALATTRIBUTEINSTANCE_H_ +#define SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDDELETETEMPORALATTRIBUTEINSTANCE_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 +*/ + + + +// ---------------------------------------------------------------------------- +// +// orionldDeleteTemporalAttributeInstance - +// +extern bool orionldDeleteTemporalAttributeInstance(void); + +#endif // SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDDELETETEMPORALATTRIBUTEINSTANCE_H_ diff --git a/src/lib/orionld/serviceRoutines/orionldDeleteTemporalEntity.cpp b/src/lib/orionld/serviceRoutines/orionldDeleteTemporalEntity.cpp new file mode 100644 index 0000000000..c76acdeecb --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldDeleteTemporalEntity.cpp @@ -0,0 +1,43 @@ +/* +* +* 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" + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/common/orionldError.h" // orionldError +#include "orionld/rest/OrionLdRestService.h" // OrionLdRestService +#include "orionld/serviceRoutines/orionldDeleteTemporalEntity.h" // Own Interface + + + +// ---------------------------------------------------------------------------- +// +// orionldDeleteTemporalEntity - +// +bool orionldDeleteTemporalEntity(void) +{ + orionldError(OrionldOperationNotSupported, "Not Implemented", orionldState.serviceP->url, 501); + orionldState.noLinkHeader = true; // We don't want the Link header for non-implemented requests + return false; +} diff --git a/src/lib/orionld/serviceRoutines/orionldDeleteTemporalEntity.h b/src/lib/orionld/serviceRoutines/orionldDeleteTemporalEntity.h new file mode 100644 index 0000000000..d56bb7dd45 --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldDeleteTemporalEntity.h @@ -0,0 +1,37 @@ +#ifndef SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDDELETETEMPORALENTITY_H_ +#define SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDDELETETEMPORALENTITY_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 +*/ + + + +// ---------------------------------------------------------------------------- +// +// orionldDeleteTemporalEntity - +// +extern bool orionldDeleteTemporalEntity(void); + +#endif // SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDDELETETEMPORALENTITY_H_ diff --git a/src/lib/orionld/serviceRoutines/orionldGetSubscription.cpp b/src/lib/orionld/serviceRoutines/orionldGetSubscription.cpp index 496002d845..73e7448bf5 100644 --- a/src/lib/orionld/serviceRoutines/orionldGetSubscription.cpp +++ b/src/lib/orionld/serviceRoutines/orionldGetSubscription.cpp @@ -26,6 +26,7 @@ extern "C" { +#include "kalloc/kaStrdup.h" // kaStrdup #include "kjson/KjNode.h" // KjNode #include "kjson/kjBuilder.h" // kjString, kjInteger, kjChildAdd #include "kjson/kjLookup.h" // kjLookup @@ -37,8 +38,10 @@ extern "C" #include "orionld/common/orionldError.h" // orionldError #include "orionld/common/numberToDate.h" // numberToDate #include "cache/subCache.h" // CachedSubscription, subCacheItemLookup +#include "orionld/pernot/pernotSubCacheLookup.h" // pernotSubCacheLookup #include "orionld/legacyDriver/legacyGetSubscription.h" // legacyGetSubscription #include "orionld/kjTree/kjTreeFromCachedSubscription.h" // kjTreeFromCachedSubscription +#include "orionld/kjTree/kjTreeFromPernotSubscription.h" // kjTreeFromPernotSubscription #include "orionld/payloadCheck/PCHECK.h" // PCHECK_URI #include "orionld/dbModel/dbModelToApiSubscription.h" // dbModelToApiSubscription #include "orionld/mongoc/mongocSubscriptionLookup.h" // mongocSubscriptionLookup @@ -52,8 +55,13 @@ extern "C" // static void subTimestampSet(KjNode* apiSubP, const char* fieldName, double valueInSubCache) { - if (valueInSubCache <= 0) + if (valueInSubCache <= 0.1) + { + KjNode* timestampNodeP = kjLookup(apiSubP, fieldName); + if (timestampNodeP != NULL) + kjChildRemove(apiSubP, timestampNodeP); return; + } KjNode* timestampNodeP = kjLookup(apiSubP, fieldName); @@ -67,9 +75,8 @@ static void subTimestampSet(KjNode* apiSubP, const char* fieldName, double value } else { - // We have to assume the current timestamp has a char buffer that is of sufficient length. - // We actually know that, as the string was initially created as a timestamp - strncpy(timestampNodeP->value.s, dateTime, 64); + timestampNodeP->type = KjString; + timestampNodeP->value.s = kaStrdup(&orionldState.kalloc, dateTime); } } @@ -101,9 +108,19 @@ static void subCounterSet(KjNode* apiSubP, const char* fieldName, int64_t valueI // // orionldSubCounters - FIXME: Own Module // -void orionldSubCounters(KjNode* apiSubP, CachedSubscription* cSubP) +// Three options: +// 1. cSubP given +// 2. pSubP given +// 3, None of them (look up apiSubP::subscriptionId in both cashes) +// +void orionldSubCounters(KjNode* apiSubP, CachedSubscription* cSubP, PernotSubscription* pSubP) { - if (cSubP == NULL) + KjNode* notificationP = kjLookup(apiSubP, "notification"); + + if (notificationP == NULL) + LM_RVE(("API Subscription without a notification field !!!")); + + if ((cSubP == NULL) && (pSubP == NULL)) { KjNode* subIdP = kjLookup(apiSubP, "id"); @@ -111,19 +128,42 @@ void orionldSubCounters(KjNode* apiSubP, CachedSubscription* cSubP) return; cSubP = subCacheItemLookup(orionldState.tenantP->tenant, subIdP->value.s); + if (cSubP == NULL) - return; + { + pSubP = pernotSubCacheLookup(orionldState.tenantP->tenant, subIdP->value.s); + if (pSubP == NULL) + LM_RVE(("Can't find subscription '%s' in any subscription cache", subIdP->value.s)); + } } - KjNode* notificationNodeP = kjLookup(apiSubP, "notification"); + double lastNotificationTime = (cSubP != NULL)? cSubP->lastNotificationTime : pSubP->lastNotificationTime; + double lastSuccess = (cSubP != NULL)? cSubP->lastSuccess : pSubP->lastSuccessTime; + double lastFailure = (cSubP != NULL)? cSubP->lastFailure : pSubP->lastFailureTime; + int timesSent = 0; + int timesFailed = 0; + int noMatch = 0; - if (notificationNodeP != NULL) + if (cSubP != NULL) { - subTimestampSet(notificationNodeP, "lastNotification", cSubP->lastNotificationTime); - subTimestampSet(notificationNodeP, "lastSuccess", cSubP->lastSuccess); - subTimestampSet(notificationNodeP, "lastFailure", cSubP->lastFailure); - subCounterSet(notificationNodeP, "timesSent", cSubP->dbCount + cSubP->count); + timesSent = cSubP->dbCount + cSubP->count; + timesFailed = cSubP->dbFailures + cSubP->failures; } + else + { + timesSent = pSubP->notificationAttempts + pSubP->notificationAttemptsDb; + timesFailed = pSubP->notificationErrors + pSubP->notificationErrorsDb; + noMatch = pSubP->noMatch + pSubP->noMatchDb; + } + // + // Set the values + // + subTimestampSet(notificationP, "lastNotification", lastNotificationTime); + subTimestampSet(notificationP, "lastSuccess", lastSuccess); + subTimestampSet(notificationP, "lastFailure", lastFailure); + subCounterSet(notificationP, "timesSent", timesSent); + subCounterSet(notificationP, "timesFailed", timesFailed); + subCounterSet(notificationP, "noMatch", noMatch); } @@ -143,12 +183,14 @@ static bool orionldGetSubscriptionFromDb(void) orionldError(OrionldResourceNotFound, "Subscription Not Found", orionldState.wildcard[0], 404); return false; } + kjTreeLog(dbSubP, "DB Sub", LmtSubCacheStats); KjNode* coordinatesNodeP = NULL; // Not needed here, but dbModelToApiSubscription requires it KjNode* contextNodeP = NULL; // Not needed here, but dbModelToApiSubscription requires it KjNode* showChangesP = NULL; // Not needed here, but dbModelToApiSubscription requires it KjNode* sysAttrsP = NULL; // Not needed here, but dbModelToApiSubscription requires it RenderFormat renderFormat = RF_NORMALIZED; // Not needed here, but dbModelToApiSubscription requires it + double timeInterval = 0; KjNode* apiSubP = dbModelToApiSubscription(dbSubP, orionldState.tenantP->tenant, false, @@ -157,7 +199,8 @@ static bool orionldGetSubscriptionFromDb(void) &contextNodeP, &showChangesP, &sysAttrsP, - &renderFormat); + &renderFormat, + &timeInterval); if (apiSubP == NULL) { @@ -166,10 +209,18 @@ static bool orionldGetSubscriptionFromDb(void) } // Need to take counters and timestamps from sub-cache - CachedSubscription* cSubP = subCacheItemLookup(orionldState.tenantP->tenant, orionldState.wildcard[0]); - - if (cSubP != NULL) - orionldSubCounters(apiSubP, cSubP); + if (timeInterval == 0) + { + CachedSubscription* cSubP = subCacheItemLookup(orionldState.tenantP->tenant, orionldState.wildcard[0]); + if (cSubP != NULL) + orionldSubCounters(apiSubP, cSubP, NULL); + } + else + { + PernotSubscription* pSubP = pernotSubCacheLookup(orionldState.wildcard[0], orionldState.tenantP->tenant); + if (pSubP != NULL) + orionldSubCounters(apiSubP, NULL, pSubP); + } orionldState.httpStatusCode = 200; orionldState.responseTree = apiSubP; @@ -199,13 +250,28 @@ bool orionldGetSubscription(void) return orionldGetSubscriptionFromDb(); } - char* subscriptionId = orionldState.wildcard[0]; - CachedSubscription* cSubP = subCacheItemLookup(orionldState.tenantP->tenant, subscriptionId); + char* subscriptionId = orionldState.wildcard[0]; + // "Normal" (onchange) subscription? + CachedSubscription* cSubP = subCacheItemLookup(orionldState.tenantP->tenant, subscriptionId); if (cSubP != NULL) { orionldState.httpStatusCode = 200; + orionldState.responseTree = kjTreeFromCachedSubscription(cSubP, orionldState.uriParamOptions.sysAttrs, orionldState.out.contentType == JSONLD); + orionldSubCounters(orionldState.responseTree, cSubP, NULL); + + return true; + } + + // pernot subscription? + PernotSubscription* pSubP = pernotSubCacheLookup(subscriptionId, orionldState.tenantP->tenant); + if (pSubP != NULL) + { + orionldState.httpStatusCode = 200; + + orionldState.responseTree = kjTreeFromPernotSubscription(pSubP, orionldState.uriParamOptions.sysAttrs, orionldState.out.contentType == JSONLD); + orionldSubCounters(orionldState.responseTree, NULL, pSubP); return true; } diff --git a/src/lib/orionld/serviceRoutines/orionldGetSubscriptions.cpp b/src/lib/orionld/serviceRoutines/orionldGetSubscriptions.cpp index 611e1ee585..4d91d92bd5 100644 --- a/src/lib/orionld/serviceRoutines/orionldGetSubscriptions.cpp +++ b/src/lib/orionld/serviceRoutines/orionldGetSubscriptions.cpp @@ -36,6 +36,7 @@ extern "C" #include "orionld/common/orionldError.h" // orionldError #include "orionld/types/OrionldHeader.h" // orionldHeaderAdd, HttpResultsCount #include "orionld/common/tenantList.h" // tenant0 +#include "orionld/kjTree/kjTreeFromPernotSubscription.h" // kjTreeFromPernotSubscription #include "orionld/legacyDriver/legacyGetSubscriptions.h" // legacyGetSubscriptions #include "orionld/kjTree/kjTreeFromCachedSubscription.h" // kjTreeFromCachedSubscription #include "orionld/mongoc/mongocSubscriptionsGet.h" // mongocSubscriptionsGet @@ -67,7 +68,7 @@ static bool tenantMatch(OrionldTenant* requestTenantP, const char* subscriptionT } -extern void orionldSubCounters(KjNode* apiSubP, CachedSubscription* cSubP); +extern void orionldSubCounters(KjNode* apiSubP, CachedSubscription* cSubP, PernotSubscription* pSubP); // ----------------------------------------------------------------------------- // // orionldGetSubscriptionsFromDb - @@ -113,6 +114,7 @@ static bool orionldGetSubscriptionsFromDb(void) KjNode* showChangesP = NULL; KjNode* sysAttrsP = NULL; RenderFormat renderFormat = RF_NORMALIZED; + double timeInterval = 0; KjNode* apiSubP = dbModelToApiSubscription(dbSubP, orionldState.tenantP->tenant, false, @@ -121,7 +123,8 @@ static bool orionldGetSubscriptionsFromDb(void) &contextNodeP, &showChangesP, &sysAttrsP, - &renderFormat); + &renderFormat, + &timeInterval); if (apiSubP == NULL) { @@ -138,7 +141,7 @@ static bool orionldGetSubscriptionsFromDb(void) kjChildAdd(apiSubP, nodeP); } - orionldSubCounters(apiSubP, NULL); + orionldSubCounters(apiSubP, NULL, NULL); kjChildAdd(apiSubV, apiSubP); } @@ -148,6 +151,43 @@ static bool orionldGetSubscriptionsFromDb(void) +// ---------------------------------------------------------------------------- +// +// subCacheCount - move to subCache.cpp +// +int subCacheCount(void) +{ + int count = 0; + for (CachedSubscription* cSubP = subCacheHeadGet(); cSubP != NULL; cSubP = cSubP->next) + { + if (tenantMatch(orionldState.tenantP, cSubP->tenant) == true) + ++count; + } + + return count; +} + + + +// ---------------------------------------------------------------------------- +// +// pernotSubCacheCount - move to pernotSubCacheCount.cpp +// +int pernotSubCacheCount(void) +{ + int count = 0; + + for (PernotSubscription* pSubP = pernotSubCache.head; pSubP != NULL; pSubP = pSubP->next) + { + if (pSubP->tenantP == orionldState.tenantP) + ++count; + } + + return count; +} + + + // ---------------------------------------------------------------------------- // // orionldGetSubscriptions - @@ -158,12 +198,7 @@ bool orionldGetSubscriptions(void) return legacyGetSubscriptions(); if (orionldState.uriParamOptions.fromDb == true) - { - // - // GET Subscriptions with mongoc - // return orionldGetSubscriptionsFromDb(); - } // // Not Legacy, not "From DB" - Getting the subscriptions from the subscription cache @@ -171,50 +206,72 @@ bool orionldGetSubscriptions(void) int offset = orionldState.uriParams.offset; int limit = orionldState.uriParams.limit; + int subs = 0; KjNode* subArray = kjArray(orionldState.kjsonP, NULL); int ix = 0; - int subs = 0; - - if (orionldState.uriParams.count == true) // Empty loop over the subscriptions, just to count how many there are - { - int count = 0; - for (CachedSubscription* cSubP = subCacheHeadGet(); cSubP != NULL; cSubP = cSubP->next) - { - if (tenantMatch(orionldState.tenantP, cSubP->tenant) == true) - ++count; - } + int cSubs = subCacheCount(); + int pSubs = pernotSubCacheCount(); - orionldHeaderAdd(&orionldState.out.headers, HttpResultsCount, NULL, count); - } + if (orionldState.uriParams.count == true) + orionldHeaderAdd(&orionldState.out.headers, HttpResultsCount, NULL, cSubs + pSubs); if (limit != 0) { - for (CachedSubscription* cSubP = subCacheHeadGet(); cSubP != NULL; cSubP = cSubP->next) + // Start with Pernot Subscriptions + if (offset < pSubs) { - if (tenantMatch(orionldState.tenantP, cSubP->tenant) == false) - continue; - - if (ix < offset) + for (PernotSubscription* pSubP = pernotSubCache.head; pSubP != NULL; pSubP = pSubP->next) { - ++ix; - continue; + if (pSubP->tenantP == orionldState.tenantP) + { + KjNode* subP = kjTreeFromPernotSubscription(pSubP, orionldState.uriParamOptions.sysAttrs, orionldState.out.contentType == JSONLD); + + if (subP == NULL) + { + LM_E(("Internal Error (kjTreeFromPernotSubscription failed for subscription '%s')", pSubP->subscriptionId)); + ++ix; + continue; + } + + kjChildAdd(subArray, subP); + ++ix; + ++subs; + + if (subs >= limit) + break; + } } + } - KjNode* subP = kjTreeFromCachedSubscription(cSubP, orionldState.uriParamOptions.sysAttrs, orionldState.out.contentType == JSONLD); - - if (subP == NULL) + if (subs < limit) + { + for (CachedSubscription* cSubP = subCacheHeadGet(); cSubP != NULL; cSubP = cSubP->next) { - LM_E(("Internal Error (kjTreeFromCachedSubscription failed for subscription '%s')", cSubP->subscriptionId)); - ++ix; - continue; - } + if (tenantMatch(orionldState.tenantP, cSubP->tenant) == false) + continue; - kjChildAdd(subArray, subP); - ++ix; - ++subs; + if (ix < offset) + { + ++ix; + continue; + } - if (subs >= limit) - break; + KjNode* subP = kjTreeFromCachedSubscription(cSubP, orionldState.uriParamOptions.sysAttrs, orionldState.out.contentType == JSONLD); + + if (subP == NULL) + { + LM_E(("Internal Error (kjTreeFromCachedSubscription failed for subscription '%s')", cSubP->subscriptionId)); + ++ix; + continue; + } + + kjChildAdd(subArray, subP); + ++ix; + ++subs; + + if (subs >= limit) + break; + } } } else diff --git a/src/lib/orionld/serviceRoutines/orionldPatchSubscription.cpp b/src/lib/orionld/serviceRoutines/orionldPatchSubscription.cpp index f7a0be8961..2f2a695330 100644 --- a/src/lib/orionld/serviceRoutines/orionldPatchSubscription.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPatchSubscription.cpp @@ -823,6 +823,7 @@ bool orionldPatchSubscription(void) KjNode* notifierInfoP = NULL; KjNode* showChangesP = NULL; KjNode* sysAttrsP = NULL; + double timeInterval = 0; RenderFormat renderFormat = RF_NORMALIZED; bool r; @@ -843,6 +844,7 @@ bool orionldPatchSubscription(void) &mqttChange, &showChangesP, &sysAttrsP, + &timeInterval, &renderFormat); if (r == false) { @@ -865,6 +867,50 @@ bool orionldPatchSubscription(void) return false; } + KjNode* dbTimeIntervalP = kjLookup(dbSubscriptionP, "timeInterval"); + double dbTimeInterval = 0; + + if (dbTimeIntervalP != NULL) + dbTimeInterval = (dbTimeIntervalP->type == KjInt)? dbTimeIntervalP->value.i : dbTimeIntervalP->value.f; + + bool subWasPernot = (dbTimeInterval != 0); + + // + // If the subscription used to be "on-change", timeInterval cannot be set + // + if (subWasPernot == false) + { + if (timeInterval != 0) + { + if (qNodeP != NULL) + qRelease(qNodeP); + orionldError(OrionldBadRequestData, "Invalid modification (on-change to timeInterval subscription)", subscriptionId, 400); + return false; + } + } + else // If the subscription used to be "pernot", watchedAttributes+throtttling cannot be set + { +#if 0 + // + // These checks really belong to pCheckSubscription() - just need to pass it the dbTimeInterval value + // + KjNode* watchedAttributesP = kjLookup(subTree, "watchedAttributes"); + KjNode* throttlingP = kjLookup(subTree, "throttling"); + + if (watchedAttributesP != NULL) + orionldError(OrionldBadRequestData, "Invalid modification (pernot subscription cannot have watchedAttributes", subscriptionId, 400); + if (throttlingP != NULL) + orionldError(OrionldBadRequestData, "Invalid modification (pernot subscription cannot have throttlingP", subscriptionId, 400); + + if ((watchedAttributesP != NULL) || (throttlingP != NULL)) + return false; +#else + orionldError(OrionldOperationNotSupported, "Not Implemented", "Patching of periodic notification subscriptions", 501); + return false; +#endif + } + + // // If the subscription used to be an MQTT subscription, the MQTT connection might need closing // Only if MQTT data is modified though (or it stops being an MQTT subscription) @@ -913,7 +959,19 @@ bool orionldPatchSubscription(void) // modified. // ngsildSubscriptionPatch() performs that modification. // - CachedSubscription* cSubP = subCacheItemLookup(orionldState.tenantP->tenant, subscriptionId); + CachedSubscription* cSubP = NULL; + + if (timeInterval == 0) + { + cSubP = subCacheItemLookup(orionldState.tenantP->tenant, subscriptionId); + if (cSubP == NULL) + { + orionldError(OrionldResourceNotFound, "Subscription not found", subscriptionId, 404); + return false; + } + } + else + LM_X(131, ("Can't reach this point, right? ;-)")); if (ngsildSubscriptionPatch(dbSubscriptionP, cSubP, orionldState.requestTree, qP, geoqP, qRenderedForDb) == false) { @@ -973,8 +1031,16 @@ bool orionldPatchSubscription(void) } // Modify the subscription in the subscription cache - if (subCacheItemUpdate(orionldState.tenantP, subscriptionId, patchBody, geoCoordinatesP, qNodeP, qRenderedForDb, showChangesP) == false) - LM_E(("Internal Error (unable to update the cached subscription '%s' after a PATCH)", subscriptionId)); + if (timeInterval == 0) + { + if (subCacheItemUpdate(orionldState.tenantP, subscriptionId, patchBody, geoCoordinatesP, qNodeP, qRenderedForDb, showChangesP) == false) + LM_E(("Internal Error (unable to update the cached subscription '%s' after a PATCH)", subscriptionId)); + } + else + { + // Update the subscription in the pernot-cache + LM_X(1, ("Implement PATCH for pernot subscriptions!")); + } // All OK? 204 No Content orionldState.httpStatusCode = 204; diff --git a/src/lib/orionld/serviceRoutines/orionldPatchTemporalAttributeInstance.cpp b/src/lib/orionld/serviceRoutines/orionldPatchTemporalAttributeInstance.cpp new file mode 100644 index 0000000000..66a2b710d7 --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldPatchTemporalAttributeInstance.cpp @@ -0,0 +1,43 @@ +/* +* +* 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" + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/common/orionldError.h" // orionldError +#include "orionld/rest/OrionLdRestService.h" // OrionLdRestService +#include "orionld/serviceRoutines/orionldPatchTemporalAttributeInstance.h" // Own Interface + + + +// ---------------------------------------------------------------------------- +// +// orionldPatchTemporalAttributeInstance - +// +bool orionldPatchTemporalAttributeInstance(void) +{ + orionldError(OrionldOperationNotSupported, "Not Implemented", orionldState.serviceP->url, 501); + orionldState.noLinkHeader = true; // We don't want the Link header for non-implemented requests + return false; +} diff --git a/src/lib/orionld/serviceRoutines/orionldPatchTemporalAttributeInstance.h b/src/lib/orionld/serviceRoutines/orionldPatchTemporalAttributeInstance.h new file mode 100644 index 0000000000..3d1301b102 --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldPatchTemporalAttributeInstance.h @@ -0,0 +1,37 @@ +#ifndef SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDPATCHTEMPORALATTRIBUTEINSTANCE_H_ +#define SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDPATCHTEMPORALATTRIBUTEINSTANCE_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 +*/ + + + +// ---------------------------------------------------------------------------- +// +// orionldPatchTemporalAttributeInstance - +// +extern bool orionldPatchTemporalAttributeInstance(void); + +#endif // SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDPATCHTEMPORALATTRIBUTEINSTANCE_H_ diff --git a/src/lib/orionld/serviceRoutines/orionldPostSubscriptions.cpp b/src/lib/orionld/serviceRoutines/orionldPostSubscriptions.cpp index 2342d97820..3d532f1c26 100644 --- a/src/lib/orionld/serviceRoutines/orionldPostSubscriptions.cpp +++ b/src/lib/orionld/serviceRoutines/orionldPostSubscriptions.cpp @@ -22,8 +22,6 @@ * * Author: Ken Zangelin */ -#include "logMsg/logMsg.h" // LM_* - extern "C" { #include "kbase/kMacros.h" // K_FT @@ -31,8 +29,11 @@ extern "C" #include "kjson/KjNode.h" // KjNode #include "kjson/kjLookup.h" // kjLookup #include "kjson/kjBuilder.h" // kjString, kjChildAdd, ... +#include "kjson/kjClone.h" // kjClone } +#include "logMsg/logMsg.h" // LM_* + #include "rest/httpHeaderAdd.h" // httpHeaderLocationAdd #include "cache/subCache.h" // subCacheItemLookup, CachedSubscription @@ -45,6 +46,11 @@ extern "C" #include "orionld/dbModel/dbModelFromApiSubscription.h" // dbModelFromApiSubscription #include "orionld/mongoc/mongocSubscriptionExists.h" // mongocSubscriptionExists #include "orionld/mongoc/mongocSubscriptionInsert.h" // mongocSubscriptionInsert +#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/pernotSubCacheLookup.h" // pernotSubCacheLookup #include "orionld/mqtt/mqttParse.h" // mqttParse #include "orionld/mqtt/mqttConnectionEstablish.h" // mqttConnectionEstablish #include "orionld/mqtt/mqttDisconnect.h" // mqttDisconnect @@ -83,6 +89,7 @@ bool orionldPostSubscriptions(void) bool qIsMq = false; KjNode* showChangesP = NULL; KjNode* sysAttrsP = NULL; + double timeInterval = 0; RenderFormat renderFormat = RF_NORMALIZED; b = pCheckSubscription(subP, @@ -102,10 +109,11 @@ bool orionldPostSubscriptions(void) &mqtt, &showChangesP, &sysAttrsP, + &timeInterval, &renderFormat); if (qRenderedForDb != NULL) - LM_T(LmtSR, ("qRenderedForDb: '%s'", qRenderedForDb)); + LM_T(LmtQ, ("qRenderedForDb: '%s'", qRenderedForDb)); if (b == false) { @@ -116,18 +124,18 @@ bool orionldPostSubscriptions(void) } // Subscription id special treats + char subscriptionId[80]; if (subIdP != NULL) { subId = subIdP->value.s; - // 'id' needs to be '_id' - mongo stuff ... - subIdP->name = (char*) "_id"; // dbModel ... - // // If the subscription already exists, a "409 Conflict" is returned // char* detail = NULL; - if ((subCacheItemLookup(orionldState.tenantP->tenant, subId) != NULL) || (mongocSubscriptionExists(subId, &detail) == true)) + if ((subCacheItemLookup(orionldState.tenantP->tenant, subId) != NULL) || + (pernotSubCacheLookup(orionldState.tenantP->tenant, subId) != NULL) || + (mongocSubscriptionExists(subId, &detail) == true)) { if (detail == NULL) orionldError(OrionldAlreadyExists, "Subscription already exists", subId, 409); @@ -139,14 +147,15 @@ bool orionldPostSubscriptions(void) return false; } + + strncpy(subscriptionId, subId, sizeof(subscriptionId) - 1); } else { - char subscriptionId[80]; strncpy(subscriptionId, "urn:ngsi-ld:subscription:", sizeof(subscriptionId) - 1); uuidGenerate(&subscriptionId[25], sizeof(subscriptionId) - 25, false); - subIdP = kjString(orionldState.kjsonP, "_id", subscriptionId); + subIdP = kjString(orionldState.kjsonP, "id", subscriptionId); } // Add subId to the tree @@ -206,6 +215,12 @@ bool orionldPostSubscriptions(void) if (mqtt == true) { + if (timeInterval != 0) + { + orionldError(OrionldBadRequestData, "Not Implemented", "Notifications in MQTT for Periodic Notification Subscription", 501); + return false; + } + char* detail = NULL; char* uri = kaStrdup(&orionldState.kalloc, uriP->value.s); // Can't destroy uriP->value.s ... mqttParse is destructive! @@ -250,17 +265,46 @@ bool orionldPostSubscriptions(void) } // sub to cache - BEFORE we change the tree to be according to the DB Model (as the DB model might change some day ...) - CachedSubscription* cSubP = subCacheApiSubscriptionInsert(subP, - qTree, - geoCoordinatesP, - orionldState.contextP, - orionldState.tenantP->tenant, - showChangesP, - sysAttrsP, - renderFormat); + CachedSubscription* cSubP = NULL; + PernotSubscription* pSubP = NULL; + + if (timeInterval == 0) + { + cSubP = subCacheApiSubscriptionInsert(subP, + qTree, + geoCoordinatesP, + orionldState.contextP, + orionldState.tenantP->tenant, + showChangesP, + sysAttrsP, + renderFormat); + } + else + { + // Add subscription to the pernot-cache + LM_T(LmtPernot, ("qRenderedForDb: '%s'", qRenderedForDb)); + if (qTree != NULL) + qPresent(qTree, "Pernot", "Q for pernot subscription", LmtPernot); + pSubP = pernotSubCacheAdd(subscriptionId, + subP, + endpointP, + qTree, + geoCoordinatesP, + orionldState.contextP, + orionldState.tenantP, + showChangesP, + sysAttrsP, + renderFormat, + timeInterval); + + // Signal that there's a new Pernot subscription in the cache + // ++pernotSubCache.newSubs; + // LM_T(LmtPernotLoop, ("pernotSubCache.newSubs == %d", pernotSubCache.newSubs)); + } // dbModel KjNode* dbSubscriptionP = subP; + subIdP->name = (char*) "_id"; // 'id' needs to be '_id' - mongo stuff ... dbModelFromApiSubscription(dbSubscriptionP, false); // sub to db - mongocSubscriptionInsert(subP); @@ -271,7 +315,10 @@ bool orionldPostSubscriptions(void) if (mqttSubscription == true) mqttDisconnect(mqttHost, mqttPort, mqttUser, mqttPassword, mqttVersion); - subCacheItemRemove(cSubP); + if (cSubP != NULL) + subCacheItemRemove(cSubP); + else + pernotSubCacheRemove(pSubP); if (qTree != NULL) qRelease(qTree); @@ -281,19 +328,27 @@ bool orionldPostSubscriptions(void) // // MQTT details of the cached subscription + // - For now, timeInterval cannot be done via MQTT // - bzero(&cSubP->httpInfo.mqtt, sizeof(cSubP->httpInfo.mqtt)); - if (mqttSubscription == true) + if (timeInterval == 0) + { + bzero(&cSubP->httpInfo.mqtt, sizeof(cSubP->httpInfo.mqtt)); + if (mqttSubscription == true) + { + cSubP->httpInfo.mqtt.mqtts = mqtts; + cSubP->httpInfo.mqtt.port = mqttPort; + cSubP->httpInfo.mqtt.qos = mqttQoS; + + if (mqttHost != NULL) strncpy(cSubP->httpInfo.mqtt.host, mqttHost, sizeof(cSubP->httpInfo.mqtt.host) - 1); + if (mqttUser != NULL) strncpy(cSubP->httpInfo.mqtt.username, mqttUser, sizeof(cSubP->httpInfo.mqtt.username) - 1); + if (mqttPassword != NULL) strncpy(cSubP->httpInfo.mqtt.password, mqttPassword, sizeof(cSubP->httpInfo.mqtt.password) - 1); + if (mqttVersion != NULL) strncpy(cSubP->httpInfo.mqtt.version, mqttVersion, sizeof(cSubP->httpInfo.mqtt.version) - 1); + if (mqttTopic != NULL) strncpy(cSubP->httpInfo.mqtt.topic, mqttTopic, sizeof(cSubP->httpInfo.mqtt.topic) - 1); + } + } + else if (mqttSubscription == true) { - cSubP->httpInfo.mqtt.mqtts = mqtts; - cSubP->httpInfo.mqtt.port = mqttPort; - cSubP->httpInfo.mqtt.qos = mqttQoS; - - if (mqttHost != NULL) strncpy(cSubP->httpInfo.mqtt.host, mqttHost, sizeof(cSubP->httpInfo.mqtt.host) - 1); - if (mqttUser != NULL) strncpy(cSubP->httpInfo.mqtt.username, mqttUser, sizeof(cSubP->httpInfo.mqtt.username) - 1); - if (mqttPassword != NULL) strncpy(cSubP->httpInfo.mqtt.password, mqttPassword, sizeof(cSubP->httpInfo.mqtt.password) - 1); - if (mqttVersion != NULL) strncpy(cSubP->httpInfo.mqtt.version, mqttVersion, sizeof(cSubP->httpInfo.mqtt.version) - 1); - if (mqttTopic != NULL) strncpy(cSubP->httpInfo.mqtt.topic, mqttTopic, sizeof(cSubP->httpInfo.mqtt.topic) - 1); + // This is already taken care of. } orionldState.httpStatusCode = 201; diff --git a/src/lib/orionld/serviceRoutines/orionldPostTemporalAttributes.cpp b/src/lib/orionld/serviceRoutines/orionldPostTemporalAttributes.cpp new file mode 100644 index 0000000000..91f536112e --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldPostTemporalAttributes.cpp @@ -0,0 +1,43 @@ +/* +* +* 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" + +#include "orionld/common/orionldState.h" // orionldState +#include "orionld/common/orionldError.h" // orionldError +#include "orionld/rest/OrionLdRestService.h" // OrionLdRestService +#include "orionld/serviceRoutines/orionldPostTemporalAttributes.h" // Own Interface + + + +// ---------------------------------------------------------------------------- +// +// orionldPostTemporalAttributes - +// +bool orionldPostTemporalAttributes(void) +{ + orionldError(OrionldOperationNotSupported, "Not Implemented", orionldState.serviceP->url, 501); + orionldState.noLinkHeader = true; // We don't want the Link header for non-implemented requests + return false; +} diff --git a/src/lib/orionld/serviceRoutines/orionldPostTemporalAttributes.h b/src/lib/orionld/serviceRoutines/orionldPostTemporalAttributes.h new file mode 100644 index 0000000000..e2a07441a0 --- /dev/null +++ b/src/lib/orionld/serviceRoutines/orionldPostTemporalAttributes.h @@ -0,0 +1,37 @@ +#ifndef SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDPOSTTEMPORALATTRIBUTES_H_ +#define SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDPOSTTEMPORALATTRIBUTES_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 +*/ + + + +// ---------------------------------------------------------------------------- +// +// orionldPostTemporalAttributes - +// +extern bool orionldPostTemporalAttributes(void); + +#endif // SRC_LIB_ORIONLD_SERVICEROUTINES_ORIONLDPOSTTEMPORALATTRIBUTES_H_ 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 fa3b135c35..a7e6d4e6cd 100644 --- a/test/functionalTest/cases/0000_cli/bool_option_with_value.test +++ b/test/functionalTest/cases/0000_cli/bool_option_with_value.test @@ -77,6 +77,7 @@ Usage: orionld [option '-U' (extended usage)] [option '-corsMaxAge' ] [option '-cprForwardLimit' ] [option '-subCacheIval' ] + [option '-subCacheFlushIval' ] [option '-noCache' (disable subscription cache for lookups)] [option '-connectionMemory' ] [option '-maxConnections' ] @@ -100,6 +101,7 @@ Usage: orionld [option '-U' (extended usage)] [option '-ngsiv1Autocast' (automatic cast for number, booleans and dates in NGSIv1 update/create attribute operations)] [option '-ctxTimeout' ] [option '-ctxAttempts' ] + [option '-pernot' (enable Pernot - Periodic Notifications)] [option '-troe' (enable TRoE - temporal representation of entities)] [option '-troeHost' ] [option '-troePort' ] diff --git a/test/functionalTest/cases/0000_cli/command_line_options.test b/test/functionalTest/cases/0000_cli/command_line_options.test index a0bbce38b5..99bbd2d25f 100644 --- a/test/functionalTest/cases/0000_cli/command_line_options.test +++ b/test/functionalTest/cases/0000_cli/command_line_options.test @@ -66,6 +66,7 @@ Usage: orionld [option '-U' (extended usage)] [option '-corsMaxAge' ] [option '-cprForwardLimit' ] [option '-subCacheIval' ] + [option '-subCacheFlushIval' ] [option '-noCache' (disable subscription cache for lookups)] [option '-connectionMemory' ] [option '-maxConnections' ] @@ -89,6 +90,7 @@ Usage: orionld [option '-U' (extended usage)] [option '-ngsiv1Autocast' (automatic cast for number, booleans and dates in NGSIv1 update/create attribute operations)] [option '-ctxTimeout' ] [option '-ctxAttempts' ] + [option '-pernot' (enable Pernot - Periodic Notifications)] [option '-troe' (enable TRoE - temporal representation of entities)] [option '-troeHost' ] [option '-troePort' ] diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_PATCH_Entity-with-Relationship-array.test b/test/functionalTest/cases/0000_ngsild/ngsild_PATCH_Entity-with-Relationship-array.test new file mode 100644 index 0000000000..55e6a29ac3 --- /dev/null +++ b/test/functionalTest/cases/0000_ngsild/ngsild_PATCH_Entity-with-Relationship-array.test @@ -0,0 +1,145 @@ +# 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-- +Entity Patch using Smart Input Format + +--SHELL-INIT-- +dbInit CB +orionldStart CB -experimental + +--SHELL-- + +# +# 01. Create an entity 'urn:E1' with a Relationship 'occupier' whose object is an array of one item +# 02. GET 'urn:E1' +# 03. PATCH entity 'urn:E1', making 'occupier' have an array of two +# 04. GET the entity and see that occupier::object is an array of two items +# + + +echo "01. Create an entity 'urn:E1' with a Relationship 'occupier' whose object is an array of one item" +echo "=================================================================================================" +payload='{ + "id": "urn:E1", + "type": "T", + "occupier": { + "object": [ + "urn:ngsi-ld:Person:You" + ] + } +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "02. GET 'urn:E1'" +echo "================" +orionCurl --url /ngsi-ld/v1/entities/urn:E1 +echo +echo + + +echo "03. PATCH entity 'urn:E1', making 'occupier' have an array of two" +echo "=================================================================" +payload='{ + "occupier": { + "object": [ + "urn:ngsi-ld:Person:He", + "urn:ngsi-ld:Person:She" + ] + } +}' +orionCurl --url /ngsi-ld/v1/entities/urn:E1 -X PATCH --payload "$payload" +echo +echo + + +echo "04. GET the entity and see that occupier::object is an array of two items" +echo "=========================================================================" +orionCurl --url /ngsi-ld/v1/entities/urn:E1 +echo +echo + + +--REGEXPECT-- +01. Create an entity 'urn:E1' with a Relationship 'occupier' whose object is an array of one item +================================================================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +02. GET 'urn:E1' +================ +HTTP/1.1 200 OK +Content-Length: 97 +Content-Type: application/json +Date: REGEX(.*) +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" + +[ + { + "entities": [ + { + "type": "T" + } + ], + "id": "urn:S1", + "isActive": true, + "jsonldContext": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld", + "notification": { + "attributes": [ + "P1", + "P2" + ], + "endpoint": { + "accept": "application/json", + "uri": "http://127.0.0.1:9997/notify" + }, + "format": "normalized", + "lastNotification": "202REGEX(.*)Z", + "status": "ok", + "sysAttrs": false, + "timesSent": 1 + }, + "origin": "cache", + "q": "P3==1|P3==2", + "status": "active", + "timeInterval": 2, + "type": "Subscription" + }, + { + "entities": [ + { + "type": "T" + } + ], + "id": "urn:S2", + "isActive": true, + "jsonldContext": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld", + "notification": { + "attributes": [ + "P1", + "P2" + ], + "endpoint": { + "accept": "application/json", + "uri": "http://127.0.0.1:9997/notify" + }, + "format": "normalized", + "status": "ok" + }, + "origin": "cache", + "q": "P3==1|P3==2", + "status": "active", + "type": "Subscription" + } +] + + +04. GET subs (from DB) +====================== +HTTP/1.1 200 OK +Content-Length: 768 +Content-Type: application/json +Date: REGEX(.*) +Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" + +[ + { + "entities": [ + { + "type": "T" + } + ], + "id": "urn:S1", + "isActive": true, + "jsonldContext": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld", + "notification": { + "attributes": [ + "P1", + "P2" + ], + "endpoint": { + "accept": "application/json", + "uri": "http://127.0.0.1:9997/notify" + }, + "format": "normalized", + "status": "ok" + }, + "origin": "database", + "q": "P3==1|P3==2", + "status": "active", + "timeInterval": 2, + "type": "Subscription" + }, + { + "entities": [ + { + "type": "T" + } + ], + "id": "urn:S2", + "isActive": true, + "jsonldContext": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld", + "notification": { + "attributes": [ + "P1", + "P2" + ], + "endpoint": { + "accept": "application/json", + "uri": "http://127.0.0.1:9997/notify" + }, + "format": "normalized", + "status": "ok" + }, + "origin": "database", + "q": "P3==1|P3==2", + "status": "active", + "type": "Subscription" + } +] + + +--TEARDOWN-- +brokerStop CB +dbDrop CB diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_langprop-and-notifications-new.test b/test/functionalTest/cases/0000_ngsild/ngsild_langprop-and-notifications-new.test index 88ee411ed7..029be27d0f 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_langprop-and-notifications-new.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_langprop-and-notifications-new.test @@ -1,4 +1,4 @@ -# Copyright 2022 FIWARE Foundation e.V. +1# Copyright 2022 FIWARE Foundation e.V. # # This file is part of Orion-LD Context Broker. # @@ -226,7 +226,7 @@ bye 04. GET subscription S2 - see lang=en ===================================== HTTP/1.1 200 OK -Content-Length: 340 +Content-Length: 423 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -813,6 +813,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0001", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -863,6 +864,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0002", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -913,6 +915,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0003", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -963,6 +966,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0004", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1013,6 +1017,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0005", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1063,6 +1068,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0006", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1113,6 +1119,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0007", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1163,6 +1170,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0008", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1213,6 +1221,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0009", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1263,6 +1272,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0010", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1313,6 +1323,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0011", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1363,6 +1374,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0012", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1413,6 +1425,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0013", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1463,6 +1476,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0014", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1513,6 +1527,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0015", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1563,6 +1578,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0016", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1613,6 +1629,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0017", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1663,6 +1680,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0018", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1713,6 +1731,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0019", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1763,6 +1782,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0020", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1792,7 +1812,7 @@ Link: ; 04. GET subscriptions with limit 5 - see the first 5 ==================================================== HTTP/1.1 200 OK -Content-Length: 3331 +Content-Length: 3781 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -1825,6 +1845,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0001", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1875,6 +1896,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0002", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1925,6 +1947,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0003", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -1975,6 +1998,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0004", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2025,6 +2049,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0005", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2054,7 +2079,7 @@ Link: ; 05. GET subscriptions with limit 5 and offset 25, see subs 25-29 ================================================================ HTTP/1.1 200 OK -Content-Length: 3331 +Content-Length: 3781 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -2087,6 +2112,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0026", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2137,6 +2163,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0027", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2187,6 +2214,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0028", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2237,6 +2265,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0029", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2287,6 +2316,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0030", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2327,7 +2357,7 @@ NGSILD-Results-Count: 50 07. GET subscriptions count with limit 1 - see HTTP header (NGSILD-Results-Count) and ONE (sub0001) subscription in the array ============================================================================================================================= HTTP/1.1 200 OK -Content-Length: 860 +Content-Length: 950 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -2426,6 +2457,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0037", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2476,6 +2508,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0038", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2526,6 +2559,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0039", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2576,6 +2610,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0040", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2605,7 +2640,7 @@ NGSILD-Results-Count: 40 09. GET subscriptions from tenant t1, with offset 36 AND with Accept: application/ld+json - see 4 subs ====================================================================================================== HTTP/1.1 200 OK -Content-Length: 3005 +Content-Length: 3365 Content-Type: application/ld+json Date: REGEX(.*) NGSILD-Results-Count: 40 @@ -2639,6 +2674,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0037", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2690,6 +2726,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0038", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2741,6 +2778,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0039", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2792,6 +2830,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0040", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2821,7 +2860,7 @@ NGSILD-Results-Count: 40 10. GET subscriptions from DB - see the first 20 ================================================ HTTP/1.1 200 OK -Content-Length: 13381 +Content-Length: 15181 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -2854,6 +2893,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0001", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2904,6 +2944,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0002", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -2954,6 +2995,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0003", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3004,6 +3046,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0004", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3054,6 +3097,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0005", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3104,6 +3148,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0006", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3154,6 +3199,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0007", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3204,6 +3250,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0008", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3254,6 +3301,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0009", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3304,6 +3352,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0010", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3354,6 +3403,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0011", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3404,6 +3454,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0012", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3454,6 +3505,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0013", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3504,6 +3556,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0014", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3554,6 +3607,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0015", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3604,6 +3658,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0016", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3654,6 +3709,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0017", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3704,6 +3760,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0018", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3754,6 +3811,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0019", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3804,6 +3862,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0020", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3833,7 +3892,7 @@ Link: ; 11. GET subscriptions from DB - with limit 5 - see the first 5 ============================================================== HTTP/1.1 200 OK -Content-Length: 3346 +Content-Length: 3796 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -3866,6 +3925,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0001", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3916,6 +3976,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0002", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -3966,6 +4027,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0003", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4016,6 +4078,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0004", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4066,6 +4129,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0005", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4095,7 +4159,7 @@ Link: ; 12. GET subscriptions from DB - with limit 5 and offset 25, see subs 25-29 ========================================================================== HTTP/1.1 200 OK -Content-Length: 3346 +Content-Length: 3796 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -4128,6 +4192,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0026", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4178,6 +4243,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0027", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4228,6 +4294,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0028", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4278,6 +4345,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0029", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4328,6 +4396,7 @@ Link: ; }, "id": "urn:ngsi-ld:Subscription:sub0030", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4368,7 +4437,7 @@ NGSILD-Results-Count: 50 14. GET subscriptions from DB - count with limit 1 - see HTTP header (NGSILD-Results-Count) and ONE (sub0001) subscription in the array ======================================================================================================================================= HTTP/1.1 200 OK -Content-Length: 863 +Content-Length: 953 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -4467,6 +4537,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0037", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4517,6 +4588,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0038", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4567,6 +4639,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0039", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4617,6 +4690,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0040", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4646,7 +4720,7 @@ NGSILD-Results-Count: 40 16. GET subscriptions from DB - from tenant t1, with offset 36 AND with Accept: application/ld+json - see 4 subs ================================================================================================================ HTTP/1.1 200 OK -Content-Length: 3017 +Content-Length: 3377 Content-Type: application/ld+json Date: REGEX(.*) NGSILD-Results-Count: 40 @@ -4680,6 +4754,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0037", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4731,6 +4806,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0038", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4782,6 +4858,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0039", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", @@ -4833,6 +4910,7 @@ NGSILD-Results-Count: 40 }, "id": "urn:ngsi-ld:Subscription:sub0040", "isActive": false, + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", "notification": { "attributes": [ "P1", diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription-with-mqtt-notification.test b/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription-with-mqtt-notification.test index ddddcc36d8..7a6d416c0b 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription-with-mqtt-notification.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription-with-mqtt-notification.test @@ -259,7 +259,7 @@ bye 03. GET the subscription ======================== HTTP/1.1 200 OK -Content-Length: 487 +Content-Length: REGEX(.*) Content-Type: application/json Date: REGEX(.*) Link: AirQualityObserved as longname =========================================================================== HTTP/1.1 200 OK -Content-Length: 487 +Content-Length: 557 Content-Type: application/json Date: REGEX(.*) Link: AirQualityObserved as shortname ========================================================================= HTTP/1.1 200 OK -Content-Length: 449 +Content-Length: 519 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -426,6 +428,7 @@ Link: ; rel="http://www.w3. ], "id": "urn:ngsi-ld:Subscription:mqttNotification", "isActive": true, + "jsonldContext": "https://fiware.github.io/data-models/context.jsonld", "notification": { "endpoint": { "accept": "application/json", diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_and_q_issue_1180.test b/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_and_q_issue_1180.test index 9a0f723253..bb5e4d0101 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_and_q_issue_1180.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_and_q_issue_1180.test @@ -156,7 +156,7 @@ bye 03. GET S1 - see q with %22 =========================== HTTP/1.1 200 OK -Content-Length: 290 +Content-Length: 373 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -684,6 +679,7 @@ Link: ; ], "id": "http://a.b.c/subs/sub09", "isActive": true, + "jsonldContext": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld", "modifiedAt": REGEX(.*), "notification": { "attributes": [ diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_delete.test b/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_delete.test index 9692cb51c8..bdd63b3be3 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_delete.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_delete.test @@ -151,7 +151,7 @@ Location: /ngsi-ld/v1/subscriptions/http://a.b.c/subs/sub01 02. GET subscription 'http://a.b.c/subs/sub01' and see it ========================================================= HTTP/1.1 200 OK -Content-Length: 789 +Content-Length: 883 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -196,7 +196,8 @@ Link: ; }, "expiresAt": "2028-12-31T10:00:00.123Z", "throttling": 5, - "origin": "cache" + "origin": "cache", + "jsonldContext": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld" } diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_issue_1401.test b/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_issue_1401.test index 2b6f61d4ec..903a895267 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_issue_1401.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_issue_1401.test @@ -129,7 +129,7 @@ Location: /ngsi-ld/v1/subscriptions/urn:ngsi-ld:Subscription:S1 02. GET S1 from CB1 =================== HTTP/1.1 200 OK -Content-Length: 423 +Content-Length: 506 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -369,6 +371,7 @@ Link: ; rel="h }, "id": "urn:ngsi-ld:Subscription:S1", "isActive": false, + "jsonldContext": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld", "lang": "es", "modifiedAt": "202REGEX(.*)Z", "notification": { @@ -448,7 +451,7 @@ OK, slept 2.1 seconds 08. GET Sub 1 from CB2 - make sure it's identical in CB2 ======================================================== HTTP/1.1 200 OK -Content-Length: 598 +Content-Length: 681 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -472,6 +475,7 @@ Link: ; rel="h }, "id": "urn:ngsi-ld:Subscription:S1", "isActive": false, + "jsonldContext": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld", "lang": "es", "modifiedAt": "202REGEX(.*)Z", "notification": { diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_patch_geoq.test b/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_patch_geoq.test index ec745b3b98..efe6031e75 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_patch_geoq.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_new_subscription_patch_geoq.test @@ -173,7 +173,7 @@ Location: /ngsi-ld/v1/subscriptions/urn:S1 02. GET S1 from cache, see the "geoq" fields ============================================ HTTP/1.1 200 OK -Content-Length: 358 +Content-Length: 441 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -213,6 +213,7 @@ Link: ; rel="http://www.w3. ], "id": "urn:ngsi-ld:subscription:REGEX(.*)", "isActive": true, + "jsonldContext": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld", "notification": { "attributes": [ "Engine_Oxigen", diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_notification-stats.test b/test/functionalTest/cases/0000_ngsild/ngsild_notification-stats.test index 7e10333fa1..ac701ce7cb 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_notification-stats.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_notification-stats.test @@ -297,7 +297,7 @@ Link: 5;https://uri=etsi=org/ngsi-ld/default-context/b.value<9)|https://uri=etsi=org/ngsi-ld/default-context/a.value<4", + "timeInterval" : 2, + "createdAt" : REGEX(.*), + "modifiedAt" : REGEX(.*), + "throttling" : 0, + "expression" : { + "geometry" : "", + "coords" : "", + "georel" : "", + "geoproperty" : "", + "q" : "P;!P", + "mq" : "P.P;!P.P" + }, + "attrs" : [ + "https://uri.etsi.org/ngsi-ld/default-context/P1", + "https://uri.etsi.org/ngsi-ld/default-context/P2" + ], + "reference" : "http://REGEX(.*)/notify", + "mimeType" : "application/json", + "format" : "normalized", + "conditions" : [ ], + "status" : "active", + "custom" : false, + "servicePath" : "/#", + "blacklist" : false, + "ldContext" : "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld" +} +bye + + +08. GET urn:S1 from cache +========================= +HTTP/1.1 200 OK +Content-Length: 574 +Content-Type: application/json +Date: REGEX(.*) +Link: 5;b<9)|a<4", + "status": "active", + "subscriptionName": "S1", + "timeInterval": 2, + "type": "Subscription" +} + + +09. GET urn:S1 from database +============================ +HTTP/1.1 200 OK +Content-Length: 577 +Content-Type: application/json +Date: REGEX(.*) +Link: 5;b<9)|a<4", + "status": "active", + "subscriptionName": "S1", + "timeInterval": 2, + "type": "Subscription" +} + + +10. Sleep 3 secs to make sure the broker flushes the subs in cache to DB (counters and timestamps) +================================================================================================== + + +11. See urn:S1 in database - check "noMatch == 2" +================================================= +MongoDB shell version REGEX(.*) +connecting to: REGEX(.*) +MongoDB server version: REGEX(.*) +{ + "_id" : "urn:S1", + "description" : "Test Subscription No 1", + "name" : "S1", + "entities" : [ + { + "id" : "urn:E1", + "type" : "https://uri.etsi.org/ngsi-ld/default-context/T", + "isPattern" : "false", + "isTypePattern" : false + }, + { + "id" : "urn:E.*", + "type" : "https://uri.etsi.org/ngsi-ld/default-context/T", + "isPattern" : "true", + "isTypePattern" : false + } + ], + "ldQ" : "(https://uri=etsi=org/ngsi-ld/default-context/a.value>5;https://uri=etsi=org/ngsi-ld/default-context/b.value<9)|https://uri=etsi=org/ngsi-ld/default-context/a.value<4", + "timeInterval" : 2, + "createdAt" : REGEX(.*), + "modifiedAt" : REGEX(.*), + "throttling" : 0, + "expression" : { + "geometry" : "", + "coords" : "", + "georel" : "", + "geoproperty" : "", + "q" : "P;!P", + "mq" : "P.P;!P.P" + }, + "attrs" : [ + "https://uri.etsi.org/ngsi-ld/default-context/P1", + "https://uri.etsi.org/ngsi-ld/default-context/P2" + ], + "reference" : "http://127.0.0.1:9997/notify", + "mimeType" : "application/json", + "format" : "normalized", + "conditions" : [ ], + "status" : "active", + "custom" : false, + "servicePath" : "/#", + "blacklist" : false, + "ldContext" : "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld", + "count" : REGEX((1|2)), + "lastNotification" : REGEX(.*), + "noMatch" : REGEX((1|2)) +} +bye + + +12. Restart broker +================== +Broker restarted + + +13. GET urn:S1 from cache (noMatch==3 due to initial attempt after restart) +=========================================================================== +HTTP/1.1 200 OK +Content-Length: 724 +Content-Type: application/json +Date: REGEX(.*) +Link: 5;b<9)|a<4", + "status": "active", + "subscriptionName": "S1", + "timeInterval": 2, + "type": "Subscription" +} + + +14. GET urn:S1 from database +============================ +HTTP/1.1 200 OK +Content-Length: 577 +Content-Type: application/json +Date: REGEX(.*) +Link: 5;b<9)|a<4", + "status": "active", + "subscriptionName": "S1", + "timeInterval": 2, + "type": "Subscription" +} + + +--TEARDOWN-- +brokerStop CB +dbDrop CB diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_pernot_counters_and_timestamps.test b/test/functionalTest/cases/0000_ngsild/ngsild_pernot_counters_and_timestamps.test new file mode 100644 index 0000000000..a781753b72 --- /dev/null +++ b/test/functionalTest/cases/0000_ngsild/ngsild_pernot_counters_and_timestamps.test @@ -0,0 +1,390 @@ +# 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-- +Pernot Subscription - periodic notifications and counters/timestamps + +--SHELL-INIT-- +dbInit CB +orionldStart CB -pernot -experimental -subCacheFlushIval 2 +accumulatorStart --pretty-print 127.0.0.1 ${LISTENER_PORT} + +--SHELL-- + +# +# Need to test: +# - noMatch +# - lastSuccess +# - lastFailure +# - lastNotification +# - timesSent +# - timesFailed +# - consecutiveErrors +# - lastErrorReason +# * subCacheRefresh (wait a few secs and re-query the sub) +# * restart of broker +# * dirty +# +# noMatch: +# 01. Create a pernot subscription urn:S1 with a timeInterval of 2 seconds +# 02. Sleep 100 ms to await first attempt (that resulted in no match as there are no entities) +# 03. GET urn:S1 and see noMatch == 1, and the other relevant counters/timestamps +# +# lastSuccess+lastNotification+timesSent +# 11. Create a matching entity urn:E4 +# 12. Sleep 2.1 secs to give the broker time to notify the accumulator +# 13. Dump the accumulator to see the notification +# 14. GET urn:S1 and see lastSuccess, lastNotification, no lastFailure, noMatch=1, timesSent=2, timesFailed=0 +# 15. See the subscription in the database +# +# lastFailure+timesFailed +# 21. Kill accumulator +# 22. sleep 2.1 seconds to get another pernot attempt +# 23. GET urn:S1 and see lastFailure, and timesFailed=1, consecutiveErrors=1, lastErrorReason +# +# subCacheRefresh +# 31. Sleep 2 secs to give the broker time to refresh its subCache +# 32. GET urn:S1 and see the exact same results at step 10 +# +# restart of broker +# 41. Restart the broker +# 42. GET urn:S1 and see the exact same results at step 10 (perhaps one more notification attempt) +# +# flush the cache to DB +# 51. Update urn:E4 twice and sleep a second to give the broker time to flush the sub-cache contents to DB +# 52. GET urn:S1 and see the exact same results at step 10 +# 53. See urn:S1 in DB and have the exact same numbers as step 16 +# + +echo "01. Create a pernot subscription urn:S1 with timeInterval" +echo "=========================================================" +payload='{ + "id": "urn:S1", + "type": "Subscription", + "description": "Test Subscription No 1", + "subscriptionName": "S1", + "entities": [ + { + "id": "urn:E1", + "type": "T" + }, + { + "idPattern": "urn:E.*", + "type": "T" + } + ], + "q": "(a>5&b<9)|a<4", + "timeInterval": 2, + "notification": { + "attributes": [ "P1", "P2" ], + "endpoint": { + "uri": "http://127.0.0.1:'${LISTENER_PORT}'/notify" + } + } +}' +orionCurl --url /ngsi-ld/v1/subscriptions --payload "$payload" +echo +echo + + +echo "02. Sleep 100 ms to await first attempt (that resulted in no match as there are no entities)" +echo "============================================================================================" +sleep 0.1 +echo slept 100 ms +echo +echo + + +echo "03. GET urn:S1 and see noMatch == 1, and the other relevant counters/timestamps" +echo "===============================================================================" +orionCurl --url /ngsi-ld/v1/subscriptions/urn:S1 +echo +echo + + +echo "11. Create a matching entity urn:E4" +echo "===================================" +payload='{ + "id": "urn:E1", + "type": "T", + "a": 1, + "P1": 1 +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "12. Sleep 2.1 secs to give the broker time to notify the accumulator" +echo "====================================================================" +sleep 2.1 +echo Slept 2100ms +echo +echo + + +echo "13. Dump the accumulator to see the notification" +echo "================================================" +accumulatorDump +accumulatorReset +echo +echo + + +echo "14. GET urn:S1 and see lastSuccess, lastNotification, no lastFailure, noMatch=1, timesSent=2, timesFailed=0" +echo "===========================================================================================================" +orionCurl --url /ngsi-ld/v1/subscriptions/urn:S1 +echo +echo + +echo "15. See the subscription in the database" +echo "========================================" +mongoCmd2 ftest "db.csubs.findOne()" +echo +echo + + +# 13. Restart the broker +# orionldStop +# orionldStart CB -pernot -experimental -subCacheIval 4 -cSubCounters 2 +# 14. GET urn:S1 and see the exact same results at step 10 (perhaps one more notification attempt) + +# flush the cache to DB +# 15. Update urn:E4 twice and sleep a second to give the broker time to flush the sub-cache contents to DB +# 16. GET urn:S1 and see the exact same results at step 10 +# 17. See urn:S1 in DB and have the exact same numbers as step 16 + + +--REGEXPECT-- +01. Create a pernot subscription urn:S1 with timeInterval +========================================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/subscriptions/urn:S1 + + + +02. Sleep 100 ms to await first attempt (that resulted in no match as there are no entities) +============================================================================================ +slept 100 ms + + +03. GET urn:S1 and see noMatch == 1, and the other relevant counters/timestamps +=============================================================================== +HTTP/1.1 200 OK +Content-Length: 574 +Content-Type: application/json +Date: REGEX(.*) +Link: 5;b<9)|a<4", + "status": "active", + "subscriptionName": "S1", + "timeInterval": 2, + "type": "Subscription" +} + + +11. Create a matching entity urn:E4 +=================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:E1 + + + +12. Sleep 2.1 secs to give the broker time to notify the accumulator +==================================================================== +Slept 2100ms + + +13. Dump the accumulator to see the notification +================================================ +POST http://REGEX(.*)/notify?subscriptionId=urn:S1 +Content-Length: 229 +User-Agent: orionld/REGEX(.*) +Host: REGEX(.*) +Accept: application/json +Content-Type: application/json +Link: 5;b<9)|a<4", + "status": "active", + "subscriptionName": "S1", + "timeInterval": 2, + "type": "Subscription" +} + + +15. See the subscription in the database +======================================== +MongoDB shell version REGEX(.*) +connecting to: REGEX(.*) +MongoDB server version: REGEX(.*) +{ + "_id" : "urn:S1", + "description" : "Test Subscription No 1", + "name" : "S1", + "entities" : [ + { + "id" : "urn:E1", + "type" : "https://uri.etsi.org/ngsi-ld/default-context/T", + "isPattern" : "false", + "isTypePattern" : false + }, + { + "id" : "urn:E.*", + "type" : "https://uri.etsi.org/ngsi-ld/default-context/T", + "isPattern" : "true", + "isTypePattern" : false + } + ], + "ldQ" : "(https://uri=etsi=org/ngsi-ld/default-context/a.value>5;https://uri=etsi=org/ngsi-ld/default-context/b.value<9)|https://uri=etsi=org/ngsi-ld/default-context/a.value<4", + "timeInterval" : 2, + "createdAt" : REGEX(.*), + "modifiedAt" : REGEX(.*), + "throttling" : 0, + "expression" : { + "geometry" : "", + "coords" : "", + "georel" : "", + "geoproperty" : "", + "q" : "P;!P", + "mq" : "P.P;!P.P" + }, + "attrs" : [ + "https://uri.etsi.org/ngsi-ld/default-context/P1", + "https://uri.etsi.org/ngsi-ld/default-context/P2" + ], + "reference" : "http://127.0.0.1:9997/notify", + "mimeType" : "application/json", + "format" : "normalized", + "conditions" : [ ], + "status" : "active", + "custom" : false, + "servicePath" : "/#", + "blacklist" : false, + "ldContext" : "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld", + "count" : 1, + "lastNotification" : REGEX(.*), + "noMatch" : 1 +} +bye + + +--TEARDOWN-- +brokerStop CB +dbDrop CB +accumulatorStop diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_pernot_noMatch.test b/test/functionalTest/cases/0000_ngsild/ngsild_pernot_noMatch.test new file mode 100644 index 0000000000..05ad871742 --- /dev/null +++ b/test/functionalTest/cases/0000_ngsild/ngsild_pernot_noMatch.test @@ -0,0 +1,146 @@ +# 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-- +periodic notifications and noMatch + +--SHELL-INIT-- +dbInit CB +orionldStart CB -pernot -experimental -subCacheFlushIval 1 + +--SHELL-- +# +# This test looks at subscription::notification::noMatch +# +# The broker is started without subCacheIval (no cache refresh) and a -subCacheFlushIval of 1 second + +# 01. Create a pernot subscription urn:S1 with a timeInterval of 1.40 seconds +# 02. Sleep 0.1 seconds to give the broker a chance for the initial notification attempt (it's a no-match as no entities match) +# 03. GET urnS1 from cache - see noMatch=1, and no other counters/timestamps +# 04. GET urnS1 from database - see noMatch=1, and no other counters/timestamps +# 05. See urn:S1 in mongo - see noMatch=0, and no other counters/timestamps (as no flush to DB has been done) +# +# 06. Sleep 1.0 seconds (time enough for urn:S1 to be flushed to DB, but not for a new notification attempt) +# 07. See urn:S1 in mongo - see noMatch=1, and no other counters/timestamps (no flush to DB has been done + +# 08. Sleep 2.0 more seconds (time enough for two more attempts) +# 09. GET urnS1 from cache - see noMatch=3, and no other counters/timestamps +# 10. GET urnS1 from database - see noMatch=3, and no other counters/timestamps +# 11. See urn:S1 in mongo - see noMatch=3, and no other counters/timestamps + + +echo "01. Create a pernot subscription urn:S1 with a timeInterval of 1.40 seconds" +echo "===========================================================================" +payload='{ + "id": "urn:S1", + "type": "Subscription", + "description": "Test Subscription No 1", + "subscriptionName": "S1", + "entities": [ + { + "id": "urn:E1", + "type": "T" + } + ], + "timeInterval": 1.4, + "notification": { + "endpoint": { + "uri": "http://127.0.0.1:'${LISTENER_PORT}'/notify" + } + } +}' +orionCurl --url /ngsi-ld/v1/subscriptions --payload "$payload" +echo +echo + + +echo "02. Sleep 0.1 seconds to give the broker a chance for the initial notification attempt (it's a no-match as no entities match)" +echo "=============================================================================================================================" +sleep .1 +echo Slept 0.1 seconds +echo +echo + + +echo "03. GET urnS1 from cache - see noMatch=1, and no other counters/timestamps" +echo "==========================================================================" +orionCurl --url /ngsi-ld/v1/subscriptions/urn:S1 +echo +echo + + +--REGEXPECT-- +01. Create a pernot subscription urn:S1 with a timeInterval of 1.40 seconds +=========================================================================== +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/subscriptions/urn:S1 + + + +02. Sleep 0.1 seconds to give the broker a chance for the initial notification attempt (it's a no-match as no entities match) +============================================================================================================================= +Slept 0.1 seconds + + +03. GET urnS1 from cache - see noMatch=1, and no other counters/timestamps +========================================================================== +HTTP/1.1 200 OK +Content-Length: 496 +Content-Type: application/json +Date: REGEX(.*) +Link: 25);(https://uri.etsi.org/ngsi-ld/default-context/humidity<20|humidity>80)", + "q": "(temp<18|temp>25);(humidity<20|humidity>80)", "status": "paused", "subscriptionName": "S1", "throttling": 5, @@ -309,7 +310,7 @@ Link: 25);(https://uri.etsi.org/ngsi-ld/default-context/humidity<20|humidity>80)", + "q": "(temp<18|temp>25);(humidity<20|humidity>80)", "status": "paused", "subscriptionName": "S1", "throttling": 5, @@ -381,7 +383,7 @@ Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -405,6 +407,7 @@ Link: 25);(https://uri.etsi.org/ngsi-ld/default-context/humidity<20|humidity>80)", + "q": "(temp<18|temp>25);(humidity<20|humidity>80)", "status": "paused", "subscriptionName": "S1", "throttling": 5, @@ -453,7 +456,7 @@ Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -477,6 +480,7 @@ Link: 25);(https://uri.etsi.org/ngsi-ld/default-context/humidity<20|humidity>80)", + "q": "(temp<18|temp>25);(humidity<20|humidity>80)", "status": "paused", "subscriptionName": "S1", "throttling": 5, @@ -588,7 +592,7 @@ bye 09. GET the subscription from cache =================================== HTTP/1.1 200 OK -Content-Length: 933 +Content-Length: 937 Content-Type: application/json Date: REGEX(.*) Link: 25);(https://uri.etsi.org/ngsi-ld/default-context/humidity<20|humidity>80)", + "q": "(temp<18|temp>25);(humidity<20|humidity>80)", "status": "paused", "subscriptionName": "S1", "throttling": 5, @@ -660,7 +665,7 @@ Link: 25);(https://uri.etsi.org/ngsi-ld/default-context/humidity<20|humidity>80)", + "q": "(temp<18|temp>25);(humidity<20|humidity>80)", "status": "paused", "subscriptionName": "S1", "throttling": 5, diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_subscription-and-patches-makes-a-crash.test b/test/functionalTest/cases/0000_ngsild/ngsild_subscription-and-patches-makes-a-crash.test index a92b1123c0..e6e080171a 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_subscription-and-patches-makes-a-crash.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_subscription-and-patches-makes-a-crash.test @@ -189,7 +189,7 @@ Ngsild-Attribute-Format: Simplified 07. See the good subscription and its counters/timestamps =========================================================== HTTP/1.1 200 OK -Content-Length: 432 +Content-Length: 515 Content-Type: application/json Date: REGEX(.*) Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" @@ -202,6 +202,7 @@ Link: ; rel="h ], "id": "urn:ngsi-ld:Subscription:SubscriptionPrivateSub", "isActive": true, + "jsonldContext": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld", "notification": { "endpoint": { "accept": "application/json", diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_subscription-on-type-star.test b/test/functionalTest/cases/0000_ngsild/ngsild_subscription-on-type-star.test new file mode 100644 index 0000000000..5b33bb7385 --- /dev/null +++ b/test/functionalTest/cases/0000_ngsild/ngsild_subscription-on-type-star.test @@ -0,0 +1,134 @@ +# 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-- +Subscription with type: "*", and test of notifications + +--SHELL-INIT-- +dbInit CB +orionldStart CB -experimental +accumulatorStart --pretty-print + +--SHELL-- + +# +# 01. Create a subscription on EVERYTHING +# 02. Create an entity, any +# 03. See the notification in the accumulator +# + + +echo "01. Create a subscription on EVERYTHING" +echo "=======================================" +payload='{ + "id": "urn:S1", + "description": "Notify ALL", + "type": "Subscription", + "entities": [ + { + "type": "*" + } + ], + "notification": { + "endpoint": { + "uri": "http://localhost:'$LISTENER_PORT'/notify" + } + } +}' +orionCurl --url /ngsi-ld/v1/subscriptions --payload "$payload" +echo +echo + + +echo "02. Create an entity, any" +echo "=========================" +payload='{ + "id": "urn:ngsi-ld:Blower:bd01", + "type": "BlowerDevice", + "airflow": 127 +}' +orionCurl --url /ngsi-ld/v1/entities --payload "$payload" +echo +echo + + +echo "03. See the notification in the accumulator" +echo "===========================================" +accumulatorDump +echo +echo + + +--REGEXPECT-- +01. Create a subscription on EVERYTHING +======================================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/subscriptions/urn:S1 + + + +02. Create an entity, any +========================= +HTTP/1.1 201 Created +Content-Length: 0 +Date: REGEX(.*) +Location: /ngsi-ld/v1/entities/urn:ngsi-ld:Blower:bd01 + + + +03. See the notification in the accumulator +=========================================== +POST http://REGEX(.*)/notify?subscriptionId=urn:S1 +Content-Length: 264 +User-Agent: REGEX(.*) +Host: REGEX(.*) +Accept: application/json +Content-Type: application/json +Link: 10", - "geoQ": { - "geometry": "Point", - "coordinates": [1,2], - "georel": "near", - "geoproperty": "location" - }, "csf": "not implemented", "isActive": false, "notification": { @@ -653,17 +647,17 @@ Date: REGEX(.*) } -07. Attempt to create a subscription with both 'watchedAttributes' and 'timeInterval' - see failure -=================================================================================================== +07. Attempt to create a subscription with 'timeInterval' - see 501 +================================================================== HTTP/1.1 501 Not Implemented -Content-Length: 144 +Content-Length: 161 Content-Type: application/json Date: REGEX(.*) { - "detail": "Subscription::timeInterval is not implemented", + "detail": "Periodic Notification Subscriptions are not implemented", "title": "Not Implemented", - "type": "https://uri.etsi.org/ngsi-ld/errors/BadRequestData" + "type": "https://uri.etsi.org/ngsi-ld/errors/OperationNotSupported" } diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_creation_errors.test b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_creation_errors.test index 09d547832e..77daf2af26 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_creation_errors.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_creation_errors.test @@ -96,7 +96,6 @@ orionldStart CB # 18.1. Attempt to create a subscription with an invalid field called 'INVALID' # # *19.1. Attempt to create a subscription without ENTITIES nor WATCHEDATTRIBUTES (duplicate of 06.1) -# *19.2. Attempt to create a subscription with both TIMEINTERVAL and WATCHEDATTRIBUTES present # 19.3. Attempt to create a subscription without NOTIFICATION # *19.4. Creation of a subscription without TIMEINTERVAL nor WATCHEDATTRIBUTES present (entities is present) # @@ -292,63 +291,6 @@ echo echo -echo "19.2. Attempt to create a subscription with both TIMEINTERVAL and WATCHEDATTRIBUTES present" -echo "===========================================================================================" -payload='{ - "id": "http://a.b.c/subs/sub03", - "type": "Subscription", - "name": "Test subscription 01", - "description": "Description of Test subscription 01", - "entities": [ - { - "id": "urn:ngsi-ld:E01", - "type": "T1" - }, - { - "id": "http://a.b.c/E02", - "type": "T2" - }, - { - "idPattern": ".*E03.*", - "type": "T3" - } - ], - "watchedAttributes": [ "P2" ], - "timeInterval": 12, - "q": "P2>10", - "geoQ": { - "geometry": "Point", - "coordinates": [1,2], - "georel": "near;maxDistance==5", - "geoproperty": "location" - }, - "csf": "not implemented", - "isActive": false, - "notification": { - "attributes": [ "P1", "P2", "A3" ], - "format": "keyValues", - "endpoint": { - "uri": "http://valid.url/url", - "accept": "application/ld+json" - }, - "status": "ignored", - "timesSent": "ignored", - "lastNotification": "ignored", - "lastFailure": "ignored", - "lastSuccess": "ignored" - }, - "expires": "2028-12-31T10:00:00", - "throttling": 5, - "status": "to be ignored - read-only", - "@context": "https://fiware.github.io/NGSI-LD_TestSuite/ldContext/testContext.jsonld", - "createdAt": "ignored", - "modifiedAt": "ignored" -}' -orionCurl --url /ngsi-ld/v1/subscriptions --payload "$payload" -H "Content-Type: application/ld+json" -echo -echo - - echo "19.4. Creation of a subscription without TIMEINTERVAL nor WATCHEDATTRIBUTES present (entities is present)" echo "=========================================================================================================" payload='{ @@ -551,20 +493,6 @@ Date: REGEX(.*) } -19.2. Attempt to create a subscription with both TIMEINTERVAL and WATCHEDATTRIBUTES present -=========================================================================================== -HTTP/1.1 501 Not Implemented -Content-Length: 144 -Content-Type: application/json -Date: REGEX(.*) - -{ - "detail": "Subscription::timeInterval is not implemented", - "title": "Not Implemented", - "type": "https://uri.etsi.org/ngsi-ld/errors/BadRequestData" -} - - 19.4. Creation of a subscription without TIMEINTERVAL nor WATCHEDATTRIBUTES present (entities is present) ========================================================================================================= HTTP/1.1 201 Created diff --git a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_issue_1179-with-experimental.test b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_issue_1179-with-experimental.test index f0058351b0..d1ac077bfb 100644 --- a/test/functionalTest/cases/0000_ngsild/ngsild_subscription_issue_1179-with-experimental.test +++ b/test/functionalTest/cases/0000_ngsild/ngsild_subscription_issue_1179-with-experimental.test @@ -346,7 +346,7 @@ NGSILD-Tenant: captn 07. GET subscriptions ===================== HTTP/1.1 200 OK -Content-Length: 2123 +Content-Length: 2372 Content-Type: application/json Date: REGEX(.*) Link: -# 04. Get 501 for DELETE /ngsi-ld/v1/temporal/entities/ -# 05. Get 501 for POST /ngsi-ld/v1/temporal/entities//attrs -# 06. Get 501 for DELETE /ngsi-ld/v1/temporal/entities//attrs/{attrId} -# 07. Get 501 for POST /ngsi-ld/v1/temporal/entityOperations/query +# 04. Get 501 for POST /ngsi-ld/v1/temporal/entityOperations/query +# +# 05. Get 501 for DELETE /ngsi-ld/v1/temporal/entities/ +# 06. Get 501 for POST /ngsi-ld/v1/temporal/entities//attrs +# 07. Get 501 for DELETE /ngsi-ld/v1/temporal/entities//attrs/{attrId} +# 08. Get 501 for DELETE /ngsi-ld/v1/temporal/entities//attrs/{attrId}/{instanceId} +# 09. Get 501 for PATCH /ngsi-ld/v1/temporal/entities//attrs/{attrId}/{instanceId} # echo "02. Get 501 for GET /ngsi-ld/v1/temporal/entities" echo "=================================================" -orionCurl --url /ngsi-ld/v1/temporal/entities +orionCurl --url /ngsi-ld/v1/temporal/entities?badParam=urn:S1 echo echo echo "03. Get 501 for GET /ngsi-ld/v1/temporal/entities/" echo "=======================================================" -orionCurl --url /ngsi-ld/v1/temporal/entities/urn:ngsi-ld:City:Madrid +orionCurl --url /ngsi-ld/v1/temporal/entities/urn:ngsi-ld:City:Madrid?badParam=urn:S1 echo echo -echo "04. Get 501 for DELETE /ngsi-ld/v1/temporal/entities/" +echo "04. Get 501 for POST /ngsi-ld/v1/temporal/entityOperations/query" +echo "================================================================" +payload='{ + "q": "A1>10" +}' +orionCurl --url /ngsi-ld/v1/temporal/entityOperations/query --payload "$payload" +echo +echo + + +echo "05. Get 501 for DELETE /ngsi-ld/v1/temporal/entities/" echo "==========================================================" orionCurl --url /ngsi-ld/v1/temporal/entities/urn:ngsi-ld:City:Madrid -X DELETE echo @@ -63,7 +76,7 @@ echo -echo "05. Get 501 for POST /ngsi-ld/v1/temporal/entities//attrs" +echo "06. Get 501 for POST /ngsi-ld/v1/temporal/entities//attrs" echo "==============================================================" payload='{ "attr1": { @@ -76,19 +89,36 @@ echo echo -echo "06. Get 501 for DELETE /ngsi-ld/v1/temporal/entities//attrs/{attrId}" +echo "07. Get 501 for DELETE /ngsi-ld/v1/temporal/entities//attrs/{attrId}" echo "=========================================================================" orionCurl --url /ngsi-ld/v1/temporal/entities/urn:ngsi-ld:City:Madrid/attrs/attr1 -X DELETE echo echo -echo "07. Get 501 for POST /ngsi-ld/v1/temporal/entityOperations/query" -echo "================================================================" +echo "08. Get 501 for DELETE /ngsi-ld/v1/temporal/entities//attrs/{attrId}/{instanceId}" +echo "======================================================================================" +orionCurl --url /ngsi-ld/v1/temporal/entities/urn:ngsi-ld:City:Madrid/attrs/attr1/instance1 -X DELETE +echo +echo + + +echo "09. Get 501 for PATCH /ngsi-ld/v1/temporal/entities//attrs/{attrId}/{instanceId}" +echo "=====================================================================================" payload='{ - "q": "A1>10" + "value": 1 }' -orionCurl --url /ngsi-ld/v1/temporal/entityOperations/query --payload "$payload" +orionCurl --url /ngsi-ld/v1/temporal/entities/urn:ngsi-ld:City:Madrid/attrs/attr1/instance1 -X PATCH --payload "$payload" +echo +echo + + +echo "10. Get 405 for POST /ngsi-ld/v1/temporal/entities//attrs/{attrId}/{instanceId}" +echo "====================================================================================" +payload='{ + "value": 1 +}' +orionCurl --url /ngsi-ld/v1/temporal/entities/urn:ngsi-ld:City:Madrid/attrs/attr1/instance1 --payload "$payload" echo echo @@ -122,7 +152,21 @@ Date: REGEX(.*) } -04. Get 501 for DELETE /ngsi-ld/v1/temporal/entities/ +04. Get 501 for POST /ngsi-ld/v1/temporal/entityOperations/query +================================================================ +HTTP/1.1 501 Not Implemented +Content-Length: 200 +Content-Type: application/json +Date: REGEX(.*) + +{ + "detail": "/ngsi-ld/v1/temporal/entityOperations/query", + "title": "Not Implemented in Orion-LD, please use Mintaka for this operation", + "type": "https://uri.etsi.org/ngsi-ld/errors/OperationNotSupported" +} + + +05. Get 501 for DELETE /ngsi-ld/v1/temporal/entities/ ========================================================== HTTP/1.1 501 Not Implemented Content-Length: 137 @@ -136,7 +180,7 @@ Date: REGEX(.*) } -05. Get 501 for POST /ngsi-ld/v1/temporal/entities//attrs +06. Get 501 for POST /ngsi-ld/v1/temporal/entities//attrs ============================================================== HTTP/1.1 501 Not Implemented Content-Length: 143 @@ -150,7 +194,7 @@ Date: REGEX(.*) } -06. Get 501 for DELETE /ngsi-ld/v1/temporal/entities//attrs/{attrId} +07. Get 501 for DELETE /ngsi-ld/v1/temporal/entities//attrs/{attrId} ========================================================================= HTTP/1.1 501 Not Implemented Content-Length: 145 @@ -164,20 +208,43 @@ Date: REGEX(.*) } -07. Get 501 for POST /ngsi-ld/v1/temporal/entityOperations/query -================================================================ +08. Get 501 for DELETE /ngsi-ld/v1/temporal/entities//attrs/{attrId}/{instanceId} +====================================================================================== HTTP/1.1 501 Not Implemented -Content-Length: 200 +Content-Length: 145 Content-Type: application/json Date: REGEX(.*) { - "detail": "/ngsi-ld/v1/temporal/entityOperations/query", - "title": "Not Implemented in Orion-LD, please use Mintaka for this operation", + "detail": "/ngsi-ld/v1/temporal/entities/*/attrs/*", + "title": "Not Implemented", "type": "https://uri.etsi.org/ngsi-ld/errors/OperationNotSupported" } +09. Get 501 for PATCH /ngsi-ld/v1/temporal/entities//attrs/{attrId}/{instanceId} +===================================================================================== +HTTP/1.1 501 Not Implemented +Content-Length: 147 +Content-Type: application/json +Date: REGEX(.*) + +{ + "detail": "/ngsi-ld/v1/temporal/entities/*/attrs/*/*", + "title": "Not Implemented", + "type": "https://uri.etsi.org/ngsi-ld/errors/OperationNotSupported" +} + + +10. Get 405 for POST /ngsi-ld/v1/temporal/entities//attrs/{attrId}/{instanceId} +==================================================================================== +HTTP/1.1 405 Method Not Allowed +Allow: GET,PATCH,DELETE +Content-Length: 0 +Date: REGEX(.*) + + + --TEARDOWN-- brokerStop CB dbDrop CB diff --git a/test/functionalTest/cases/1952_sub_notification_wo_attrs/1952_sub_notification_wo_attrs.test b/test/functionalTest/cases/1952_sub_notification_wo_attrs/1952_sub_notification_wo_attrs.test index 6a80b4e305..e49805604d 100644 --- a/test/functionalTest/cases/1952_sub_notification_wo_attrs/1952_sub_notification_wo_attrs.test +++ b/test/functionalTest/cases/1952_sub_notification_wo_attrs/1952_sub_notification_wo_attrs.test @@ -132,4 +132,3 @@ Fiware-Correlator: REGEX([0-9a-f\-]{36}) --TEARDOWN-- brokerStop CB dbDrop CB - diff --git a/test/unittests/main_UnitTest.cpp b/test/unittests/main_UnitTest.cpp index 078754b848..3c27ace71b 100644 --- a/test/unittests/main_UnitTest.cpp +++ b/test/unittests/main_UnitTest.cpp @@ -105,10 +105,11 @@ char dbPwd[64] = { 0 }; bool experimental = false; bool mongocOnly = false; bool debugCurl = false; -int cSubCounters = 0; +uint32_t cSubCounters = 0; char localIpAndPort[135]; unsigned long long inReqPayloadMaxSize; unsigned long long outReqMsgMaxSize; +bool triggerOperation = false;