Mercurial > hg > orthanc
changeset 6218:add00150107b
UserData in jobs
line wrap: on
line diff
--- a/NEWS Thu Jul 03 15:37:40 2025 +0200 +++ b/NEWS Fri Jul 04 15:33:46 2025 +0200 @@ -6,6 +6,13 @@ * Lua: new "SetStableStatus" function. +REST API +-------- + +* When creating a job, you can now add a "UserData" field in the payload. + This data will travel along with the job and will be available in the + /jobs/{jobId} route. + Plugin SDK ----------
--- a/OrthancFramework/Sources/JobsEngine/IJob.h Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancFramework/Sources/JobsEngine/IJob.h Fri Jul 04 15:33:46 2025 +0200 @@ -76,5 +76,7 @@ // This function can only be called if the job has reached its // "success" state virtual void DeleteAllOutputs() {} + + virtual bool GetUserData(Json::Value& userData) const = 0; }; }
--- a/OrthancFramework/Sources/JobsEngine/JobInfo.cpp Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancFramework/Sources/JobsEngine/JobInfo.cpp Fri Jul 04 15:33:46 2025 +0200 @@ -190,6 +190,11 @@ target["CreationTime"] = boost::posix_time::to_iso_string(creationTime_); target["EffectiveRuntime"] = static_cast<double>(runtime_.total_milliseconds()) / 1000.0; target["Progress"] = boost::math::iround(status_.GetProgress() * 100.0f); + + if (status_.HasUserData()) + { + target["UserData"] = status_.GetUserData(); + } target["Type"] = status_.GetJobType(); target["Content"] = status_.GetPublicContent();
--- a/OrthancFramework/Sources/JobsEngine/JobStatus.cpp Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancFramework/Sources/JobsEngine/JobStatus.cpp Fri Jul 04 15:33:46 2025 +0200 @@ -59,7 +59,8 @@ job.GetJobType(jobType_); job.GetPublicContent(publicContent_); - + job.GetUserData(userData_); + hasSerialized_ = job.Serialize(serialized_); }
--- a/OrthancFramework/Sources/JobsEngine/JobStatus.h Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancFramework/Sources/JobsEngine/JobStatus.h Fri Jul 04 15:33:46 2025 +0200 @@ -38,6 +38,7 @@ Json::Value serialized_; bool hasSerialized_; std::string details_; + Json::Value userData_; public: JobStatus(); @@ -87,5 +88,15 @@ { return details_; } + + bool HasUserData() const + { + return !userData_.isNull(); + } + + const Json::Value& GetUserData() const + { + return userData_; + } }; }
--- a/OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp Fri Jul 04 15:33:46 2025 +0200 @@ -43,6 +43,7 @@ static const char* RUNTIME = "Runtime"; static const char* ERROR_CODE = "ErrorCode"; static const char* ERROR_DETAILS = "ErrorDetails"; + static const char* USER_DATA = "UserData"; class JobsRegistry::JobHandler : public boost::noncopyable @@ -296,6 +297,13 @@ target[ERROR_CODE] = static_cast<int>(lastStatus_.GetErrorCode()); target[ERROR_DETAILS] = lastStatus_.GetDetails(); + // New in Orthanc 1.12.9 + Json::Value userData; + if (job_->GetUserData(userData)) + { + target[USER_DATA] = userData; + } + return true; } else
--- a/OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.h Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.h Fri Jul 04 15:33:46 2025 +0200 @@ -139,5 +139,10 @@ } void AwakeTrailingSleep(); + + virtual bool GetUserData(Json::Value& userData) const ORTHANC_OVERRIDE + { + return false; + } }; }
--- a/OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.cpp Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.cpp Fri Jul 04 15:33:46 2025 +0200 @@ -235,7 +235,7 @@ static const char* KEY_POSITION = "Position"; static const char* KEY_TYPE = "Type"; static const char* KEY_COMMANDS = "Commands"; - + static const char* KEY_USER_DATA = "UserData"; void SetOfCommandsJob::GetPublicContent(Json::Value& value) const { @@ -254,6 +254,7 @@ target[KEY_PERMISSIVE] = permissive_; target[KEY_POSITION] = static_cast<unsigned int>(position_); target[KEY_DESCRIPTION] = description_; + target[KEY_USER_DATA] = userData_; target[KEY_COMMANDS] = Json::arrayValue; Json::Value& tmp = target[KEY_COMMANDS]; @@ -280,7 +281,13 @@ permissive_ = SerializationToolbox::ReadBoolean(source, KEY_PERMISSIVE); position_ = SerializationToolbox::ReadUnsignedInteger(source, KEY_POSITION); description_ = SerializationToolbox::ReadString(source, KEY_DESCRIPTION); - + + // new in 1.12.9 + if (source.isMember(KEY_USER_DATA)) + { + userData_ = source[KEY_USER_DATA]; + } + if (!source.isMember(KEY_COMMANDS) || source[KEY_COMMANDS].type() != Json::arrayValue) {
--- a/OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.h Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.h Fri Jul 04 15:33:46 2025 +0200 @@ -63,6 +63,7 @@ bool permissive_; size_t position_; std::string description_; + Json::Value userData_; public: SetOfCommandsJob(); @@ -116,5 +117,21 @@ { return false; } + + void SetUserData(const Json::Value& userData) + { + userData_ = userData; + } + + virtual bool GetUserData(Json::Value& userData) const ORTHANC_OVERRIDE + { + if (!userData_.isNull()) + { + userData = userData_; + return true; + } + return false; + } + }; }
--- a/OrthancFramework/UnitTestsSources/JobsTests.cpp Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancFramework/UnitTestsSources/JobsTests.cpp Fri Jul 04 15:33:46 2025 +0200 @@ -137,6 +137,10 @@ { return false; } + virtual bool GetUserData(Json::Value& userData) const ORTHANC_OVERRIDE + { + return false; + } };
--- a/OrthancServer/Plugins/Engine/PluginsJob.h Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancServer/Plugins/Engine/PluginsJob.h Fri Jul 04 15:33:46 2025 +0200 @@ -83,6 +83,12 @@ // TODO return false; } + + virtual bool GetUserData(Json::Value& userData) const ORTHANC_OVERRIDE + { + return false; + } + }; }
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp Fri Jul 04 15:33:46 2025 +0200 @@ -324,6 +324,7 @@ static const char* KEY_PRIORITY = "Priority"; static const char* KEY_SYNCHRONOUS = "Synchronous"; static const char* KEY_ASYNCHRONOUS = "Asynchronous"; + static const char* KEY_USER_DATA = "UserData"; bool OrthancRestApi::IsSynchronousJobRequest(bool isDefaultSynchronous, @@ -441,6 +442,11 @@ job->SetPermissive(false); } + if (body.isMember(KEY_USER_DATA)) + { + job->SetUserData(body[KEY_USER_DATA]); + } + SubmitGenericJob(call, raii.release(), isDefaultSynchronous, body); } @@ -467,6 +473,11 @@ job->SetPermissive(false); } + if (body.isMember(KEY_USER_DATA)) + { + job->SetUserData(body[KEY_USER_DATA]); + } + SubmitGenericJob(call, raii.release(), isDefaultSynchronous, body); } @@ -492,7 +503,9 @@ DocumentSubmitGenericJob(call); call.GetDocumentation() .SetRequestField(KEY_PERMISSIVE, RestApiCallDocumentation::Type_Boolean, - "If `true`, ignore errors during the individual steps of the job.", false); + "If `true`, ignore errors during the individual steps of the job.", false) + .SetRequestField(KEY_USER_DATA, RestApiCallDocumentation::Type_JsonObject, + "User data that will travel along with the job.", false); }
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestArchive.cpp Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestArchive.cpp Fri Jul 04 15:33:46 2025 +0200 @@ -44,7 +44,8 @@ static const char* const KEY_TRANSCODE = "Transcode"; static const char* const KEY_LOSSY_QUALITY = "LossyQuality"; static const char* const KEY_FILENAME = "Filename"; - + static const char* const KEY_USER_DATA = "UserData"; + static const char* const GET_TRANSCODE = "transcode"; static const char* const GET_LOSSY_QUALITY = "lossy-quality"; static const char* const GET_FILENAME = "filename"; @@ -124,6 +125,7 @@ int& priority, /* out */ unsigned int& loaderThreads, /* out */ std::string& filename, /* out */ + Json::Value& userData, /* out */ const Json::Value& body, /* in */ const bool defaultExtended /* in */, const std::string& defaultFilename /* in */) @@ -174,6 +176,12 @@ filename = defaultFilename; } + if (body.type() == Json::objectValue && + body.isMember(KEY_USER_DATA) && body[KEY_USER_DATA].isString()) + { + userData = body[KEY_USER_DATA].asString(); + } + { OrthancConfiguration::ReaderLock lock; loaderThreads = lock.GetConfiguration().GetUnsignedIntegerParameter(CONFIG_LOADER_THREADS, 0); // New in Orthanc 1.10.0 @@ -548,6 +556,8 @@ "(including file extension)", false) .SetRequestField("Priority", RestApiCallDocumentation::Type_Number, "In asynchronous mode, the priority of the job. The higher the value, the higher the priority.", false) + .SetRequestField(KEY_USER_DATA, RestApiCallDocumentation::Type_JsonObject, + "In asynchronous mode, user data that will be attached to the job.", false) .AddAnswerType(MimeType_Zip, "In synchronous mode, the ZIP file containing the archive") .AddAnswerType(MimeType_Json, "In asynchronous mode, information about the job that has been submitted to " "generate the archive: https://orthanc.uclouvain.be/book/users/advanced-rest.html#jobs") @@ -593,9 +603,10 @@ unsigned int loaderThreads; std::string filename; unsigned int lossyQuality; + Json::Value userData; GetJobParameters(synchronous, extended, transcode, transferSyntax, lossyQuality, - priority, loaderThreads, filename, body, DEFAULT_IS_EXTENDED, "Archive.zip"); + priority, loaderThreads, filename, userData, body, DEFAULT_IS_EXTENDED, "Archive.zip"); std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended, ResourceType_Patient)); AddResourcesOfInterest(*job, body); @@ -607,6 +618,7 @@ } job->SetLoaderThreads(loaderThreads); + job->SetUserData(userData); SubmitJob(call.GetOutput(), context, job, priority, synchronous, filename); } @@ -783,8 +795,10 @@ unsigned int loaderThreads; std::string filename; unsigned int lossyQuality; + Json::Value userData; + GetJobParameters(synchronous, extended, transcode, transferSyntax, lossyQuality, - priority, loaderThreads, filename, body, false /* by default, not extented */, id + ".zip"); + priority, loaderThreads, filename, userData, body, false /* by default, not extented */, id + ".zip"); std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended, LEVEL)); job->AddResource(id, true, LEVEL); @@ -796,6 +810,7 @@ } job->SetLoaderThreads(loaderThreads); + job->SetUserData(userData); SubmitJob(call.GetOutput(), context, job, priority, synchronous, filename); }
--- a/OrthancServer/Sources/ServerJobs/ArchiveJob.h Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancServer/Sources/ServerJobs/ArchiveJob.h Fri Jul 04 15:33:46 2025 +0200 @@ -58,6 +58,7 @@ bool enableExtendedSopClass_; std::string description_; std::string filename_; + Json::Value userData_; boost::shared_ptr<ZipWriterIterator> writer_; size_t currentStep_; @@ -137,5 +138,20 @@ virtual bool DeleteOutput(const std::string& key) ORTHANC_OVERRIDE; virtual void DeleteAllOutputs() ORTHANC_OVERRIDE; + + void SetUserData(const Json::Value& userData) + { + userData_ = userData; + } + + virtual bool GetUserData(Json::Value& userData) const ORTHANC_OVERRIDE + { + if (!userData_.isNull()) + { + userData = userData_; + return true; + } + return false; + } }; }
--- a/OrthancServer/Sources/ServerJobs/ThreadedSetOfInstancesJob.cpp Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancServer/Sources/ServerJobs/ThreadedSetOfInstancesJob.cpp Fri Jul 04 15:33:46 2025 +0200 @@ -367,6 +367,7 @@ static const char* KEY_PARENT_RESOURCES = "ParentResources"; static const char* KEY_DESCRIPTION = "Description"; static const char* KEY_PERMISSIVE = "Permissive"; + static const char* KEY_USER_DATA = "UserData"; static const char* KEY_CURRENT_STEP = "CurrentStep"; static const char* KEY_TYPE = "Type"; static const char* KEY_INSTANCES = "Instances"; @@ -402,6 +403,7 @@ target[KEY_TYPE] = type; target[KEY_PERMISSIVE] = permissive_; + target[KEY_USER_DATA] = userData_; target[KEY_CURRENT_STEP] = static_cast<unsigned int>(currentStep_); target[KEY_DESCRIPTION] = description_; target[KEY_KEEP_SOURCE] = keepSource_; @@ -456,6 +458,17 @@ { currentStep_ = static_cast<ThreadedJobStep>(SerializationToolbox::ReadUnsignedInteger(source, KEY_CURRENT_STEP)); } + + if (source.isMember(KEY_PERMISSIVE)) + { + SerializationToolbox::ReadBoolean(source, KEY_PERMISSIVE); + } + + // new in 1.12.9 + if (source.isMember(KEY_USER_DATA)) + { + userData_ = source[KEY_USER_DATA]; + } } @@ -538,6 +551,27 @@ return description_; } + + void ThreadedSetOfInstancesJob::SetUserData(const Json::Value& userData) + { + boost::recursive_mutex::scoped_lock lock(mutex_); + + userData_ = userData; + } + + bool ThreadedSetOfInstancesJob::GetUserData(Json::Value& userData) const + { + boost::recursive_mutex::scoped_lock lock(mutex_); + + if (!userData_.isNull()) + { + userData = userData_; + return true; + } + return false; + } + + void ThreadedSetOfInstancesJob::SetErrorCode(ErrorCode errorCode) { boost::recursive_mutex::scoped_lock lock(mutex_);
--- a/OrthancServer/Sources/ServerJobs/ThreadedSetOfInstancesJob.h Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancServer/Sources/ServerJobs/ThreadedSetOfInstancesJob.h Fri Jul 04 15:33:46 2025 +0200 @@ -62,6 +62,7 @@ bool permissive_; ThreadedJobStep currentStep_; std::string description_; + Json::Value userData_; size_t workersCount_; ServerContext& context_; @@ -170,5 +171,8 @@ return context_; } + void SetUserData(const Json::Value& userData); + + virtual bool GetUserData(Json::Value& userData) const ORTHANC_OVERRIDE; }; }
--- a/OrthancServer/UnitTestsSources/ServerJobsTests.cpp Thu Jul 03 15:37:40 2025 +0200 +++ b/OrthancServer/UnitTestsSources/ServerJobsTests.cpp Fri Jul 04 15:33:46 2025 +0200 @@ -141,6 +141,10 @@ { return false; } + virtual bool GetUserData(Json::Value& userData) const ORTHANC_OVERRIDE + { + return false; + } }; @@ -148,7 +152,6 @@ { private: bool trailingStepDone_; - protected: virtual bool HandleInstance(const std::string& instance) ORTHANC_OVERRIDE { @@ -207,6 +210,10 @@ { s = "DummyInstancesJob"; } + virtual bool GetUserData(Json::Value& userData) const ORTHANC_OVERRIDE + { + return false; + } };