From e060f8a4be08f91c271837ac7cdfe778f6dec86b Mon Sep 17 00:00:00 2001 From: Miguel Prieto Date: Wed, 11 Sep 2024 11:35:36 -0400 Subject: [PATCH 1/7] Added (incubating) client-v3 project to conductor-clients dir --- conductor-clients/README.md | 1 + .../java/conductor-java-sdk/LICENSE | 201 +++ .../java/conductor-java-sdk/README.md | 89 ++ .../java/conductor-java-sdk/build.gradle | 55 + .../conductor-client-spring/build.gradle | 76 + .../client/spring/ClientProperties.java | 113 ++ .../ConductorClientAutoConfiguration.java | 102 ++ .../ConductorWorkerAutoConfiguration.java | 47 + .../spring/SpringWorkerConfiguration.java | 51 + .../conductor-client/build.gradle | 107 ++ .../client/automator/TaskRunner.java | 393 ++++++ .../automator/TaskRunnerConfigurer.java | 325 +++++ .../automator/events/PollCompleted.java | 29 + .../client/automator/events/PollFailure.java | 31 + .../client/automator/events/PollStarted.java | 23 + .../events/TaskExecutionCompleted.java | 33 + .../events/TaskExecutionFailure.java | 35 + .../events/TaskExecutionStarted.java | 29 + .../automator/events/TaskRunnerEvent.java | 27 + .../client/automator/filters/PollFilter.java | 18 + .../config/ConductorClientConfiguration.java | 50 + .../client/config/PropertyFactory.java | 153 ++ .../exception/ConductorClientException.java | 204 +++ .../client/http/ConductorClient.java | 577 ++++++++ .../client/http/ConductorClientRequest.java | 150 ++ .../client/http/ConductorClientResponse.java | 58 + .../client/http/ConnectionPoolConfig.java | 26 + .../conductor/client/http/EventClient.java | 119 ++ .../conductor/client/http/HeaderSupplier.java | 23 + .../conductor/client/http/MetadataClient.java | 218 +++ .../netflix/conductor/client/http/Param.java | 36 + .../conductor/client/http/TaskClient.java | 459 ++++++ .../conductor/client/http/WorkflowClient.java | 497 +++++++ .../conductor/client/worker/Worker.java | 117 ++ .../common/config/ObjectMapperProvider.java | 41 + .../conductor/common/metadata/Auditable.java | 96 ++ .../conductor/common/metadata/SchemaDef.java | 36 + .../metadata/events/EventExecution.java | 178 +++ .../common/metadata/events/EventHandler.java | 474 +++++++ .../common/metadata/tasks/PollData.java | 89 ++ .../conductor/common/metadata/tasks/Task.java | 775 ++++++++++ .../common/metadata/tasks/TaskDef.java | 437 ++++++ .../common/metadata/tasks/TaskExecLog.java | 92 ++ .../common/metadata/tasks/TaskResult.java | 263 ++++ .../common/metadata/tasks/TaskType.java | 126 ++ .../workflow/DynamicForkJoinTask.java | 91 ++ .../workflow/DynamicForkJoinTaskList.java | 38 + .../workflow/IdempotencyStrategy.java | 18 + .../metadata/workflow/RateLimitConfig.java | 46 + .../workflow/RerunWorkflowRequest.java | 68 + .../metadata/workflow/SkipTaskRequest.java | 39 + .../workflow/StartWorkflowRequest.java | 174 +++ .../metadata/workflow/StateChangeEvent.java | 42 + .../metadata/workflow/SubWorkflowParams.java | 153 ++ .../workflow/UpgradeWorkflowRequest.java | 58 + .../common/metadata/workflow/WorkflowDef.java | 392 ++++++ .../metadata/workflow/WorkflowDefSummary.java | 85 ++ .../metadata/workflow/WorkflowTask.java | 729 ++++++++++ .../conductor/common/model/BulkResponse.java | 75 + .../common/run/ExternalStorageLocation.java | 50 + .../conductor/common/run/SearchResult.java | 59 + .../conductor/common/run/TaskSummary.java | 334 +++++ .../conductor/common/run/Workflow.java | 536 +++++++ .../conductor/common/run/WorkflowSummary.java | 354 +++++ .../common/run/WorkflowTestRequest.java | 100 ++ .../common/utils/ConstraintParamUtil.java | 113 ++ .../conductor/common/utils/EnvUtils.java | 44 + .../common/utils/ExternalPayloadStorage.java | 79 ++ .../conductor/common/utils/SummaryUtil.java | 57 + .../conductor/common/utils/TaskUtils.java | 49 + .../common/validation/ErrorResponse.java | 89 ++ .../common/validation/ValidationError.java | 64 + .../client/http/ClientSpecification.groovy | 34 + .../client/http/EventClientSpec.groovy | 101 ++ .../client/http/MetadataClientSpec.groovy | 107 ++ .../client/http/TaskClientSpec.groovy | 137 ++ .../client/http/WorkflowClientSpec.groovy | 150 ++ .../automator/PollingSemaphoreTest.java | 73 + .../automator/TaskPollExecutorTest.java | 647 +++++++++ .../automator/TaskRunnerConfigurerTest.java | 230 +++ .../client/config/TestPropertyFactory.java | 72 + .../netflix/conductor/client/sample/Main.java | 44 + .../conductor/client/sample/SampleWorker.java | 45 + .../client/testing/AbstractWorkflowTests.java | 134 ++ .../client/testing/LoanWorkflowInput.java | 38 + .../client/testing/LoanWorkflowTest.java | 108 ++ .../client/testing/RegressionTest.java | 73 + .../client/testing/SubWorkflowTest.java | 75 + .../client/worker/TestWorkflowTask.java | 75 + .../src/test/resources/config.properties | 11 + .../src/test/resources/tasks.json | 70 + .../test_data/loan_workflow_input.json | 20 + .../resources/test_data/workflow1_run.json | 190 +++ .../resources/workflows/PopulationMinMax.json | 38 + .../workflows/calculate_loan_workflow.json | 41 + .../test/resources/workflows/kitchensink.json | 465 ++++++ .../test/resources/workflows/workflow1.json | 43 + .../conductor-java-sdk/examples/build.gradle | 25 + .../sdk/examples/TaskRegistration.java | 57 + .../conductor/sdk/examples/TaskRunner.java | 150 ++ .../examples/events/EventListenerExample.java | 105 ++ .../sdk/examples/helloworld/Main.java | 46 + .../examples/helloworld/workers/Workers.java | 23 + .../workflowdef/GreetingsWorkflow.java | 41 + .../sdk/examples/sendemail/Main.java | 47 + .../examples/sendemail/workers/Workers.java | 44 + .../workflowdef/SendEmailWorkflow.java | 52 + .../conductor/sdk/examples/shipment/Main.java | 62 + .../sdk/examples/shipment/Order.java | 103 ++ .../sdk/examples/shipment/Shipment.java | 43 + .../sdk/examples/shipment/ShipmentState.java | 56 + .../examples/shipment/ShipmentWorkers.java | 146 ++ .../examples/shipment/ShipmentWorkflow.java | 185 +++ .../conductor/sdk/examples/shipment/User.java | 117 ++ .../sdk/examples/taskdomains/Main.java | 79 ++ .../sdk/examples/taskdomains/Workers.java | 101 ++ .../sdk/examples/AuthorizationManagement.java | 123 ++ .../sdk/examples/MetadataManagement.java | 180 +++ .../sdk/examples/SchedulerManagement.java | 106 ++ .../sdk/examples/WorkflowManagement.java | 88 ++ .../sdk/examples/WorkflowManagement2.java | 118 ++ .../sdk/examples/util/ClientUtil.java | 57 + .../sdk/examples/workflowops/Main.java | 107 ++ .../workflowdef/GreetingsWorkflow.java | 85 ++ .../examples/src/main/resources/logback.xml | 16 + .../examples/src/main/resources/script.js | 11 + .../src/main/resources/task_domain_wf.json | 85 ++ .../examples/src/main/resources/workflow.json | 17 + .../java/conductor-java-sdk/gradle.properties | 1 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58695 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + .../java/conductor-java-sdk/gradlew | 183 +++ .../java/conductor-java-sdk/gradlew.bat | 100 ++ .../java/conductor-java-sdk/licenseheader.txt | 12 + .../orkes-client/build.gradle | 108 ++ .../io/orkes/conductor/client/ApiClient.java | 157 +++ .../conductor/client/AuthorizationClient.java | 99 ++ .../conductor/client/IntegrationClient.java | 77 + .../orkes/conductor/client/OrkesClients.java | 70 + .../orkes/conductor/client/PromptClient.java | 51 + .../conductor/client/SchedulerClient.java | 51 + .../orkes/conductor/client/SecretClient.java | 36 + .../conductor/client/http/ApiCallback.java | 60 + .../conductor/client/http/ApiException.java | 35 + .../conductor/client/http/ApiResponse.java | 62 + .../client/http/ApplicationResource.java | 194 +++ .../client/http/AuthorizationResource.java | 72 + .../conductor/client/http/EventResource.java | 100 ++ .../conductor/client/http/GroupResource.java | 133 ++ .../client/http/IntegrationResource.java | 226 +++ .../client/http/MetadataResource.java | 168 +++ .../client/http/OrkesAuthentication.java | 129 ++ .../client/http/OrkesAuthorizationClient.java | 202 +++ .../client/http/OrkesEventClient.java | 71 + .../client/http/OrkesIntegrationClient.java | 113 ++ .../client/http/OrkesMetadataClient.java | 127 ++ .../client/http/OrkesPromptClient.java | 149 ++ .../client/http/OrkesSchedulerClient.java | 106 ++ .../client/http/OrkesSecretClient.java | 75 + .../client/http/OrkesTaskClient.java | 204 +++ .../client/http/OrkesWorkflowClient.java | 237 ++++ .../io/orkes/conductor/client/http/Pair.java | 55 + .../client/http/SchedulerResource.java | 212 +++ .../conductor/client/http/SecretResource.java | 142 ++ .../conductor/client/http/TagsResource.java | 139 ++ .../conductor/client/http/TokenResource.java | 55 + .../conductor/client/http/UserResource.java | 109 ++ .../client/http/WorkflowBulkResource.java | 101 ++ .../client/http/WorkflowResource.java | 171 +++ .../client/model/AccessKeyResponse.java | 22 + .../client/model/AccessKeyStatus.java | 18 + .../client/model/AuthorizationRequest.java | 114 ++ .../client/model/ConductorApplication.java | 66 + .../conductor/client/model/ConductorUser.java | 130 ++ .../model/CorrelationIdsSearchRequest.java | 29 + .../client/model/CreateAccessKeyResponse.java | 34 + .../CreateOrUpdateApplicationRequest.java | 41 + .../client/model/ExternalStorageLocation.java | 50 + .../client/model/GenerateTokenRequest.java | 27 + .../conductor/client/model/GrantedAccess.java | 96 ++ .../client/model/GrantedAccessResponse.java | 47 + .../orkes/conductor/client/model/Group.java | 158 +++ .../conductor/client/model/Permission.java | 36 + .../io/orkes/conductor/client/model/Role.java | 75 + .../client/model/SaveScheduleRequest.java | 216 +++ ...SearchResultWorkflowScheduleExecution.java | 23 + ...hResultWorkflowScheduleExecutionModel.java | 58 + .../model/SearchResultWorkflowSummary.java | 64 + .../orkes/conductor/client/model/Subject.java | 82 ++ .../conductor/client/model/SubjectRef.java | 101 ++ .../conductor/client/model/TagObject.java | 100 ++ .../conductor/client/model/TagString.java | 96 ++ .../conductor/client/model/TargetRef.java | 101 ++ .../conductor/client/model/TaskDetails.java | 93 ++ .../client/model/TerminateWorkflow.java | 52 + .../conductor/client/model/TokenResponse.java | 24 + .../client/model/UpsertGroupRequest.java | 177 +++ .../client/model/UpsertUserRequest.java | 133 ++ .../conductor/client/model/WorkflowRun.java | 49 + .../client/model/WorkflowSchedule.java | 188 +++ .../model/WorkflowScheduleExecutionModel.java | 204 +++ .../client/model/WorkflowStateUpdate.java | 26 + .../client/model/WorkflowStatus.java | 156 ++ .../model/event/QueueConfiguration.java | 81 ++ .../model/event/QueueWorkerConfiguration.java | 39 + .../client/model/integration/Category.java | 27 + .../client/model/integration/Integration.java | 38 + .../model/integration/IntegrationApi.java | 36 + .../integration/IntegrationApiUpdate.java | 26 + .../model/integration/IntegrationDef.java | 30 + .../model/integration/IntegrationUpdate.java | 28 + .../PromptTemplateTestRequest.java | 31 + .../model/integration/ai/ChatCompletion.java | 34 + .../model/integration/ai/ChatMessage.java | 30 + .../integration/ai/EmbeddingRequest.java | 24 + .../model/integration/ai/IndexDocInput.java | 55 + .../model/integration/ai/IndexedDoc.java | 38 + .../model/integration/ai/LLMResponse.java | 26 + .../model/integration/ai/LLMWorkerInput.java | 33 + .../model/integration/ai/PromptTemplate.java | 34 + .../ai/PromptTemplateTestRequest.java | 32 + .../integration/ai/StoreEmbeddingsInput.java | 31 + .../model/integration/ai/TextCompletion.java | 24 + .../model/integration/ai/VectorDBInput.java | 32 + .../conductor/client/worker/WorkerFn.java | 21 + .../conductor/client/worker/Workers.java | 126 ++ .../orkes-spring/build.gradle | 78 + ...OrkesConductorClientAutoConfiguration.java | 106 ++ .../java/conductor-java-sdk/sdk/README.md | 11 + .../java/conductor-java-sdk/sdk/build.gradle | 88 ++ .../sdk/healthcheck/HealthCheckClient.java | 66 + .../sdk/testing/LocalServerRunner.java | 206 +++ .../sdk/testing/WorkflowTestRunner.java | 65 + .../sdk/workflow/def/ConductorWorkflow.java | 385 +++++ .../sdk/workflow/def/ValidationError.java | 20 + .../sdk/workflow/def/WorkflowBuilder.java | 215 +++ .../sdk/workflow/def/tasks/DoWhile.java | 97 ++ .../sdk/workflow/def/tasks/Dynamic.java | 41 + .../sdk/workflow/def/tasks/DynamicFork.java | 115 ++ .../workflow/def/tasks/DynamicForkInput.java | 51 + .../sdk/workflow/def/tasks/Event.java | 46 + .../sdk/workflow/def/tasks/ForkJoin.java | 130 ++ .../sdk/workflow/def/tasks/Http.java | 310 ++++ .../conductor/sdk/workflow/def/tasks/JQ.java | 43 + .../sdk/workflow/def/tasks/Javascript.java | 148 ++ .../sdk/workflow/def/tasks/Join.java | 46 + .../sdk/workflow/def/tasks/SetVariable.java | 33 + .../sdk/workflow/def/tasks/SimpleTask.java | 47 + .../sdk/workflow/def/tasks/SubWorkflow.java | 90 ++ .../sdk/workflow/def/tasks/Switch.java | 167 +++ .../sdk/workflow/def/tasks/Task.java | 241 ++++ .../sdk/workflow/def/tasks/TaskRegistry.java | 48 + .../sdk/workflow/def/tasks/Terminate.java | 66 + .../sdk/workflow/def/tasks/Wait.java | 61 + .../workflow/executor/WorkflowExecutor.java | 260 ++++ .../executor/task/AnnotatedWorker.java | 250 ++++ .../task/AnnotatedWorkerExecutor.java | 218 +++ .../executor/task/DynamicForkWorker.java | 119 ++ .../executor/task/NonRetryableException.java | 32 + .../workflow/executor/task/TaskContext.java | 80 ++ .../executor/task/WorkerConfiguration.java | 36 + .../sdk/workflow/task/InputParam.java | 26 + .../sdk/workflow/task/OutputParam.java | 24 + .../sdk/workflow/task/WorkerTask.java | 32 + .../sdk/workflow/utils/InputOutputGetter.java | 105 ++ .../sdk/workflow/utils/MapBuilder.java | 39 + .../workflow/def/TaskConversionsTests.java | 548 ++++++++ .../workflow/def/WorkflowCreationTests.java | 225 +++ .../workflow/def/WorkflowDefTaskTests.java | 55 + .../sdk/workflow/def/WorkflowState.java | 36 + .../executor/task/AnnotatedWorkerTests.java | 344 +++++ .../executor/task/TestWorkerConfig.java | 41 + .../sdk/workflow/testing/Task1Input.java | 41 + .../workflow/testing/TestWorkflowInput.java | 54 + .../testing/WorkflowTestFrameworkTests.java | 208 +++ .../application-integrationtest.properties | 55 + .../sdk/src/test/resources/log4j2.xml | 31 + .../sdk/src/test/resources/script.js | 11 + .../src/test/resources/simple_workflow.json | 151 ++ .../sdk/src/test/resources/tasks.json | 1252 +++++++++++++++++ .../src/test/resources/test-server.properties | 5 + .../sdk/testing_framework.md | 74 + .../java/conductor-java-sdk/sdk/worker_sdk.md | 117 ++ .../conductor-java-sdk/sdk/workflow_sdk.md | 122 ++ .../java/conductor-java-sdk/settings.gradle | 9 + .../conductor-java-sdk/tests/build.gradle | 40 + .../conductor/client/LoadTestWorker.java | 78 + .../conductor/client/LocalWorkerTest.java | 53 + .../conductor/client/WorkflowRetryTest.java | 128 ++ .../client/http/AuthorizationClientTests.java | 343 +++++ .../client/http/EventClientTests.java | 77 + .../client/http/MetadataClientTests.java | 115 ++ .../conductor/client/http/PathSuffixTest.java | 42 + .../client/http/SchedulerClientTests.java | 76 + .../client/http/SecretClientTests.java | 62 + .../client/http/TaskClientTests.java | 228 +++ .../client/http/WorkflowClientTests.java | 194 +++ .../client/http/WorkflowStateUpdateTests.java | 128 ++ .../conductor/client/util/ClientTestUtil.java | 56 + .../orkes/conductor/client/util/Commons.java | 58 + .../conductor/client/util/SimpleWorker.java | 33 + .../orkes/conductor/client/util/TestUtil.java | 66 + .../conductor/client/util/WorkflowUtil.java | 34 + .../LocalServerWorkflowExecutionTests.java | 70 + .../client/worker/WorkflowExecutionTests.java | 90 ++ .../orkes/conductor/sdk/WorkflowSDKTests.java | 72 + .../src/test/resources/application.properties | 7 + .../tests/src/test/resources/logback-test.xml | 14 + .../metadata/sub_workflow_tests.json | 131 ++ .../test/resources/metadata/workflows.json | 594 ++++++++ .../src/test/resources/sample_tasks.json | 59 + .../src/test/resources/sample_workflow.json | 117 ++ .../tests/src/test/resources/sdk_test.json | 252 ++++ .../java/conductor-java-sdk/versions.gradle | 17 + 314 files changed, 35524 insertions(+) create mode 100644 conductor-clients/README.md create mode 100644 conductor-clients/java/conductor-java-sdk/LICENSE create mode 100644 conductor-clients/java/conductor-java-sdk/README.md create mode 100644 conductor-clients/java/conductor-java-sdk/build.gradle create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client-spring/build.gradle create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/build.gradle create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollCompleted.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollFailure.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollStarted.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionCompleted.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionFailure.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionStarted.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskRunnerEvent.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/filters/PollFilter.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientResponse.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConnectionPoolConfig.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/EventClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/HeaderSupplier.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/Param.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/TaskClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/worker/Worker.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/Auditable.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/SchemaDef.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/IdempotencyStrategy.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RateLimitConfig.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StateChangeEvent.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/UpgradeWorkflowRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/model/BulkResponse.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/SearchResult.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/TaskSummary.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/Workflow.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/EnvUtils.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ValidationError.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/ClientSpecification.groovy create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/EventClientSpec.groovy create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/MetadataClientSpec.groovy create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/TaskClientSpec.groovy create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/WorkflowClientSpec.groovy create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/Main.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/config.properties create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/tasks.json create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/test_data/loan_workflow_input.json create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/test_data/workflow1_run.json create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/PopulationMinMax.json create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/calculate_loan_workflow.json create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/kitchensink.json create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/workflow1.json create mode 100644 conductor-clients/java/conductor-java-sdk/examples/build.gradle create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRegistration.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRunner.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/events/EventListenerExample.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/Main.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workers/Workers.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workflowdef/GreetingsWorkflow.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/Main.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workers/Workers.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workflowdef/SendEmailWorkflow.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Main.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Order.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Shipment.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentState.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkers.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkflow.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/User.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Main.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Workers.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/AuthorizationManagement.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/MetadataManagement.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/SchedulerManagement.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement2.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/util/ClientUtil.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/Main.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/workflowdef/GreetingsWorkflow.java create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/resources/logback.xml create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/resources/script.js create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/resources/task_domain_wf.json create mode 100644 conductor-clients/java/conductor-java-sdk/examples/src/main/resources/workflow.json create mode 100644 conductor-clients/java/conductor-java-sdk/gradle.properties create mode 100644 conductor-clients/java/conductor-java-sdk/gradle/wrapper/gradle-wrapper.jar create mode 100644 conductor-clients/java/conductor-java-sdk/gradle/wrapper/gradle-wrapper.properties create mode 100755 conductor-clients/java/conductor-java-sdk/gradlew create mode 100644 conductor-clients/java/conductor-java-sdk/gradlew.bat create mode 100644 conductor-clients/java/conductor-java-sdk/licenseheader.txt create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/build.gradle create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/ApiClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/AuthorizationClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/IntegrationClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/OrkesClients.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/PromptClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SchedulerClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SecretClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiCallback.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiException.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiResponse.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApplicationResource.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/AuthorizationResource.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/EventResource.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/GroupResource.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/IntegrationResource.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/MetadataResource.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthentication.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthorizationClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesEventClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesIntegrationClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesMetadataClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesPromptClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSchedulerClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSecretClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesTaskClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesWorkflowClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/Pair.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SchedulerResource.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SecretResource.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TagsResource.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TokenResource.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/UserResource.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowBulkResource.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowResource.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyResponse.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyStatus.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AuthorizationRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorApplication.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorUser.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CorrelationIdsSearchRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateAccessKeyResponse.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateOrUpdateApplicationRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ExternalStorageLocation.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GenerateTokenRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccess.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccessResponse.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Group.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Permission.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Role.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SaveScheduleRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecution.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecutionModel.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowSummary.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Subject.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SubjectRef.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagObject.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagString.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TargetRef.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TaskDetails.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TerminateWorkflow.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TokenResponse.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertGroupRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertUserRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowRun.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowSchedule.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowScheduleExecutionModel.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStateUpdate.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStatus.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueConfiguration.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueWorkerConfiguration.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Category.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Integration.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApi.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApiUpdate.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationDef.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationUpdate.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/PromptTemplateTestRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatCompletion.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatMessage.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/EmbeddingRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexDocInput.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexedDoc.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMResponse.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMWorkerInput.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplate.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplateTestRequest.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/StoreEmbeddingsInput.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/TextCompletion.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/VectorDBInput.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/WorkerFn.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/Workers.java create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-spring/build.gradle create mode 100644 conductor-clients/java/conductor-java-sdk/orkes-spring/src/main/java/io/orkes/conductor/client/spring/OrkesConductorClientAutoConfiguration.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/README.md create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/build.gradle create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/application-integrationtest.properties create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/log4j2.xml create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/script.js create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/simple_workflow.json create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/tasks.json create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/test-server.properties create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/testing_framework.md create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/worker_sdk.md create mode 100644 conductor-clients/java/conductor-java-sdk/sdk/workflow_sdk.md create mode 100644 conductor-clients/java/conductor-java-sdk/settings.gradle create mode 100644 conductor-clients/java/conductor-java-sdk/tests/build.gradle create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LoadTestWorker.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LocalWorkerTest.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/WorkflowRetryTest.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/AuthorizationClientTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/EventClientTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/MetadataClientTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/PathSuffixTest.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SchedulerClientTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SecretClientTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/TaskClientTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowClientTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowStateUpdateTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/ClientTestUtil.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/Commons.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/SimpleWorker.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/TestUtil.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/WorkflowUtil.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/LocalServerWorkflowExecutionTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/WorkflowExecutionTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/sdk/WorkflowSDKTests.java create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/resources/application.properties create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/resources/logback-test.xml create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/resources/metadata/sub_workflow_tests.json create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/resources/metadata/workflows.json create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/resources/sample_tasks.json create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/resources/sample_workflow.json create mode 100644 conductor-clients/java/conductor-java-sdk/tests/src/test/resources/sdk_test.json create mode 100644 conductor-clients/java/conductor-java-sdk/versions.gradle diff --git a/conductor-clients/README.md b/conductor-clients/README.md new file mode 100644 index 000000000..40782e081 --- /dev/null +++ b/conductor-clients/README.md @@ -0,0 +1 @@ +# Conductor Clients diff --git a/conductor-clients/java/conductor-java-sdk/LICENSE b/conductor-clients/java/conductor-java-sdk/LICENSE new file mode 100644 index 000000000..6a1d025d8 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} Netflix, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/README.md b/conductor-clients/java/conductor-java-sdk/README.md new file mode 100644 index 000000000..d8bc0d304 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/README.md @@ -0,0 +1,89 @@ +# Conductor Java Client/SDK V3 + +## Overview + +**This project is currently in incubating status**. + +It is under active development and subject to changes as it evolves. While the core features are functional, the project is not yet considered stable, and breaking changes may occur as we refine the architecture and add new functionality. + +These changes are largely driven by **"dependency optimization"** and a redesign of the client to introduces filters, events and listeners to allow extensibility through callbacks or Event-Driven Architecture, IoC. + +This client has a reduced dependency set. The aim is to minimize Classpath pollution and prevent potential conflicts. + +Take Netflix Eureka as an example, Spring Cloud users have reported version conflicts. Some of them weren't even using Eureka. So, we've decided to remove the direct dependency. + +In the client it's used by the `TaskPollExecutor` before polling to make the following check: + +```java +if (eurekaClient != null + && !eurekaClient.getInstanceRemoteStatus().equals(InstanceStatus.UP) + && !discoveryOverride) { + LOGGER.debug("Instance is NOT UP in discovery - will not poll"); + return; +} +``` + +You will be able to achieve the same with a `PollFilter`. It could look something like this: + +```java + var runnerConfigurer = new TaskRunnerConfigurer + .Builder(taskClient, List.of(new ApprovalWorker())) + .withThreadCount(10) + .withPollFilter((String taskType, String domain) -> { + return eurekaClient.getInstanceRemoteStatus().equals(InstanceStatus.UP); + }) + .withListener(PollCompleted.class, (e) -> { + log.info("Poll Completed {}", e); + var timer = prometheusRegistry.timer("poll_success", "type", e.getTaskType()); + timer.record(e.getDuration()); + }) + .withListener(TaskExecutionFailure.class, (e) -> { + log.error("Task Execution Failure {}", e); + var counter = prometheusRegistry.counter("execute_failure", "type", e.getTaskType(), "id", e.getTaskId()); + counter.increment(); + }) + .build(); +runnerConfigurer.init(); +``` + +The telemetry part was also removed but you can achieve the same with Events and Listeners as shown in the example. + +### Breaking Changes + +While we aim to minimize breaking changes, there are a few areas where such changes are necessary. + +Below are two specific examples of where changes may affect your existing code: + +#### (1) Jersey Config + +The `WorkflowClient` and other clients will retain the same methods, but constructors with dependencies on Jersey are being removed. For example: + +```java +public WorkflowClient(ClientConfig config, ClientHandler handler) { + this(config, new DefaultConductorClientConfiguration(), handler); +} +``` + +#### (2) Eureka Client + +In the Worker API we've removed the Eureka Client configuration option (from `TaskRunnerConfigurer`). + +```java +* @param eurekaClient Eureka client - used to identify if the server is in discovery or + * not. When the server goes out of discovery, the polling is terminated. If passed + * null, discovery check is not done. + * @return Builder instance + */ +public Builder withEurekaClient(EurekaClient eurekaClient) { + this.eurekaClient = eurekaClient; + return this; +} +``` + +## TODO + +- Stabilize the codebase +- Complete documentation +- Gather community feedback +- Achieve production readiness + diff --git a/conductor-clients/java/conductor-java-sdk/build.gradle b/conductor-clients/java/conductor-java-sdk/build.gradle new file mode 100644 index 000000000..5c402e1a7 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/build.gradle @@ -0,0 +1,55 @@ +plugins { + id 'com.diffplug.spotless' version '6.+' +} + +group = 'io.orkes.conductor' + +apply from: "$rootDir/versions.gradle" + +subprojects { + apply plugin: 'java' + apply plugin: 'com.diffplug.spotless' + group = 'io.orkes.conductor' + + spotless { + java { + removeUnusedImports() + importOrder('java', 'javax', 'org', 'com.netflix', 'io.orkes', '', '\\#com.netflix', '\\#') + licenseHeaderFile("$rootDir/licenseheader.txt") + } + } + + compileJava { + sourceCompatibility = 11 + targetCompatibility = 11 + } + + dependencies { + // ### candidates to be shadowed ### + implementation "com.google.guava:guava:${versions.guava}" + // ################################# + + implementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}" + implementation "com.fasterxml.jackson.core:jackson-core:${versions.jackson}" + implementation "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}" + implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${versions.jackson}" + + implementation "org.slf4j:slf4j-api:${versions.slf4j}" + implementation "org.apache.commons:commons-lang3:${versions.commonsLang}" + annotationProcessor "org.projectlombok:lombok:${versions.lombok}" + compileOnly "org.projectlombok:lombok:${versions.lombok}" + } + + repositories { + mavenCentral() + mavenLocal() + maven { + url "https://s01.oss.sonatype.org/content/repositories/releases/" + } + } + + tasks.withType(Javadoc) { + options.addStringOption('Xdoclint:none', '-quiet') + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/build.gradle b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/build.gradle new file mode 100644 index 000000000..637d103b8 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/build.gradle @@ -0,0 +1,76 @@ +plugins { + id 'java-library' + id 'idea' + id 'maven-publish' + id 'signing' +} + +compileJava { + sourceCompatibility = 17 + targetCompatibility = 17 +} + +repositories { + mavenCentral() +} + +dependencies { + api project(":conductor-client") + api project(":sdk") + + implementation 'org.springframework.boot:spring-boot-starter:3.3.0' +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + pom { + name = 'Conductor OSS Client Spring' + description = 'Spring autoconfig for Conductor Client and SDK' + url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + scm { + connection = 'scm:git:git://github.com/conductor-oss/conductor-java-sdk.git' + developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor-java-sdk.git' + url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + } + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + organization = 'Orkes' + organizationUrl = 'https://orkes.io' + name = 'Orkes Development Team' + email = 'developers@orkes.io' + } + } + } + } + } + + repositories { + maven { + if (project.hasProperty("mavenCentral")) { + println "Publishing to Sonatype Repository" + url = "https://s01.oss.sonatype.org/${project.version.endsWith('-SNAPSHOT') ? "content/repositories/snapshots/" : "service/local/staging/deploy/maven2/"}" + credentials { + username project.properties.username + password project.properties.password + } + } else { + url = "s3://orkes-artifacts-repo/${project.version.endsWith('-SNAPSHOT') ? "snapshots" : "releases"}" + authentication { + awsIm(AwsImAuthentication) + } + } + } + } +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java new file mode 100644 index 000000000..c90272bbc --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java @@ -0,0 +1,113 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.spring; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("conductor.client") +public class ClientProperties { + + private String rootUri; + + private String workerNamePrefix = "workflow-worker-%d"; + + private int threadCount = 1; + + private Duration sleepWhenRetryDuration = Duration.ofMillis(500); + + private int updateRetryCount = 3; + + private Map taskToDomain = new HashMap<>(); + + private Map taskThreadCount = new HashMap<>(); + + private int shutdownGracePeriodSeconds = 10; + + private int taskPollTimeout = 100; + + public String getRootUri() { + return rootUri; + } + + public void setRootUri(String rootUri) { + this.rootUri = rootUri; + } + + public String getWorkerNamePrefix() { + return workerNamePrefix; + } + + public void setWorkerNamePrefix(String workerNamePrefix) { + this.workerNamePrefix = workerNamePrefix; + } + + public int getThreadCount() { + return threadCount; + } + + public void setThreadCount(int threadCount) { + this.threadCount = threadCount; + } + + public Duration getSleepWhenRetryDuration() { + return sleepWhenRetryDuration; + } + + public void setSleepWhenRetryDuration(Duration sleepWhenRetryDuration) { + this.sleepWhenRetryDuration = sleepWhenRetryDuration; + } + + public int getUpdateRetryCount() { + return updateRetryCount; + } + + public void setUpdateRetryCount(int updateRetryCount) { + this.updateRetryCount = updateRetryCount; + } + + public Map getTaskToDomain() { + return taskToDomain; + } + + public void setTaskToDomain(Map taskToDomain) { + this.taskToDomain = taskToDomain; + } + + public int getShutdownGracePeriodSeconds() { + return shutdownGracePeriodSeconds; + } + + public void setShutdownGracePeriodSeconds(int shutdownGracePeriodSeconds) { + this.shutdownGracePeriodSeconds = shutdownGracePeriodSeconds; + } + + public Map getTaskThreadCount() { + return taskThreadCount; + } + + public void setTaskThreadCount(Map taskThreadCount) { + this.taskThreadCount = taskThreadCount; + } + + public int getTaskPollTimeout() { + return taskPollTimeout; + } + + public void setTaskPollTimeout(int taskPollTimeout) { + this.taskPollTimeout = taskPollTimeout; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java new file mode 100644 index 000000000..f157d5377 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java @@ -0,0 +1,102 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.spring; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import com.netflix.conductor.client.automator.TaskRunnerConfigurer; +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.http.WorkflowClient; +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; +import com.netflix.conductor.sdk.workflow.executor.task.AnnotatedWorkerExecutor; + +import lombok.extern.slf4j.Slf4j; + +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(ClientProperties.class) +@Slf4j +public class ConductorClientAutoConfiguration { + + @ConditionalOnMissingBean + @Bean + public ConductorClient conductorClient(ClientProperties clientProperties) { + // TODO allow configuration of other properties via application.properties + return ConductorClient.builder() + .basePath(clientProperties.getRootUri()) + .build(); + } + + @ConditionalOnMissingBean + @Bean + public TaskClient taskClient(ConductorClient client) { + return new TaskClient(client); + } + + @ConditionalOnMissingBean + @Bean + public AnnotatedWorkerExecutor annotatedWorkerExecutor(TaskClient taskClient) { + return new AnnotatedWorkerExecutor(taskClient); + } + + @ConditionalOnMissingBean + @Bean(initMethod = "init", destroyMethod = "shutdown") + public TaskRunnerConfigurer taskRunnerConfigurer(Environment env, + TaskClient taskClient, + ClientProperties clientProperties, + List workers) { + Map taskThreadCount = new HashMap<>(); + for (Worker worker : workers) { + String key = "conductor.worker." + worker.getTaskDefName() + ".threadCount"; + int threadCount = env.getProperty(key, Integer.class, 10); + log.info("Using {} threads for {} worker", threadCount, worker.getTaskDefName()); + taskThreadCount.put(worker.getTaskDefName(), threadCount); + } + + if (clientProperties.getTaskThreadCount() != null) { + clientProperties.getTaskThreadCount().putAll(taskThreadCount); + } else { + clientProperties.setTaskThreadCount(taskThreadCount); + } + + return new TaskRunnerConfigurer.Builder(taskClient, workers) + .withTaskThreadCount(clientProperties.getTaskThreadCount()) + .withThreadCount(clientProperties.getThreadCount()) + .withSleepWhenRetry((int) clientProperties.getSleepWhenRetryDuration().toMillis()) + .withUpdateRetryCount(clientProperties.getUpdateRetryCount()) + .withTaskToDomain(clientProperties.getTaskToDomain()) + .withShutdownGracePeriodSeconds(clientProperties.getShutdownGracePeriodSeconds()) + .withTaskPollTimeout(clientProperties.getTaskPollTimeout()) + .build(); + } + + @Bean + public WorkflowExecutor workflowExecutor(ConductorClient client, AnnotatedWorkerExecutor annotatedWorkerExecutor) { + return new WorkflowExecutor(client, annotatedWorkerExecutor); + } + + @ConditionalOnMissingBean + @Bean + public WorkflowClient workflowClient(ConductorClient client) { + return new WorkflowClient(client); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java new file mode 100644 index 000000000..db0432ebf --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.spring; + +import java.util.Map; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.sdk.workflow.executor.task.AnnotatedWorkerExecutor; +import com.netflix.conductor.sdk.workflow.executor.task.WorkerConfiguration; + +@Component +public class ConductorWorkerAutoConfiguration implements ApplicationListener { + + private final TaskClient taskClient; + + public ConductorWorkerAutoConfiguration(TaskClient taskClient) { + this.taskClient = taskClient; + } + + @Override + public void onApplicationEvent(ContextRefreshedEvent refreshedEvent) { + ApplicationContext applicationContext = refreshedEvent.getApplicationContext(); + Environment environment = applicationContext.getEnvironment(); + WorkerConfiguration configuration = new SpringWorkerConfiguration(environment); + AnnotatedWorkerExecutor annotatedWorkerExecutor = new AnnotatedWorkerExecutor(taskClient, configuration); + + Map beans = applicationContext.getBeansWithAnnotation(Component.class); + beans.values().forEach(annotatedWorkerExecutor::addBean); + annotatedWorkerExecutor.startPolling(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java new file mode 100644 index 000000000..0dec407d3 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java @@ -0,0 +1,51 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.spring; + +import org.springframework.core.env.Environment; + +import com.netflix.conductor.sdk.workflow.executor.task.WorkerConfiguration; + +class SpringWorkerConfiguration extends WorkerConfiguration { + + private final Environment environment; + + public SpringWorkerConfiguration(Environment environment) { + this.environment = environment; + } + + @Override + public int getPollingInterval(String taskName) { + return getProperty(taskName, "pollingInterval", Integer.class, 0); + } + + @Override + public int getThreadCount(String taskName) { + return getProperty(taskName, "threadCount", Integer.class, 0); + } + + @Override + public String getDomain(String taskName) { + return getProperty(taskName, "domain", String.class, null); + } + + private T getProperty(String taskName, String property, Class type, T defaultValue) { + String key = "conductor.worker." + taskName + "." + property; + T value = environment.getProperty(key, type, defaultValue); + if (value == defaultValue) { + key = "conductor.worker.all." + property; + value = environment.getProperty(key, type, defaultValue); + } + return value; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/build.gradle b/conductor-clients/java/conductor-java-sdk/conductor-client/build.gradle new file mode 100644 index 000000000..6d66d5745 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/build.gradle @@ -0,0 +1,107 @@ +plugins { + id 'java-library' + id 'idea' + id 'maven-publish' + id 'signing' + id 'com.github.johnrengelman.shadow' version '8.1.1' + id 'groovy' +} + +dependencies { + implementation "com.squareup.okhttp3:okhttp:${versions.okHttp}" + + // test dependencies + testImplementation "org.junit.jupiter:junit-jupiter-api:${versions.junit}" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${versions.junit}" + + testImplementation "org.powermock:powermock-module-junit4:2.0.9" + testImplementation "org.powermock:powermock-api-mockito2:2.0.9" + + testImplementation 'org.spockframework:spock-core:2.3-groovy-3.0' + testImplementation 'org.codehaus.groovy:groovy:3.0.15' + testImplementation 'ch.qos.logback:logback-classic:1.5.6' +} + +java { + withSourcesJar() + withJavadocJar() +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + pom { + name = 'Conductor Client' + description = 'Conductor OSS client (http)' + url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + scm { + connection = 'scm:git:git://github.com/conductor-oss/conductor-java-sdk.git' + developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor-java-sdk.git' + url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + } + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + organization = 'Orkes' + organizationUrl = 'https://orkes.io' + name = 'Orkes Development Team' + email = 'developers@orkes.io' + } + } + } + } + } + + repositories { + maven { + if (project.hasProperty("mavenCentral")) { + println "Publishing to Sonatype Repository" + url = "https://s01.oss.sonatype.org/${project.version.endsWith('-SNAPSHOT') ? "content/repositories/snapshots/" : "service/local/staging/deploy/maven2/"}" + credentials { + username project.properties.username + password project.properties.password + } + } else { + url = "s3://orkes-artifacts-repo/${project.version.endsWith('-SNAPSHOT') ? "snapshots" : "releases"}" + authentication { + awsIm(AwsImAuthentication) + } + } + } + } +} + +signing { + def signingKeyId = findProperty('signingKeyId') + if (signingKeyId) { + println 'Signing the artifact with keys' + signing { + def signingKey = findProperty('signingKey') + def signingPassword = findProperty('signingPassword') + if (signingKeyId && signingKey && signingPassword) { + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + } + + sign publishing.publications + } + } +} + +test { + useJUnitPlatform() +} + +shadowJar { + archiveFileName = "orkes-conductor-client-$version-all.jar" + mergeServiceFiles() +} + +tasks.build { + dependsOn shadowJar +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java new file mode 100644 index 000000000..4facd4d71 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java @@ -0,0 +1,393 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.automator; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.client.automator.events.PollCompleted; +import com.netflix.conductor.client.automator.events.PollFailure; +import com.netflix.conductor.client.automator.events.PollStarted; +import com.netflix.conductor.client.automator.events.TaskExecutionCompleted; +import com.netflix.conductor.client.automator.events.TaskExecutionFailure; +import com.netflix.conductor.client.automator.events.TaskExecutionStarted; +import com.netflix.conductor.client.automator.events.TaskRunnerEvent; +import com.netflix.conductor.client.automator.filters.PollFilter; +import com.netflix.conductor.client.config.PropertyFactory; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; + +import com.google.common.base.Stopwatch; +import com.google.common.util.concurrent.Uninterruptibles; + +class TaskRunner { + + private static final Logger LOGGER = LoggerFactory.getLogger(TaskRunner.class); + private final TaskClient taskClient; + private final int updateRetryCount; + private final ExecutorService executorService; + private final int taskPollTimeout; + private final Semaphore permits; + private final Worker worker; + private final int pollingIntervalInMillis; + private final String taskType; + private final int errorAt; + private int pollingErrorCount; + private String domain; + private volatile boolean pollingAndExecuting = true; + private final List pollFilters; + private final Map, List>> listeners; + + TaskRunner(Worker worker, + TaskClient taskClient, + int updateRetryCount, + Map taskToDomain, + String workerNamePrefix, + int threadCount, + int taskPollTimeout, + List pollFilters, + Map, List>> listeners) { + this.worker = worker; + this.taskClient = taskClient; + this.updateRetryCount = updateRetryCount; + this.taskPollTimeout = taskPollTimeout; + this.pollingIntervalInMillis = worker.getPollingInterval(); + this.taskType = worker.getTaskDefName(); + this.permits = new Semaphore(threadCount); + this.pollFilters = pollFilters; + this.listeners = listeners; + + //1. Is there a worker level override? + this.domain = PropertyFactory.getString(taskType, Worker.PROP_DOMAIN, null); + if (this.domain == null) { + //2. If not, is there a blanket override? + this.domain = PropertyFactory.getString(Worker.PROP_ALL_WORKERS, Worker.PROP_DOMAIN, null); + } + if (this.domain == null) { + //3. was it supplied as part of the config? + this.domain = taskToDomain.get(taskType); + } + + int defaultLoggingInterval = 100; + int errorInterval = PropertyFactory.getInteger(taskType, Worker.PROP_LOG_INTERVAL, 0); + if (errorInterval == 0) { + errorInterval = PropertyFactory.getInteger(Worker.PROP_ALL_WORKERS, Worker.PROP_LOG_INTERVAL, 0); + } + if (errorInterval == 0) { + errorInterval = defaultLoggingInterval; + } + this.errorAt = errorInterval; + LOGGER.info("Polling errors will be sampled at every {} error (after the first 100 errors) for taskType {}", this.errorAt, taskType); + this.executorService = Executors.newFixedThreadPool(threadCount, + new BasicThreadFactory.Builder() + .namingPattern(workerNamePrefix) + .uncaughtExceptionHandler(uncaughtExceptionHandler) + .build()); + LOGGER.info("Starting Worker for taskType '{}' with {} threads, {} ms polling interval and domain {}", + taskType, + threadCount, + pollingIntervalInMillis, + domain); + LOGGER.info("Polling errors for taskType {} will be printed at every {} occurrence.", taskType, errorAt); + } + + public void pollAndExecute() { + Stopwatch stopwatch = null; + while (pollingAndExecuting) { + if (Thread.currentThread().isInterrupted()) { + break; // Exit the loop if interrupted + } + + try { + List tasks = pollTasksForWorker(); + if (tasks.isEmpty()) { + if (stopwatch == null) { + stopwatch = Stopwatch.createStarted(); + } + Uninterruptibles.sleepUninterruptibly(pollingIntervalInMillis, TimeUnit.MILLISECONDS); + continue; + } + if (stopwatch != null) { + stopwatch.stop(); + LOGGER.trace("Poller for task {} waited for {} ms before getting {} tasks to execute", taskType, stopwatch.elapsed(TimeUnit.MILLISECONDS), tasks.size()); + stopwatch = null; + } + tasks.forEach(task -> this.executorService.submit(() -> this.processTask(task))); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + } + + public void shutdown(int timeout) { + try { + pollingAndExecuting = false; + executorService.shutdown(); + if (executorService.awaitTermination(timeout, TimeUnit.SECONDS)) { + LOGGER.debug("tasks completed, shutting down"); + } else { + LOGGER.warn(String.format("forcing shutdown after waiting for %s second", timeout)); + executorService.shutdownNow(); + } + } catch (InterruptedException ie) { + LOGGER.warn("shutdown interrupted, invoking shutdownNow"); + executorService.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + private List pollTasksForWorker() { + publish(new PollStarted(taskType)); + + if (worker.paused()) { + LOGGER.trace("Worker {} has been paused. Not polling anymore!", worker.getClass()); + return List.of(); + } + + for (PollFilter filter : pollFilters) { + if (!filter.filter(taskType, domain)) { + LOGGER.trace("Filter returned false, not polling."); + return List.of(); + } + } + + int pollCount = 0; + while (permits.tryAcquire()) { + pollCount++; + } + + if (pollCount == 0) { + return List.of(); + } + + List tasks = new LinkedList<>(); + Stopwatch stopwatch = Stopwatch.createStarted(); //TODO move this to the top? + try { + LOGGER.trace("Polling task of type: {} in domain: '{}' with size {}", taskType, domain, pollCount); + tasks = pollTask(domain, pollCount); + permits.release(pollCount - tasks.size()); //release extra permits + stopwatch.stop(); + long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS); + LOGGER.debug("Time taken to poll {} task with a batch size of {} is {} ms", taskType, tasks.size(), elapsed); + publish(new PollCompleted(taskType, elapsed)); + } catch (Throwable e) { + permits.release(pollCount - tasks.size()); + + //For the first 100 errors, just print them as is... + boolean printError = pollingErrorCount < 100 || pollingErrorCount % errorAt == 0; + pollingErrorCount++; + if (pollingErrorCount > 10_000_000) { + //Reset after 10 million errors + pollingErrorCount = 0; + } + if (printError) { + LOGGER.error("Error polling for taskType: {}, error = {}", taskType, e.getMessage(), e); + } + + if (stopwatch.isRunning()) { + stopwatch.stop(); + } + + long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS); + publish(new PollFailure(taskType, elapsed, e)); + } + + return tasks; + } + + private List pollTask(String domain, int count) { + if (count < 1) { + return Collections.emptyList(); + } + String workerId = worker.getIdentity(); + LOGGER.debug("poll {} in the domain {} with batch size {}", taskType, domain, count); + return taskClient.batchPollTasksInDomain( + taskType, domain, workerId, count, this.taskPollTimeout); + } + + @SuppressWarnings("FieldCanBeLocal") + private final Thread.UncaughtExceptionHandler uncaughtExceptionHandler = + (thread, error) -> { + // JVM may be in unstable state, try to send metrics then exit + LOGGER.error("Uncaught exception. Thread {} will exit now", thread, error); + }; + + private void processTask(Task task) { + publish(new TaskExecutionStarted(taskType, task.getTaskId(), worker.getIdentity())); + LOGGER.trace("Executing task: {} of type: {} in worker: {} at {}", task.getTaskId(), taskType, worker.getClass().getSimpleName(), worker.getIdentity()); + LOGGER.trace("task {} is getting executed after {} ms of getting polled", task.getTaskId(), (System.currentTimeMillis() - task.getStartTime())); + Stopwatch stopwatch = Stopwatch.createStarted(); + try { + executeTask(worker, task); + stopwatch.stop(); + long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS); + LOGGER.trace( + "Took {} ms to execute and update task with id {}", + elapsed, + task.getTaskId()); + } catch (Throwable t) { + task.setStatus(Task.Status.FAILED); + TaskResult result = new TaskResult(task); + handleException(t, result, worker, task); + } finally { + permits.release(); + } + } + + private void executeTask(Worker worker, Task task) { + if (task == null || task.getTaskDefName().isEmpty()) { + LOGGER.warn("Empty task {}", worker.getTaskDefName()); + return; + } + + Stopwatch stopwatch = Stopwatch.createStarted(); + TaskResult result = null; + try { + LOGGER.trace( + "Executing task: {} in worker: {} at {}", + task.getTaskId(), + worker.getClass().getSimpleName(), + worker.getIdentity()); + result = worker.execute(task); + stopwatch.stop(); + publish(new TaskExecutionCompleted(taskType, task.getTaskId(), worker.getIdentity(), stopwatch.elapsed(TimeUnit.MILLISECONDS))); + result.setWorkflowInstanceId(task.getWorkflowInstanceId()); + result.setTaskId(task.getTaskId()); + result.setWorkerId(worker.getIdentity()); + } catch (Exception e) { + if (stopwatch.isRunning()) { + stopwatch.stop(); + } + publish(new TaskExecutionFailure(taskType, task.getTaskId(), worker.getIdentity(), e, stopwatch.elapsed(TimeUnit.MILLISECONDS))); + + LOGGER.error( + "Unable to execute task: {} of type: {}", + task.getTaskId(), + task.getTaskDefName(), + e); + if (result == null) { + task.setStatus(Task.Status.FAILED); + result = new TaskResult(task); + } + handleException(e, result, worker, task); + } + + LOGGER.trace( + "Task: {} executed by worker: {} at {} with status: {}", + task.getTaskId(), + worker.getClass().getSimpleName(), + worker.getIdentity(), + result.getStatus()); + Stopwatch updateStopWatch = Stopwatch.createStarted(); + updateTaskResult(updateRetryCount, task, result, worker); + updateStopWatch.stop(); + LOGGER.trace( + "Time taken to update the {} {} ms", + task.getTaskType(), + updateStopWatch.elapsed(TimeUnit.MILLISECONDS)); + } + + private void updateTaskResult(int count, Task task, TaskResult result, Worker worker) { + try { + // upload if necessary + Optional optionalExternalStorageLocation = + retryOperation( + (TaskResult taskResult) -> upload(taskResult, task.getTaskType()), + count, + result, + "evaluateAndUploadLargePayload"); + + if (optionalExternalStorageLocation.isPresent()) { + result.setExternalOutputPayloadStoragePath(optionalExternalStorageLocation.get()); + result.setOutputData(null); + } + + retryOperation( + (TaskResult taskResult) -> { + taskClient.updateTask(taskResult); + return null; + }, + count, + result, + "updateTask"); + } catch (Exception e) { + worker.onErrorUpdate(task); + LOGGER.error( + String.format( + "Failed to update result: %s for task: %s in worker: %s", + result.toString(), task.getTaskDefName(), worker.getIdentity()), + e); + } + } + + //FIXME + private Optional upload(TaskResult result, String taskType) { + // do nothing + return Optional.empty(); + } + + private R retryOperation(Function operation, int count, T input, String opName) { + int index = 0; + while (index < count) { + try { + return operation.apply(input); + } catch (Exception e) { + LOGGER.error("Error executing {}", opName, e); + index++; + Uninterruptibles.sleepUninterruptibly(500L * (count + 1), TimeUnit.MILLISECONDS); + } + } + throw new RuntimeException("Exhausted retries performing " + opName); + } + + private void publish(TaskRunnerEvent event) { + if (listeners.get(event.getClass()) == null) { + return; + } + + CompletableFuture.runAsync(() -> { + List> eventListeners = listeners.get(event.getClass()); + for (Consumer listener : eventListeners) { + ((Consumer) listener).accept(event); + } + }); + } + + private void handleException(Throwable t, TaskResult result, Worker worker, Task task) { + LOGGER.error(String.format("Error while executing task %s", task.toString()), t); + result.setStatus(TaskResult.Status.FAILED); + result.setReasonForIncompletion("Error while executing the task: " + t); + StringWriter stringWriter = new StringWriter(); + t.printStackTrace(new PrintWriter(stringWriter)); + result.log(stringWriter.toString()); + updateTaskResult(updateRetryCount, task, result, worker); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java new file mode 100644 index 000000000..ea0949d78 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java @@ -0,0 +1,325 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.automator; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Consumer; + +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.client.automator.events.TaskRunnerEvent; +import com.netflix.conductor.client.automator.filters.PollFilter; +import com.netflix.conductor.client.config.ConductorClientConfiguration; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.worker.Worker; + +import com.google.common.base.Preconditions; + +public class TaskRunnerConfigurer { + private static final Logger LOGGER = LoggerFactory.getLogger(TaskRunnerConfigurer.class); + private final TaskClient taskClient; + private final List workers; + private final int sleepWhenRetry; + private final int updateRetryCount; + private final int shutdownGracePeriodSeconds; + private final String workerNamePrefix; + private final Map taskToDomain; + private final Map taskToThreadCount; + private final Map taskPollTimeout; + private final Map taskPollCount; + private final Integer defaultPollTimeout; + private final int threadCount; + private final List taskRunners; + private ScheduledExecutorService scheduledExecutorService; + private final List pollFilters; + private final Map, List>> listeners; + + /** + * @see TaskRunnerConfigurer.Builder + * @see TaskRunnerConfigurer#init() + */ + private TaskRunnerConfigurer(TaskRunnerConfigurer.Builder builder) { + this.taskClient = builder.taskClient; + this.sleepWhenRetry = builder.sleepWhenRetry; + this.updateRetryCount = builder.updateRetryCount; + this.workerNamePrefix = builder.workerNamePrefix; + this.taskToDomain = builder.taskToDomain; + this.taskToThreadCount = builder.taskToThreadCount; + this.taskPollTimeout = builder.taskPollTimeout; + this.taskPollCount = builder.taskPollCount; + this.defaultPollTimeout = builder.defaultPollTimeout; + this.shutdownGracePeriodSeconds = builder.shutdownGracePeriodSeconds; + this.workers = new LinkedList<>(); + this.threadCount = builder.threadCount; + this.pollFilters = builder.pollFilters; + this.listeners = builder.listeners; + builder.workers.forEach(this.workers::add); + taskRunners = new LinkedList<>(); + } + + /** + * @return Thread Count for the shared executor pool + */ + @Deprecated + public int getThreadCount() { + return this.threadCount; + } + + /** + * @return Thread Count for individual task type + */ + public Map getTaskThreadCount() { + return taskToThreadCount; + } + + /** + * @return seconds before forcing shutdown of worker + */ + public int getShutdownGracePeriodSeconds() { + return shutdownGracePeriodSeconds; + } + + /** + * @return sleep time in millisecond before task update retry is done when receiving error from + * the Conductor server + */ + public int getSleepWhenRetry() { + return sleepWhenRetry; + } + + /** + * @return Number of times updateTask should be retried when receiving error from Conductor + * server + */ + public int getUpdateRetryCount() { + return updateRetryCount; + } + + /** + * @return prefix used for worker names + */ + public String getWorkerNamePrefix() { + return workerNamePrefix; + } + + /** + * Starts the polling. Must be called after {@link TaskRunnerConfigurer.Builder#build()} method. + */ + public synchronized void init() { + this.scheduledExecutorService = Executors.newScheduledThreadPool(workers.size(), + new BasicThreadFactory.Builder() + .namingPattern("TaskRunner %d") + .build()); + workers.forEach(worker -> scheduledExecutorService.submit(() -> this.startWorker(worker))); + } + + /** + * Invoke this method within a PreDestroy block within your application to facilitate a graceful + * shutdown of your worker, during process termination. + */ + public void shutdown() { + if (taskRunners != null) { + synchronized (taskRunners) { + taskRunners.forEach(taskRunner -> taskRunner.shutdown(shutdownGracePeriodSeconds)); + } + } + scheduledExecutorService.shutdown(); + } + + private void startWorker(Worker worker) { + final Integer threadCountForTask = this.taskToThreadCount.getOrDefault(worker.getTaskDefName(), threadCount); + final Integer taskPollTimeout = this.taskPollTimeout.getOrDefault(worker.getTaskDefName(), defaultPollTimeout); + LOGGER.info("Domain map for tasks = {}", taskToDomain); + final TaskRunner taskRunner = new TaskRunner( + worker, + taskClient, + updateRetryCount, + taskToDomain, + workerNamePrefix, + threadCountForTask, + taskPollTimeout, + pollFilters, + listeners); + // startWorker(worker) is executed by several threads. + // taskRunners.add(taskRunner) without synchronization could lead to a race condition and unpredictable behavior, + // including potential null values being inserted or corrupted state. + synchronized (taskRunners) { + taskRunners.add(taskRunner); + } + + taskRunner.pollAndExecute(); + } + + /** + * Builder used to create the instances of TaskRunnerConfigurer + */ + public static class Builder { + + private String workerNamePrefix = "workflow-worker-%d"; + private int sleepWhenRetry = 500; + private int updateRetryCount = 3; + private int threadCount = -1; + private int shutdownGracePeriodSeconds = 10; + private int defaultPollTimeout = 100; + private int defaultPollCount = 20; + private final Iterable workers; + private final TaskClient taskClient; + private Map taskToDomain = new HashMap<>(); + private Map taskToThreadCount = + new HashMap<>(); + private Map taskPollTimeout = new HashMap<>(); + private Map taskPollCount = new HashMap<>(); + private final List pollFilters = new LinkedList<>(); + private final Map, List>> listeners = new HashMap<>(); + + public Builder(TaskClient taskClient, Iterable workers) { + Preconditions.checkNotNull(taskClient, "TaskClient cannot be null"); + Preconditions.checkNotNull(workers, "Workers cannot be null"); + this.taskClient = taskClient; + this.workers = workers; + } + + /** + * @param workerNamePrefix prefix to be used for worker names, defaults to workflow-worker- + * if not supplied. + * @return Returns the current instance. + */ + public TaskRunnerConfigurer.Builder withWorkerNamePrefix(String workerNamePrefix) { + this.workerNamePrefix = workerNamePrefix; + return this; + } + + /** + * @param sleepWhenRetry time in milliseconds, for which the thread should sleep when task + * update call fails, before retrying the operation. + * @return Returns the current instance. + */ + public TaskRunnerConfigurer.Builder withSleepWhenRetry(int sleepWhenRetry) { + this.sleepWhenRetry = sleepWhenRetry; + return this; + } + + /** + * @param updateRetryCount number of times to retry the failed updateTask operation + * @return Builder instance + * @see #withSleepWhenRetry(int) + */ + public TaskRunnerConfigurer.Builder withUpdateRetryCount(int updateRetryCount) { + this.updateRetryCount = updateRetryCount; + return this; + } + + /** + * @param conductorClientConfiguration client configuration to handle external payloads + * @return Builder instance + */ + public TaskRunnerConfigurer.Builder withConductorClientConfiguration( + ConductorClientConfiguration conductorClientConfiguration) { + return this; + } + + /** + * @param shutdownGracePeriodSeconds waiting seconds before forcing shutdown of your worker + * @return Builder instance + */ + public TaskRunnerConfigurer.Builder withShutdownGracePeriodSeconds( + int shutdownGracePeriodSeconds) { + if (shutdownGracePeriodSeconds < 1) { + throw new IllegalArgumentException( + "Seconds of shutdownGracePeriod cannot be less than 1"); + } + this.shutdownGracePeriodSeconds = shutdownGracePeriodSeconds; + return this; + } + + public TaskRunnerConfigurer.Builder withTaskToDomain(Map taskToDomain) { + this.taskToDomain = taskToDomain; + return this; + } + + public TaskRunnerConfigurer.Builder withTaskThreadCount( + Map taskToThreadCount) { + this.taskToThreadCount = taskToThreadCount; + return this; + } + + public TaskRunnerConfigurer.Builder withTaskToThreadCount( + Map taskToThreadCount) { + this.taskToThreadCount = taskToThreadCount; + return this; + } + + public TaskRunnerConfigurer.Builder withTaskPollTimeout( + Map taskPollTimeout) { + this.taskPollTimeout = taskPollTimeout; + return this; + } + + public TaskRunnerConfigurer.Builder withTaskPollTimeout(Integer taskPollTimeout) { + this.defaultPollTimeout = taskPollTimeout; + return this; + } + + public TaskRunnerConfigurer.Builder withTaskPollCount(Map taskPollCount) { + this.taskPollCount = taskPollCount; + return this; + } + + public TaskRunnerConfigurer.Builder withTaskPollCount(int defaultPollCount) { + this.defaultPollCount = defaultPollCount; + return this; + } + + /** + * Builds an instance of the TaskRunnerConfigurer. + * + *

Please see {@link TaskRunnerConfigurer#init()} method. The method must be called after + * this constructor for the polling to start. + * + * @return Builder instance + */ + public TaskRunnerConfigurer build() { + return new TaskRunnerConfigurer(this); + } + + /** + * @param threadCount # of threads assigned to the workers. Should be at-least the size of + * taskWorkers to avoid starvation in a busy system. + * @return Builder instance + */ + public Builder withThreadCount(int threadCount) { + if (threadCount < 1) { + throw new IllegalArgumentException("No. of threads cannot be less than 1"); + } + this.threadCount = threadCount; + return this; + } + + public Builder withPollFilter(PollFilter filter) { + pollFilters.add(filter); + return this; + } + + public Builder withListener(Class eventType, Consumer listener) { + listeners.computeIfAbsent(eventType, k -> new LinkedList<>()).add(listener); + return this; + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollCompleted.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollCompleted.java new file mode 100644 index 000000000..3ef4f8cdf --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollCompleted.java @@ -0,0 +1,29 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.automator.events; + +import java.time.Duration; + +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public final class PollCompleted extends TaskRunnerEvent { + private final Duration duration; + + public PollCompleted(String taskType, long durationInMillis) { + super(taskType); + this.duration = Duration.ofMillis(durationInMillis); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollFailure.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollFailure.java new file mode 100644 index 000000000..8981da4e6 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollFailure.java @@ -0,0 +1,31 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.automator.events; + +import java.time.Duration; + +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public final class PollFailure extends TaskRunnerEvent { + private final Duration duration; + private final Throwable cause; + + public PollFailure(String taskType, long durationInMillis, Throwable cause) { + super(taskType); + this.duration = Duration.ofMillis(durationInMillis); + this.cause = cause; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollStarted.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollStarted.java new file mode 100644 index 000000000..61ea80e6e --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollStarted.java @@ -0,0 +1,23 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.automator.events; + +import lombok.ToString; + +@ToString +public final class PollStarted extends TaskRunnerEvent { + + public PollStarted(String taskType) { + super(taskType); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionCompleted.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionCompleted.java new file mode 100644 index 000000000..5880938de --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionCompleted.java @@ -0,0 +1,33 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.automator.events; + +import java.time.Duration; + +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public final class TaskExecutionCompleted extends TaskRunnerEvent { + public final String taskId; + public final String workerId; + private final Duration duration; + + public TaskExecutionCompleted(String taskType, String taskId, String workerId, long durationInMillis) { + super(taskType); + this.taskId = taskId; + this.workerId = workerId; + this.duration = Duration.ofMillis(durationInMillis); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionFailure.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionFailure.java new file mode 100644 index 000000000..c4991857a --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionFailure.java @@ -0,0 +1,35 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.automator.events; + +import java.time.Duration; + +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public final class TaskExecutionFailure extends TaskRunnerEvent { + public final String taskId; + public final String workerId; + private final Duration duration; + private final Throwable cause; + + public TaskExecutionFailure(String taskType, String taskId, String workerId, Throwable cause, long durationInMillis) { + super(taskType); + this.cause = cause; + this.taskId = taskId; + this.workerId = workerId; + this.duration = Duration.ofMillis(durationInMillis); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionStarted.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionStarted.java new file mode 100644 index 000000000..8dae66ebe --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionStarted.java @@ -0,0 +1,29 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.automator.events; + +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public final class TaskExecutionStarted extends TaskRunnerEvent { + public final String taskId; + public final String workerId; + + public TaskExecutionStarted(String taskType, String taskId, String workerId) { + super(taskType); + this.taskId = taskId; + this.workerId = workerId; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskRunnerEvent.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskRunnerEvent.java new file mode 100644 index 000000000..fd4123abe --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskRunnerEvent.java @@ -0,0 +1,27 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.automator.events; + +import java.time.Instant; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +@AllArgsConstructor +@Getter +@ToString +public class TaskRunnerEvent { + private final Instant time = Instant.now(); + private final String taskType; +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/filters/PollFilter.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/filters/PollFilter.java new file mode 100644 index 000000000..8de09542e --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/filters/PollFilter.java @@ -0,0 +1,18 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.automator.filters; + +@FunctionalInterface +public interface PollFilter { + boolean filter(String type, String domain); +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java new file mode 100644 index 000000000..0aaad6fb8 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java @@ -0,0 +1,50 @@ +/* + * Copyright 2018 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.config; + +public interface ConductorClientConfiguration { + + /** + * @return the workflow input payload size threshold in KB, beyond which the payload will be + * processed based on {@link + * ConductorClientConfiguration#isExternalPayloadStorageEnabled()}. + */ + int getWorkflowInputPayloadThresholdKB(); + + /** + * @return the max value of workflow input payload size threshold in KB, beyond which the + * payload will be rejected regardless external payload storage is enabled. + */ + int getWorkflowInputMaxPayloadThresholdKB(); + + /** + * @return the task output payload size threshold in KB, beyond which the payload will be + * processed based on {@link + * ConductorClientConfiguration#isExternalPayloadStorageEnabled()}. + */ + int getTaskOutputPayloadThresholdKB(); + + /** + * @return the max value of task output payload size threshold in KB, beyond which the payload + * will be rejected regardless external payload storage is enabled. + */ + int getTaskOutputMaxPayloadThresholdKB(); + + /** + * @return the flag which controls the use of external storage for storing workflow/task input + * and output JSON payloads with size greater than threshold. If it is set to true, the + * payload is stored in external location. If it is set to false, the payload is rejected + * and the task/workflow execution fails. + */ + boolean isExternalPayloadStorageEnabled(); +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java new file mode 100644 index 000000000..690671816 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java @@ -0,0 +1,153 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.config; + +import java.io.InputStream; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; + +/** + * Used to configure the Conductor workers using conductor-workers.properties. + */ +public class PropertyFactory { + + private static final Properties PROPERTIES = loadProperties("conductor-workers.properties"); + + private static final String PROPERTY_PREFIX = "conductor.worker"; + + // Handle potential parsing exceptions ? + + @RequiredArgsConstructor + private static class WorkerProperty { + + final String key; + + String getString() { + if (PROPERTIES == null) { + return null; + } + + return PROPERTIES.getProperty(key); + } + + Integer getInteger() { + String value = getString(); + return value == null ? null : Integer.parseInt(value); + } + + Boolean getBoolean() { + String value = getString(); + return value == null ? null : Boolean.parseBoolean(value); + } + + String getString(String defaultValue) { + String value = getString(); + return value == null ? defaultValue : value; + } + + Integer getInteger(Integer defaultValue) { + String value = getString(); + return value == null ? defaultValue : Integer.parseInt(value); + } + + Boolean getBoolean(Boolean defaultValue) { + String value = getString(); + return value == null ? defaultValue : Boolean.parseBoolean(value); + } + } + + private final WorkerProperty global; + + private final WorkerProperty local; + + private static final ConcurrentHashMap PROPERTY_FACTORY_MAP = + new ConcurrentHashMap<>(); + + private PropertyFactory(String prefix, String propName, String workerName) { + this.global = new WorkerProperty(prefix + "." + propName); + this.local = new WorkerProperty(prefix + "." + workerName + "." + propName); + } + + /** + * @param defaultValue Default Value + * @return Returns the value as integer. If not value is set (either global or worker specific), + * then returns the default value. + */ + public Integer getInteger(int defaultValue) { + Integer value = local.getInteger(); + if (value == null) { + value = global.getInteger(defaultValue); + } + return value; + } + + /** + * @param defaultValue Default Value + * @return Returns the value as String. If not value is set (either global or worker specific), + * then returns the default value. + */ + public String getString(String defaultValue) { + String value = local.getString(); + if (value == null) { + value = global.getString(defaultValue); + } + return value; + } + + /** + * @param defaultValue Default Value + * @return Returns the value as Boolean. If not value is set (either global or worker specific), + * then returns the default value. + */ + public Boolean getBoolean(Boolean defaultValue) { + Boolean value = local.getBoolean(); + if (value == null) { + value = global.getBoolean(defaultValue); + } + return value; + } + + public static Integer getInteger(String workerName, String property, Integer defaultValue) { + return getPropertyFactory(workerName, property).getInteger(defaultValue); + } + + public static Boolean getBoolean(String workerName, String property, Boolean defaultValue) { + return getPropertyFactory(workerName, property).getBoolean(defaultValue); + } + + public static String getString(String workerName, String property, String defaultValue) { + return getPropertyFactory(workerName, property).getString(defaultValue); + } + + private static PropertyFactory getPropertyFactory(String workerName, String property) { + String key = property + "." + workerName; + return PROPERTY_FACTORY_MAP.computeIfAbsent( + key, t -> new PropertyFactory(PROPERTY_PREFIX, property, workerName)); + } + + @SneakyThrows + private static Properties loadProperties(String file) { + Properties properties = new Properties(); + try (InputStream input = PropertyFactory.class.getClassLoader().getResourceAsStream(file)) { + if (input == null) { + return null; + } + properties.load(input); + } + + return properties; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java new file mode 100644 index 000000000..67e392b2b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java @@ -0,0 +1,204 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.exception; + +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; + +import com.netflix.conductor.common.validation.ValidationError; + +public class ConductorClientException extends RuntimeException { + + private int status; + private String instance; + private String code; + private boolean retryable; + + public List getValidationErrors() { + return validationErrors; + } + + public void setValidationErrors(List validationErrors) { + this.validationErrors = validationErrors; + } + + private List validationErrors; + + private Map> responseHeaders; + private String responseBody; + + public ConductorClientException() { + } + + public ConductorClientException(Throwable throwable) { + super(throwable.getMessage(), throwable); + } + + public ConductorClientException(String message) { + super(message); + } + + public ConductorClientException(String message, + Throwable throwable, + int code, + Map> responseHeaders, + String responseBody) { + super(message, throwable); + setCode(String.valueOf(code)); + setStatus(code); + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + } + + public ConductorClientException(String message, + int code, + Map> responseHeaders, + String responseBody) { + this(message, null, code, responseHeaders, responseBody); + setCode(String.valueOf(code)); + setStatus(code); + } + + public ConductorClientException(String message, + Throwable throwable, + int code, + Map> responseHeaders) { + this(message, throwable, code, responseHeaders, null); + setCode(String.valueOf(code)); + setStatus(code); + } + + public ConductorClientException(int code, Map> responseHeaders, String responseBody) { + this(null, null, code, responseHeaders, responseBody); + setCode(String.valueOf(code)); + setStatus(code); + } + + public ConductorClientException(int code, String message) { + super(message); + setCode(String.valueOf(code)); + setStatus(code); + } + + public ConductorClientException(int code, + String message, + Map> responseHeaders, + String responseBody) { + this(code, message); + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + setCode(String.valueOf(code)); + setStatus(code); + } + + public boolean isClientError() { + return getStatus() > 399 && getStatus() < 499; + } + + /** + * @return HTTP status code + */ + public int getStatusCode() { + return getStatus(); + } + + /** + * Get the HTTP response headers. + * + * @return A map of list of string + */ + public Map> getResponseHeaders() { + return responseHeaders; + } + + /** + * Get the HTTP response body. + * + * @return Response body in the form of string + */ + public String getResponseBody() { + return responseBody; + } + + @Override + public String getMessage() { + return getStatusCode() + + ": " + + (StringUtils.isBlank(responseBody) ? super.getMessage() : responseBody); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getClass().getName()).append(": "); + + if (getMessage() != null) { + builder.append(getMessage()); + } + + if (status > 0) { + builder.append(" {status=").append(status); + if (this.code != null) { + builder.append(", code='").append(code).append("'"); + } + + builder.append(", retryable: ").append(retryable); + } + + if (this.instance != null) { + builder.append(", instance: ").append(instance); + } + + if (this.validationErrors != null) { + builder.append(", validationErrors: ").append(validationErrors); + } + + builder.append("}"); + return builder.toString(); + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public void setStatus(int status) { + this.status = status; + } + + + public String getInstance() { + return instance; + } + + public void setInstance(String instance) { + this.instance = instance; + } + + public boolean isRetryable() { + return retryable; + } + + public void setRetryable(boolean retryable) { + this.retryable = retryable; + } + + public int getStatus() { + return this.status; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClient.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClient.java new file mode 100644 index 000000000..e977236d6 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClient.java @@ -0,0 +1,577 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Type; +import java.net.Proxy; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.client.exception.ConductorClientException; +import com.netflix.conductor.common.config.ObjectMapperProvider; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.SneakyThrows; +import okhttp3.Call; +import okhttp3.ConnectionPool; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.internal.http.HttpMethod; + +public class ConductorClient { + private static final Logger LOGGER = LoggerFactory.getLogger(ConductorClient.class); + protected final OkHttpClient okHttpClient; + protected final String basePath; + protected final ObjectMapper objectMapper; + private final boolean verifyingSsl; + private final InputStream sslCaCert; + private final KeyManager[] keyManagers; + private final List headerSuppliers; + + public static Builder builder() { + return new Builder(); + } + + @SneakyThrows + protected ConductorClient(Builder builder) { + builder.validateAndAssignDefaults(); + final OkHttpClient.Builder okHttpBuilder = builder.okHttpClientBuilder; + this.objectMapper = builder.objectMapperSupplier.get(); + this.basePath = builder.basePath(); + this.verifyingSsl = builder.verifyingSsl(); + this.sslCaCert = builder.sslCaCert(); + this.keyManagers = builder.keyManagers(); + this.headerSuppliers = builder.headerSupplier(); + + if (builder.connectTimeout() > -1) { + okHttpBuilder.connectTimeout(builder.connectTimeout(), TimeUnit.MILLISECONDS); + } + + if (builder.readTimeout() > -1) { + okHttpBuilder.readTimeout(builder.readTimeout(), TimeUnit.MILLISECONDS); + } + + if (builder.writeTimeout() > -1) { + okHttpBuilder.writeTimeout(builder.writeTimeout(), TimeUnit.MILLISECONDS); + } + + if (builder.getProxy() != null) { + okHttpBuilder.proxy(builder.getProxy()); + } + + ConnectionPoolConfig connectionPoolConfig = builder.getConnectionPoolConfig(); + if (connectionPoolConfig != null) { + okHttpBuilder.connectionPool(new ConnectionPool( + connectionPoolConfig.getMaxIdleConnections(), + connectionPoolConfig.getKeepAliveDuration(), + connectionPoolConfig.getTimeUnit() + )); + } + + if (!verifyingSsl) { + unsafeClient(okHttpBuilder); + } else if (sslCaCert != null) { + trustCertificates(okHttpBuilder); + } + + this.okHttpClient = okHttpBuilder.build(); + this.headerSuppliers.forEach(it -> it.init(this)); + } + + public ConductorClient() { + this(new Builder()); + } + + public ConductorClient(String basePath) { + this(new Builder().basePath(basePath)); + } + + public String getBasePath() { + return basePath; + } + + public void shutdown() { + okHttpClient.dispatcher().executorService().shutdown(); + okHttpClient.connectionPool().evictAll(); + if (okHttpClient.cache() != null) { + try { + okHttpClient.cache().close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public ConductorClientResponse execute(ConductorClientRequest req) { + return execute(req, null); + } + + public ConductorClientResponse execute(ConductorClientRequest req, TypeReference typeReference) { + Map headerParams = req.getHeaderParams() == null ? new HashMap<>() : new HashMap<>(req.getHeaderParams()); + List pathParams = req.getPathParams() == null ? new ArrayList<>() : new ArrayList<>(req.getPathParams()); + List queryParams = req.getQueryParams() == null ? new ArrayList<>() : new ArrayList<>(req.getQueryParams()); + + Request request = buildRequest(req.getMethod().toString(), + req.getPath(), + pathParams, + queryParams, + headerParams, + req.getBody()); + + Call call = okHttpClient.newCall(request); + if (typeReference == null) { + execute(call, null); + return null; + } + + return execute(call, typeReference.getType()); + } + + private String parameterToString(Object param) { + if (param == null) { + return ""; + } else if (param instanceof Collection) { + StringBuilder b = new StringBuilder(); + for (Object o : (Collection) param) { + if (b.length() > 0) { + b.append(","); + } + b.append(o); + } + return b.toString(); + } + + return String.valueOf(param); + } + + private boolean isJsonMime(String mime) { + String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; + return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); + } + + private String urlEncode(String str) { + return URLEncoder.encode(str, StandardCharsets.UTF_8); + } + + @SneakyThrows + private T deserialize(Response response, Type returnType) { + if (returnType == null) { + return null; + } + + String body = bodyAsString(response); + if (body == null || "".equals(body)) { + return null; + } + + String contentType = response.header("Content-Type"); + if (contentType == null || isJsonMime(contentType)) { + // This is hacky. It's required because Conductor's API is returning raw strings as JSON + if (returnType.equals(String.class)) { + //noinspection unchecked + return (T) body; + } + + JavaType javaType = objectMapper.getTypeFactory().constructType(returnType); + return objectMapper.readValue(body, javaType); + } else if (returnType.equals(String.class)) { + //noinspection unchecked + return (T) body; + } + + throw new ConductorClientException( + "Content type \"" + contentType + "\" is not supported for type: " + returnType, + response.code(), + response.headers().toMultimap(), + body); + } + + @Nullable + private String bodyAsString(Response response) { + if (response.body() == null) { + return null; + } + + try { + return response.body().string(); + } catch (IOException e) { + throw new ConductorClientException(response.message(), + e, + response.code(), + response.headers().toMultimap()); + } + } + + @SneakyThrows + private RequestBody serialize(String contentType, @NotNull Object body) { + //FIXME review this, what if we want to send something other than a JSON in the request + if (!isJsonMime(contentType)) { + throw new ConductorClientException("Content type \"" + contentType + "\" is not supported"); + } + + String content; + if (body instanceof String) { + content = (String) body; + } else { + content = objectMapper.writeValueAsString(body); + } + + return RequestBody.create(content, MediaType.parse(contentType)); + } + + protected T handleResponse(Response response, Type returnType) { + if (!response.isSuccessful()) { + String respBody = bodyAsString(response); + throw new ConductorClientException(response.message(), + response.code(), + response.headers().toMultimap(), + respBody); + } + + try { + if (returnType == null || response.code() == 204) { + return null; + } else { + return deserialize(response, returnType); + } + } finally { + if (response.body() != null) { + response.body().close(); + } + } + } + + protected Request buildRequest(String method, + String path, + List pathParams, + List queryParams, + Map headers, + Object body) { + final HttpUrl url = buildUrl(replacePathParams(path, pathParams), queryParams); + final Request.Builder requestBuilder = new Request.Builder().url(url); + processHeaderParams(requestBuilder, addHeadersFromProviders(method, path, headers)); + RequestBody reqBody = requestBody(method, getContentType(headers), body); + return requestBuilder.method(method, reqBody).build(); + } + + private Map addHeadersFromProviders(String method, String path, Map headers) { + if (headerSuppliers.isEmpty()) { + return headers; + } + + Map all = new HashMap<>(); + for (HeaderSupplier supplier : headerSuppliers) { + all.putAll(supplier.get(method, path)); + } + // request headers take precedence + all.putAll(headers); + return all; + } + + @NotNull + private static String getContentType(Map headerParams) { + String contentType = headerParams.get("Content-Type"); + if (contentType == null) { + contentType = "application/json"; + } + + return contentType; + } + + private String replacePathParams(String path, List pathParams) { + for (Param param : pathParams) { + path = path.replace("{" + param.name() + "}", urlEncode(param.value())); + } + + return path; + } + + @Nullable + private RequestBody requestBody(String method, String contentType, Object body) { + if (!HttpMethod.permitsRequestBody(method)) { + return null; + } + + if (body == null && "DELETE".equals(method)) { + return null; + } else if (body == null) { + return RequestBody.create("", MediaType.parse(contentType)); + } + + return serialize(contentType, body); + } + + private HttpUrl buildUrl(String path, List queryParams) { + HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(basePath + path)) + .newBuilder(); + for (Param param : queryParams) { + urlBuilder.addQueryParameter(param.name(), param.value()); + } + + return urlBuilder.build(); + } + + private void processHeaderParams(Request.Builder requestBuilder, Map headers) { + for (Entry header : headers.entrySet()) { + requestBuilder.header(header.getKey(), parameterToString(header.getValue())); + } + } + + @SneakyThrows + private static void unsafeClient(OkHttpClient.Builder okhttpClientBuilder) { + LOGGER.warn("Unsafe client - Disabling SSL certificate validation is dangerous and should only be used in development environments"); + // Create a trust manager that does not validate certificate chains + final TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + } + }; + final SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, trustAllCerts, new SecureRandom()); + // Creates a ssl socket factory with our all-trusting manager + final javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + okhttpClientBuilder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]); + okhttpClientBuilder.hostnameVerifier((hostname, session) -> true); + } + + //TODO review this - not sure if it's working 2024-08-07 + private void trustCertificates(OkHttpClient.Builder okhttpClientBuilder) throws GeneralSecurityException { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + Collection certificates = certificateFactory.generateCertificates(sslCaCert); + if (certificates.isEmpty()) { + throw new IllegalArgumentException("expected non-empty set of trusted certificates"); + } + KeyStore caKeyStore = newEmptyKeyStore(null); + int index = 0; + for (Certificate certificate : certificates) { + String certificateAlias = "ca" + index++; + caKeyStore.setCertificateEntry(certificateAlias, certificate); + } + + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(caKeyStore); + TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagers, trustManagers, new SecureRandom()); + okhttpClientBuilder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0]); + } + + private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { + try { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, password); + return keyStore; + } catch (IOException e) { + throw new AssertionError(e); + } + } + + private ConductorClientResponse execute(Call call, Type returnType) { + try { + Response response = call.execute(); + T data = handleResponse(response, returnType); + return new ConductorClientResponse<>(response.code(), response.headers().toMultimap(), data); + } catch (IOException e) { + throw new ConductorClientException(e); + } + } + + public static class Builder { + private final OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder(); + private String basePath = "http://localhost:8080/api"; + private boolean verifyingSsl = true; + private InputStream sslCaCert; + private KeyManager[] keyManagers; + private long connectTimeout = -1; + private long readTimeout = -1; + private long writeTimeout = -1; + private Proxy proxy; + private ConnectionPoolConfig connectionPoolConfig; + private Supplier objectMapperSupplier = () -> new ObjectMapperProvider().getObjectMapper(); + private final List headerSuppliers = new ArrayList<>(); + + public String basePath() { + return basePath; + } + + public Builder basePath(String basePath) { + this.basePath = basePath; + return this; + } + + public boolean verifyingSsl() { + return verifyingSsl; + } + + public Builder verifyingSsl(boolean verifyingSsl) { + this.verifyingSsl = verifyingSsl; + return this; + } + + public InputStream sslCaCert() { + return sslCaCert; + } + + public Builder sslCaCert(InputStream sslCaCert) { + this.sslCaCert = sslCaCert; + return this; + } + + public KeyManager[] keyManagers() { + return keyManagers; + } + + public Builder keyManagers(KeyManager[] keyManagers) { + this.keyManagers = keyManagers; + return this; + } + + public long connectTimeout() { + return connectTimeout; + } + + public Builder connectTimeout(long connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + public long readTimeout() { + return readTimeout; + } + + public Builder readTimeout(long readTimeout) { + this.readTimeout = readTimeout; + return this; + } + + public long writeTimeout() { + return writeTimeout; + } + + public Builder writeTimeout(long writeTimeout) { + this.writeTimeout = writeTimeout; + return this; + } + + public Builder proxy(Proxy proxy) { + this.proxy = proxy; + return this; + } + + public ConnectionPoolConfig getConnectionPoolConfig() { + return this.connectionPoolConfig; + } + + public Builder connectionPoolConfig(ConnectionPoolConfig config) { + this.connectionPoolConfig = config; + return this; + } + + Proxy getProxy() { + return proxy; + } + + /** + * Use it to apply additional custom configurations to the OkHttp3 client. E.g.: add an interceptor. + * + * @param configurer + * @return + */ + public Builder configureOkHttp(Consumer configurer) { + configurer.accept(this.okHttpClientBuilder); + return this; + } + + /** + * Use it to supply a custom ObjectMapper. + * + * @param objectMapperSupplier + * @return + */ + public Builder objectMapperSupplier(Supplier objectMapperSupplier) { + this.objectMapperSupplier = objectMapperSupplier; + return this; + } + + public Builder addHeaderSupplier(HeaderSupplier headerSupplier) { + this.headerSuppliers.add(headerSupplier); + return this; + } + + public List headerSupplier() { + return headerSuppliers; + } + + public ConductorClient build() { + return new ConductorClient(this); + } + + void validateAndAssignDefaults() { + if (StringUtils.isBlank(basePath)) { + throw new IllegalArgumentException("basePath cannot be blank"); + } + + if (basePath.endsWith("/")) { + basePath = basePath.substring(0, basePath.length() - 1); + } + + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientRequest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientRequest.java new file mode 100644 index 000000000..da98d5090 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientRequest.java @@ -0,0 +1,150 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode +public class ConductorClientRequest { + + public enum Method { + GET, POST, PUT, DELETE, PATCH + } + + private final Method method; + private final String path; + private final List pathParams; + private final List queryParams; + private final Map headerParams; + private final Object body; + + private ConductorClientRequest(Builder builder) { + this.method = builder.method; + this.path = builder.path; + this.pathParams = builder.pathParams; + this.queryParams = builder.queryParams; + this.headerParams = builder.headerParams; + this.body = builder.body; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Method method; + private String path; + private final List pathParams = new ArrayList<>(); + private final List queryParams = new ArrayList<>(); + private final Map headerParams = new HashMap<>(); + private Object body; + + public Builder method(Method method) { + if (method == null) { + throw new IllegalArgumentException("Method cannot be null"); + } + this.method = method; + return this; + } + + public Builder path(String path) { + if (path == null || path.isEmpty()) { + throw new IllegalArgumentException("Path cannot be null or empty"); + } + this.path = path; + return this; + } + + public Builder addPathParam(String name, Integer value) { + return addPathParam(name, Integer.toString(value)); + } + + public Builder addPathParam(String name, String value) { + if (name == null || name.isEmpty() || value == null) { + throw new IllegalArgumentException("Path parameter name and value cannot be null or empty"); + } + this.pathParams.add(new Param(name, value)); + return this; + } + + public Builder addQueryParam(String name, Long value) { + if (value == null) { + return this; + } + + addQueryParam(name, Long.toString(value)); + return this; + } + + public Builder addQueryParam(String name, Integer value) { + if (value == null) { + return this; + } + + addQueryParam(name, Integer.toString(value)); + return this; + } + + public Builder addQueryParam(String name, Boolean value) { + if (value == null) { + return this; + } + + addQueryParam(name, Boolean.toString(value)); + return this; + } + + public Builder addQueryParams(String name, List values) { + values.forEach(it -> addQueryParam(name, it)); + return this; + } + + public Builder addQueryParam(String name, String value) { + if (value == null) { + return this; + } + + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("Query parameter name cannot be null or empty"); + } + + this.queryParams.add(new Param(name, value)); + return this; + } + + public Builder addHeaderParam(String name, String value) { + if (name == null || name.isEmpty() || value == null) { + throw new IllegalArgumentException("Header parameter name and value cannot be null or empty"); + } + + this.headerParams.put(name, value); + return this; + } + + public Builder body(Object body) { + this.body = body; + return this; + } + + public ConductorClientRequest build() { + return new ConductorClientRequest(this); + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientResponse.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientResponse.java new file mode 100644 index 000000000..72eb92911 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientResponse.java @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http; + +import java.util.List; +import java.util.Map; + +/** + * API response returned by API call. + * + * @param The type of data that is deserialized from response body + */ +public class ConductorClientResponse { + private final int statusCode; + private final Map> headers; + private final T data; + + /** + * @param statusCode The status code of HTTP response + * @param headers The headers of HTTP response + */ + public ConductorClientResponse(int statusCode, Map> headers) { + this(statusCode, headers, null); + } + + /** + * @param statusCode The status code of HTTP response + * @param headers The headers of HTTP response + * @param data The object deserialized from response bod + */ + public ConductorClientResponse(int statusCode, Map> headers, T data) { + this.statusCode = statusCode; + this.headers = headers; + this.data = data; + } + + public int getStatusCode() { + return statusCode; + } + + public Map> getHeaders() { + return headers; + } + + public T getData() { + return data; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConnectionPoolConfig.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConnectionPoolConfig.java new file mode 100644 index 000000000..896642a91 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConnectionPoolConfig.java @@ -0,0 +1,26 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http; + +import java.util.concurrent.TimeUnit; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class ConnectionPoolConfig { + private final int maxIdleConnections; + private final long keepAliveDuration; + private final TimeUnit timeUnit; +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/EventClient.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/EventClient.java new file mode 100644 index 000000000..85b539fdb --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/EventClient.java @@ -0,0 +1,119 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http; + +import java.util.List; + +import org.apache.commons.lang3.Validate; + +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.common.metadata.events.EventHandler; + +import com.fasterxml.jackson.core.type.TypeReference; + + + +// Client class for all Event Handler operations +public final class EventClient { + + private ConductorClient client; + + /** Creates a default metadata client */ + public EventClient() { + } + + public EventClient(ConductorClient client) { + this.client = client; + } + + /** + * Kept only for backwards compatibility + * + * @param rootUri basePath for the ApiClient + */ + @Deprecated + public void setRootURI(String rootUri) { + if (client != null) { + client.shutdown(); + } + client = new ConductorClient(rootUri); + } + + /** + * Register an event handler with the server. + * + * @param eventHandler the eventHandler definition. + */ + public void registerEventHandler(EventHandler eventHandler) { + Validate.notNull(eventHandler, "Event Handler definition cannot be null"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/event") + .body(eventHandler) + .build(); + + client.execute(request); + } + + /** + * Updates an event handler with the server. + * + * @param eventHandler the eventHandler definition. + */ + public void updateEventHandler(EventHandler eventHandler) { + Validate.notNull(eventHandler, "Event Handler definition cannot be null"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/event") + .body(eventHandler) + .build(); + + client.execute(request); + } + + /** + * @param event name of the event. + * @param activeOnly if true, returns only the active handlers. + * @return Returns the list of all the event handlers for a given event. + */ + public List getEventHandlers(String event, boolean activeOnly) { + Validate.notBlank(event, "Event cannot be blank"); + + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/event/{name}") + .addPathParam("name", event) + .addQueryParam("activeOnly", activeOnly) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Removes the event handler definition from the conductor server + * + * @param name the name of the event handler to be unregistered + */ + public void unregisterEventHandler(String name) { + Validate.notBlank(name, "Event handler name cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/event/{name}") + .addPathParam("name", name) + .build(); + client.execute(request); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/HeaderSupplier.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/HeaderSupplier.java new file mode 100644 index 000000000..b6f862800 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/HeaderSupplier.java @@ -0,0 +1,23 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http; + +import java.util.Map; + + +public interface HeaderSupplier { + + void init(ConductorClient client); + + Map get(String method, String path); +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java new file mode 100644 index 000000000..9d0f7314f --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java @@ -0,0 +1,218 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http; + +import java.util.List; + +import org.apache.commons.lang3.Validate; + +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; + +import com.fasterxml.jackson.core.type.TypeReference; + + +public final class MetadataClient { + + private ConductorClient client; + + /** Creates a default metadata client */ + public MetadataClient() { + } + + public MetadataClient(ConductorClient client) { + this.client = client; + } + + /** + * Kept only for backwards compatibility + * + * @param rootUri basePath for the ApiClient + */ + @Deprecated + public void setRootURI(String rootUri) { + if (client != null) { + client.shutdown(); + } + client = new ConductorClient(rootUri); + } + + /** + * Register a workflow definition with the server + * + * @param workflowDef the workflow definition + */ + public void registerWorkflowDef(WorkflowDef workflowDef) { + Validate.notNull(workflowDef, "WorkflowDef cannot be null"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/metadata/workflow") + .body(workflowDef) + .build(); + + client.execute(request); + } + + /** + * Updates a list of existing workflow definitions + * + * @param workflowDefs List of workflow definitions to be updated + */ + public void updateWorkflowDefs(List workflowDefs) { + Validate.notEmpty(workflowDefs, "Workflow definitions cannot be null or empty"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/metadata/workflow") + .body(workflowDefs) + .build(); + + client.execute(request); + } + + /** + * Retrieve the workflow definition + * + * @param name the name of the workflow + * @param version the version of the workflow def + * @return Workflow definition for the given workflow and version + */ + public WorkflowDef getWorkflowDef(String name, Integer version) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/metadata/workflow/{name}") + .addPathParam("name", name) + .addQueryParam("version", version) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public List getAllWorkflowsWithLatestVersions() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("metadata/workflow/latest-versions") + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Removes the workflow definition of a workflow from the conductor server. It does not remove + * associated workflows. Use with caution. + * + * @param name Name of the workflow to be unregistered. + * @param version Version of the workflow definition to be unregistered. + */ + public void unregisterWorkflowDef(String name, Integer version) { + Validate.notBlank(name, "Name cannot be blank"); + Validate.notNull(version, "version cannot be null"); + + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/metadata/workflow/{name}/{version}") + .addPathParam("name", name) + .addPathParam("version", Integer.toString(version)) + .build(); + + client.execute(request); + } + + // Task Metadata Operations + + /** + * Registers a list of task types with the conductor server + * + * @param taskDefs List of task types to be registered. + */ + public void registerTaskDefs(List taskDefs) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/metadata/taskdefs") + .body(taskDefs) + .build(); + + client.execute(request); + } + + /** + * Updates an existing task definition + * + * @param taskDef the task definition to be updated + */ + public void updateTaskDef(TaskDef taskDef) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/metadata/taskdefs") + .body(taskDef) + .build(); + + client.execute(request); + } + + /** + * Retrieve the task definition of a given task type + * + * @param taskType type of task for which to retrieve the definition + * @return Task Definition for the given task type + */ + public TaskDef getTaskDef(String taskType) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/metadata/taskdefs/{taskType}") + .addPathParam("taskType", taskType) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Removes the task definition of a task type from the conductor server. Use with caution. + * + * @param taskType Task type to be unregistered. + */ + public void unregisterTaskDef(String taskType) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/metadata/taskdefs/{taskType}") + .addPathParam("taskType", taskType) + .build(); + + client.execute(request); + } + + /** + * + * @return All the registered task definitions + */ + public List getAllTaskDefs() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/metadata/taskdefs") + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/Param.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/Param.java new file mode 100644 index 000000000..d96cb6d60 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/Param.java @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http; + +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +/** + * Used for path variables and query params + */ +@RequiredArgsConstructor +@EqualsAndHashCode +@ToString +public final class Param { + private final String name; + private final String value; + + public String name() { + return name; + } + + public String value() { + return value; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/TaskClient.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/TaskClient.java new file mode 100644 index 000000000..d192bf3ee --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/TaskClient.java @@ -0,0 +1,459 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; + +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.common.metadata.tasks.PollData; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskExecLog; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.common.run.SearchResult; +import com.netflix.conductor.common.run.TaskSummary; +import com.netflix.conductor.common.utils.ExternalPayloadStorage; + +import com.fasterxml.jackson.core.type.TypeReference; + +/** Client for conductor task management including polling for task, updating task status etc. */ +public final class TaskClient { + + private ConductorClient client; + + /** Creates a default task client */ + public TaskClient() { + } + + public TaskClient(ConductorClient client) { + this.client = client; + } + + /** + * Kept only for backwards compatibility + * + * @param rootUri basePath for the ApiClient + */ + @Deprecated + public void setRootURI(String rootUri) { + if (client != null) { + client.shutdown(); + } + client = new ConductorClient(rootUri); + } + + /** + * Perform a poll for a task of a specific task type. + * + * @param taskType The taskType to poll for + * @param domain The domain of the task type + * @param workerId Name of the client worker. Used for logging. + * @return Task waiting to be executed. + */ + public Task pollTask(String taskType, String workerId, String domain){ + Validate.notBlank(taskType, "Task type cannot be blank"); + Validate.notBlank(workerId, "Worker id cannot be blank"); + + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/tasks/poll/{taskType}") + .addPathParam("taskType", taskType) + .addQueryParam("workerid", workerId) + .addQueryParam("domain", domain) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + Task task = resp.getData(); + populateTaskPayloads(task); + return task; + } + + /** + * Perform a batch poll for tasks by task type. Batch size is configurable by count. + * + * @param taskType Type of task to poll for + * @param workerId Name of the client worker. Used for logging. + * @param count Maximum number of tasks to be returned. Actual number of tasks returned can be + * less than this number. + * @param timeoutInMillisecond Long poll wait timeout. + * @return List of tasks awaiting to be executed. + */ + public List batchPollTasksByTaskType(String taskType, String workerId, int count, int timeoutInMillisecond) { + Validate.notBlank(taskType, "Task type cannot be blank"); + Validate.notBlank(workerId, "Worker id cannot be blank"); + Validate.isTrue(count > 0, "Count must be greater than 0"); + + List tasks = batchPoll(taskType, workerId, null, count, timeoutInMillisecond); + tasks.forEach(this::populateTaskPayloads); + return tasks; + } + + /** + * Batch poll for tasks in a domain. Batch size is configurable by count. + * + * @param taskType Type of task to poll for + * @param domain The domain of the task type + * @param workerId Name of the client worker. Used for logging. + * @param count Maximum number of tasks to be returned. Actual number of tasks returned can be + * less than this number. + * @param timeoutInMillisecond Long poll wait timeout. + * @return List of tasks awaiting to be executed. + */ + public List batchPollTasksInDomain(String taskType, String domain, String workerId, int count, int timeoutInMillisecond){ + Validate.notBlank(taskType, "Task type cannot be blank"); + Validate.notBlank(workerId, "Worker id cannot be blank"); + Validate.isTrue(count > 0, "Count must be greater than 0"); + + List tasks = batchPoll(taskType, workerId, domain, count, timeoutInMillisecond); + tasks.forEach(this::populateTaskPayloads); + return tasks; + } + + /** + * Updates the result of a task execution. If the size of the task output payload is bigger than + * {@link ExternalPayloadStorage}, if enabled, else the task is marked as + * FAILED_WITH_TERMINAL_ERROR. + * + * @param taskResult the {@link TaskResult} of the executed task to be updated. + */ + public void updateTask(TaskResult taskResult) { + Validate.notNull(taskResult, "Task result cannot be null"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/tasks") + .body(taskResult) + .build(); + + client.execute(request); + } + + //TODO FIXME OSS MISMATCH - https://github.com/conductor-oss/conductor-java-sdk/issues/27 + public Optional evaluateAndUploadLargePayload(Map taskOutputData, String taskType) { + throw new UnsupportedOperationException("No external storage support YET"); + } + + /** + * Ack for the task poll. + * + * @param taskId Id of the task to be polled + * @param workerId user identified worker. + * @return true if the task was found with the given ID and acknowledged. False otherwise. If + * the server returns false, the client should NOT attempt to ack again. + */ + public Boolean ack(String taskId, String workerId) { + Validate.notBlank(taskId, "Task id cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("tasks/{taskId}/ack") + .addPathParam("taskId", taskId) + .addQueryParam("workerid", workerId) + .build(); + + ConductorClientResponse response = client.execute(request, new TypeReference<>() { + }); + + return response.getData(); + } + + /** + * Log execution messages for a task. + * + * @param taskId id of the task + * @param logMessage the message to be logged + */ + public void logMessageForTask(String taskId, String logMessage) { + Validate.notBlank(taskId, "Task id cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/tasks/{taskId}/log") + .addPathParam("taskId", taskId) + .body(logMessage) + .build(); + + client.execute(request); + } + + /** + * Fetch execution logs for a task. + * + * @param taskId id of the task. + */ + public List getTaskLogs(String taskId){ + Validate.notBlank(taskId, "Task id cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/tasks/{taskId}/log") + .addPathParam("taskId", taskId) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Retrieve information about the task + * + * @param taskId ID of the task + * @return Task details + */ + public Task getTaskDetails(String taskId) { + Validate.notBlank(taskId, "Task id cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/tasks/{taskId}") + .addPathParam("taskId", taskId) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Removes a task from a taskType queue + * + * @param taskType the taskType to identify the queue + * @param taskId the id of the task to be removed + */ + public void removeTaskFromQueue(String taskType, String taskId) { + Validate.notBlank(taskType, "Task type cannot be blank"); + Validate.notBlank(taskId, "Task id cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("tasks/queue/{taskType}/{taskId}") + .addPathParam("taskType", taskType) + .addPathParam("taskId", taskId) + .build(); + + client.execute(request); + } + + public int getQueueSizeForTask(String taskType) { + return getQueueSizeForTask(taskType, null, null, null); + } + + public int getQueueSizeForTask(String taskType, String domain, String isolationGroupId, String executionNamespace) { + Validate.notBlank(taskType, "Task type cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/tasks/queue/size") //FIXME Not supported by Orkes Conductor. Orkes Conductor only has "/tasks/queue/sizes" + .addQueryParam("taskType", taskType) + .addQueryParam("domain", domain) + .addQueryParam("isolationGroupId", isolationGroupId) + .addQueryParam("executionNamespace", executionNamespace) + .build(); + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + Integer queueSize = resp.getData(); + return queueSize != null ? queueSize : 0; + } + + /** + * Get last poll data for a given task type + * + * @param taskType the task type for which poll data is to be fetched + * @return returns the list of poll data for the task type + */ + public List getPollData(String taskType) { + Validate.notBlank(taskType, "Task type cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/tasks/queue/polldata") + .addQueryParam("taskType", taskType) + .build(); + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Get the last poll data for all task types + * + * @return returns a list of poll data for all task types + */ + public List getAllPollData() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/tasks/queue/polldata") + .build(); + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Requeue pending tasks for all running workflows + * + * @return returns the number of tasks that have been requeued + */ + public String requeueAllPendingTasks() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/tasks/queue/requeue") + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Requeue pending tasks of a specific task type + * + * @return returns the number of tasks that have been requeued + */ + public String requeuePendingTasksByTaskType(String taskType) { + Validate.notBlank(taskType, "Task type cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/tasks/queue/requeue/{taskType}") + .addPathParam("taskType", taskType) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + /** + * Search for tasks based on payload + * + * @param query the search string + * @return returns the {@link SearchResult} containing the {@link TaskSummary} matching the + * query + */ + public SearchResult search(String query) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/tasks/search") + .addQueryParam("query", query) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Search for tasks based on payload + * + * @param query the search string + * @return returns the {@link SearchResult} containing the {@link Task} matching the query + */ + public SearchResult searchV2(String query) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("tasks/search-v2") + .addQueryParam("query", query) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Paginated search for tasks based on payload + * + * @param start start value of page + * @param size number of tasks to be returned + * @param sort sort order + * @param freeText additional free text query + * @param query the search query + * @return the {@link SearchResult} containing the {@link TaskSummary} that match the query + */ + public SearchResult search(Integer start, Integer size, String sort, String freeText, String query) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/tasks/search") + .addQueryParam("start", start) + .addQueryParam("size", size) + .addQueryParam("sort", sort) + .addQueryParam("freeText", freeText) + .addQueryParam("query", query) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Paginated search for tasks based on payload + * + * @param start start value of page + * @param size number of tasks to be returned + * @param sort sort order + * @param freeText additional free text query + * @param query the search query + * @return the {@link SearchResult} containing the {@link Task} that match the query + */ + public SearchResult searchV2(Integer start, Integer size, String sort, String freeText, String query) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("tasks/search-v2") + .addQueryParam("start", start) + .addQueryParam("size", size) + .addQueryParam("sort", sort) + .addQueryParam("freeText", freeText) + .addQueryParam("query", query) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + //TODO FIXME OSS MISMATCH - https://github.com/conductor-oss/conductor-java-sdk/issues/27 + //implement populateTaskPayloads - Download from external Storage and set input and output of task + private void populateTaskPayloads(Task task) { + if (StringUtils.isNotBlank(task.getExternalInputPayloadStoragePath()) + || StringUtils.isNotBlank(task.getExternalOutputPayloadStoragePath())) { + throw new UnsupportedOperationException("No external storage support"); + } + } + + private List batchPoll(String taskType, String workerid, String domain, Integer count, Integer timeout) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/tasks/poll/batch/{taskType}") + .addPathParam("taskType", taskType) + .addQueryParam("workerid", workerid) + .addQueryParam("domain", domain) + .addQueryParam("count", count) + .addQueryParam("timeout", timeout) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java new file mode 100644 index 000000000..ad53e19a4 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java @@ -0,0 +1,497 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http; + +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; + +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.common.metadata.workflow.RerunWorkflowRequest; +import com.netflix.conductor.common.metadata.workflow.SkipTaskRequest; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; +import com.netflix.conductor.common.model.BulkResponse; +import com.netflix.conductor.common.run.SearchResult; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.common.run.WorkflowSummary; +import com.netflix.conductor.common.run.WorkflowTestRequest; +import com.netflix.conductor.common.utils.ExternalPayloadStorage; + +import com.fasterxml.jackson.core.type.TypeReference; + + +public final class WorkflowClient { + + private ConductorClient client; + + /** Creates a default workflow client */ + public WorkflowClient() { + } + + public WorkflowClient(ConductorClient client) { + this.client = client; + } + + /** + * Kept only for backwards compatibility + * + * @param rootUri basePath for the ApiClient + */ + @Deprecated + public void setRootURI(String rootUri) { + if (client != null) { + client.shutdown(); + } + client = new ConductorClient(rootUri); + } + + /** + * Starts a workflow. If the size of the workflow input payload is bigger than {@link + * ExternalPayloadStorage}, if enabled, else the workflow is rejected. + * + * @param startWorkflowRequest the {@link StartWorkflowRequest} object to start the workflow + * @return the id of the workflow instance that can be used for tracking + */ + public String startWorkflow(StartWorkflowRequest startWorkflowRequest) { + Validate.notNull(startWorkflowRequest, "StartWorkflowRequest cannot be null"); + Validate.notBlank(startWorkflowRequest.getName(), "Workflow name cannot be null or empty"); + Validate.isTrue( + StringUtils.isBlank(startWorkflowRequest.getExternalInputPayloadStoragePath()), + "External Storage Path must not be set"); + + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow") + .body(startWorkflowRequest) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Retrieve a workflow by workflow id + * + * @param workflowId the id of the workflow + * @param includeTasks specify if the tasks in the workflow need to be returned + * @return the requested workflow + */ + public Workflow getWorkflow(String workflowId, boolean includeTasks) { + Validate.notBlank(workflowId, "workflow id cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/workflow/{workflowId}") + .addPathParam("workflowId", workflowId) + .addQueryParam("includeTasks", includeTasks) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + Workflow workflow = resp.getData(); + populateWorkflowOutput(workflow); + return workflow; + } + + /** + * Retrieve all workflows for a given correlation id and name + * + * @param name the name of the workflow + * @param correlationId the correlation id + * @param includeClosed specify if all workflows are to be returned or only running workflows + * @param includeTasks specify if the tasks in the workflow need to be returned + * @return list of workflows for the given correlation id and name + */ + public List getWorkflows(String name, String correlationId, boolean includeClosed, boolean includeTasks){ + Validate.notBlank(name, "name cannot be blank"); + Validate.notBlank(correlationId, "correlationId cannot be blank"); + + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/workflow/{name}/correlated/{correlationId}") + .addPathParam("name", name) + .addPathParam("correlationId", correlationId) + .addQueryParam("includeClosed", includeClosed) + .addQueryParam("includeTasks", includeTasks) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + List workflows = resp.getData(); + workflows.forEach(this::populateWorkflowOutput); + return workflows; + } + + /** + * Removes a workflow from the system + * + * @param workflowId the id of the workflow to be deleted + * @param archiveWorkflow flag to indicate if the workflow should be archived before deletion + */ + public void deleteWorkflow(String workflowId, boolean archiveWorkflow) { + Validate.notBlank(workflowId, "Workflow id cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/workflow/{workflowId}/remove") + .addPathParam("workflowId", workflowId) + .addQueryParam("archiveWorkflow", archiveWorkflow) + .build(); + + client.execute(request); + } + + /** + * Terminates the execution of all given workflows instances + * + * @param workflowIds the ids of the workflows to be terminated + * @param reason the reason to be logged and displayed + * @return the {@link BulkResponse} contains bulkErrorResults and bulkSuccessfulResults + */ + public BulkResponse terminateWorkflows(List workflowIds, String reason) { + Validate.isTrue(!workflowIds.isEmpty(), "workflow id cannot be blank"); + + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/bulk/terminate") + .addQueryParam("reason", reason) + .body(workflowIds) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Retrieve all running workflow instances for a given name and version + * + * @param workflowName the name of the workflow + * @param version the version of the wokflow definition. Defaults to 1. + * @return the list of running workflow instances + */ + public List getRunningWorkflow(String workflowName, Integer version) { + return getRunningWorkflow(workflowName, version, null, null); + } + + /** + * Retrieve all workflow instances for a given workflow name between a specific time period + * + * @param workflowName the name of the workflow + * @param version the version of the workflow definition. Defaults to 1. + * @param startTime the start time of the period + * @param endTime the end time of the period + * @return returns a list of workflows created during the specified during the time period + */ + public List getWorkflowsByTimePeriod(String workflowName, int version, Long startTime, Long endTime) { + Validate.notBlank(workflowName, "Workflow name cannot be blank"); + Validate.notNull(startTime, "Start time cannot be null"); + Validate.notNull(endTime, "End time cannot be null"); + + return getRunningWorkflow(workflowName, version, startTime, endTime); + } + + /** + * Starts the decision task for the given workflow instance + * + * @param workflowId the id of the workflow instance + */ + public void runDecider(String workflowId) { + Validate.notBlank(workflowId, "workflow id cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/workflow/decide/{workflowId}") + .addPathParam("workflowId", workflowId) + .build(); + + client.execute(request); + } + + /** + * Pause a workflow by workflow id + * + * @param workflowId the workflow id of the workflow to be paused + */ + public void pauseWorkflow(String workflowId) { + Validate.notBlank(workflowId, "workflow id cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/workflow/{workflowId}/pause") + .addPathParam("workflowId", workflowId) + .build(); + + client.execute(request); + } + + /** + * Resume a paused workflow by workflow id + * + * @param workflowId the workflow id of the paused workflow + */ + public void resumeWorkflow(String workflowId) { + Validate.notBlank(workflowId, "workflow id cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/workflow/{workflowId}/resume") + .addPathParam("workflowId", workflowId) + .build(); + + client.execute(request); + } + + /** + * Skips a given task from a current RUNNING workflow + * + * @param workflowId the id of the workflow instance + * @param taskReferenceName the reference name of the task to be skipped + */ + public void skipTaskFromWorkflow(String workflowId, String taskReferenceName) { + Validate.notBlank(workflowId, "workflow id cannot be blank"); + Validate.notBlank(taskReferenceName, "Task reference name cannot be blank"); + + //FIXME skipTaskRequest content is always empty + SkipTaskRequest skipTaskRequest = new SkipTaskRequest(); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/workflow/{workflowId}/skiptask/{taskReferenceName}") + .addPathParam("workflowId", workflowId) + .addPathParam("taskReferenceName", taskReferenceName) + .body(skipTaskRequest) //FIXME review this. It was passed as a query param?! + .build(); + + client.execute(request); + } + + /** + * Reruns the workflow from a specific task + * + * @param workflowId the id of the workflow + * @param rerunWorkflowRequest the request containing the task to rerun from + * @return the id of the workflow + */ + public String rerunWorkflow(String workflowId, RerunWorkflowRequest rerunWorkflowRequest) { + Validate.notBlank(workflowId, "workflow id cannot be blank"); + Validate.notNull(rerunWorkflowRequest, "RerunWorkflowRequest cannot be null"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/{workflowId}/rerun") + .addPathParam("workflowId", workflowId) + .body(rerunWorkflowRequest) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Restart a completed workflow + * + * @param workflowId the workflow id of the workflow to be restarted + * @param useLatestDefinitions if true, use the latest workflow and task definitions when + * restarting the workflow if false, use the workflow and task definitions embedded in the + * workflow execution when restarting the workflow + */ + public void restart(String workflowId, boolean useLatestDefinitions) { + Validate.notBlank(workflowId, "workflow id cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/{workflowId}/restart") + .addPathParam("workflowId", workflowId) + .addQueryParam("useLatestDefinitions", useLatestDefinitions) + .build(); + + client.execute(request); + } + + /** + * Retries the last failed task in a workflow + * + * @param workflowId the workflow id of the workflow with the failed task + */ + public void retryLastFailedTask(String workflowId) { + Validate.notBlank(workflowId, "workflow id cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/{workflowId}/retry") + .addPathParam("workflowId", workflowId) + .build(); + + client.execute(request); + } + + /** + * Resets the callback times of all IN PROGRESS tasks to 0 for the given workflow + * + * @param workflowId the id of the workflow + */ + public void resetCallbacksForInProgressTasks(String workflowId) { + Validate.notBlank(workflowId, "workflow id cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/{workflowId}/resetcallbacks") + .addPathParam("workflowId", workflowId) + .build(); + client.execute(request); + } + + /** + * Terminates the execution of the given workflow instance + * + * @param workflowId the id of the workflow to be terminated + * @param reason the reason to be logged and displayed + */ + public void terminateWorkflow(String workflowId, String reason) { + Validate.notBlank(workflowId, "workflow id cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/workflow/{workflowId}") + .addPathParam("workflowId", workflowId) + .addQueryParam("reason", reason) + .build(); + + client.execute(request); + } + + /** + * Search for workflows based on payload + * + * @param query the search query + * @return the {@link SearchResult} containing the {@link WorkflowSummary} that match the query + */ + public SearchResult search(String query) { + return search(null, null, null, "", query); + } + + /** + * Search for workflows based on payload + * + * @param query the search query + * @return the {@link SearchResult} containing the {@link Workflow} that match the query + */ + public SearchResult searchV2(String query) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/workflow/search-v2") + .addQueryParam("query", query) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Paginated search for workflows based on payload + * + * @param start start value of page + * @param size number of workflows to be returned + * @param sort sort order + * @param freeText additional free text query + * @param query the search query + * @return the {@link SearchResult} containing the {@link WorkflowSummary} that match the query + */ + public SearchResult search( + Integer start, Integer size, String sort, String freeText, String query) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/workflow/search") + .addQueryParam("start", start) + .addQueryParam("size", size) + .addQueryParam("sort", sort) + .addQueryParam("freeText", freeText) + .addQueryParam("query", query) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + /** + * Paginated search for workflows based on payload + * + * @param start start value of page + * @param size number of workflows to be returned + * @param sort sort order + * @param freeText additional free text query + * @param query the search query + * @return the {@link SearchResult} containing the {@link Workflow} that match the query + */ + public SearchResult searchV2(Integer start, Integer size, String sort, String freeText, String query) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/workflow/search-v2") + .addQueryParam("start", start) + .addQueryParam("size", size) + .addQueryParam("sort", sort) + .addQueryParam("freeText", freeText) + .addQueryParam("query", query) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public Workflow testWorkflow(WorkflowTestRequest testRequest) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/test") + .body(testRequest) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + + /** + * Populates the workflow output from external payload storage if the external storage path is + * specified. + * + * @param workflow the workflow for which the output is to be populated. + */ + private void populateWorkflowOutput(Workflow workflow) { + //TODO FIXME OSS MISMATCH - https://github.com/conductor-oss/conductor-java-sdk/issues/27 + if (StringUtils.isNotBlank(workflow.getExternalOutputPayloadStoragePath())) { + throw new UnsupportedOperationException("No external storage support"); + } + } + + private List getRunningWorkflow(String name, Integer version, Long startTime, Long endTime) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/workflow/running/{name}") + .addPathParam("name", name) + .addQueryParam("version", version) + .addQueryParam("startTime", startTime) + .addQueryParam("endTime", endTime) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/worker/Worker.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/worker/Worker.java new file mode 100644 index 000000000..5ea4ed5e2 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/worker/Worker.java @@ -0,0 +1,117 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.worker; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.function.Function; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.client.config.PropertyFactory; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; + +public interface Worker { + + String PROP_DOMAIN = "domain"; + String PROP_ALL_WORKERS = "all"; + String PROP_LOG_INTERVAL = "log_interval"; + String PROP_POLL_INTERVAL = "poll_interval"; + String PROP_PAUSED = "paused"; + + /** + * Retrieve the name of the task definition the worker is currently working on. + * + * @return the name of the task definition. + */ + String getTaskDefName(); + + /** + * Executes a task and returns the updated task. + * + * @param task Task to be executed. + * @return the {@link TaskResult} object If the task is not completed yet, return with the + * status as IN_PROGRESS. + */ + TaskResult execute(Task task); + + /** + * Called when the task coordinator fails to update the task to the server. Client should store + * the task id (in a database) and retry the update later + * + * @param task Task which cannot be updated back to the server. + */ + default void onErrorUpdate(Task task) {} + + /** + * Override this method to pause the worker from polling. + * + * @return true if the worker is paused and no more tasks should be polled from server. + */ + default boolean paused() { + return PropertyFactory.getBoolean(getTaskDefName(), PROP_PAUSED, false); + } + + /** + * Override this method to app specific rules. + * + * @return returns the serverId as the id of the instance that the worker is running. + */ + default String getIdentity() { + String serverId; + try { + // What if 2 workers run in the same host? + serverId = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + serverId = System.getenv("HOSTNAME"); + } + + LoggerHolder.logger.debug("Setting worker id to {}", serverId); + return serverId; + } + + /** + * Override this method to change the interval between polls. + * + * @return interval in millisecond at which the server should be polled for worker tasks. + */ + default int getPollingInterval() { + return PropertyFactory.getInteger(getTaskDefName(), PROP_POLL_INTERVAL, 1000); + } + + static Worker create(String taskType, Function executor) { + return new Worker() { + + @Override + public String getTaskDefName() { + return taskType; + } + + @Override + public TaskResult execute(Task task) { + return executor.apply(task); + } + + @Override + public boolean paused() { + return Worker.super.paused(); + } + }; + } +} + +final class LoggerHolder { + static final Logger logger = LoggerFactory.getLogger(Worker.class); +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java new file mode 100644 index 000000000..2a1101c77 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java @@ -0,0 +1,41 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +public class ObjectMapperProvider { + + private static final ObjectMapper objectMapper = _getObjectMapper(); + + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + private static ObjectMapper _getObjectMapper() { + final ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false); + objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false); + objectMapper.setDefaultPropertyInclusion( + JsonInclude.Value.construct( + JsonInclude.Include.NON_NULL, JsonInclude.Include.ALWAYS)); + objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + objectMapper.registerModule(new JavaTimeModule()); + return objectMapper; + } +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/Auditable.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/Auditable.java new file mode 100644 index 000000000..59fecb6bc --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/Auditable.java @@ -0,0 +1,96 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata; + +public abstract class Auditable { + + private String ownerApp; + + private Long createTime; + + private Long updateTime; + + private String createdBy; + + private String updatedBy; + + /** + * @return the ownerApp + */ + public String getOwnerApp() { + return ownerApp; + } + + /** + * @param ownerApp the ownerApp to set + */ + public void setOwnerApp(String ownerApp) { + this.ownerApp = ownerApp; + } + + /** + * @return the createTime + */ + public Long getCreateTime() { + return createTime == null ? 0 : createTime; + } + + /** + * @param createTime the createTime to set + */ + public void setCreateTime(Long createTime) { + this.createTime = createTime; + } + + /** + * @return the updateTime + */ + public Long getUpdateTime() { + return updateTime == null ? 0 : updateTime; + } + + /** + * @param updateTime the updateTime to set + */ + public void setUpdateTime(Long updateTime) { + this.updateTime = updateTime; + } + + /** + * @return the createdBy + */ + public String getCreatedBy() { + return createdBy; + } + + /** + * @param createdBy the createdBy to set + */ + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + /** + * @return the updatedBy + */ + public String getUpdatedBy() { + return updatedBy; + } + + /** + * @param updatedBy the updatedBy to set + */ + public void setUpdatedBy(String updatedBy) { + this.updatedBy = updatedBy; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/SchemaDef.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/SchemaDef.java new file mode 100644 index 000000000..2e4e6d0c7 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/SchemaDef.java @@ -0,0 +1,36 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata; + +import java.util.Map; + +public class SchemaDef extends Auditable { + + public enum Type { + + JSON, AVRO, PROTOBUF + } + + private String name; + + private final int version = 1; + + private Type type; + + // Schema definition stored here + private Map data; + + // Externalized schema definition (eg. via AVRO, Protobuf registry) + // If using Orkes Schema registry, this points to the name of the schema in the registry + private String externalRef; +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java new file mode 100644 index 000000000..53cf3ad53 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java @@ -0,0 +1,178 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.events; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import com.netflix.conductor.common.metadata.events.EventHandler.Action; + +public class EventExecution { + + public enum Status { + + IN_PROGRESS, COMPLETED, FAILED, SKIPPED + } + + private String id; + + private String messageId; + + private String name; + + private String event; + + private long created; + + private Status status; + + private Action.Type action; + + private Map output = new HashMap<>(); + + public EventExecution() { + } + + public EventExecution(String id, String messageId) { + this.id = id; + this.messageId = messageId; + } + + /** + * @return the id + */ + public String getId() { + return id; + } + + /** + * @param id the id to set + */ + public void setId(String id) { + this.id = id; + } + + /** + * @return the messageId + */ + public String getMessageId() { + return messageId; + } + + /** + * @param messageId the messageId to set + */ + public void setMessageId(String messageId) { + this.messageId = messageId; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the event + */ + public String getEvent() { + return event; + } + + /** + * @param event the event to set + */ + public void setEvent(String event) { + this.event = event; + } + + /** + * @return the created + */ + public long getCreated() { + return created; + } + + /** + * @param created the created to set + */ + public void setCreated(long created) { + this.created = created; + } + + /** + * @return the status + */ + public Status getStatus() { + return status; + } + + /** + * @param status the status to set + */ + public void setStatus(Status status) { + this.status = status; + } + + /** + * @return the action + */ + public Action.Type getAction() { + return action; + } + + /** + * @param action the action to set + */ + public void setAction(Action.Type action) { + this.action = action; + } + + /** + * @return the output + */ + public Map getOutput() { + return output; + } + + /** + * @param output the output to set + */ + public void setOutput(Map output) { + this.output = output; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + EventExecution execution = (EventExecution) o; + return created == execution.created && Objects.equals(id, execution.id) && Objects.equals(messageId, execution.messageId) && Objects.equals(name, execution.name) && Objects.equals(event, execution.event) && status == execution.status && action == execution.action && Objects.equals(output, execution.output); + } + + public int hashCode() { + return Objects.hash(id, messageId, name, event, created, status, action, output); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java new file mode 100644 index 000000000..a016b0c84 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java @@ -0,0 +1,474 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.events; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Defines an event handler + */ +public class EventHandler { + + private String name; + + private String event; + + private String condition; + + private List actions = new LinkedList<>(); + + private boolean active; + + private String evaluatorType; + + public EventHandler() { + } + + /** + * @return the name MUST be unique within a conductor instance + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the event + */ + public String getEvent() { + return event; + } + + /** + * @param event the event to set + */ + public void setEvent(String event) { + this.event = event; + } + + /** + * @return the condition + */ + public String getCondition() { + return condition; + } + + /** + * @param condition the condition to set + */ + public void setCondition(String condition) { + this.condition = condition; + } + + /** + * @return the actions + */ + public List getActions() { + return actions; + } + + /** + * @param actions the actions to set + */ + public void setActions(List actions) { + this.actions = actions; + } + + /** + * @return the active + */ + public boolean isActive() { + return active; + } + + /** + * @param active if set to false, the event handler is deactivated + */ + public void setActive(boolean active) { + this.active = active; + } + + /** + * @return the evaluator type + */ + public String getEvaluatorType() { + return evaluatorType; + } + + /** + * @param evaluatorType the evaluatorType to set + */ + public void setEvaluatorType(String evaluatorType) { + this.evaluatorType = evaluatorType; + } + + public static class Action { + + public enum Type { + + start_workflow, complete_task, fail_task, terminate_workflow, update_workflow_variables + } + + private Type action; + + private StartWorkflow start_workflow; + + private TaskDetails complete_task; + + private TaskDetails fail_task; + + private boolean expandInlineJSON; + + private TerminateWorkflow terminate_workflow; + + private UpdateWorkflowVariables update_workflow_variables; + + /** + * @return the action + */ + public Type getAction() { + return action; + } + + /** + * @param action the action to set + */ + public void setAction(Type action) { + this.action = action; + } + + /** + * @return the start_workflow + */ + public StartWorkflow getStart_workflow() { + return start_workflow; + } + + /** + * @param start_workflow the start_workflow to set + */ + public void setStart_workflow(StartWorkflow start_workflow) { + this.start_workflow = start_workflow; + } + + /** + * @return the complete_task + */ + public TaskDetails getComplete_task() { + return complete_task; + } + + /** + * @param complete_task the complete_task to set + */ + public void setComplete_task(TaskDetails complete_task) { + this.complete_task = complete_task; + } + + /** + * @return the fail_task + */ + public TaskDetails getFail_task() { + return fail_task; + } + + /** + * @param fail_task the fail_task to set + */ + public void setFail_task(TaskDetails fail_task) { + this.fail_task = fail_task; + } + + /** + * @param expandInlineJSON when set to true, the in-lined JSON strings are expanded to a + * full json document + */ + public void setExpandInlineJSON(boolean expandInlineJSON) { + this.expandInlineJSON = expandInlineJSON; + } + + /** + * @return true if the json strings within the payload should be expanded. + */ + public boolean isExpandInlineJSON() { + return expandInlineJSON; + } + + /** + * @return the terminate_workflow + */ + public TerminateWorkflow getTerminate_workflow() { + return terminate_workflow; + } + + /** + * @param terminate_workflow the terminate_workflow to set + */ + public void setTerminate_workflow(TerminateWorkflow terminate_workflow) { + this.terminate_workflow = terminate_workflow; + } + + /** + * @return the update_workflow_variables + */ + public UpdateWorkflowVariables getUpdate_workflow_variables() { + return update_workflow_variables; + } + + /** + * @param update_workflow_variables the update_workflow_variables to set + */ + public void setUpdate_workflow_variables(UpdateWorkflowVariables update_workflow_variables) { + this.update_workflow_variables = update_workflow_variables; + } + } + + public static class TaskDetails { + + private String workflowId; + + private String taskRefName; + + private Map output = new HashMap<>(); + + private String taskId; + + /** + * @return the workflowId + */ + public String getWorkflowId() { + return workflowId; + } + + /** + * @param workflowId the workflowId to set + */ + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + + /** + * @return the taskRefName + */ + public String getTaskRefName() { + return taskRefName; + } + + /** + * @param taskRefName the taskRefName to set + */ + public void setTaskRefName(String taskRefName) { + this.taskRefName = taskRefName; + } + + /** + * @return the output + */ + public Map getOutput() { + return output; + } + + /** + * @param output the output to set + */ + public void setOutput(Map output) { + this.output = output; + } + + /** + * @return the taskId + */ + public String getTaskId() { + return taskId; + } + + /** + * @param taskId the taskId to set + */ + public void setTaskId(String taskId) { + this.taskId = taskId; + } + } + + public static class StartWorkflow { + + private String name; + + private Integer version; + + private String correlationId; + + private Map input = new HashMap<>(); + + private Map taskToDomain; + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the version + */ + public Integer getVersion() { + return version; + } + + /** + * @param version the version to set + */ + public void setVersion(Integer version) { + this.version = version; + } + + /** + * @return the correlationId + */ + public String getCorrelationId() { + return correlationId; + } + + /** + * @param correlationId the correlationId to set + */ + public void setCorrelationId(String correlationId) { + this.correlationId = correlationId; + } + + /** + * @return the input + */ + public Map getInput() { + return input; + } + + /** + * @param input the input to set + */ + public void setInput(Map input) { + this.input = input; + } + + public Map getTaskToDomain() { + return taskToDomain; + } + + public void setTaskToDomain(Map taskToDomain) { + this.taskToDomain = taskToDomain; + } + } + + public static class TerminateWorkflow { + + private String workflowId; + + private String terminationReason; + + /** + * @return the workflowId + */ + public String getWorkflowId() { + return workflowId; + } + + /** + * @param workflowId the workflowId to set + */ + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + + /** + * @return the reasonForTermination + */ + public String getTerminationReason() { + return terminationReason; + } + + /** + * @param terminationReason the reasonForTermination to set + */ + public void setTerminationReason(String terminationReason) { + this.terminationReason = terminationReason; + } + } + + public static class UpdateWorkflowVariables { + + private String workflowId; + + private Map variables; + + private Boolean appendArray; + + /** + * @return the workflowId + */ + public String getWorkflowId() { + return workflowId; + } + + /** + * @param workflowId the workflowId to set + */ + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + + /** + * @return the variables + */ + public Map getVariables() { + return variables; + } + + /** + * @param variables the variables to set + */ + public void setVariables(Map variables) { + this.variables = variables; + } + + /** + * @return appendArray + */ + public Boolean isAppendArray() { + return appendArray; + } + + /** + * @param appendArray the appendArray to set + */ + public void setAppendArray(Boolean appendArray) { + this.appendArray = appendArray; + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java new file mode 100644 index 000000000..350ff5f9a --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java @@ -0,0 +1,89 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.tasks; + +import java.util.Objects; + +public class PollData { + + private String queueName; + + private String domain; + + private String workerId; + + private long lastPollTime; + + public PollData() { + super(); + } + + public PollData(String queueName, String domain, String workerId, long lastPollTime) { + super(); + this.queueName = queueName; + this.domain = domain; + this.workerId = workerId; + this.lastPollTime = lastPollTime; + } + + public String getQueueName() { + return queueName; + } + + public void setQueueName(String queueName) { + this.queueName = queueName; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public String getWorkerId() { + return workerId; + } + + public void setWorkerId(String workerId) { + this.workerId = workerId; + } + + public long getLastPollTime() { + return lastPollTime; + } + + public void setLastPollTime(long lastPollTime) { + this.lastPollTime = lastPollTime; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PollData pollData = (PollData) o; + return getLastPollTime() == pollData.getLastPollTime() && Objects.equals(getQueueName(), pollData.getQueueName()) && Objects.equals(getDomain(), pollData.getDomain()) && Objects.equals(getWorkerId(), pollData.getWorkerId()); + } + + public int hashCode() { + return Objects.hash(getQueueName(), getDomain(), getWorkerId(), getLastPollTime()); + } + + public String toString() { + return "PollData{" + "queueName='" + queueName + '\'' + ", domain='" + domain + '\'' + ", workerId='" + workerId + '\'' + ", lastPollTime=" + lastPollTime + '}'; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java new file mode 100644 index 000000000..ef421189d --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java @@ -0,0 +1,775 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.tasks; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; + +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +public class Task { + + public enum Status { + + IN_PROGRESS(false, true, true), + CANCELED(true, false, false), + FAILED(true, false, true), + FAILED_WITH_TERMINAL_ERROR(true, false, // No retries even if retries are configured, the task and the related + false), + // workflow should be terminated + COMPLETED(true, true, true), + COMPLETED_WITH_ERRORS(true, true, true), + SCHEDULED(false, true, true), + TIMED_OUT(true, false, true), + SKIPPED(true, true, false); + + private final boolean terminal; + + private final boolean successful; + + private final boolean retriable; + + Status(boolean terminal, boolean successful, boolean retriable) { + this.terminal = terminal; + this.successful = successful; + this.retriable = retriable; + } + + public boolean isTerminal() { + return terminal; + } + + public boolean isSuccessful() { + return successful; + } + + public boolean isRetriable() { + return retriable; + } + } + + private String taskType; + + private Status status; + + private Map inputData = new HashMap<>(); + + private String referenceTaskName; + + private int retryCount; + + private int seq; + + private String correlationId; + + private int pollCount; + + private String taskDefName; + + /** + * Time when the task was scheduled + */ + private long scheduledTime; + + /** + * Time when the task was first polled + */ + private long startTime; + + /** + * Time when the task completed executing + */ + private long endTime; + + /** + * Time when the task was last updated + */ + private long updateTime; + + private int startDelayInSeconds; + + private String retriedTaskId; + + private boolean retried; + + private boolean executed; + + private boolean callbackFromWorker = true; + + private long responseTimeoutSeconds; + + private String workflowInstanceId; + + private String workflowType; + + private String taskId; + + private String reasonForIncompletion; + + private long callbackAfterSeconds; + + private String workerId; + + private Map outputData = new HashMap<>(); + + private WorkflowTask workflowTask; + + private String domain; + + // id 31 is reserved + private int rateLimitPerFrequency; + + private int rateLimitFrequencyInSeconds; + + private String externalInputPayloadStoragePath; + + private String externalOutputPayloadStoragePath; + + private int workflowPriority; + + private String executionNameSpace; + + private String isolationGroupId; + + private int iteration; + + private String subWorkflowId; + + /** + * Use to note that a sub workflow associated with SUB_WORKFLOW task has an action performed on + * it directly. + */ + private boolean subworkflowChanged; + + // If the task is an event associated with a parent task, the id of the parent task + private String parentTaskId; + + public Task() { + } + + /** + * @return Type of the task + * @see TaskType + */ + public String getTaskType() { + return taskType; + } + + public void setTaskType(String taskType) { + this.taskType = taskType; + } + + /** + * @return Status of the task + */ + public Status getStatus() { + return status; + } + + /** + * @param status Status of the task + */ + public void setStatus(Status status) { + this.status = status; + } + + public Map getInputData() { + return inputData; + } + + public void setInputData(Map inputData) { + if (inputData == null) { + inputData = new HashMap<>(); + } + this.inputData = inputData; + } + + /** + * @return the referenceTaskName + */ + public String getReferenceTaskName() { + return referenceTaskName; + } + + /** + * @param referenceTaskName the referenceTaskName to set + */ + public void setReferenceTaskName(String referenceTaskName) { + this.referenceTaskName = referenceTaskName; + } + + /** + * @return the correlationId + */ + public String getCorrelationId() { + return correlationId; + } + + /** + * @param correlationId the correlationId to set + */ + public void setCorrelationId(String correlationId) { + this.correlationId = correlationId; + } + + /** + * @return the retryCount + */ + public int getRetryCount() { + return retryCount; + } + + /** + * @param retryCount the retryCount to set + */ + public void setRetryCount(int retryCount) { + this.retryCount = retryCount; + } + + /** + * @return the scheduledTime + */ + public long getScheduledTime() { + return scheduledTime; + } + + /** + * @param scheduledTime the scheduledTime to set + */ + public void setScheduledTime(long scheduledTime) { + this.scheduledTime = scheduledTime; + } + + /** + * @return the startTime + */ + public long getStartTime() { + return startTime; + } + + /** + * @param startTime the startTime to set + */ + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + /** + * @return the endTime + */ + public long getEndTime() { + return endTime; + } + + /** + * @param endTime the endTime to set + */ + public void setEndTime(long endTime) { + this.endTime = endTime; + } + + /** + * @return the startDelayInSeconds + */ + public int getStartDelayInSeconds() { + return startDelayInSeconds; + } + + /** + * @param startDelayInSeconds the startDelayInSeconds to set + */ + public void setStartDelayInSeconds(int startDelayInSeconds) { + this.startDelayInSeconds = startDelayInSeconds; + } + + /** + * @return the retriedTaskId + */ + public String getRetriedTaskId() { + return retriedTaskId; + } + + /** + * @param retriedTaskId the retriedTaskId to set + */ + public void setRetriedTaskId(String retriedTaskId) { + this.retriedTaskId = retriedTaskId; + } + + /** + * @return the seq + */ + public int getSeq() { + return seq; + } + + /** + * @param seq the seq to set + */ + public void setSeq(int seq) { + this.seq = seq; + } + + /** + * @return the updateTime + */ + public long getUpdateTime() { + return updateTime; + } + + /** + * @param updateTime the updateTime to set + */ + public void setUpdateTime(long updateTime) { + this.updateTime = updateTime; + } + + /** + * @return the queueWaitTime + */ + public long getQueueWaitTime() { + if (this.startTime > 0 && this.scheduledTime > 0) { + if (this.updateTime > 0 && getCallbackAfterSeconds() > 0) { + long waitTime = System.currentTimeMillis() - (this.updateTime + (getCallbackAfterSeconds() * 1000)); + return waitTime > 0 ? waitTime : 0; + } else { + return this.startTime - this.scheduledTime; + } + } + return 0L; + } + + /** + * @return True if the task has been retried after failure + */ + public boolean isRetried() { + return retried; + } + + /** + * @param retried the retried to set + */ + public void setRetried(boolean retried) { + this.retried = retried; + } + + /** + * @return True if the task has completed its lifecycle within conductor (from start to + * completion to being updated in the datastore) + */ + public boolean isExecuted() { + return executed; + } + + /** + * @param executed the executed value to set + */ + public void setExecuted(boolean executed) { + this.executed = executed; + } + + /** + * @return No. of times task has been polled + */ + public int getPollCount() { + return pollCount; + } + + public void setPollCount(int pollCount) { + this.pollCount = pollCount; + } + + public void incrementPollCount() { + ++this.pollCount; + } + + public boolean isCallbackFromWorker() { + return callbackFromWorker; + } + + public void setCallbackFromWorker(boolean callbackFromWorker) { + this.callbackFromWorker = callbackFromWorker; + } + + /** + * @return Name of the task definition + */ + public String getTaskDefName() { + if (taskDefName == null || "".equals(taskDefName)) { + taskDefName = taskType; + } + return taskDefName; + } + + /** + * @param taskDefName Name of the task definition + */ + public void setTaskDefName(String taskDefName) { + this.taskDefName = taskDefName; + } + + /** + * @return the timeout for task to send response. After this timeout, the task will be re-queued + */ + public long getResponseTimeoutSeconds() { + return responseTimeoutSeconds; + } + + /** + * @param responseTimeoutSeconds - timeout for task to send response. After this timeout, the + * task will be re-queued + */ + public void setResponseTimeoutSeconds(long responseTimeoutSeconds) { + this.responseTimeoutSeconds = responseTimeoutSeconds; + } + + /** + * @return the workflowInstanceId + */ + public String getWorkflowInstanceId() { + return workflowInstanceId; + } + + /** + * @param workflowInstanceId the workflowInstanceId to set + */ + public void setWorkflowInstanceId(String workflowInstanceId) { + this.workflowInstanceId = workflowInstanceId; + } + + public String getWorkflowType() { + return workflowType; + } + + /** + * @param workflowType the name of the workflow + * @return the task object with the workflow type set + */ + public com.netflix.conductor.common.metadata.tasks.Task setWorkflowType(String workflowType) { + this.workflowType = workflowType; + return this; + } + + /** + * @return the taskId + */ + public String getTaskId() { + return taskId; + } + + /** + * @param taskId the taskId to set + */ + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + /** + * @return the reasonForIncompletion + */ + public String getReasonForIncompletion() { + return reasonForIncompletion; + } + + /** + * @param reasonForIncompletion the reasonForIncompletion to set + */ + public void setReasonForIncompletion(String reasonForIncompletion) { + this.reasonForIncompletion = StringUtils.substring(reasonForIncompletion, 0, 500); + } + + /** + * @return the callbackAfterSeconds + */ + public long getCallbackAfterSeconds() { + return callbackAfterSeconds; + } + + /** + * @param callbackAfterSeconds the callbackAfterSeconds to set + */ + public void setCallbackAfterSeconds(long callbackAfterSeconds) { + this.callbackAfterSeconds = callbackAfterSeconds; + } + + /** + * @return the workerId + */ + public String getWorkerId() { + return workerId; + } + + /** + * @param workerId the workerId to set + */ + public void setWorkerId(String workerId) { + this.workerId = workerId; + } + + /** + * @return the outputData + */ + public Map getOutputData() { + return outputData; + } + + /** + * @param outputData the outputData to set + */ + public void setOutputData(Map outputData) { + if (outputData == null) { + outputData = new HashMap<>(); + } + this.outputData = outputData; + } + + /** + * @return Workflow Task definition + */ + public WorkflowTask getWorkflowTask() { + return workflowTask; + } + + /** + * @param workflowTask Task definition + */ + public void setWorkflowTask(WorkflowTask workflowTask) { + this.workflowTask = workflowTask; + } + + /** + * @return the domain + */ + public String getDomain() { + return domain; + } + + /** + * @param domain the Domain + */ + public void setDomain(String domain) { + this.domain = domain; + } + + /** + * @return {@link Optional} containing the task definition if available + */ + public Optional getTaskDefinition() { + return Optional.ofNullable(this.getWorkflowTask()).map(WorkflowTask::getTaskDefinition); + } + + public int getRateLimitPerFrequency() { + return rateLimitPerFrequency; + } + + public void setRateLimitPerFrequency(int rateLimitPerFrequency) { + this.rateLimitPerFrequency = rateLimitPerFrequency; + } + + public int getRateLimitFrequencyInSeconds() { + return rateLimitFrequencyInSeconds; + } + + public void setRateLimitFrequencyInSeconds(int rateLimitFrequencyInSeconds) { + this.rateLimitFrequencyInSeconds = rateLimitFrequencyInSeconds; + } + + /** + * @return the external storage path for the task input payload + */ + public String getExternalInputPayloadStoragePath() { + return externalInputPayloadStoragePath; + } + + /** + * @param externalInputPayloadStoragePath the external storage path where the task input payload + * is stored + */ + public void setExternalInputPayloadStoragePath(String externalInputPayloadStoragePath) { + this.externalInputPayloadStoragePath = externalInputPayloadStoragePath; + } + + /** + * @return the external storage path for the task output payload + */ + public String getExternalOutputPayloadStoragePath() { + return externalOutputPayloadStoragePath; + } + + /** + * @param externalOutputPayloadStoragePath the external storage path where the task output + * payload is stored + */ + public void setExternalOutputPayloadStoragePath(String externalOutputPayloadStoragePath) { + this.externalOutputPayloadStoragePath = externalOutputPayloadStoragePath; + } + + public void setIsolationGroupId(String isolationGroupId) { + this.isolationGroupId = isolationGroupId; + } + + public String getIsolationGroupId() { + return isolationGroupId; + } + + public String getExecutionNameSpace() { + return executionNameSpace; + } + + public void setExecutionNameSpace(String executionNameSpace) { + this.executionNameSpace = executionNameSpace; + } + + /** + * @return the iteration + */ + public int getIteration() { + return iteration; + } + + /** + * @param iteration iteration + */ + public void setIteration(int iteration) { + this.iteration = iteration; + } + + public boolean isLoopOverTask() { + return iteration > 0; + } + + /** + * @return the priority defined on workflow + */ + public int getWorkflowPriority() { + return workflowPriority; + } + + /** + * @param workflowPriority Priority defined for workflow + */ + public void setWorkflowPriority(int workflowPriority) { + this.workflowPriority = workflowPriority; + } + + public boolean isSubworkflowChanged() { + return subworkflowChanged; + } + + public void setSubworkflowChanged(boolean subworkflowChanged) { + this.subworkflowChanged = subworkflowChanged; + } + + public String getSubWorkflowId() { + // For backwards compatibility + if (StringUtils.isNotBlank(subWorkflowId)) { + return subWorkflowId; + } else { + return this.getOutputData() != null && this.getOutputData().get("subWorkflowId") != null ? (String) this.getOutputData().get("subWorkflowId") : this.getInputData() != null ? (String) this.getInputData().get("subWorkflowId") : null; + } + } + + public void setSubWorkflowId(String subWorkflowId) { + this.subWorkflowId = subWorkflowId; + // For backwards compatibility + if (this.getOutputData() != null && this.getOutputData().containsKey("subWorkflowId")) { + this.getOutputData().put("subWorkflowId", subWorkflowId); + } + } + + public String getParentTaskId() { + return parentTaskId; + } + + public void setParentTaskId(String parentTaskId) { + this.parentTaskId = parentTaskId; + } + + public Task copy() { + Task copy = new Task(); + copy.setCallbackAfterSeconds(callbackAfterSeconds); + copy.setCallbackFromWorker(callbackFromWorker); + copy.setCorrelationId(correlationId); + copy.setInputData(inputData); + copy.setOutputData(outputData); + copy.setReferenceTaskName(referenceTaskName); + copy.setStartDelayInSeconds(startDelayInSeconds); + copy.setTaskDefName(taskDefName); + copy.setTaskType(taskType); + copy.setWorkflowInstanceId(workflowInstanceId); + copy.setWorkflowType(workflowType); + copy.setResponseTimeoutSeconds(responseTimeoutSeconds); + copy.setStatus(status); + copy.setRetryCount(retryCount); + copy.setPollCount(pollCount); + copy.setTaskId(taskId); + copy.setWorkflowTask(workflowTask); + copy.setDomain(domain); + copy.setRateLimitPerFrequency(rateLimitPerFrequency); + copy.setRateLimitFrequencyInSeconds(rateLimitFrequencyInSeconds); + copy.setExternalInputPayloadStoragePath(externalInputPayloadStoragePath); + copy.setExternalOutputPayloadStoragePath(externalOutputPayloadStoragePath); + copy.setWorkflowPriority(workflowPriority); + copy.setIteration(iteration); + copy.setExecutionNameSpace(executionNameSpace); + copy.setIsolationGroupId(isolationGroupId); + copy.setSubWorkflowId(getSubWorkflowId()); + copy.setSubworkflowChanged(subworkflowChanged); + copy.setParentTaskId(parentTaskId); + return copy; + } + + /** + * @return a deep copy of the task instance To be used inside copy Workflow method to provide a + * valid deep copied object. Note: This does not copy the following fields: + *

+ */ + public Task deepCopy() { + Task deepCopy = copy(); + deepCopy.setStartTime(startTime); + deepCopy.setScheduledTime(scheduledTime); + deepCopy.setEndTime(endTime); + deepCopy.setWorkerId(workerId); + deepCopy.setReasonForIncompletion(reasonForIncompletion); + deepCopy.setSeq(seq); + deepCopy.setParentTaskId(parentTaskId); + return deepCopy; + } + + public String toString() { + return "Task{" + "taskType='" + taskType + '\'' + ", status=" + status + ", inputData=" + inputData + ", referenceTaskName='" + referenceTaskName + '\'' + ", retryCount=" + retryCount + ", seq=" + seq + ", correlationId='" + correlationId + '\'' + ", pollCount=" + pollCount + ", taskDefName='" + taskDefName + '\'' + ", scheduledTime=" + scheduledTime + ", startTime=" + startTime + ", endTime=" + endTime + ", updateTime=" + updateTime + ", startDelayInSeconds=" + startDelayInSeconds + ", retriedTaskId='" + retriedTaskId + '\'' + ", retried=" + retried + ", executed=" + executed + ", callbackFromWorker=" + callbackFromWorker + ", responseTimeoutSeconds=" + responseTimeoutSeconds + ", workflowInstanceId='" + workflowInstanceId + '\'' + ", workflowType='" + workflowType + '\'' + ", taskId='" + taskId + '\'' + ", reasonForIncompletion='" + reasonForIncompletion + '\'' + ", callbackAfterSeconds=" + callbackAfterSeconds + ", workerId='" + workerId + '\'' + ", outputData=" + outputData + ", workflowTask=" + workflowTask + ", domain='" + domain + '\'' + ", rateLimitPerFrequency=" + rateLimitPerFrequency + ", rateLimitFrequencyInSeconds=" + rateLimitFrequencyInSeconds + ", workflowPriority=" + workflowPriority + ", externalInputPayloadStoragePath='" + externalInputPayloadStoragePath + '\'' + ", externalOutputPayloadStoragePath='" + externalOutputPayloadStoragePath + '\'' + ", isolationGroupId='" + isolationGroupId + '\'' + ", executionNameSpace='" + executionNameSpace + '\'' + ", subworkflowChanged='" + subworkflowChanged + '\'' + '}'; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Task task = (Task) o; + return getRetryCount() == task.getRetryCount() && getSeq() == task.getSeq() && getPollCount() == task.getPollCount() && getScheduledTime() == task.getScheduledTime() && getStartTime() == task.getStartTime() && getEndTime() == task.getEndTime() && getUpdateTime() == task.getUpdateTime() && getStartDelayInSeconds() == task.getStartDelayInSeconds() && isRetried() == task.isRetried() && isExecuted() == task.isExecuted() && isCallbackFromWorker() == task.isCallbackFromWorker() && getResponseTimeoutSeconds() == task.getResponseTimeoutSeconds() && getCallbackAfterSeconds() == task.getCallbackAfterSeconds() && getRateLimitPerFrequency() == task.getRateLimitPerFrequency() && getRateLimitFrequencyInSeconds() == task.getRateLimitFrequencyInSeconds() && Objects.equals(getTaskType(), task.getTaskType()) && getStatus() == task.getStatus() && getIteration() == task.getIteration() && getWorkflowPriority() == task.getWorkflowPriority() && Objects.equals(getInputData(), task.getInputData()) && Objects.equals(getReferenceTaskName(), task.getReferenceTaskName()) && Objects.equals(getCorrelationId(), task.getCorrelationId()) && Objects.equals(getTaskDefName(), task.getTaskDefName()) && Objects.equals(getRetriedTaskId(), task.getRetriedTaskId()) && Objects.equals(getWorkflowInstanceId(), task.getWorkflowInstanceId()) && Objects.equals(getWorkflowType(), task.getWorkflowType()) && Objects.equals(getTaskId(), task.getTaskId()) && Objects.equals(getReasonForIncompletion(), task.getReasonForIncompletion()) && Objects.equals(getWorkerId(), task.getWorkerId()) && Objects.equals(getOutputData(), task.getOutputData()) && Objects.equals(getWorkflowTask(), task.getWorkflowTask()) && Objects.equals(getDomain(), task.getDomain()) && Objects.equals(getExternalInputPayloadStoragePath(), task.getExternalInputPayloadStoragePath()) && Objects.equals(getExternalOutputPayloadStoragePath(), task.getExternalOutputPayloadStoragePath()) && Objects.equals(getIsolationGroupId(), task.getIsolationGroupId()) && Objects.equals(getExecutionNameSpace(), task.getExecutionNameSpace()) && Objects.equals(getParentTaskId(), task.getParentTaskId()); + } + + public int hashCode() { + return Objects.hash(getTaskType(), getStatus(), getInputData(), getReferenceTaskName(), getWorkflowPriority(), getRetryCount(), getSeq(), getCorrelationId(), getPollCount(), getTaskDefName(), getScheduledTime(), getStartTime(), getEndTime(), getUpdateTime(), getStartDelayInSeconds(), getRetriedTaskId(), isRetried(), isExecuted(), isCallbackFromWorker(), getResponseTimeoutSeconds(), getWorkflowInstanceId(), getWorkflowType(), getTaskId(), getReasonForIncompletion(), getCallbackAfterSeconds(), getWorkerId(), getOutputData(), getWorkflowTask(), getDomain(), getRateLimitPerFrequency(), getRateLimitFrequencyInSeconds(), getExternalInputPayloadStoragePath(), getExternalOutputPayloadStoragePath(), getIsolationGroupId(), getExecutionNameSpace(), getParentTaskId()); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java new file mode 100644 index 000000000..947231e87 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java @@ -0,0 +1,437 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.tasks; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.netflix.conductor.common.metadata.Auditable; +import com.netflix.conductor.common.metadata.SchemaDef; + +public class TaskDef extends Auditable { + + public enum TimeoutPolicy { + + RETRY, TIME_OUT_WF, ALERT_ONLY + } + + public enum RetryLogic { + + FIXED, EXPONENTIAL_BACKOFF, LINEAR_BACKOFF + } + + public static final int ONE_HOUR = 60 * 60; + + /** + * Unique name identifying the task. The name is unique across + */ + private String name; + + private String description; + + private int // Default + retryCount = 3; + + private long timeoutSeconds; + + private List inputKeys = new ArrayList<>(); + + private List outputKeys = new ArrayList<>(); + + private TimeoutPolicy timeoutPolicy = TimeoutPolicy.TIME_OUT_WF; + + private RetryLogic retryLogic = RetryLogic.FIXED; + + private int retryDelaySeconds = 60; + + private long responseTimeoutSeconds = ONE_HOUR; + + private Integer concurrentExecLimit; + + private Map inputTemplate = new HashMap<>(); + + // This field is deprecated, do not use id 13. + // @ProtoField(id = 13) + // private Integer rateLimitPerSecond; + private Integer rateLimitPerFrequency; + + private Integer rateLimitFrequencyInSeconds; + + private String isolationGroupId; + + private String executionNameSpace; + + private String ownerEmail; + + private Integer pollTimeoutSeconds; + + private Integer backoffScaleFactor = 1; + + private String baseType; + + private SchemaDef inputSchema; + + private SchemaDef outputSchema; + + private boolean enforceSchema; + + public TaskDef() { + } + + public TaskDef(String name) { + this.name = name; + } + + public TaskDef(String name, String description) { + this.name = name; + this.description = description; + } + + public TaskDef(String name, String description, int retryCount, long timeoutSeconds) { + this.name = name; + this.description = description; + this.retryCount = retryCount; + this.timeoutSeconds = timeoutSeconds; + } + + public TaskDef(String name, String description, String ownerEmail, int retryCount, long timeoutSeconds, long responseTimeoutSeconds) { + this.name = name; + this.description = description; + this.ownerEmail = ownerEmail; + this.retryCount = retryCount; + this.timeoutSeconds = timeoutSeconds; + this.responseTimeoutSeconds = responseTimeoutSeconds; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * @param description the description to set + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * @return the retryCount + */ + public int getRetryCount() { + return retryCount; + } + + /** + * @param retryCount the retryCount to set + */ + public void setRetryCount(int retryCount) { + this.retryCount = retryCount; + } + + /** + * @return the timeoutSeconds + */ + public long getTimeoutSeconds() { + return timeoutSeconds; + } + + /** + * @param timeoutSeconds the timeoutSeconds to set + */ + public void setTimeoutSeconds(long timeoutSeconds) { + this.timeoutSeconds = timeoutSeconds; + } + + /** + * @return Returns the input keys + */ + public List getInputKeys() { + return inputKeys; + } + + /** + * @param inputKeys Set of keys that the task accepts in the input map + */ + public void setInputKeys(List inputKeys) { + this.inputKeys = inputKeys; + } + + /** + * @return Returns the output keys for the task when executed + */ + public List getOutputKeys() { + return outputKeys; + } + + /** + * @param outputKeys Sets the output keys + */ + public void setOutputKeys(List outputKeys) { + this.outputKeys = outputKeys; + } + + /** + * @return the timeoutPolicy + */ + public TimeoutPolicy getTimeoutPolicy() { + return timeoutPolicy; + } + + /** + * @param timeoutPolicy the timeoutPolicy to set + */ + public void setTimeoutPolicy(TimeoutPolicy timeoutPolicy) { + this.timeoutPolicy = timeoutPolicy; + } + + /** + * @return the retryLogic + */ + public RetryLogic getRetryLogic() { + return retryLogic; + } + + /** + * @param retryLogic the retryLogic to set + */ + public void setRetryLogic(RetryLogic retryLogic) { + this.retryLogic = retryLogic; + } + + /** + * @return the retryDelaySeconds + */ + public int getRetryDelaySeconds() { + return retryDelaySeconds; + } + + /** + * @return the timeout for task to send response. After this timeout, the task will be re-queued + */ + public long getResponseTimeoutSeconds() { + return responseTimeoutSeconds; + } + + /** + * @param responseTimeoutSeconds - timeout for task to send response. After this timeout, the + * task will be re-queued + */ + public void setResponseTimeoutSeconds(long responseTimeoutSeconds) { + this.responseTimeoutSeconds = responseTimeoutSeconds; + } + + /** + * @param retryDelaySeconds the retryDelaySeconds to set + */ + public void setRetryDelaySeconds(int retryDelaySeconds) { + this.retryDelaySeconds = retryDelaySeconds; + } + + /** + * @return the inputTemplate + */ + public Map getInputTemplate() { + return inputTemplate; + } + + /** + * @return rateLimitPerFrequency The max number of tasks that will be allowed to be executed per + * rateLimitFrequencyInSeconds. + */ + public Integer getRateLimitPerFrequency() { + return rateLimitPerFrequency == null ? 0 : rateLimitPerFrequency; + } + + /** + * @param rateLimitPerFrequency The max number of tasks that will be allowed to be executed per + * rateLimitFrequencyInSeconds. Setting the value to 0 removes the rate limit + */ + public void setRateLimitPerFrequency(Integer rateLimitPerFrequency) { + this.rateLimitPerFrequency = rateLimitPerFrequency; + } + + /** + * @return rateLimitFrequencyInSeconds: The time bucket that is used to rate limit tasks based + * on {@link #getRateLimitPerFrequency()} If null or not set, then defaults to 1 second + */ + public Integer getRateLimitFrequencyInSeconds() { + return rateLimitFrequencyInSeconds == null ? 1 : rateLimitFrequencyInSeconds; + } + + /** + * @param rateLimitFrequencyInSeconds: The time window/bucket for which the rate limit needs to + * be applied. This will only have affect if {@link #getRateLimitPerFrequency()} is greater + * than zero + */ + public void setRateLimitFrequencyInSeconds(Integer rateLimitFrequencyInSeconds) { + this.rateLimitFrequencyInSeconds = rateLimitFrequencyInSeconds; + } + + /** + * @param concurrentExecLimit Limit of number of concurrent task that can be IN_PROGRESS at a + * given time. Seting the value to 0 removes the limit. + */ + public void setConcurrentExecLimit(Integer concurrentExecLimit) { + this.concurrentExecLimit = concurrentExecLimit; + } + + /** + * @return Limit of number of concurrent task that can be IN_PROGRESS at a given time + */ + public Integer getConcurrentExecLimit() { + return concurrentExecLimit; + } + + /** + * @return concurrency limit + */ + public int concurrencyLimit() { + return concurrentExecLimit == null ? 0 : concurrentExecLimit; + } + + /** + * @param inputTemplate the inputTemplate to set + */ + public void setInputTemplate(Map inputTemplate) { + this.inputTemplate = inputTemplate; + } + + public String getIsolationGroupId() { + return isolationGroupId; + } + + public void setIsolationGroupId(String isolationGroupId) { + this.isolationGroupId = isolationGroupId; + } + + public String getExecutionNameSpace() { + return executionNameSpace; + } + + public void setExecutionNameSpace(String executionNameSpace) { + this.executionNameSpace = executionNameSpace; + } + + /** + * @return the email of the owner of this task definition + */ + public String getOwnerEmail() { + return ownerEmail; + } + + /** + * @param ownerEmail the owner email to set + */ + public void setOwnerEmail(String ownerEmail) { + this.ownerEmail = ownerEmail; + } + + /** + * @param pollTimeoutSeconds the poll timeout to set + */ + public void setPollTimeoutSeconds(Integer pollTimeoutSeconds) { + this.pollTimeoutSeconds = pollTimeoutSeconds; + } + + /** + * @return the poll timeout of this task definition + */ + public Integer getPollTimeoutSeconds() { + return pollTimeoutSeconds; + } + + /** + * @param backoffScaleFactor the backoff rate to set + */ + public void setBackoffScaleFactor(Integer backoffScaleFactor) { + this.backoffScaleFactor = backoffScaleFactor; + } + + /** + * @return the backoff rate of this task definition + */ + public Integer getBackoffScaleFactor() { + return backoffScaleFactor; + } + + public String getBaseType() { + return baseType; + } + + public void setBaseType(String baseType) { + this.baseType = baseType; + } + + public SchemaDef getInputSchema() { + return inputSchema; + } + + public void setInputSchema(SchemaDef inputSchema) { + this.inputSchema = inputSchema; + } + + public SchemaDef getOutputSchema() { + return outputSchema; + } + + public void setOutputSchema(SchemaDef outputSchema) { + this.outputSchema = outputSchema; + } + + public boolean isEnforceSchema() { + return enforceSchema; + } + + public void setEnforceSchema(boolean enforceSchema) { + this.enforceSchema = enforceSchema; + } + + public String toString() { + return name; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TaskDef taskDef = (TaskDef) o; + return getRetryCount() == taskDef.getRetryCount() && getTimeoutSeconds() == taskDef.getTimeoutSeconds() && getRetryDelaySeconds() == taskDef.getRetryDelaySeconds() && getBackoffScaleFactor() == taskDef.getBackoffScaleFactor() && getResponseTimeoutSeconds() == taskDef.getResponseTimeoutSeconds() && Objects.equals(getName(), taskDef.getName()) && Objects.equals(getDescription(), taskDef.getDescription()) && Objects.equals(getInputKeys(), taskDef.getInputKeys()) && Objects.equals(getOutputKeys(), taskDef.getOutputKeys()) && getTimeoutPolicy() == taskDef.getTimeoutPolicy() && getRetryLogic() == taskDef.getRetryLogic() && Objects.equals(getConcurrentExecLimit(), taskDef.getConcurrentExecLimit()) && Objects.equals(getRateLimitPerFrequency(), taskDef.getRateLimitPerFrequency()) && Objects.equals(getInputTemplate(), taskDef.getInputTemplate()) && Objects.equals(getIsolationGroupId(), taskDef.getIsolationGroupId()) && Objects.equals(getExecutionNameSpace(), taskDef.getExecutionNameSpace()) && Objects.equals(getOwnerEmail(), taskDef.getOwnerEmail()) && Objects.equals(getBaseType(), taskDef.getBaseType()) && Objects.equals(getInputSchema(), taskDef.getInputSchema()) && Objects.equals(getOutputSchema(), taskDef.getOutputSchema()); + } + + public int hashCode() { + return Objects.hash(getName(), getDescription(), getRetryCount(), getTimeoutSeconds(), getInputKeys(), getOutputKeys(), getTimeoutPolicy(), getRetryLogic(), getRetryDelaySeconds(), getBackoffScaleFactor(), getResponseTimeoutSeconds(), getConcurrentExecLimit(), getRateLimitPerFrequency(), getInputTemplate(), getIsolationGroupId(), getExecutionNameSpace(), getOwnerEmail(), getBaseType(), getInputSchema(), getOutputSchema()); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java new file mode 100644 index 000000000..9c5153b0c --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java @@ -0,0 +1,92 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.tasks; + +import java.util.Objects; + +/** + * Model that represents the task's execution log. + */ +public class TaskExecLog { + + private String log; + + private String taskId; + + private long createdTime; + + public TaskExecLog() { + } + + public TaskExecLog(String log) { + this.log = log; + this.createdTime = System.currentTimeMillis(); + } + + /** + * @return Task Exec Log + */ + public String getLog() { + return log; + } + + /** + * @param log The Log + */ + public void setLog(String log) { + this.log = log; + } + + /** + * @return the taskId + */ + public String getTaskId() { + return taskId; + } + + /** + * @param taskId the taskId to set + */ + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + /** + * @return the createdTime + */ + public long getCreatedTime() { + return createdTime; + } + + /** + * @param createdTime the createdTime to set + */ + public void setCreatedTime(long createdTime) { + this.createdTime = createdTime; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TaskExecLog that = (TaskExecLog) o; + return createdTime == that.createdTime && Objects.equals(log, that.log) && Objects.equals(taskId, that.taskId); + } + + public int hashCode() { + return Objects.hash(log, taskId, createdTime); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java new file mode 100644 index 000000000..b5489c708 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java @@ -0,0 +1,263 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.tasks; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.apache.commons.lang3.StringUtils; + +/** + * Result of the task execution. + */ +public class TaskResult { + + public enum Status { + + IN_PROGRESS, FAILED, FAILED_WITH_TERMINAL_ERROR, COMPLETED + } + + private String workflowInstanceId; + + private String taskId; + + private String reasonForIncompletion; + + private long callbackAfterSeconds; + + private String workerId; + + private Status status; + + private Map outputData = new HashMap<>(); + + private List logs = new CopyOnWriteArrayList<>(); + + private String externalOutputPayloadStoragePath; + + private String subWorkflowId; + + private boolean extendLease; + + public TaskResult(Task task) { + this.workflowInstanceId = task.getWorkflowInstanceId(); + this.taskId = task.getTaskId(); + this.reasonForIncompletion = task.getReasonForIncompletion(); + this.callbackAfterSeconds = task.getCallbackAfterSeconds(); + this.workerId = task.getWorkerId(); + this.outputData = task.getOutputData(); + this.externalOutputPayloadStoragePath = task.getExternalOutputPayloadStoragePath(); + this.subWorkflowId = task.getSubWorkflowId(); + switch(task.getStatus()) { + case CANCELED: + case COMPLETED_WITH_ERRORS: + case TIMED_OUT: + case SKIPPED: + this.status = Status.FAILED; + break; + case SCHEDULED: + this.status = Status.IN_PROGRESS; + break; + default: + this.status = Status.valueOf(task.getStatus().name()); + break; + } + } + + public TaskResult() { + } + + /** + * @return Workflow instance id for which the task result is produced + */ + public String getWorkflowInstanceId() { + return workflowInstanceId; + } + + public void setWorkflowInstanceId(String workflowInstanceId) { + this.workflowInstanceId = workflowInstanceId; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public String getReasonForIncompletion() { + return reasonForIncompletion; + } + + public void setReasonForIncompletion(String reasonForIncompletion) { + this.reasonForIncompletion = StringUtils.substring(reasonForIncompletion, 0, 500); + } + + public long getCallbackAfterSeconds() { + return callbackAfterSeconds; + } + + /** + * When set to non-zero values, the task remains in the queue for the specified seconds before + * sent back to the worker when polled. Useful for the long running task, where the task is + * updated as IN_PROGRESS and should not be polled out of the queue for a specified amount of + * time. (delayed queue implementation) + * + * @param callbackAfterSeconds Amount of time in seconds the task should be held in the queue + * before giving it to a polling worker. + */ + public void setCallbackAfterSeconds(long callbackAfterSeconds) { + this.callbackAfterSeconds = callbackAfterSeconds; + } + + public String getWorkerId() { + return workerId; + } + + /** + * @param workerId a free form string identifying the worker host. Could be hostname, IP Address + * or any other meaningful identifier that can help identify the host/process which executed + * the task, in case of troubleshooting. + */ + public void setWorkerId(String workerId) { + this.workerId = workerId; + } + + /** + * @return the status + */ + public Status getStatus() { + return status; + } + + /** + * @param status Status of the task + *

IN_PROGRESS: Use this for long running tasks, indicating the task is still in + * progress and should be checked again at a later time. e.g. the worker checks the status + * of the job in the DB, while the job is being executed by another process. + *

FAILED, FAILED_WITH_TERMINAL_ERROR, COMPLETED: Terminal statuses for the task. + * Use FAILED_WITH_TERMINAL_ERROR when you do not want the task to be retried. + * @see #setCallbackAfterSeconds(long) + */ + public void setStatus(Status status) { + this.status = status; + } + + public Map getOutputData() { + return outputData; + } + + /** + * @param outputData output data to be set for the task execution result + */ + public void setOutputData(Map outputData) { + this.outputData = outputData; + } + + /** + * Adds output + * + * @param key output field + * @param value value + * @return current instance + */ + public TaskResult addOutputData(String key, Object value) { + this.outputData.put(key, value); + return this; + } + + /** + * @return Task execution logs + */ + public List getLogs() { + return logs; + } + + /** + * @param logs Task execution logs + */ + public void setLogs(List logs) { + this.logs = logs; + } + + /** + * @param log Log line to be added + * @return Instance of TaskResult + */ + public TaskResult log(String log) { + this.logs.add(new TaskExecLog(log)); + return this; + } + + /** + * @return the path where the task output is stored in external storage + */ + public String getExternalOutputPayloadStoragePath() { + return externalOutputPayloadStoragePath; + } + + /** + * @param externalOutputPayloadStoragePath path in the external storage where the task output is + * stored + */ + public void setExternalOutputPayloadStoragePath(String externalOutputPayloadStoragePath) { + this.externalOutputPayloadStoragePath = externalOutputPayloadStoragePath; + } + + public String getSubWorkflowId() { + return subWorkflowId; + } + + public void setSubWorkflowId(String subWorkflowId) { + this.subWorkflowId = subWorkflowId; + } + + public boolean isExtendLease() { + return extendLease; + } + + public void setExtendLease(boolean extendLease) { + this.extendLease = extendLease; + } + + public String toString() { + return "TaskResult{" + "workflowInstanceId='" + workflowInstanceId + '\'' + ", taskId='" + taskId + '\'' + ", reasonForIncompletion='" + reasonForIncompletion + '\'' + ", callbackAfterSeconds=" + callbackAfterSeconds + ", workerId='" + workerId + '\'' + ", status=" + status + ", outputData=" + outputData + ", logs=" + logs + ", externalOutputPayloadStoragePath='" + externalOutputPayloadStoragePath + '\'' + ", subWorkflowId='" + subWorkflowId + '\'' + ", extendLease='" + extendLease + '\'' + '}'; + } + + public static TaskResult complete() { + return newTaskResult(Status.COMPLETED); + } + + public static TaskResult failed() { + return newTaskResult(Status.FAILED); + } + + public static TaskResult failed(String failureReason) { + TaskResult result = newTaskResult(Status.FAILED); + result.setReasonForIncompletion(failureReason); + return result; + } + + public static TaskResult inProgress() { + return newTaskResult(Status.IN_PROGRESS); + } + + public static TaskResult newTaskResult(Status status) { + TaskResult result = new TaskResult(); + result.setStatus(status); + return result; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java new file mode 100644 index 000000000..4fd0fa004 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java @@ -0,0 +1,126 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.tasks; + +import java.util.HashSet; +import java.util.Set; + +public enum TaskType { + + SIMPLE, + DYNAMIC, + FORK_JOIN, + FORK_JOIN_DYNAMIC, + DECISION, + SWITCH, + JOIN, + DO_WHILE, + SUB_WORKFLOW, + START_WORKFLOW, + EVENT, + WAIT, + HUMAN, + USER_DEFINED, + HTTP, + LAMBDA, + INLINE, + EXCLUSIVE_JOIN, + TERMINATE, + KAFKA_PUBLISH, + JSON_JQ_TRANSFORM, + SET_VARIABLE, + NOOP; + + /** + * TaskType constants representing each of the possible enumeration values. Motivation: to not + * have any hardcoded/inline strings used in the code. + */ + public static final String TASK_TYPE_DECISION = "DECISION"; + + public static final String TASK_TYPE_SWITCH = "SWITCH"; + + public static final String TASK_TYPE_DYNAMIC = "DYNAMIC"; + + public static final String TASK_TYPE_JOIN = "JOIN"; + + public static final String TASK_TYPE_DO_WHILE = "DO_WHILE"; + + public static final String TASK_TYPE_FORK_JOIN_DYNAMIC = "FORK_JOIN_DYNAMIC"; + + public static final String TASK_TYPE_EVENT = "EVENT"; + + public static final String TASK_TYPE_WAIT = "WAIT"; + + public static final String TASK_TYPE_HUMAN = "HUMAN"; + + public static final String TASK_TYPE_SUB_WORKFLOW = "SUB_WORKFLOW"; + + public static final String TASK_TYPE_START_WORKFLOW = "START_WORKFLOW"; + + public static final String TASK_TYPE_FORK_JOIN = "FORK_JOIN"; + + public static final String TASK_TYPE_SIMPLE = "SIMPLE"; + + public static final String TASK_TYPE_HTTP = "HTTP"; + + public static final String TASK_TYPE_LAMBDA = "LAMBDA"; + + public static final String TASK_TYPE_INLINE = "INLINE"; + + public static final String TASK_TYPE_EXCLUSIVE_JOIN = "EXCLUSIVE_JOIN"; + + public static final String TASK_TYPE_TERMINATE = "TERMINATE"; + + public static final String TASK_TYPE_KAFKA_PUBLISH = "KAFKA_PUBLISH"; + + public static final String TASK_TYPE_JSON_JQ_TRANSFORM = "JSON_JQ_TRANSFORM"; + + public static final String TASK_TYPE_SET_VARIABLE = "SET_VARIABLE"; + + public static final String TASK_TYPE_FORK = "FORK"; + + public static final String TASK_TYPE_NOOP = "NOOP"; + + private static final Set BUILT_IN_TASKS = new HashSet<>(); + + static { + BUILT_IN_TASKS.add(TASK_TYPE_DECISION); + BUILT_IN_TASKS.add(TASK_TYPE_SWITCH); + BUILT_IN_TASKS.add(TASK_TYPE_FORK); + BUILT_IN_TASKS.add(TASK_TYPE_JOIN); + BUILT_IN_TASKS.add(TASK_TYPE_EXCLUSIVE_JOIN); + BUILT_IN_TASKS.add(TASK_TYPE_DO_WHILE); + } + + /** + * Converts a task type string to {@link TaskType}. For an unknown string, the value is + * defaulted to {@link TaskType#USER_DEFINED}. + * + *

NOTE: Use {@link Enum#valueOf(Class, String)} if the default of USER_DEFINED is not + * necessary. + * + * @param taskType The task type string. + * @return The {@link TaskType} enum. + */ + public static TaskType of(String taskType) { + try { + return TaskType.valueOf(taskType); + } catch (IllegalArgumentException iae) { + return TaskType.USER_DEFINED; + } + } + + public static boolean isBuiltIn(String taskType) { + return BUILT_IN_TASKS.contains(taskType); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java new file mode 100644 index 000000000..8a441a75a --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java @@ -0,0 +1,91 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.workflow; + +import java.util.HashMap; +import java.util.Map; + +import com.netflix.conductor.common.metadata.tasks.TaskType; + +public class DynamicForkJoinTask { + + private String taskName; + + private String workflowName; + + private String referenceName; + + private Map input = new HashMap<>(); + + private String type = TaskType.SIMPLE.name(); + + public DynamicForkJoinTask() { + } + + public DynamicForkJoinTask(String taskName, String workflowName, String referenceName, Map input) { + super(); + this.taskName = taskName; + this.workflowName = workflowName; + this.referenceName = referenceName; + this.input = input; + } + + public DynamicForkJoinTask(String taskName, String workflowName, String referenceName, String type, Map input) { + super(); + this.taskName = taskName; + this.workflowName = workflowName; + this.referenceName = referenceName; + this.input = input; + this.type = type; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getWorkflowName() { + return workflowName; + } + + public void setWorkflowName(String workflowName) { + this.workflowName = workflowName; + } + + public String getReferenceName() { + return referenceName; + } + + public void setReferenceName(String referenceName) { + this.referenceName = referenceName; + } + + public Map getInput() { + return input; + } + + public void setInput(Map input) { + this.input = input; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java new file mode 100644 index 000000000..b1fa27ac0 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java @@ -0,0 +1,38 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.workflow; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class DynamicForkJoinTaskList { + + private List dynamicTasks = new ArrayList<>(); + + public void add(String taskName, String workflowName, String referenceName, Map input) { + dynamicTasks.add(new DynamicForkJoinTask(taskName, workflowName, referenceName, input)); + } + + public void add(DynamicForkJoinTask dtask) { + dynamicTasks.add(dtask); + } + + public List getDynamicTasks() { + return dynamicTasks; + } + + public void setDynamicTasks(List dynamicTasks) { + this.dynamicTasks = dynamicTasks; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/IdempotencyStrategy.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/IdempotencyStrategy.java new file mode 100644 index 000000000..2449dd320 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/IdempotencyStrategy.java @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.workflow; + +public enum IdempotencyStrategy { + + FAIL, RETURN_EXISTING +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RateLimitConfig.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RateLimitConfig.java new file mode 100644 index 000000000..a27d91752 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RateLimitConfig.java @@ -0,0 +1,46 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.workflow; + +/** + * Rate limit configuration for workflows + */ +public class RateLimitConfig { + + /** + * Key that defines the rate limit. Rate limit key is a combination of workflow payload such as + * name, or correlationId etc. + */ + private String rateLimitKey; + + /** + * Number of concurrently running workflows that are allowed per key + */ + private int concurrentExecLimit; + + public String getRateLimitKey() { + return rateLimitKey; + } + + public void setRateLimitKey(String rateLimitKey) { + this.rateLimitKey = rateLimitKey; + } + + public int getConcurrentExecLimit() { + return concurrentExecLimit; + } + + public void setConcurrentExecLimit(int concurrentExecLimit) { + this.concurrentExecLimit = concurrentExecLimit; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java new file mode 100644 index 000000000..cf762983b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.workflow; + +import java.util.Map; + +public class RerunWorkflowRequest { + + private String reRunFromWorkflowId; + + private Map workflowInput; + + private String reRunFromTaskId; + + private Map taskInput; + + private String correlationId; + + public String getReRunFromWorkflowId() { + return reRunFromWorkflowId; + } + + public void setReRunFromWorkflowId(String reRunFromWorkflowId) { + this.reRunFromWorkflowId = reRunFromWorkflowId; + } + + public Map getWorkflowInput() { + return workflowInput; + } + + public void setWorkflowInput(Map workflowInput) { + this.workflowInput = workflowInput; + } + + public String getReRunFromTaskId() { + return reRunFromTaskId; + } + + public void setReRunFromTaskId(String reRunFromTaskId) { + this.reRunFromTaskId = reRunFromTaskId; + } + + public Map getTaskInput() { + return taskInput; + } + + public void setTaskInput(Map taskInput) { + this.taskInput = taskInput; + } + + public String getCorrelationId() { + return correlationId; + } + + public void setCorrelationId(String correlationId) { + this.correlationId = correlationId; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java new file mode 100644 index 000000000..abfc841ec --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.workflow; + +import java.util.Map; + +public class SkipTaskRequest { + + private Map taskInput; + + private Map taskOutput; + + public Map getTaskInput() { + return taskInput; + } + + public void setTaskInput(Map taskInput) { + this.taskInput = taskInput; + } + + public Map getTaskOutput() { + return taskOutput; + } + + public void setTaskOutput(Map taskOutput) { + this.taskOutput = taskOutput; + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java new file mode 100644 index 000000000..5ca2ce001 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java @@ -0,0 +1,174 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.workflow; + +import java.util.HashMap; +import java.util.Map; + +public class StartWorkflowRequest { + + private String name; + + private Integer version; + + private String correlationId; + + private Map input = new HashMap<>(); + + private Map taskToDomain = new HashMap<>(); + + private WorkflowDef workflowDef; + + private String externalInputPayloadStoragePath; + + private Integer priority = 0; + + private String createdBy; + + private String idempotencyKey; + + private IdempotencyStrategy idempotencyStrategy; + + public String getIdempotencyKey() { + return idempotencyKey; + } + + public void setIdempotencyKey(String idempotencyKey) { + this.idempotencyKey = idempotencyKey; + } + + public IdempotencyStrategy getIdempotencyStrategy() { + return idempotencyStrategy; + } + + public void setIdempotencyStrategy(IdempotencyStrategy idempotencyStrategy) { + this.idempotencyStrategy = idempotencyStrategy; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public StartWorkflowRequest withName(String name) { + this.name = name; + return this; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public StartWorkflowRequest withVersion(Integer version) { + this.version = version; + return this; + } + + public String getCorrelationId() { + return correlationId; + } + + public void setCorrelationId(String correlationId) { + this.correlationId = correlationId; + } + + public StartWorkflowRequest withCorrelationId(String correlationId) { + this.correlationId = correlationId; + return this; + } + + public String getExternalInputPayloadStoragePath() { + return externalInputPayloadStoragePath; + } + + public void setExternalInputPayloadStoragePath(String externalInputPayloadStoragePath) { + this.externalInputPayloadStoragePath = externalInputPayloadStoragePath; + } + + public StartWorkflowRequest withExternalInputPayloadStoragePath(String externalInputPayloadStoragePath) { + this.externalInputPayloadStoragePath = externalInputPayloadStoragePath; + return this; + } + + public Integer getPriority() { + return priority; + } + + public void setPriority(Integer priority) { + this.priority = priority; + } + + public StartWorkflowRequest withPriority(Integer priority) { + this.priority = priority; + return this; + } + + public Map getInput() { + return input; + } + + public void setInput(Map input) { + this.input = input; + } + + public StartWorkflowRequest withInput(Map input) { + this.input = input; + return this; + } + + public Map getTaskToDomain() { + return taskToDomain; + } + + public void setTaskToDomain(Map taskToDomain) { + this.taskToDomain = taskToDomain; + } + + public StartWorkflowRequest withTaskToDomain(Map taskToDomain) { + this.taskToDomain = taskToDomain; + return this; + } + + public WorkflowDef getWorkflowDef() { + return workflowDef; + } + + public void setWorkflowDef(WorkflowDef workflowDef) { + this.workflowDef = workflowDef; + } + + public StartWorkflowRequest withWorkflowDef(WorkflowDef workflowDef) { + this.workflowDef = workflowDef; + return this; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public StartWorkflowRequest withCreatedBy(String createdBy) { + this.createdBy = createdBy; + return this; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StateChangeEvent.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StateChangeEvent.java new file mode 100644 index 000000000..16f1a4fb8 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StateChangeEvent.java @@ -0,0 +1,42 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.workflow; + +import java.util.Map; + +public class StateChangeEvent { + + private String type; + + private Map payload; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Map getPayload() { + return payload; + } + + public void setPayload(Map payload) { + this.payload = payload; + } + + public String toString() { + return "StateChangeEvent{" + "type='" + type + '\'' + ", payload=" + payload + '}'; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java new file mode 100644 index 000000000..73c800afa --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java @@ -0,0 +1,153 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.workflow; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; + +import com.netflix.conductor.common.utils.TaskUtils; + +public class SubWorkflowParams { + + private String name; + + private Integer version; + + private Map taskToDomain; + + // workaround as WorkflowDef cannot directly be used due to cyclic dependency issue in protobuf + // imports + private Object workflowDefinition; + + private String idempotencyKey; + + private IdempotencyStrategy idempotencyStrategy; + + public String getIdempotencyKey() { + return idempotencyKey; + } + + public void setIdempotencyKey(String idempotencyKey) { + this.idempotencyKey = idempotencyKey; + } + + public IdempotencyStrategy getIdempotencyStrategy() { + return idempotencyStrategy; + } + + public void setIdempotencyStrategy(IdempotencyStrategy idempotencyStrategy) { + this.idempotencyStrategy = idempotencyStrategy; + } + + /** + * @return the name + */ + public String getName() { + if (workflowDefinition != null) { + if (workflowDefinition instanceof WorkflowDef) { + return ((WorkflowDef) workflowDefinition).getName(); + } + } + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the version + */ + public Integer getVersion() { + if (workflowDefinition != null) { + if (workflowDefinition instanceof WorkflowDef) { + return ((WorkflowDef) workflowDefinition).getVersion(); + } + } + return version; + } + + /** + * @param version the version to set + */ + public void setVersion(Integer version) { + this.version = version; + } + + /** + * @return the taskToDomain + */ + public Map getTaskToDomain() { + return taskToDomain; + } + + /** + * @param taskToDomain the taskToDomain to set + */ + public void setTaskToDomain(Map taskToDomain) { + this.taskToDomain = taskToDomain; + } + + /** + * @return the workflowDefinition as an Object + */ + public Object getWorkflowDefinition() { + return workflowDefinition; + } + + @Deprecated + public void setWorkflowDef(WorkflowDef workflowDef) { + this.setWorkflowDefinition(workflowDef); + } + + @Deprecated + public WorkflowDef getWorkflowDef() { + return (WorkflowDef) workflowDefinition; + } + + /** + * @param workflowDef the workflowDefinition to set + */ + public void setWorkflowDefinition(Object workflowDef) { + if (workflowDef == null) { + this.workflowDefinition = null; + } else if (workflowDef instanceof WorkflowDef) { + this.workflowDefinition = workflowDef; + } else if (workflowDef instanceof String) { + if (!(((String) workflowDef).startsWith("${")) || !(((String) workflowDef).endsWith("}"))) { + throw new IllegalArgumentException("workflowDefinition is a string, but not a valid DSL string"); + } else { + this.workflowDefinition = workflowDef; + } + } else if (workflowDef instanceof LinkedHashMap) { + this.workflowDefinition = TaskUtils.convertToWorkflowDef(workflowDef); + } else { + throw new IllegalArgumentException("workflowDefinition must be either null, or WorkflowDef, or a valid DSL string"); + } + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SubWorkflowParams that = (SubWorkflowParams) o; + return Objects.equals(getName(), that.getName()) && Objects.equals(getVersion(), that.getVersion()) && Objects.equals(getTaskToDomain(), that.getTaskToDomain()) && Objects.equals(getWorkflowDefinition(), that.getWorkflowDefinition()); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/UpgradeWorkflowRequest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/UpgradeWorkflowRequest.java new file mode 100644 index 000000000..4ab9ba928 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/UpgradeWorkflowRequest.java @@ -0,0 +1,58 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.workflow; + +import java.util.Map; + +public class UpgradeWorkflowRequest { + + public Map getTaskOutput() { + return taskOutput; + } + + public void setTaskOutput(Map taskOutput) { + this.taskOutput = taskOutput; + } + + public Map getWorkflowInput() { + return workflowInput; + } + + public void setWorkflowInput(Map workflowInput) { + this.workflowInput = workflowInput; + } + + private Map taskOutput; + + private Map workflowInput; + + private Integer version; + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java new file mode 100644 index 000000000..b4afd450b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java @@ -0,0 +1,392 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.workflow; + +import java.util.*; + +import com.netflix.conductor.common.metadata.Auditable; +import com.netflix.conductor.common.metadata.SchemaDef; +import com.netflix.conductor.common.metadata.tasks.TaskType; + +public class WorkflowDef extends Auditable { + + public enum TimeoutPolicy { + + TIME_OUT_WF, ALERT_ONLY + } + + private String name; + + private String description; + + private int version = 1; + + private List tasks = new LinkedList<>(); + + private List inputParameters = new LinkedList<>(); + + private Map outputParameters = new HashMap<>(); + + private String failureWorkflow; + + private int schemaVersion = 2; + + // By default a workflow is restartable + private boolean restartable = true; + + private boolean workflowStatusListenerEnabled = false; + + private String ownerEmail; + + private TimeoutPolicy timeoutPolicy = TimeoutPolicy.ALERT_ONLY; + + private long timeoutSeconds; + + private Map variables = new HashMap<>(); + + private Map inputTemplate = new HashMap<>(); + + private String workflowStatusListenerSink; + + private RateLimitConfig rateLimitConfig; + + private SchemaDef inputSchema; + + private SchemaDef outputSchema; + + private boolean enforceSchema = true; + + public boolean isEnforceSchema() { + return enforceSchema; + } + + public void setEnforceSchema(boolean enforceSchema) { + this.enforceSchema = enforceSchema; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * @param description the description to set + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * @return the tasks + */ + public List getTasks() { + return tasks; + } + + /** + * @param tasks the tasks to set + */ + public void setTasks(List tasks) { + this.tasks = tasks; + } + + /** + * @return the inputParameters + */ + public List getInputParameters() { + return inputParameters; + } + + /** + * @param inputParameters the inputParameters to set + */ + public void setInputParameters(List inputParameters) { + this.inputParameters = inputParameters; + } + + /** + * @return the outputParameters + */ + public Map getOutputParameters() { + return outputParameters; + } + + /** + * @param outputParameters the outputParameters to set + */ + public void setOutputParameters(Map outputParameters) { + this.outputParameters = outputParameters; + } + + /** + * @return the version + */ + public int getVersion() { + return version; + } + + /** + * @return the failureWorkflow + */ + public String getFailureWorkflow() { + return failureWorkflow; + } + + /** + * @param failureWorkflow the failureWorkflow to set + */ + public void setFailureWorkflow(String failureWorkflow) { + this.failureWorkflow = failureWorkflow; + } + + /** + * @param version the version to set + */ + public void setVersion(int version) { + this.version = version; + } + + /** + * This method determines if the workflow is restartable or not + * + * @return true: if the workflow is restartable false: if the workflow is non restartable + */ + public boolean isRestartable() { + return restartable; + } + + /** + * This method is called only when the workflow definition is created + * + * @param restartable true: if the workflow is restartable false: if the workflow is non + * restartable + */ + public void setRestartable(boolean restartable) { + this.restartable = restartable; + } + + /** + * @return the schemaVersion + */ + public int getSchemaVersion() { + return schemaVersion; + } + + /** + * @param schemaVersion the schemaVersion to set + */ + public void setSchemaVersion(int schemaVersion) { + this.schemaVersion = schemaVersion; + } + + /** + * @return true is workflow listener will be invoked when workflow gets into a terminal state + */ + public boolean isWorkflowStatusListenerEnabled() { + return workflowStatusListenerEnabled; + } + + /** + * Specify if workflow listener is enabled to invoke a callback for completed or terminated + * workflows + * + * @param workflowStatusListenerEnabled + */ + public void setWorkflowStatusListenerEnabled(boolean workflowStatusListenerEnabled) { + this.workflowStatusListenerEnabled = workflowStatusListenerEnabled; + } + + /** + * @return the email of the owner of this workflow definition + */ + public String getOwnerEmail() { + return ownerEmail; + } + + /** + * @param ownerEmail the owner email to set + */ + public void setOwnerEmail(String ownerEmail) { + this.ownerEmail = ownerEmail; + } + + /** + * @return the timeoutPolicy + */ + public TimeoutPolicy getTimeoutPolicy() { + return timeoutPolicy; + } + + /** + * @param timeoutPolicy the timeoutPolicy to set + */ + public void setTimeoutPolicy(TimeoutPolicy timeoutPolicy) { + this.timeoutPolicy = timeoutPolicy; + } + + /** + * @return the time after which a workflow is deemed to have timed out + */ + public long getTimeoutSeconds() { + return timeoutSeconds; + } + + /** + * @param timeoutSeconds the timeout in seconds to set + */ + public void setTimeoutSeconds(long timeoutSeconds) { + this.timeoutSeconds = timeoutSeconds; + } + + /** + * @return the global workflow variables + */ + public Map getVariables() { + return variables; + } + + /** + * @param variables the set of global workflow variables to set + */ + public void setVariables(Map variables) { + this.variables = variables; + } + + public Map getInputTemplate() { + return inputTemplate; + } + + public void setInputTemplate(Map inputTemplate) { + this.inputTemplate = inputTemplate; + } + + public String key() { + return getKey(name, version); + } + + public static String getKey(String name, int version) { + return name + "." + version; + } + + public String getWorkflowStatusListenerSink() { + return workflowStatusListenerSink; + } + + public void setWorkflowStatusListenerSink(String workflowStatusListenerSink) { + this.workflowStatusListenerSink = workflowStatusListenerSink; + } + + public RateLimitConfig getRateLimitConfig() { + return rateLimitConfig; + } + + public void setRateLimitConfig(RateLimitConfig rateLimitConfig) { + this.rateLimitConfig = rateLimitConfig; + } + + public SchemaDef getInputSchema() { + return inputSchema; + } + + public void setInputSchema(SchemaDef inputSchema) { + this.inputSchema = inputSchema; + } + + public SchemaDef getOutputSchema() { + return outputSchema; + } + + public void setOutputSchema(SchemaDef outputSchema) { + this.outputSchema = outputSchema; + } + + public boolean containsType(String taskType) { + return collectTasks().stream().anyMatch(t -> t.getType().equals(taskType)); + } + + public WorkflowTask getNextTask(String taskReferenceName) { + WorkflowTask workflowTask = getTaskByRefName(taskReferenceName); + if (workflowTask != null && TaskType.TERMINATE.name().equals(workflowTask.getType())) { + return null; + } + Iterator iterator = tasks.iterator(); + while (iterator.hasNext()) { + WorkflowTask task = iterator.next(); + if (task.getTaskReferenceName().equals(taskReferenceName)) { + // If taskReferenceName matches, break out + break; + } + WorkflowTask nextTask = task.next(taskReferenceName, null); + if (nextTask != null) { + return nextTask; + } else if (TaskType.DO_WHILE.name().equals(task.getType()) && !task.getTaskReferenceName().equals(taskReferenceName) && task.has(taskReferenceName)) { + // If the task is child of Loop Task and at last position, return null. + return null; + } + if (task.has(taskReferenceName)) { + break; + } + } + if (iterator.hasNext()) { + return iterator.next(); + } + return null; + } + + public WorkflowTask getTaskByRefName(String taskReferenceName) { + return collectTasks().stream().filter(workflowTask -> workflowTask.getTaskReferenceName().equals(taskReferenceName)).findFirst().orElse(null); + } + + public List collectTasks() { + List tasks = new LinkedList<>(); + for (WorkflowTask workflowTask : this.tasks) { + tasks.addAll(workflowTask.collectTasks()); + } + return tasks; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + WorkflowDef that = (WorkflowDef) o; + return version == that.version && Objects.equals(name, that.name); + } + + public int hashCode() { + return Objects.hash(name, version); + } + + public String toString() { + return "WorkflowDef{" + "name='" + name + '\'' + ", description='" + description + '\'' + ", version=" + version + ", tasks=" + tasks + ", inputParameters=" + inputParameters + ", outputParameters=" + outputParameters + ", failureWorkflow='" + failureWorkflow + '\'' + ", schemaVersion=" + schemaVersion + ", restartable=" + restartable + ", workflowStatusListenerEnabled=" + workflowStatusListenerEnabled + ", ownerEmail='" + ownerEmail + '\'' + ", timeoutPolicy=" + timeoutPolicy + ", timeoutSeconds=" + timeoutSeconds + ", variables=" + variables + ", inputTemplate=" + inputTemplate + ", workflowStatusListenerSink='" + workflowStatusListenerSink + '\'' + ", rateLimitConfig=" + rateLimitConfig + ", inputSchema=" + inputSchema + ", outputSchema=" + outputSchema + ", enforceSchema=" + enforceSchema + '}'; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java new file mode 100644 index 000000000..8f4720095 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java @@ -0,0 +1,85 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.workflow; + +import java.util.Objects; + +public class WorkflowDefSummary implements Comparable { + + private String name; + + private int version = 1; + + private Long createTime; + + /** + * @return the version + */ + public int getVersion() { + return version; + } + + /** + * @return the workflow name + */ + public String getName() { + return name; + } + + /** + * @return the createTime + */ + public Long getCreateTime() { + return createTime; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + WorkflowDefSummary that = (WorkflowDefSummary) o; + return getVersion() == that.getVersion() && Objects.equals(getName(), that.getName()); + } + + public void setName(String name) { + this.name = name; + } + + public void setVersion(int version) { + this.version = version; + } + + public void setCreateTime(Long createTime) { + this.createTime = createTime; + } + + public int hashCode() { + return Objects.hash(getName(), getVersion()); + } + + public String toString() { + return "WorkflowDef{name='" + name + ", version=" + version + "}"; + } + + public int compareTo(WorkflowDefSummary o) { + int res = this.name.compareTo(o.name); + if (res != 0) { + return res; + } + res = Integer.compare(this.version, o.version); + return res; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java new file mode 100644 index 000000000..338e6e302 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java @@ -0,0 +1,729 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.metadata.workflow; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskType; + +/** + * This is the task definition definied as part of the {@link WorkflowDef}. The tasks definied in + * the Workflow definition are saved as part of {@link WorkflowDef#getTasks} + */ +public class WorkflowTask { + + public static class CacheConfig { + + private String key; + + private int ttlInSecond; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public int getTtlInSecond() { + return ttlInSecond; + } + + public void setTtlInSecond(int ttlInSecond) { + this.ttlInSecond = ttlInSecond; + } + } + + private String name; + + private String taskReferenceName; + + private String description; + + private Map inputParameters = new HashMap<>(); + + private String type = TaskType.SIMPLE.name(); + + private String dynamicTaskNameParam; + + private String caseValueParam; + + private String caseExpression; + + private String scriptExpression; + + public static class WorkflowTaskList { + + public List getTasks() { + return tasks; + } + + public void setTasks(List tasks) { + this.tasks = tasks; + } + + private List tasks; + } + + // Populates for the tasks of the decision type + private Map> decisionCases = new LinkedHashMap<>(); + + private String dynamicForkJoinTasksParam; + + private String dynamicForkTasksParam; + + private String dynamicForkTasksInputParamName; + + private List defaultCase = new LinkedList<>(); + + private List> forkTasks = new LinkedList<>(); + + private int // No. of seconds (at-least) to wait before starting a task. + startDelay; + + private SubWorkflowParams subWorkflowParam; + + private List joinOn = new LinkedList<>(); + + private String sink; + + private boolean optional = false; + + private TaskDef taskDefinition; + + private Boolean rateLimited; + + private List defaultExclusiveJoinTask = new LinkedList<>(); + + private Boolean asyncComplete = false; + + private String loopCondition; + + private List loopOver = new LinkedList<>(); + + private Integer retryCount; + + private String evaluatorType; + + private String expression; + + /* + Map of events to be emitted when the task status changed. + key can be comma separated values of the status changes prefixed with "on" + */ + // @ProtoField(id = 29) + private Map> onStateChange = new HashMap<>(); + + private String joinStatus; + + private CacheConfig cacheConfig; + + private boolean permissive; + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the taskReferenceName + */ + public String getTaskReferenceName() { + return taskReferenceName; + } + + /** + * @param taskReferenceName the taskReferenceName to set + */ + public void setTaskReferenceName(String taskReferenceName) { + this.taskReferenceName = taskReferenceName; + } + + /** + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * @param description the description to set + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * @return the inputParameters + */ + public Map getInputParameters() { + return inputParameters; + } + + /** + * @param inputParameters the inputParameters to set + */ + public void setInputParameters(Map inputParameters) { + this.inputParameters = inputParameters; + } + + /** + * @return the type + */ + public String getType() { + return type; + } + + public void setWorkflowTaskType(TaskType type) { + this.type = type.name(); + } + + /** + * @param type the type to set + */ + public void setType(String type) { + this.type = type; + } + + /** + * @return the decisionCases + */ + public Map> getDecisionCases() { + return decisionCases; + } + + /** + * @param decisionCases the decisionCases to set + */ + public void setDecisionCases(Map> decisionCases) { + this.decisionCases = decisionCases; + } + + /** + * @return the defaultCase + */ + public List getDefaultCase() { + return defaultCase; + } + + /** + * @param defaultCase the defaultCase to set + */ + public void setDefaultCase(List defaultCase) { + this.defaultCase = defaultCase; + } + + /** + * @return the forkTasks + */ + public List> getForkTasks() { + return forkTasks; + } + + /** + * @param forkTasks the forkTasks to set + */ + public void setForkTasks(List> forkTasks) { + this.forkTasks = forkTasks; + } + + /** + * @return the startDelay in seconds + */ + public int getStartDelay() { + return startDelay; + } + + /** + * @param startDelay the startDelay to set + */ + public void setStartDelay(int startDelay) { + this.startDelay = startDelay; + } + + /** + * @return the retryCount + */ + public Integer getRetryCount() { + return retryCount; + } + + /** + * @param retryCount the retryCount to set + */ + public void setRetryCount(final Integer retryCount) { + this.retryCount = retryCount; + } + + /** + * @return the dynamicTaskNameParam + */ + public String getDynamicTaskNameParam() { + return dynamicTaskNameParam; + } + + /** + * @param dynamicTaskNameParam the dynamicTaskNameParam to set to be used by DYNAMIC tasks + */ + public void setDynamicTaskNameParam(String dynamicTaskNameParam) { + this.dynamicTaskNameParam = dynamicTaskNameParam; + } + + /** + * @deprecated Use {@link WorkflowTask#getEvaluatorType()} and {@link + * WorkflowTask#getExpression()} combination. + * @return the caseValueParam + */ + @Deprecated + public String getCaseValueParam() { + return caseValueParam; + } + + @Deprecated + public String getDynamicForkJoinTasksParam() { + return dynamicForkJoinTasksParam; + } + + @Deprecated + public void setDynamicForkJoinTasksParam(String dynamicForkJoinTasksParam) { + this.dynamicForkJoinTasksParam = dynamicForkJoinTasksParam; + } + + public String getDynamicForkTasksParam() { + return dynamicForkTasksParam; + } + + public void setDynamicForkTasksParam(String dynamicForkTasksParam) { + this.dynamicForkTasksParam = dynamicForkTasksParam; + } + + public String getDynamicForkTasksInputParamName() { + return dynamicForkTasksInputParamName; + } + + public void setDynamicForkTasksInputParamName(String dynamicForkTasksInputParamName) { + this.dynamicForkTasksInputParamName = dynamicForkTasksInputParamName; + } + + /** + * @param caseValueParam the caseValueParam to set + * @deprecated Use {@link WorkflowTask#getEvaluatorType()} and {@link + * WorkflowTask#getExpression()} combination. + */ + @Deprecated + public void setCaseValueParam(String caseValueParam) { + this.caseValueParam = caseValueParam; + } + + /** + * @return A javascript expression for decision cases. The result should be a scalar value that + * is used to decide the case branches. + * @see #getDecisionCases() + * @deprecated Use {@link WorkflowTask#getEvaluatorType()} and {@link + * WorkflowTask#getExpression()} combination. + */ + @Deprecated + public String getCaseExpression() { + return caseExpression; + } + + /** + * @param caseExpression A javascript expression for decision cases. The result should be a + * scalar value that is used to decide the case branches. + * @deprecated Use {@link WorkflowTask#getEvaluatorType()} and {@link + * WorkflowTask#getExpression()} combination. + */ + @Deprecated + public void setCaseExpression(String caseExpression) { + this.caseExpression = caseExpression; + } + + public String getScriptExpression() { + return scriptExpression; + } + + public void setScriptExpression(String expression) { + this.scriptExpression = expression; + } + + public CacheConfig getCacheConfig() { + return cacheConfig; + } + + public void setCacheConfig(CacheConfig cacheConfig) { + this.cacheConfig = cacheConfig; + } + + /** + * @return the subWorkflow + */ + public SubWorkflowParams getSubWorkflowParam() { + return subWorkflowParam; + } + + /** + * @param subWorkflow the subWorkflowParam to set + */ + public void setSubWorkflowParam(SubWorkflowParams subWorkflow) { + this.subWorkflowParam = subWorkflow; + } + + /** + * @return the joinOn + */ + public List getJoinOn() { + return joinOn; + } + + /** + * @param joinOn the joinOn to set + */ + public void setJoinOn(List joinOn) { + this.joinOn = joinOn; + } + + /** + * @return the loopCondition + */ + public String getLoopCondition() { + return loopCondition; + } + + /** + * @param loopCondition the expression to set + */ + public void setLoopCondition(String loopCondition) { + this.loopCondition = loopCondition; + } + + /** + * @return the loopOver + */ + public List getLoopOver() { + return loopOver; + } + + /** + * @param loopOver the loopOver to set + */ + public void setLoopOver(List loopOver) { + this.loopOver = loopOver; + } + + /** + * @return Sink value for the EVENT type of task + */ + public String getSink() { + return sink; + } + + /** + * @param sink Name of the sink + */ + public void setSink(String sink) { + this.sink = sink; + } + + /** + * @return whether wait for an external event to complete the task, for EVENT and HTTP tasks + */ + public Boolean isAsyncComplete() { + return asyncComplete; + } + + public void setAsyncComplete(Boolean asyncComplete) { + this.asyncComplete = asyncComplete; + } + + /** + * @return If the task is optional. When set to true, the workflow execution continues even when + * the task is in failed status. + */ + public boolean isOptional() { + return optional; + } + + /** + * @return Task definition associated to the Workflow Task + */ + public TaskDef getTaskDefinition() { + return taskDefinition; + } + + /** + * @param taskDefinition Task definition + */ + public void setTaskDefinition(TaskDef taskDefinition) { + this.taskDefinition = taskDefinition; + } + + /** + * @param optional when set to true, the task is marked as optional + */ + public void setOptional(boolean optional) { + this.optional = optional; + } + + public Boolean getRateLimited() { + return rateLimited; + } + + public void setRateLimited(Boolean rateLimited) { + this.rateLimited = rateLimited; + } + + public Boolean isRateLimited() { + return rateLimited != null && rateLimited; + } + + public List getDefaultExclusiveJoinTask() { + return defaultExclusiveJoinTask; + } + + public void setDefaultExclusiveJoinTask(List defaultExclusiveJoinTask) { + this.defaultExclusiveJoinTask = defaultExclusiveJoinTask; + } + + /** + * @return the evaluatorType + */ + public String getEvaluatorType() { + return evaluatorType; + } + + /** + * @param evaluatorType the evaluatorType to set + */ + public void setEvaluatorType(String evaluatorType) { + this.evaluatorType = evaluatorType; + } + + /** + * @return An evaluation expression for switch cases evaluated by corresponding evaluator. The + * result should be a scalar value that is used to decide the case branches. + * @see #getDecisionCases() + */ + public String getExpression() { + return expression; + } + + /** + * @param expression the expression to set + */ + public void setExpression(String expression) { + this.expression = expression; + } + + public String getJoinStatus() { + return joinStatus; + } + + public void setJoinStatus(String joinStatus) { + this.joinStatus = joinStatus; + } + + public boolean isPermissive() { + return permissive; + } + + public void setPermissive(boolean permissive) { + this.permissive = permissive; + } + + private Collection> children() { + Collection> workflowTaskLists = new LinkedList<>(); + switch(TaskType.of(type)) { + case DECISION: + case SWITCH: + workflowTaskLists.addAll(decisionCases.values()); + workflowTaskLists.add(defaultCase); + break; + case FORK_JOIN: + workflowTaskLists.addAll(forkTasks); + break; + case DO_WHILE: + workflowTaskLists.add(loopOver); + break; + default: + break; + } + return workflowTaskLists; + } + + public List collectTasks() { + List tasks = new LinkedList<>(); + tasks.add(this); + for (List workflowTaskList : children()) { + for (WorkflowTask workflowTask : workflowTaskList) { + tasks.addAll(workflowTask.collectTasks()); + } + } + return tasks; + } + + public WorkflowTask next(String taskReferenceName, WorkflowTask parent) { + TaskType taskType = TaskType.of(type); + switch(taskType) { + case DO_WHILE: + case DECISION: + case SWITCH: + for (List workflowTasks : children()) { + Iterator iterator = workflowTasks.iterator(); + while (iterator.hasNext()) { + WorkflowTask task = iterator.next(); + if (task.getTaskReferenceName().equals(taskReferenceName)) { + break; + } + WorkflowTask nextTask = task.next(taskReferenceName, this); + if (nextTask != null) { + return nextTask; + } + if (task.has(taskReferenceName)) { + break; + } + } + if (iterator.hasNext()) { + return iterator.next(); + } + } + if (taskType == TaskType.DO_WHILE && this.has(taskReferenceName)) { + // come here means this is DO_WHILE task and `taskReferenceName` is the last + // task in + // this DO_WHILE task, because DO_WHILE task need to be executed to decide + // whether to + // schedule next iteration, so we just return the DO_WHILE task, and then ignore + // generating this task again in deciderService.getNextTask() + return this; + } + break; + case FORK_JOIN: + boolean found = false; + for (List workflowTasks : children()) { + Iterator iterator = workflowTasks.iterator(); + while (iterator.hasNext()) { + WorkflowTask task = iterator.next(); + if (task.getTaskReferenceName().equals(taskReferenceName)) { + found = true; + break; + } + WorkflowTask nextTask = task.next(taskReferenceName, this); + if (nextTask != null) { + return nextTask; + } + if (task.has(taskReferenceName)) { + break; + } + } + if (iterator.hasNext()) { + return iterator.next(); + } + if (found && parent != null) { + return parent.next(this.taskReferenceName, // we need to return join task... -- get my sibling from my + parent); + // parent.. + } + } + break; + case DYNAMIC: + case TERMINATE: + case SIMPLE: + return null; + default: + break; + } + return null; + } + + public boolean has(String taskReferenceName) { + if (this.getTaskReferenceName().equals(taskReferenceName)) { + return true; + } + switch(TaskType.of(type)) { + case DECISION: + case SWITCH: + case DO_WHILE: + case FORK_JOIN: + for (List childx : children()) { + for (WorkflowTask child : childx) { + if (child.has(taskReferenceName)) { + return true; + } + } + } + break; + default: + break; + } + return false; + } + + public WorkflowTask get(String taskReferenceName) { + if (this.getTaskReferenceName().equals(taskReferenceName)) { + return this; + } + for (List childx : children()) { + for (WorkflowTask child : childx) { + WorkflowTask found = child.get(taskReferenceName); + if (found != null) { + return found; + } + } + } + return null; + } + + public Map> getOnStateChange() { + return onStateChange; + } + + public void setOnStateChange(Map> onStateChange) { + this.onStateChange = onStateChange; + } + + public String toString() { + return name + "/" + taskReferenceName; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + WorkflowTask that = (WorkflowTask) o; + return Objects.equals(name, that.name) && Objects.equals(taskReferenceName, that.taskReferenceName); + } + + public int hashCode() { + return Objects.hash(name, taskReferenceName); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/model/BulkResponse.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/model/BulkResponse.java new file mode 100644 index 000000000..bdba8c471 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/model/BulkResponse.java @@ -0,0 +1,75 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Response object to return a list of succeeded entities and a map of failed ones, including error + * message, for the bulk request. + */ +public class BulkResponse { + + /** + * Key - entityId Value - error message processing this entity + */ + private final Map bulkErrorResults; + + private final List bulkSuccessfulResults; + + private final String message = "Bulk Request has been processed."; + + public BulkResponse() { + this.bulkSuccessfulResults = new ArrayList<>(); + this.bulkErrorResults = new HashMap<>(); + } + + public List getBulkSuccessfulResults() { + return bulkSuccessfulResults; + } + + public Map getBulkErrorResults() { + return bulkErrorResults; + } + + public void appendSuccessResponse(String id) { + bulkSuccessfulResults.add(id); + } + + public void appendFailedResponse(String id, String errorMessage) { + bulkErrorResults.put(id, errorMessage); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BulkResponse)) { + return false; + } + BulkResponse that = (BulkResponse) o; + return Objects.equals(bulkSuccessfulResults, that.bulkSuccessfulResults) && Objects.equals(bulkErrorResults, that.bulkErrorResults); + } + + public int hashCode() { + return Objects.hash(bulkSuccessfulResults, bulkErrorResults, message); + } + + public String toString() { + return "BulkResponse{" + "bulkSuccessfulResults=" + bulkSuccessfulResults + ", bulkErrorResults=" + bulkErrorResults + ", message='" + message + '\'' + '}'; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java new file mode 100644 index 000000000..50a29b760 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java @@ -0,0 +1,50 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.run; + +/** + * Describes the location where the JSON payload is stored in external storage. + * + *

The location is described using the following fields: + * + *

+ */ +public class ExternalStorageLocation { + + private String uri; + + private String path; + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String toString() { + return "ExternalStorageLocation{" + "uri='" + uri + '\'' + ", path='" + path + '\'' + '}'; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/SearchResult.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/SearchResult.java new file mode 100644 index 000000000..921df2559 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/SearchResult.java @@ -0,0 +1,59 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.run; + +import java.util.List; + +public class SearchResult { + + private long totalHits; + + private List results; + + public SearchResult() { + } + + public SearchResult(long totalHits, List results) { + super(); + this.totalHits = totalHits; + this.results = results; + } + + /** + * @return the totalHits + */ + public long getTotalHits() { + return totalHits; + } + + /** + * @return the results + */ + public List getResults() { + return results; + } + + /** + * @param totalHits the totalHits to set + */ + public void setTotalHits(long totalHits) { + this.totalHits = totalHits; + } + + /** + * @param results the results to set + */ + public void setResults(List results) { + this.results = results; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/TaskSummary.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/TaskSummary.java new file mode 100644 index 000000000..51bb783d0 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/TaskSummary.java @@ -0,0 +1,334 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.run; + +import com.netflix.conductor.common.metadata.tasks.Task; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class TaskSummary { + + private String workflowId; + + private String workflowType; + + private String correlationId; + + private String scheduledTime; + + private String startTime; + + private String updateTime; + + private String endTime; + + private Task.Status status; + + private String reasonForIncompletion; + + private long executionTime; + + private long queueWaitTime; + + private String taskDefName; + + private String taskType; + + private String input; + + private String output; + + + private String taskId; + + + private String externalInputPayloadStoragePath; + + + private String externalOutputPayloadStoragePath; + + + private int workflowPriority; + + /** + * @return the workflowId + */ + public String getWorkflowId() { + return workflowId; + } + + /** + * @param workflowId the workflowId to set + */ + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + + /** + * @return the workflowType + */ + public String getWorkflowType() { + return workflowType; + } + + /** + * @param workflowType the workflowType to set + */ + public void setWorkflowType(String workflowType) { + this.workflowType = workflowType; + } + + /** + * @return the correlationId + */ + public String getCorrelationId() { + return correlationId; + } + + /** + * @param correlationId the correlationId to set + */ + public void setCorrelationId(String correlationId) { + this.correlationId = correlationId; + } + + /** + * @return the scheduledTime + */ + public String getScheduledTime() { + return scheduledTime; + } + + /** + * @param scheduledTime the scheduledTime to set + */ + public void setScheduledTime(String scheduledTime) { + this.scheduledTime = scheduledTime; + } + + /** + * @return the startTime + */ + public String getStartTime() { + return startTime; + } + + /** + * @param startTime the startTime to set + */ + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + /** + * @return the updateTime + */ + public String getUpdateTime() { + return updateTime; + } + + /** + * @param updateTime the updateTime to set + */ + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + /** + * @return the endTime + */ + public String getEndTime() { + return endTime; + } + + /** + * @param endTime the endTime to set + */ + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + /** + * @return the status + */ + public Task.Status getStatus() { + return status; + } + + /** + * @param status the status to set + */ + public void setStatus(Task.Status status) { + this.status = status; + } + + /** + * @return the reasonForIncompletion + */ + public String getReasonForIncompletion() { + return reasonForIncompletion; + } + + /** + * @param reasonForIncompletion the reasonForIncompletion to set + */ + public void setReasonForIncompletion(String reasonForIncompletion) { + this.reasonForIncompletion = reasonForIncompletion; + } + + /** + * @return the executionTime + */ + public long getExecutionTime() { + return executionTime; + } + + /** + * @param executionTime the executionTime to set + */ + public void setExecutionTime(long executionTime) { + this.executionTime = executionTime; + } + + /** + * @return the queueWaitTime + */ + public long getQueueWaitTime() { + return queueWaitTime; + } + + /** + * @param queueWaitTime the queueWaitTime to set + */ + public void setQueueWaitTime(long queueWaitTime) { + this.queueWaitTime = queueWaitTime; + } + + /** + * @return the taskDefName + */ + public String getTaskDefName() { + return taskDefName; + } + + /** + * @param taskDefName the taskDefName to set + */ + public void setTaskDefName(String taskDefName) { + this.taskDefName = taskDefName; + } + + /** + * @return the taskType + */ + public String getTaskType() { + return taskType; + } + + /** + * @param taskType the taskType to set + */ + public void setTaskType(String taskType) { + this.taskType = taskType; + } + + /** + * @return input to the task + */ + public String getInput() { + return input; + } + + /** + * @param input input to the task + */ + public void setInput(String input) { + this.input = input; + } + + /** + * @return output of the task + */ + public String getOutput() { + return output; + } + + /** + * @param output Task output + */ + public void setOutput(String output) { + this.output = output; + } + + /** + * @return the taskId + */ + public String getTaskId() { + return taskId; + } + + /** + * @param taskId the taskId to set + */ + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + /** + * @return the external storage path for the task input payload + */ + public String getExternalInputPayloadStoragePath() { + return externalInputPayloadStoragePath; + } + + /** + * @param externalInputPayloadStoragePath the external storage path where the task input payload + * is stored + */ + public void setExternalInputPayloadStoragePath(String externalInputPayloadStoragePath) { + this.externalInputPayloadStoragePath = externalInputPayloadStoragePath; + } + + /** + * @return the external storage path for the task output payload + */ + public String getExternalOutputPayloadStoragePath() { + return externalOutputPayloadStoragePath; + } + + /** + * @param externalOutputPayloadStoragePath the external storage path where the task output + * payload is stored + */ + public void setExternalOutputPayloadStoragePath(String externalOutputPayloadStoragePath) { + this.externalOutputPayloadStoragePath = externalOutputPayloadStoragePath; + } + + /** + * @return the priority defined on workflow + */ + public int getWorkflowPriority() { + return workflowPriority; + } + + /** + * @param workflowPriority Priority defined for workflow + */ + public void setWorkflowPriority(int workflowPriority) { + this.workflowPriority = workflowPriority; + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/Workflow.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/Workflow.java new file mode 100644 index 000000000..fca0c8e39 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/Workflow.java @@ -0,0 +1,536 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.run; + +import java.util.*; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; + +import com.netflix.conductor.common.metadata.Auditable; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; + +public class Workflow extends Auditable { + + public enum WorkflowStatus { + + RUNNING(false, false), + COMPLETED(true, true), + FAILED(true, false), + TIMED_OUT(true, false), + TERMINATED(true, false), + PAUSED(false, true); + + private final boolean terminal; + + private final boolean successful; + + WorkflowStatus(boolean terminal, boolean successful) { + this.terminal = terminal; + this.successful = successful; + } + + public boolean isTerminal() { + return terminal; + } + + public boolean isSuccessful() { + return successful; + } + } + + private WorkflowStatus status = WorkflowStatus.RUNNING; + + private long endTime; + + private String workflowId; + + private String parentWorkflowId; + + private String parentWorkflowTaskId; + + private List tasks = new LinkedList<>(); + + private Map input = new HashMap<>(); + + private Map output = new HashMap<>(); + + // ids 10,11 are reserved + private String correlationId; + + private String reRunFromWorkflowId; + + private String reasonForIncompletion; + + // id 15 is reserved + private String event; + + private Map taskToDomain = new HashMap<>(); + + private Set failedReferenceTaskNames = new HashSet<>(); + + private WorkflowDef workflowDefinition; + + private String externalInputPayloadStoragePath; + + private String externalOutputPayloadStoragePath; + + private int priority; + + private Map variables = new HashMap<>(); + + private long lastRetriedTime; + + private Set failedTaskNames = new HashSet<>(); + + private List history = new LinkedList<>(); + + private String idempotencyKey; + + private String rateLimitKey; + + private boolean rateLimited; + + public Workflow() { + } + + public String getIdempotencyKey() { + return idempotencyKey; + } + + public void setIdempotencyKey(String idempotencyKey) { + this.idempotencyKey = idempotencyKey; + } + + public String getRateLimitKey() { + return rateLimitKey; + } + + public void setRateLimitKey(String rateLimitKey) { + this.rateLimitKey = rateLimitKey; + } + + public boolean isRateLimited() { + return rateLimited; + } + + public void setRateLimited(boolean rateLimited) { + this.rateLimited = rateLimited; + } + + public List getHistory() { + return history; + } + + public void setHistory(List history) { + this.history = history; + } + + /** + * @return the status + */ + public WorkflowStatus getStatus() { + return status; + } + + /** + * @param status the status to set + */ + public void setStatus(WorkflowStatus status) { + this.status = status; + } + + /** + * @return the startTime + */ + public long getStartTime() { + return getCreateTime(); + } + + /** + * @param startTime the startTime to set + */ + public void setStartTime(long startTime) { + this.setCreateTime(startTime); + } + + /** + * @return the endTime + */ + public long getEndTime() { + return endTime; + } + + /** + * @param endTime the endTime to set + */ + public void setEndTime(long endTime) { + this.endTime = endTime; + } + + /** + * @return the workflowId + */ + public String getWorkflowId() { + return workflowId; + } + + /** + * @param workflowId the workflowId to set + */ + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + + /** + * @return the tasks which are scheduled, in progress or completed. + */ + public List getTasks() { + return tasks; + } + + /** + * @param tasks the tasks to set + */ + public void setTasks(List tasks) { + this.tasks = tasks; + } + + /** + * @return the input + */ + public Map getInput() { + return input; + } + + /** + * @param input the input to set + */ + public void setInput(Map input) { + if (input == null) { + input = new HashMap<>(); + } + this.input = input; + } + + /** + * @return the task to domain map + */ + public Map getTaskToDomain() { + return taskToDomain; + } + + /** + * @param taskToDomain the task to domain map + */ + public void setTaskToDomain(Map taskToDomain) { + this.taskToDomain = taskToDomain; + } + + /** + * @return the output + */ + public Map getOutput() { + return output; + } + + /** + * @param output the output to set + */ + public void setOutput(Map output) { + if (output == null) { + output = new HashMap<>(); + } + this.output = output; + } + + /** + * @return The correlation id used when starting the workflow + */ + public String getCorrelationId() { + return correlationId; + } + + /** + * @param correlationId the correlation id + */ + public void setCorrelationId(String correlationId) { + this.correlationId = correlationId; + } + + public String getReRunFromWorkflowId() { + return reRunFromWorkflowId; + } + + public void setReRunFromWorkflowId(String reRunFromWorkflowId) { + this.reRunFromWorkflowId = reRunFromWorkflowId; + } + + public String getReasonForIncompletion() { + return reasonForIncompletion; + } + + public void setReasonForIncompletion(String reasonForIncompletion) { + this.reasonForIncompletion = reasonForIncompletion; + } + + /** + * @return the parentWorkflowId + */ + public String getParentWorkflowId() { + return parentWorkflowId; + } + + /** + * @param parentWorkflowId the parentWorkflowId to set + */ + public void setParentWorkflowId(String parentWorkflowId) { + this.parentWorkflowId = parentWorkflowId; + } + + /** + * @return the parentWorkflowTaskId + */ + public String getParentWorkflowTaskId() { + return parentWorkflowTaskId; + } + + /** + * @param parentWorkflowTaskId the parentWorkflowTaskId to set + */ + public void setParentWorkflowTaskId(String parentWorkflowTaskId) { + this.parentWorkflowTaskId = parentWorkflowTaskId; + } + + /** + * @return Name of the event that started the workflow + */ + public String getEvent() { + return event; + } + + /** + * @param event Name of the event that started the workflow + */ + public void setEvent(String event) { + this.event = event; + } + + public Set getFailedReferenceTaskNames() { + return failedReferenceTaskNames; + } + + public void setFailedReferenceTaskNames(Set failedReferenceTaskNames) { + this.failedReferenceTaskNames = failedReferenceTaskNames; + } + + public WorkflowDef getWorkflowDefinition() { + return workflowDefinition; + } + + public void setWorkflowDefinition(WorkflowDef workflowDefinition) { + this.workflowDefinition = workflowDefinition; + } + + /** + * @return the external storage path of the workflow input payload + */ + public String getExternalInputPayloadStoragePath() { + return externalInputPayloadStoragePath; + } + + /** + * @param externalInputPayloadStoragePath the external storage path where the workflow input + * payload is stored + */ + public void setExternalInputPayloadStoragePath(String externalInputPayloadStoragePath) { + this.externalInputPayloadStoragePath = externalInputPayloadStoragePath; + } + + /** + * @return the external storage path of the workflow output payload + */ + public String getExternalOutputPayloadStoragePath() { + return externalOutputPayloadStoragePath; + } + + /** + * @return the priority to define on tasks + */ + public int getPriority() { + return priority; + } + + /** + * @param priority priority of tasks (between 0 and 99) + */ + public void setPriority(int priority) { + if (priority < 0 || priority > 99) { + throw new IllegalArgumentException("priority MUST be between 0 and 99 (inclusive)"); + } + this.priority = priority; + } + + /** + * Convenience method for accessing the workflow definition name. + * + * @return the workflow definition name. + */ + public String getWorkflowName() { + if (workflowDefinition == null) { + throw new NullPointerException("Workflow definition is null"); + } + return workflowDefinition.getName(); + } + + /** + * Convenience method for accessing the workflow definition version. + * + * @return the workflow definition version. + */ + public int getWorkflowVersion() { + if (workflowDefinition == null) { + throw new NullPointerException("Workflow definition is null"); + } + return workflowDefinition.getVersion(); + } + + /** + * @param externalOutputPayloadStoragePath the external storage path where the workflow output + * payload is stored + */ + public void setExternalOutputPayloadStoragePath(String externalOutputPayloadStoragePath) { + this.externalOutputPayloadStoragePath = externalOutputPayloadStoragePath; + } + + /** + * @return the global workflow variables + */ + public Map getVariables() { + return variables; + } + + /** + * @param variables the set of global workflow variables to set + */ + public void setVariables(Map variables) { + this.variables = variables; + } + + /** + * Captures the last time the workflow was retried + * + * @return the last retried time of the workflow + */ + public long getLastRetriedTime() { + return lastRetriedTime; + } + + /** + * @param lastRetriedTime time in milliseconds when the workflow is retried + */ + public void setLastRetriedTime(long lastRetriedTime) { + this.lastRetriedTime = lastRetriedTime; + } + + public boolean hasParent() { + return StringUtils.isNotEmpty(parentWorkflowId); + } + + public Set getFailedTaskNames() { + return failedTaskNames; + } + + public void setFailedTaskNames(Set failedTaskNames) { + this.failedTaskNames = failedTaskNames; + } + + public Task getTaskByRefName(String refName) { + if (refName == null) { + throw new RuntimeException("refName passed is null. Check the workflow execution. For dynamic tasks, make sure referenceTaskName is set to a not null value"); + } + LinkedList found = new LinkedList<>(); + for (Task t : tasks) { + if (t.getReferenceTaskName() == null) { + throw new RuntimeException("Task " + t.getTaskDefName() + ", seq=" + t.getSeq() + " does not have reference name specified."); + } + if (t.getReferenceTaskName().equals(refName)) { + found.add(t); + } + } + if (found.isEmpty()) { + return null; + } + return found.getLast(); + } + + /** + * @return a deep copy of the workflow instance + */ + public Workflow copy() { + Workflow copy = new Workflow(); + copy.setInput(input); + copy.setOutput(output); + copy.setStatus(status); + copy.setWorkflowId(workflowId); + copy.setParentWorkflowId(parentWorkflowId); + copy.setParentWorkflowTaskId(parentWorkflowTaskId); + copy.setReRunFromWorkflowId(reRunFromWorkflowId); + copy.setCorrelationId(correlationId); + copy.setEvent(event); + copy.setReasonForIncompletion(reasonForIncompletion); + copy.setWorkflowDefinition(workflowDefinition); + copy.setPriority(priority); + copy.setTasks(tasks.stream().map(Task::deepCopy).collect(Collectors.toList())); + copy.setVariables(variables); + copy.setEndTime(endTime); + copy.setLastRetriedTime(lastRetriedTime); + copy.setTaskToDomain(taskToDomain); + copy.setFailedReferenceTaskNames(failedReferenceTaskNames); + copy.setExternalInputPayloadStoragePath(externalInputPayloadStoragePath); + copy.setExternalOutputPayloadStoragePath(externalOutputPayloadStoragePath); + return copy; + } + + public String toString() { + String name = workflowDefinition != null ? workflowDefinition.getName() : null; + Integer version = workflowDefinition != null ? workflowDefinition.getVersion() : null; + return String.format("%s.%s/%s.%s", name, version, workflowId, status); + } + + /** + * A string representation of all relevant fields that identify this workflow. Intended for use + * in log and other system generated messages. + */ + public String toShortString() { + String name = workflowDefinition != null ? workflowDefinition.getName() : null; + Integer version = workflowDefinition != null ? workflowDefinition.getVersion() : null; + return String.format("%s.%s/%s", name, version, workflowId); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Workflow workflow = (Workflow) o; + return Objects.equals(getWorkflowId(), workflow.getWorkflowId()); + } + + public int hashCode() { + return Objects.hash(getWorkflowId()); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java new file mode 100644 index 000000000..8b35d467c --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java @@ -0,0 +1,354 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.run; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.TimeZone; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; + +import com.netflix.conductor.common.run.Workflow.WorkflowStatus; +import com.netflix.conductor.common.utils.SummaryUtil; + +/** + * Captures workflow summary info to be indexed in Elastic Search. + */ +public class WorkflowSummary { + + /** + * The time should be stored as GMT + */ + private static final TimeZone GMT = TimeZone.getTimeZone("GMT"); + + private String workflowType; + + private int version; + + private String workflowId; + + private String correlationId; + + private String startTime; + + private String updateTime; + + private String endTime; + + private Workflow.WorkflowStatus status; + + private String input; + + private String output; + + private String reasonForIncompletion; + + private long executionTime; + + private String event; + + private String failedReferenceTaskNames = ""; + + private String externalInputPayloadStoragePath; + + private String externalOutputPayloadStoragePath; + + private int priority; + + private Set failedTaskNames = new HashSet<>(); + + private String createdBy; + + public WorkflowSummary() { + } + + public WorkflowSummary(Workflow workflow) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + sdf.setTimeZone(GMT); + this.workflowType = workflow.getWorkflowName(); + this.version = workflow.getWorkflowVersion(); + this.workflowId = workflow.getWorkflowId(); + this.priority = workflow.getPriority(); + this.correlationId = workflow.getCorrelationId(); + if (workflow.getCreateTime() != null) { + this.startTime = sdf.format(new Date(workflow.getCreateTime())); + } + if (workflow.getEndTime() > 0) { + this.endTime = sdf.format(new Date(workflow.getEndTime())); + } + if (workflow.getUpdateTime() != null) { + this.updateTime = sdf.format(new Date(workflow.getUpdateTime())); + } + this.status = workflow.getStatus(); + if (workflow.getInput() != null) { + this.input = SummaryUtil.serializeInputOutput(workflow.getInput()); + } + if (workflow.getOutput() != null) { + this.output = SummaryUtil.serializeInputOutput(workflow.getOutput()); + } + this.reasonForIncompletion = workflow.getReasonForIncompletion(); + if (workflow.getEndTime() > 0) { + this.executionTime = workflow.getEndTime() - workflow.getStartTime(); + } + this.event = workflow.getEvent(); + this.failedReferenceTaskNames = workflow.getFailedReferenceTaskNames().stream().collect(Collectors.joining(",")); + this.failedTaskNames = workflow.getFailedTaskNames(); + if (StringUtils.isNotBlank(workflow.getExternalInputPayloadStoragePath())) { + this.externalInputPayloadStoragePath = workflow.getExternalInputPayloadStoragePath(); + } + if (StringUtils.isNotBlank(workflow.getExternalOutputPayloadStoragePath())) { + this.externalOutputPayloadStoragePath = workflow.getExternalOutputPayloadStoragePath(); + } + } + + /** + * @return the workflowType + */ + public String getWorkflowType() { + return workflowType; + } + + /** + * @return the version + */ + public int getVersion() { + return version; + } + + /** + * @return the workflowId + */ + public String getWorkflowId() { + return workflowId; + } + + /** + * @return the correlationId + */ + public String getCorrelationId() { + return correlationId; + } + + /** + * @return the startTime + */ + public String getStartTime() { + return startTime; + } + + /** + * @return the endTime + */ + public String getEndTime() { + return endTime; + } + + /** + * @return the status + */ + public WorkflowStatus getStatus() { + return status; + } + + /** + * @return the input + */ + public String getInput() { + return input; + } + + public long getInputSize() { + return input != null ? input.length() : 0; + } + + /** + * @return the output + */ + public String getOutput() { + return output; + } + + public long getOutputSize() { + return output != null ? output.length() : 0; + } + + /** + * @return the reasonForIncompletion + */ + public String getReasonForIncompletion() { + return reasonForIncompletion; + } + + /** + * @return the executionTime + */ + public long getExecutionTime() { + return executionTime; + } + + /** + * @return the updateTime + */ + public String getUpdateTime() { + return updateTime; + } + + /** + * @return The event + */ + public String getEvent() { + return event; + } + + /** + * @param event The event + */ + public void setEvent(String event) { + this.event = event; + } + + public String getFailedReferenceTaskNames() { + return failedReferenceTaskNames; + } + + public void setFailedReferenceTaskNames(String failedReferenceTaskNames) { + this.failedReferenceTaskNames = failedReferenceTaskNames; + } + + public Set getFailedTaskNames() { + return failedTaskNames; + } + + public void setFailedTaskNames(Set failedTaskNames) { + this.failedTaskNames = failedTaskNames; + } + + public void setWorkflowType(String workflowType) { + this.workflowType = workflowType; + } + + public void setVersion(int version) { + this.version = version; + } + + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + + public void setCorrelationId(String correlationId) { + this.correlationId = correlationId; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public void setStatus(WorkflowStatus status) { + this.status = status; + } + + public void setInput(String input) { + this.input = input; + } + + public void setOutput(String output) { + this.output = output; + } + + public void setReasonForIncompletion(String reasonForIncompletion) { + this.reasonForIncompletion = reasonForIncompletion; + } + + public void setExecutionTime(long executionTime) { + this.executionTime = executionTime; + } + + /** + * @return the external storage path of the workflow input payload + */ + public String getExternalInputPayloadStoragePath() { + return externalInputPayloadStoragePath; + } + + /** + * @param externalInputPayloadStoragePath the external storage path where the workflow input + * payload is stored + */ + public void setExternalInputPayloadStoragePath(String externalInputPayloadStoragePath) { + this.externalInputPayloadStoragePath = externalInputPayloadStoragePath; + } + + /** + * @return the external storage path of the workflow output payload + */ + public String getExternalOutputPayloadStoragePath() { + return externalOutputPayloadStoragePath; + } + + /** + * @param externalOutputPayloadStoragePath the external storage path where the workflow output + * payload is stored + */ + public void setExternalOutputPayloadStoragePath(String externalOutputPayloadStoragePath) { + this.externalOutputPayloadStoragePath = externalOutputPayloadStoragePath; + } + + /** + * @return the priority to define on tasks + */ + public int getPriority() { + return priority; + } + + /** + * @param priority priority of tasks (between 0 and 99) + */ + public void setPriority(int priority) { + this.priority = priority; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + WorkflowSummary that = (WorkflowSummary) o; + return getVersion() == that.getVersion() && getExecutionTime() == that.getExecutionTime() && getPriority() == that.getPriority() && getWorkflowType().equals(that.getWorkflowType()) && getWorkflowId().equals(that.getWorkflowId()) && Objects.equals(getCorrelationId(), that.getCorrelationId()) && StringUtils.equals(getStartTime(), that.getStartTime()) && StringUtils.equals(getUpdateTime(), that.getUpdateTime()) && StringUtils.equals(getEndTime(), that.getEndTime()) && getStatus() == that.getStatus() && Objects.equals(getReasonForIncompletion(), that.getReasonForIncompletion()) && Objects.equals(getEvent(), that.getEvent()) && Objects.equals(getCreatedBy(), that.getCreatedBy()); + } + + public int hashCode() { + return Objects.hash(getWorkflowType(), getVersion(), getWorkflowId(), getCorrelationId(), getStartTime(), getUpdateTime(), getEndTime(), getStatus(), getReasonForIncompletion(), getExecutionTime(), getEvent(), getPriority(), getCreatedBy()); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java new file mode 100644 index 000000000..a82e5ee66 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java @@ -0,0 +1,100 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.run; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; + +public class WorkflowTestRequest extends StartWorkflowRequest { + + // Map of task reference name to mock output for the task + private Map> taskRefToMockOutput = new HashMap<>(); + + // If there are sub-workflows inside the workflow + // The map of task reference name to the mock for the sub-workflow + private Map subWorkflowTestRequest = new HashMap<>(); + + public static class TaskMock { + + private TaskResult.Status status = TaskResult.Status.COMPLETED; + + private Map output; + + // Time in millis for the execution of the task. Useful for + private long executionTime; + + // simulating timeout conditions + // Time in millis for the wait time in the queue. + private long queueWaitTime; + + public TaskMock() { + } + + public TaskMock(TaskResult.Status status, Map output) { + this.status = status; + this.output = output; + } + + public TaskResult.Status getStatus() { + return status; + } + + public void setStatus(TaskResult.Status status) { + this.status = status; + } + + public Map getOutput() { + return output; + } + + public void setOutput(Map output) { + this.output = output; + } + + public long getExecutionTime() { + return executionTime; + } + + public void setExecutionTime(long executionTime) { + this.executionTime = executionTime; + } + + public long getQueueWaitTime() { + return queueWaitTime; + } + + public void setQueueWaitTime(long queueWaitTime) { + this.queueWaitTime = queueWaitTime; + } + } + + public Map> getTaskRefToMockOutput() { + return taskRefToMockOutput; + } + + public void setTaskRefToMockOutput(Map> taskRefToMockOutput) { + this.taskRefToMockOutput = taskRefToMockOutput; + } + + public Map getSubWorkflowTestRequest() { + return subWorkflowTestRequest; + } + + public void setSubWorkflowTestRequest(Map subWorkflowTestRequest) { + this.subWorkflowTestRequest = subWorkflowTestRequest; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java new file mode 100644 index 000000000..c4db75a63 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java @@ -0,0 +1,113 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.utils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang3.StringUtils; + +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.common.utils.EnvUtils.SystemParameters; + +public class ConstraintParamUtil { + + /** + * Validates inputParam and returns a list of errors if input is not valid. + * + * @param input {@link Map} of inputParameters + * @param taskName TaskName of inputParameters + * @param workflow WorkflowDef + * @return {@link List} of error strings. + */ + public static List validateInputParam(Map input, String taskName, WorkflowDef workflow) { + ArrayList errorList = new ArrayList<>(); + for (Entry e : input.entrySet()) { + Object value = e.getValue(); + if (value instanceof String) { + errorList.addAll(extractParamPathComponentsFromString(e.getKey(), value.toString(), taskName, workflow)); + } else if (value instanceof Map) { + // recursive call + errorList.addAll(validateInputParam((Map) value, taskName, workflow)); + } else if (value instanceof List) { + errorList.addAll(extractListInputParam(e.getKey(), (List) value, taskName, workflow)); + } else { + e.setValue(value); + } + } + return errorList; + } + + private static List extractListInputParam(String key, List values, String taskName, WorkflowDef workflow) { + ArrayList errorList = new ArrayList<>(); + for (Object listVal : values) { + if (listVal instanceof String) { + errorList.addAll(extractParamPathComponentsFromString(key, listVal.toString(), taskName, workflow)); + } else if (listVal instanceof Map) { + errorList.addAll(validateInputParam((Map) listVal, taskName, workflow)); + } else if (listVal instanceof List) { + errorList.addAll(extractListInputParam(key, (List) listVal, taskName, workflow)); + } + } + return errorList; + } + + private static List extractParamPathComponentsFromString(String key, String value, String taskName, WorkflowDef workflow) { + ArrayList errorList = new ArrayList<>(); + if (value == null) { + String message = String.format("key: %s input parameter value: is null", key); + errorList.add(message); + return errorList; + } + String[] values = value.split("(?=(? + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.utils; + +import java.util.Optional; + +public class EnvUtils { + + public enum SystemParameters { + + CPEWF_TASK_ID, NETFLIX_ENV, NETFLIX_STACK + } + + public static boolean isEnvironmentVariable(String test) { + for (SystemParameters c : SystemParameters.values()) { + if (c.name().equals(test)) { + return true; + } + } + String value = Optional.ofNullable(System.getProperty(test)).orElseGet(() -> System.getenv(test)); + return value != null; + } + + public static String getSystemParametersValue(String sysParam, String taskId) { + if ("CPEWF_TASK_ID".equals(sysParam)) { + return taskId; + } + String value = System.getenv(sysParam); + if (value == null) { + value = System.getProperty(sysParam); + } + return value; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java new file mode 100644 index 000000000..ae57ec0b6 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java @@ -0,0 +1,79 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.utils; + +import java.io.InputStream; + +import com.netflix.conductor.common.run.ExternalStorageLocation; + +/** + * Interface used to externalize the storage of large JSON payloads in workflow and task + * input/output + */ +public interface ExternalPayloadStorage { + + enum Operation { + + READ, WRITE + } + + enum PayloadType { + + WORKFLOW_INPUT, WORKFLOW_OUTPUT, TASK_INPUT, TASK_OUTPUT + } + + /** + * Obtain a uri used to store/access a json payload in external storage. + * + * @param operation the type of {@link Operation} to be performed with the uri + * @param payloadType the {@link PayloadType} that is being accessed at the uri + * @param path (optional) the relative path for which the external storage location object is to + * be populated. If path is not specified, it will be computed and populated. + * @return a {@link ExternalStorageLocation} object which contains the uri and the path for the + * json payload + */ + ExternalStorageLocation getLocation(Operation operation, PayloadType payloadType, String path); + + /** + * Obtain an uri used to store/access a json payload in external storage with deduplication of + * data based on payloadBytes digest. + * + * @param operation the type of {@link Operation} to be performed with the uri + * @param payloadType the {@link PayloadType} that is being accessed at the uri + * @param path (optional) the relative path for which the external storage location object is to + * be populated. If path is not specified, it will be computed and populated. + * @param payloadBytes for calculating digest which is used for objectKey + * @return a {@link ExternalStorageLocation} object which contains the uri and the path for the + * json payload + */ + default ExternalStorageLocation getLocation(Operation operation, PayloadType payloadType, String path, byte[] payloadBytes) { + return getLocation(operation, payloadType, path); + } + + /** + * Upload a json payload to the specified external storage location. + * + * @param path the location to which the object is to be uploaded + * @param payload an {@link InputStream} containing the json payload which is to be uploaded + * @param payloadSize the size of the json payload in bytes + */ + void upload(String path, InputStream payload, long payloadSize); + + /** + * Download the json payload from the specified external storage location. + * + * @param path the location from where the object is to be downloaded + * @return an {@link InputStream} of the json payload at the specified location + */ + InputStream download(String path); +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java new file mode 100644 index 000000000..fc986c662 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java @@ -0,0 +1,57 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.utils; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.common.config.ObjectMapperProvider; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class SummaryUtil { + + private static final Logger logger = LoggerFactory.getLogger(SummaryUtil.class); + + private static final ObjectMapper objectMapper = new ObjectMapperProvider().getObjectMapper(); + + private static boolean isSummaryInputOutputJsonSerializationEnabled; + + private boolean isJsonSerializationEnabled; + + public void init() { + isSummaryInputOutputJsonSerializationEnabled = isJsonSerializationEnabled; + } + + /** + * Serializes the Workflow or Task's Input/Output object by Java's toString (default), or by a + * Json ObjectMapper (@see Configuration.isSummaryInputOutputJsonSerializationEnabled) + * + * @param object the Input or Output Object to serialize + * @return the serialized string of the Input or Output object + */ + public static String serializeInputOutput(Map object) { + if (!isSummaryInputOutputJsonSerializationEnabled) { + return object.toString(); + } + try { + return objectMapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + logger.error("The provided value ({}) could not be serialized as Json", object.toString(), e); + throw new RuntimeException(e); + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java new file mode 100644 index 000000000..1d2864f2e --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java @@ -0,0 +1,49 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.utils; + +import com.netflix.conductor.common.config.ObjectMapperProvider; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class TaskUtils { + + private static final ObjectMapper objectMapper; + + static { + ObjectMapperProvider provider = new ObjectMapperProvider(); + objectMapper = provider.getObjectMapper(); + } + + private static final String LOOP_TASK_DELIMITER = "__"; + + public static String appendIteration(String name, int iteration) { + return name + LOOP_TASK_DELIMITER + iteration; + } + + public static String getLoopOverTaskRefNameSuffix(int iteration) { + return LOOP_TASK_DELIMITER + iteration; + } + + public static String removeIterationFromTaskRefName(String referenceTaskName) { + String[] tokens = referenceTaskName.split(TaskUtils.LOOP_TASK_DELIMITER); + return tokens.length > 0 ? tokens[0] : referenceTaskName; + } + + public static WorkflowDef convertToWorkflowDef(Object workflowDef) { + return objectMapper.convertValue(workflowDef, new TypeReference() { + }); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java new file mode 100644 index 000000000..fa27105b6 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java @@ -0,0 +1,89 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.validation; + +import java.util.List; +import java.util.Map; + +public class ErrorResponse { + + private int status; + + private String code; + + private String message; + + private String instance; + + private boolean retryable; + + private List validationErrors; + + private Map metadata; + + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public List getValidationErrors() { + return validationErrors; + } + + public void setValidationErrors(List validationErrors) { + this.validationErrors = validationErrors; + } + + public boolean isRetryable() { + return retryable; + } + + public void setRetryable(boolean retryable) { + this.retryable = retryable; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getInstance() { + return instance; + } + + public void setInstance(String instance) { + this.instance = instance; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ValidationError.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ValidationError.java new file mode 100644 index 000000000..24569854a --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ValidationError.java @@ -0,0 +1,64 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.common.validation; + +import java.util.StringJoiner; + +/** + * Captures a validation error that can be returned in {@link ErrorResponse}. + */ +public class ValidationError { + + private String path; + + private String message; + + private String invalidValue; + + public ValidationError() { + } + + public ValidationError(String path, String message, String invalidValue) { + this.path = path; + this.message = message; + this.invalidValue = invalidValue; + } + + public String getPath() { + return path; + } + + public String getMessage() { + return message; + } + + public String getInvalidValue() { + return invalidValue; + } + + public void setPath(String path) { + this.path = path; + } + + public void setMessage(String message) { + this.message = message; + } + + public void setInvalidValue(String invalidValue) { + this.invalidValue = invalidValue; + } + + public String toString() { + return new StringJoiner(", ", ValidationError.class.getSimpleName() + "[", "]").add("path='" + path + "'").add("message='" + message + "'").add("invalidValue='" + invalidValue + "'").toString(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/ClientSpecification.groovy b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/ClientSpecification.groovy new file mode 100644 index 000000000..9fd6708c3 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/ClientSpecification.groovy @@ -0,0 +1,34 @@ +/* + * Copyright 2022 Conductor Authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http + +import com.fasterxml.jackson.databind.ObjectMapper +import com.netflix.conductor.common.config.ObjectMapperProvider +import spock.lang.Specification + +abstract class ClientSpecification extends Specification { + + protected static final String ROOT_URL = "dummyroot/" + + protected static URI createURI(String path) { + URI.create(ROOT_URL + path) + } + + protected ObjectMapper objectMapper + protected ConductorClient apiClient + + def setup() { + apiClient = Mock(ConductorClient.class) + objectMapper = new ObjectMapperProvider().getObjectMapper() + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/EventClientSpec.groovy b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/EventClientSpec.groovy new file mode 100644 index 000000000..c227bf9cb --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/EventClientSpec.groovy @@ -0,0 +1,101 @@ +/* + * Copyright 2022 Conductor Authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http + +import com.netflix.conductor.common.metadata.events.EventHandler +import spock.lang.Subject +import spock.lang.Unroll + +import static ConductorClientRequest.Method +import static ConductorClientRequest.builder + +class EventClientSpec extends ClientSpecification { + + @Subject + EventClient eventClient + + def setup() { + eventClient = new EventClient(apiClient) + } + + def "register event handler"() { + given: + def handler = new EventHandler() + + when: + eventClient.registerEventHandler(handler) + + then: + 1 * apiClient.execute(builder() + .method(Method.POST) + .body(handler) + .path("/event") + .build() + ) >> new ConductorClientResponse(200, [:]) + } + + def "update event handler"() { + given: + def handler = new EventHandler() + + when: + eventClient.updateEventHandler(handler) + + then: + 1 * apiClient.execute(builder() + .method(Method.PUT) + .body(handler) + .path("/event") + .build() + ) >> new ConductorClientResponse(200, [:]) + } + + def "unregister event handler"() { + given: + def eventName = "test" + + when: + eventClient.unregisterEventHandler(eventName) + + then: + 1 * apiClient.execute(builder() + .method(Method.DELETE) + .path('/event/{name}') + .addPathParam('name', eventName) + .build()); + } + + @Unroll + def "get event handlers activeOnly=#activeOnly"() { + given: + def handlers = [new EventHandler(), new EventHandler()] + def eventName = "test" + + when: + def eventHandlers = eventClient.getEventHandlers(eventName, activeOnly) + + then: + eventHandlers && eventHandlers.size() == 2 + + 1 * apiClient.execute(builder() + .method(Method.GET) + .path('/event/{name}') + .addPathParam('name', eventName) + .addQueryParam('activeOnly', activeOnly) + .build(), _) >> new ConductorClientResponse(200, [:], handlers) + + where: + activeOnly << [true, false] + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/MetadataClientSpec.groovy b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/MetadataClientSpec.groovy new file mode 100644 index 000000000..9f8fd43ab --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/MetadataClientSpec.groovy @@ -0,0 +1,107 @@ +/* + * Copyright 2022 Conductor Authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http + +import com.fasterxml.jackson.core.type.TypeReference +import com.netflix.conductor.client.exception.ConductorClientException +import com.netflix.conductor.common.metadata.workflow.WorkflowDef +import spock.lang.Subject + +import static ConductorClientRequest.builder + +class MetadataClientSpec extends ClientSpecification { + + @Subject + MetadataClient metadataClient + + def setup() { + metadataClient = new MetadataClient(apiClient) + } + + def "workflow delete"() { + given: + def workflowName = 'test' + int version = 1 + + when: + metadataClient.unregisterWorkflowDef(workflowName, version) + + then: + 1 * apiClient.execute(builder() + .method(ConductorClientRequest.Method.DELETE) + .path('/metadata/workflow/{name}/{version}') + .addPathParam('name', workflowName) + .addPathParam("version", version) + .build()); + } + + // not sure if this is a meaningful test + def "workflow delete throws exception"() { + given: + def workflowName = 'test' + int version = 1 + + when: + metadataClient.unregisterWorkflowDef(workflowName, version) + + then: + 1 * apiClient.execute(builder() + .method(ConductorClientRequest.Method.DELETE) + .path('/metadata/workflow/{name}/{version}') + .addPathParam('name', workflowName) + .addPathParam("version", version) + .build()) >> { throw new ConductorClientException(200, "Error while deleting workflow") } + def ex = thrown(ConductorClientException.class) + ex.message == "200: Error while deleting workflow" + } + + def "workflow delete version missing"() { + when: + metadataClient.unregisterWorkflowDef("some name", null) + + then: + def ex = thrown(NullPointerException.class) + ex.message == "version cannot be null" + } + + def "workflow delete name missing"() { + when: + metadataClient.unregisterWorkflowDef(null, 1) + + then: + def ex = thrown(NullPointerException.class) + ex.message == "Name cannot be blank" + + when: + metadataClient.unregisterWorkflowDef(" ", 1) + + then: + ex = thrown(IllegalArgumentException.class) + ex.message == "Name cannot be blank" + } + + def "workflow get all definitions latest version"() { + given: + List result = new ArrayList() + + when: + def ret = metadataClient.getAllWorkflowsWithLatestVersions() + + then: + 1 * apiClient.execute(builder() + .method(ConductorClientRequest.Method.GET) + .path('metadata/workflow/latest-versions') + .build(), _) >> new ConductorClientResponse(200, [:], result) + ret == result + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/TaskClientSpec.groovy b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/TaskClientSpec.groovy new file mode 100644 index 000000000..b0d6dd222 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/TaskClientSpec.groovy @@ -0,0 +1,137 @@ +/* + * Copyright 2022 Conductor Authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http + + +import com.netflix.conductor.common.run.SearchResult +import com.netflix.conductor.common.run.TaskSummary +import spock.lang.Subject + +import static ConductorClientRequest.builder + +class TaskClientSpec extends ClientSpecification { + + @Subject + TaskClient taskClient + + def setup() { + taskClient = new TaskClient(apiClient) + } + + //FIXME is going to fail with UnsupportedOperationException:"search operation on tasks is not supported" +// def search() { +// given: +// def query = 'my_complex_query' +// def result = new SearchResult<>() +// result.totalHits = 1 +// result.results = [new TaskSummary()] +// +// when: +// def searchResult = taskClient.search(query) +// +// then: +// 1 * apiClient.doRequest(builder() +// .method(OrkesHttpClientRequest.Method.GET) +// .path('/tasks/search') +// .addQueryParam('query', query) +// .build(), _) >> new ApiResponse(200, [:], result) +// +// searchResult.totalHits == result.totalHits +// searchResult.results && searchResult.results.size() == 1 +// searchResult.results[0] instanceof TaskSummary +// } + + //FIXME is going to fail with UnsupportedOperationException:"search operation on tasks is not supported" +// def searchV2() { +// given: +// def query = 'my_complex_query' +// def result = new SearchResult<>() +// result.totalHits = 1 +// result.results = [new Task()] +// +// when: +// def searchResult = taskClient.searchV2('my_complex_query') +// +// then: +// 1 * apiClient.doRequest(builder() +// .method(OrkesHttpClientRequest.Method.GET) +// .path('/tasks/searchV2') +// .addQueryParam('query', query) +// .build(), _) >> new ApiResponse(200, [:], result) +// +// searchResult.totalHits == result.totalHits +// searchResult.results && searchResult.results.size() == 1 +// searchResult.results[0] instanceof Task +// } + + def "search with params"() { + given: + def query = 'my_complex_query' + int start = 0 + int size = 10 + def sort = 'sort' + def freeText = 'text' + def result = new SearchResult<>() + result.totalHits = 1 + result.results = [new TaskSummary()] + + when: + def searchResult = taskClient.search(start, size, sort, freeText, query) + + then: + 1 * apiClient.execute(builder() + .method(ConductorClientRequest.Method.GET) + .path("/tasks/search") + .addQueryParam("start", start) + .addQueryParam("size", size) + .addQueryParam("sort", sort) + .addQueryParam("freeText", freeText) + .addQueryParam("query", query) + .build(), _) >> new ConductorClientResponse(200, [:], result) + + searchResult.totalHits == result.totalHits + searchResult.results && searchResult.results.size() == 1 + searchResult.results[0] instanceof TaskSummary + } + + //FIXME is going to fail with UnsupportedOperationException:"search operation on tasks is not supported" +// def "searchV2 with params"() { +// given: +// def query = 'my_complex_query' +// int start = 0 +// int size = 10 +// def sort = 'sort' +// def freeText = 'text' +// def result = new SearchResult<>() +// result.totalHits = 1 +// result.results = [new Task()] +// +// when: +// def searchResult = taskClient.searchV2(start, size, sort, freeText, query) +// +// then: +// 1 * apiClient.doRequest(builder() +// .method(OrkesHttpClientRequest.Method.GET) +// .path("/tasks/searchv2") +// .addQueryParam("start", start) +// .addQueryParam("size", size) +// .addQueryParam("sort", sort) +// .addQueryParam("freeText", freeText) +// .addQueryParam("query", query) +// .build(), _) >> new ApiResponse(200, [:], result) +// +// searchResult.totalHits == result.totalHits +// searchResult.results && searchResult.results.size() == 1 +// searchResult.results[0] instanceof TaskSummary +// } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/WorkflowClientSpec.groovy b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/WorkflowClientSpec.groovy new file mode 100644 index 000000000..7e8e3c745 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/groovy/com/netflix/conductor/client/http/WorkflowClientSpec.groovy @@ -0,0 +1,150 @@ +/* + * Copyright 2022 Conductor Authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.http + + +import com.netflix.conductor.common.run.SearchResult +import com.netflix.conductor.common.run.WorkflowSummary +import spock.lang.Subject + +import static ConductorClientRequest.Method +import static ConductorClientRequest.builder + +class WorkflowClientSpec extends ClientSpecification { + + @Subject + WorkflowClient workflowClient + + def setup() { + workflowClient = new WorkflowClient(apiClient) + } + + def search() { + given: + def query = 'my_complex_query' + def result = new SearchResult<>() + result.totalHits = 1 + result.results = [new WorkflowSummary()] + + when: + def searchResult = workflowClient.search(query) + + then: + //FIXME the order of the query params matter in the test. This is NOT good. + 1 * apiClient.execute(builder() + .method(Method.GET) + .path("/workflow/search") + .addQueryParam("start", null as String) + .addQueryParam("size", null as String) + .addQueryParam("sort", null as String) + .addQueryParam("freeText", "") //FIXME should this be null? + .addQueryParam("query", query) + .addQueryParam("skipCache", null as Boolean) + .build(), _) >> new ConductorClientResponse(200, [:], result) + + searchResult.totalHits == result.totalHits + searchResult.results && searchResult.results.size() == 1 + searchResult.results[0] instanceof WorkflowSummary + } + + //FIXME is going to fail with UnsupportedOperationException:"Please use search() API" + def searchV2() { +// given: +// def query = 'my_complex_query' +// def result = new SearchResult<>() +// result.totalHits = 1 +// result.results = [new Workflow(workflowDefinition: new WorkflowDef(), createTime: System.currentTimeMillis())] +// +// when: +// def searchResult = workflowClient.searchV2('my_complex_query') +// +// then: +// //FIXME the order of the query params matter in the test. This is NOT good. +// 1 * apiClient.doRequest(builder() +// .method(Method.GET) +// .path("/workflow/search-v2") +// .addQueryParam("start", null as String) +// .addQueryParam("size", null as String) +// .addQueryParam("sort", null as String) +// .addQueryParam("freeText", "") //FIXME should this be null? +// .addQueryParam("query", query) +// .addQueryParam("skipCache", null as Boolean) +// .build(), _) >> new ApiResponse(200, [:], result) +// +// searchResult.totalHits == result.totalHits +// searchResult.results && searchResult.results.size() == 1 +// searchResult.results[0] instanceof Workflow + } + + def "search with params"() { + given: + def query = 'my_complex_query' + def start = 0 + def size = 10 + def sort = 'sort' + def freeText = 'text' + def result = new SearchResult<>() + result.totalHits = 1 + result.results = [new WorkflowSummary()] + when: + def searchResult = workflowClient.search(start, size, sort, freeText, query) + + then: + 1 * apiClient.execute(builder() + .method(Method.GET) + .path("/workflow/search") + .addQueryParam("start", start) + .addQueryParam("size", size) + .addQueryParam("sort", sort) + .addQueryParam("freeText", freeText) + .addQueryParam("query", query) + .addQueryParam("skipCache", null as Boolean) + .build(), _) >> new ConductorClientResponse(200, [:], result) + + searchResult.totalHits == result.totalHits + searchResult.results && searchResult.results.size() == 1 + searchResult.results[0] instanceof WorkflowSummary + } + + //FIXME is going to fail with UnsupportedOperationException:"Please use search() API" + def "searchV2 with params"() { +// given: +// def query = 'my_complex_query' +// def start = 0 +// def size = 10 +// def sort = 'sort' +// def freeText = 'text' +// def result = new SearchResult<>() +// result.totalHits = 1 +// result.results = [new Workflow(workflowDefinition: new WorkflowDef(), createTime: System.currentTimeMillis())] +// +// when: +// def searchResult = workflowClient.searchV2(start, size, sort, freeText, query) +// +// then: +// 1 * apiClient.doRequest(builder() +// .method(Method.GET) +// .path("/workflow/search-v2") +// .addQueryParam("start", start) +// .addQueryParam("size", size) +// .addQueryParam("sort", sort) +// .addQueryParam("freeText", freeText) +// .addQueryParam("query", query) +// .addQueryParam("skipCache", null as Boolean) +// .build(), _) >> new ApiResponse(200, [:], result) +// +// searchResult.totalHits == result.totalHits +// searchResult.results && searchResult.results.size() == 1 +// searchResult.results[0] instanceof Workflow + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java new file mode 100644 index 000000000..54bf161fc --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.automator; + +//FIXME do we need these tests? +public class PollingSemaphoreTest { + +// @Test +// public void testBlockAfterAvailablePermitsExhausted() throws Exception { +// int threads = 5; +// ExecutorService executorService = Executors.newFixedThreadPool(threads); +// PollingSemaphore pollingSemaphore = new PollingSemaphore(threads); +// +// List> futuresList = new ArrayList<>(); +// IntStream.range(0, threads) +// .forEach( +// t -> +// futuresList.add( +// CompletableFuture.runAsync( +// () -> pollingSemaphore.acquireSlots(1), +// executorService))); +// +// CompletableFuture allFutures = +// CompletableFuture.allOf( +// futuresList.toArray(new CompletableFuture[futuresList.size()])); +// +// allFutures.get(); +// +// assertEquals(0, pollingSemaphore.availableSlots()); +// assertFalse(pollingSemaphore.acquireSlots(1)); +// +// executorService.shutdown(); +// } +// +// @Test +// public void testAllowsPollingWhenPermitBecomesAvailable() throws Exception { +// int threads = 5; +// ExecutorService executorService = Executors.newFixedThreadPool(threads); +// PollingSemaphore pollingSemaphore = new PollingSemaphore(threads); +// +// List> futuresList = new ArrayList<>(); +// IntStream.range(0, threads) +// .forEach( +// t -> +// futuresList.add( +// CompletableFuture.runAsync( +// () -> pollingSemaphore.acquireSlots(1), +// executorService))); +// +// CompletableFuture allFutures = +// CompletableFuture.allOf( +// futuresList.toArray(new CompletableFuture[futuresList.size()])); +// allFutures.get(); +// +// assertEquals(0, pollingSemaphore.availableSlots()); +// pollingSemaphore.complete(1); +// +// assertTrue(pollingSemaphore.availableSlots() > 0); +// assertTrue(pollingSemaphore.acquireSlots(1)); +// +// executorService.shutdown(); +// } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java new file mode 100644 index 000000000..ac9350016 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java @@ -0,0 +1,647 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.automator; + +import static org.mockito.Mockito.*; + +//FIXME Fix these tests +public class TaskPollExecutorTest { +/* + private static final String TEST_TASK_DEF_NAME = "test"; + + private static final Map TASK_THREAD_MAP = + Collections.singletonMap(TEST_TASK_DEF_NAME, 1); + + @Test + public void testTaskExecutionException() throws InterruptedException { + Worker worker = + Worker.create( + TEST_TASK_DEF_NAME, + task -> { + throw new NoSuchMethodError(); + }); + TaskClient taskClient = Mockito.mock(TaskClient.class); + TaskPollExecutor taskPollExecutor = + new TaskPollExecutor( + null, taskClient, 1, new HashMap<>(), "test-worker-%d", TASK_THREAD_MAP); + + when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) + .thenReturn(Arrays.asList(testTask())); + when(taskClient.ack(any(), any())).thenReturn(true); + CountDownLatch latch = new CountDownLatch(1); + doAnswer( + invocation -> { + assertEquals("test-worker-1", Thread.currentThread().getName()); + Object[] args = invocation.getArguments(); + TaskResult result = (TaskResult) args[0]; + assertEquals(TaskResult.Status.FAILED, result.getStatus()); + latch.countDown(); + return null; + }) + .when(taskClient) + .updateTask(any()); + + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate( + () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); + + latch.await(); + verify(taskClient).updateTask(any()); + } + + @SuppressWarnings("rawtypes") + @Test + public void testMultipleTasksExecution() throws InterruptedException { + String outputKey = "KEY"; + Task task = testTask(); + Worker worker = mock(Worker.class); + when(worker.getPollingInterval()).thenReturn(3000); + when(worker.getTaskDefName()).thenReturn(TEST_TASK_DEF_NAME); + when(worker.execute(any())) + .thenAnswer( + new Answer() { + private int count = 0; + Map outputMap = new HashMap<>(); + + public TaskResult answer(InvocationOnMock invocation) + throws InterruptedException { + // Sleep for 2 seconds to simulate task execution + Thread.sleep(2000L); + TaskResult taskResult = new TaskResult(task); + outputMap.put(outputKey, count++); + taskResult.setOutputData(outputMap); + return taskResult; + } + }); + + TaskClient taskClient = Mockito.mock(TaskClient.class); + TaskPollExecutor taskPollExecutor = + new TaskPollExecutor( + null, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); + when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) + .thenReturn(Arrays.asList(task)); + when(taskClient.ack(any(), any())).thenReturn(true); + CountDownLatch latch = new CountDownLatch(3); + doAnswer( + new Answer() { + private int count = 0; + + public TaskResult answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + TaskResult result = (TaskResult) args[0]; + assertEquals(IN_PROGRESS, result.getStatus()); + assertEquals(count, result.getOutputData().get(outputKey)); + count++; + latch.countDown(); + return null; + } + }) + .when(taskClient) + .updateTask(any()); + + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate( + () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); + latch.await(); + + // execute() is called 3 times on the worker (once for each task) + verify(worker, times(3)).execute(any()); + verify(taskClient, times(3)).updateTask(any()); + } + + @SuppressWarnings("unchecked") + @Test + public void testLargePayloadCanFailUpdateWithRetry() throws InterruptedException { + Task task = testTask(); + + Worker worker = mock(Worker.class); + when(worker.getPollingInterval()).thenReturn(3000); + when(worker.getTaskDefName()).thenReturn(TEST_TASK_DEF_NAME); + when(worker.execute(any())).thenReturn(new TaskResult(task)); + + TaskClient taskClient = Mockito.mock(TaskClient.class); + when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) + .thenReturn(Arrays.asList(task)); + when(taskClient.ack(any(), any())).thenReturn(true); + + doAnswer( + invocation -> { + Object[] args = invocation.getArguments(); + TaskResult result = (TaskResult) args[0]; + assertNull(result.getReasonForIncompletion()); + result.setReasonForIncompletion("some_reason_1"); + throw new ConductorClientException(); + }) + .when(taskClient) + .evaluateAndUploadLargePayload(any(Map.class), any()); + + TaskPollExecutor taskPollExecutor = + new TaskPollExecutor( + null, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); + CountDownLatch latch = new CountDownLatch(1); + doAnswer( + invocation -> { + latch.countDown(); + return null; + }) + .when(worker) + .onErrorUpdate(any()); + + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate( + () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); + latch.await(); + + // When evaluateAndUploadLargePayload fails indefinitely, task update shouldn't be called. + verify(taskClient, times(0)).updateTask(any()); + } + + @Test + public void testLargePayloadLocationUpdate() throws InterruptedException { + Task task = testTask(); + String largePayloadLocation = "large_payload_location"; + + Worker worker = mock(Worker.class); + when(worker.getPollingInterval()).thenReturn(3000); + when(worker.getTaskDefName()).thenReturn(TEST_TASK_DEF_NAME); + when(worker.execute(any())).thenReturn(new TaskResult(task)); + + TaskClient taskClient = Mockito.mock(TaskClient.class); + when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) + .thenReturn(Arrays.asList(task)); + when(taskClient.ack(any(), any())).thenReturn(true); + //noinspection unchecked + when(taskClient.evaluateAndUploadLargePayload(any(Map.class), any())) + .thenReturn(Optional.of(largePayloadLocation)); + + TaskPollExecutor taskPollExecutor = + new TaskPollExecutor( + null, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); + CountDownLatch latch = new CountDownLatch(1); + + doAnswer( + invocation -> { + Object[] args = invocation.getArguments(); + TaskResult result = (TaskResult) args[0]; + assertNull(result.getOutputData()); + assertEquals( + largePayloadLocation, + result.getExternalOutputPayloadStoragePath()); + latch.countDown(); + return null; + }) + .when(taskClient) + .updateTask(any()); + + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate( + () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); + latch.await(); + + verify(taskClient, times(1)).updateTask(any()); + } + + @Test + public void testTaskPollException() throws InterruptedException { + Task task = testTask(); + + Worker worker = mock(Worker.class); + when(worker.getPollingInterval()).thenReturn(3000); + when(worker.getTaskDefName()).thenReturn(TEST_TASK_DEF_NAME); + when(worker.execute(any())).thenReturn(new TaskResult(task)); + + TaskClient taskClient = Mockito.mock(TaskClient.class); + when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) + .thenThrow(ConductorClientException.class) + .thenReturn(Arrays.asList(task)); + + TaskPollExecutor taskPollExecutor = + new TaskPollExecutor( + null, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); + CountDownLatch latch = new CountDownLatch(1); + doAnswer( + invocation -> { + Object[] args = invocation.getArguments(); + TaskResult result = (TaskResult) args[0]; + assertEquals(IN_PROGRESS, result.getStatus()); + assertEquals(task.getTaskId(), result.getTaskId()); + latch.countDown(); + return null; + }) + .when(taskClient) + .updateTask(any()); + + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate( + () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); + + latch.await(); + verify(taskClient).updateTask(any()); + } + + @Test + public void testTaskPoll() throws InterruptedException { + Task task = testTask(); + + Worker worker = mock(Worker.class); + when(worker.getPollingInterval()).thenReturn(3000); + when(worker.getTaskDefName()).thenReturn("test"); + when(worker.execute(any())).thenReturn(new TaskResult(task)); + + TaskClient taskClient = Mockito.mock(TaskClient.class); + when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) + .thenReturn(Arrays.asList(task)); + + TaskPollExecutor taskPollExecutor = + new TaskPollExecutor( + null, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); + CountDownLatch latch = new CountDownLatch(1); + doAnswer( + invocation -> { + Object[] args = invocation.getArguments(); + TaskResult result = (TaskResult) args[0]; + assertEquals(IN_PROGRESS, result.getStatus()); + assertEquals(task.getTaskId(), result.getTaskId()); + latch.countDown(); + return null; + }) + .when(taskClient) + .updateTask(any()); + + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate( + () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); + + latch.await(); + verify(taskClient).updateTask(any()); + } + + @Test + public void testTaskPollDomain() throws InterruptedException { + TaskClient taskClient = Mockito.mock(TaskClient.class); + String testDomain = "foo"; + Map taskToDomain = new HashMap<>(); + taskToDomain.put(TEST_TASK_DEF_NAME, testDomain); + TaskPollExecutor taskPollExecutor = + new TaskPollExecutor( + null, taskClient, 1, taskToDomain, "test-worker-", TASK_THREAD_MAP); + + String workerName = "test-worker"; + Worker worker = mock(Worker.class); + when(worker.getTaskDefName()).thenReturn(TEST_TASK_DEF_NAME); + when(worker.getIdentity()).thenReturn(workerName); + + CountDownLatch latch = new CountDownLatch(1); + doAnswer( + invocation -> { + latch.countDown(); + return null; + }) + .when(taskClient) + .batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt()); + + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate( + () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); + + latch.await(); + verify(taskClient).batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt()); + } + + @Test + public void testPollOutOfDiscoveryForTask() throws InterruptedException { + Task task = testTask(); + + EurekaClient client = mock(EurekaClient.class); + when(client.getInstanceRemoteStatus()).thenReturn(InstanceInfo.InstanceStatus.UNKNOWN); + + Worker worker = mock(Worker.class); + when(worker.getPollingInterval()).thenReturn(3000); + when(worker.getTaskDefName()).thenReturn("task_run_always"); + when(worker.execute(any())).thenReturn(new TaskResult(task)); + + TaskClient taskClient = Mockito.mock(TaskClient.class); + when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) + .thenReturn(Arrays.asList(new Task())) + .thenReturn(Arrays.asList(task)); + + TaskPollExecutor taskPollExecutor = + new TaskPollExecutor( + client, + taskClient, + 1, + new HashMap<>(), + "test-worker-", + Collections.singletonMap("task_run_always", 1)); + CountDownLatch latch = new CountDownLatch(1); + doAnswer( + invocation -> { + Object[] args = invocation.getArguments(); + TaskResult result = (TaskResult) args[0]; + assertEquals(IN_PROGRESS, result.getStatus()); + assertEquals(task.getTaskId(), result.getTaskId()); + latch.countDown(); + return null; + }) + .when(taskClient) + .updateTask(any()); + + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate( + () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); + + latch.await(); + verify(taskClient).updateTask(any()); + } + + @Test + public void testPollOutOfDiscoveryAsDefaultFalseForTask() + throws ExecutionException, InterruptedException { + Task task = testTask(); + + EurekaClient client = mock(EurekaClient.class); + when(client.getInstanceRemoteStatus()).thenReturn(InstanceInfo.InstanceStatus.UNKNOWN); + + Worker worker = mock(Worker.class); + when(worker.getPollingInterval()).thenReturn(3000); + when(worker.getTaskDefName()).thenReturn("task_do_not_run_always"); + when(worker.execute(any())).thenReturn(new TaskResult(task)); + + TaskClient taskClient = Mockito.mock(TaskClient.class); + when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) + .thenReturn(Arrays.asList(task)); + + TaskPollExecutor taskPollExecutor = + new TaskPollExecutor( + client, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); + CountDownLatch latch = new CountDownLatch(1); + doAnswer( + invocation -> { + Object[] args = invocation.getArguments(); + TaskResult result = (TaskResult) args[0]; + assertEquals(IN_PROGRESS, result.getStatus()); + assertEquals(task.getTaskId(), result.getTaskId()); + latch.countDown(); + return null; + }) + .when(taskClient) + .updateTask(any()); + + ScheduledFuture f = + Executors.newSingleThreadScheduledExecutor() + .schedule( + () -> taskPollExecutor.pollAndExecute(worker), 0, TimeUnit.SECONDS); + + f.get(); + verify(taskClient, times(0)).updateTask(any()); + } + + @Test + public void testPollOutOfDiscoveryAsExplicitFalseForTask() + throws ExecutionException, InterruptedException { + Task task = testTask(); + + EurekaClient client = mock(EurekaClient.class); + when(client.getInstanceRemoteStatus()).thenReturn(InstanceInfo.InstanceStatus.UNKNOWN); + + Worker worker = mock(Worker.class); + when(worker.getPollingInterval()).thenReturn(3000); + when(worker.getTaskDefName()).thenReturn("task_explicit_do_not_run_always"); + when(worker.execute(any())).thenReturn(new TaskResult(task)); + + TaskClient taskClient = Mockito.mock(TaskClient.class); + when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) + .thenReturn(Arrays.asList(task)); + + TaskPollExecutor taskPollExecutor = + new TaskPollExecutor( + client, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); + CountDownLatch latch = new CountDownLatch(1); + doAnswer( + invocation -> { + Object[] args = invocation.getArguments(); + TaskResult result = (TaskResult) args[0]; + assertEquals(IN_PROGRESS, result.getStatus()); + assertEquals(task.getTaskId(), result.getTaskId()); + latch.countDown(); + return null; + }) + .when(taskClient) + .updateTask(any()); + + ScheduledFuture f = + Executors.newSingleThreadScheduledExecutor() + .schedule( + () -> taskPollExecutor.pollAndExecute(worker), 0, TimeUnit.SECONDS); + + f.get(); + verify(taskClient, times(0)).updateTask(any()); + } + + @Test + public void testPollOutOfDiscoveryIsIgnoredWhenDiscoveryIsUp() throws InterruptedException { + Task task = testTask(); + + EurekaClient client = mock(EurekaClient.class); + when(client.getInstanceRemoteStatus()).thenReturn(InstanceInfo.InstanceStatus.UP); + + Worker worker = mock(Worker.class); + when(worker.getPollingInterval()).thenReturn(3000); + when(worker.getTaskDefName()).thenReturn("task_ignore_override"); + when(worker.execute(any())).thenReturn(new TaskResult(task)); + + TaskClient taskClient = Mockito.mock(TaskClient.class); + when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) + .thenReturn(Arrays.asList(task)); + + TaskPollExecutor taskPollExecutor = + new TaskPollExecutor( + client, + taskClient, + 1, + new HashMap<>(), + "test-worker-", + Collections.singletonMap("task_ignore_override", 1)); + CountDownLatch latch = new CountDownLatch(1); + doAnswer( + invocation -> { + Object[] args = invocation.getArguments(); + TaskResult result = (TaskResult) args[0]; + assertEquals(IN_PROGRESS, result.getStatus()); + assertEquals(task.getTaskId(), result.getTaskId()); + latch.countDown(); + return null; + }) + .when(taskClient) + .updateTask(any()); + + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate( + () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); + + latch.await(); + verify(taskClient).updateTask(any()); + } + + @Test + public void testTaskThreadCount() throws InterruptedException { + TaskClient taskClient = Mockito.mock(TaskClient.class); + + Map taskThreadCount = new HashMap<>(); + taskThreadCount.put(TEST_TASK_DEF_NAME, 1); + + TaskPollExecutor taskPollExecutor = + new TaskPollExecutor( + null, taskClient, -1, new HashMap<>(), "test-worker-", taskThreadCount); + + String workerName = "test-worker"; + Worker worker = mock(Worker.class); + when(worker.getTaskDefName()).thenReturn(TEST_TASK_DEF_NAME); + when(worker.getIdentity()).thenReturn(workerName); + + CountDownLatch latch = new CountDownLatch(1); + doAnswer( + invocation -> { + latch.countDown(); + return null; + }) + .when(taskClient) + .batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt()); + + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate( + () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); + + latch.await(); + verify(taskClient).batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt()); + } + + @Test + public void testTaskLeaseExtend() throws InterruptedException { + Task task = testTask(); + task.setResponseTimeoutSeconds(1); + + Worker worker = mock(Worker.class); + when(worker.getPollingInterval()).thenReturn(3000); + when(worker.getTaskDefName()).thenReturn("test"); + when(worker.execute(any())).thenReturn(new TaskResult(task)); + when(worker.leaseExtendEnabled()).thenReturn(true); + + TaskClient taskClient = Mockito.mock(TaskClient.class); + when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) + .thenReturn(Arrays.asList(task)); + + TaskResult result = new TaskResult(task); + result.getLogs().add(new TaskExecLog("lease extend")); + result.setExtendLease(true); + + TaskPollExecutor taskPollExecutor = + new TaskPollExecutor( + null, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); + CountDownLatch latch = new CountDownLatch(1); + doAnswer( + invocation -> { + assertTrue( + taskPollExecutor.leaseExtendMap.containsKey(task.getTaskId())); + latch.countDown(); + return null; + }) + .when(taskClient) + .updateTask(any()); + + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate( + () -> taskPollExecutor.pollAndExecute(worker), 0, 5, TimeUnit.SECONDS); + + latch.await(); + } + + @Test + public void testBatchTasksExecution() throws InterruptedException { + int threadCount = 10; + TaskClient taskClient = Mockito.mock(TaskClient.class); + Map taskThreadCount = new HashMap<>(); + taskThreadCount.put(TEST_TASK_DEF_NAME, threadCount); + + String workerName = "test-worker"; + Worker worker = mock(Worker.class); + when(worker.getPollingInterval()).thenReturn(3000); + when(worker.getBatchPollTimeoutInMS()).thenReturn(1000); + when(worker.getTaskDefName()).thenReturn(TEST_TASK_DEF_NAME); + when(worker.getIdentity()).thenReturn(workerName); + + List tasks = new ArrayList<>(); + for (int i = 0; i < threadCount; i++) { + Task task = testTask(); + tasks.add(task); + + when(worker.execute(task)) + .thenAnswer( + new Answer() { + Map outputMap = new HashMap<>(); + + public TaskResult answer(InvocationOnMock invocation) + throws InterruptedException { + // Sleep for 1 seconds to simulate task execution + Thread.sleep(1000L); + TaskResult taskResult = new TaskResult(task); + outputMap.put("key", "value"); + taskResult.setOutputData(outputMap); + taskResult.setStatus(COMPLETED); + return taskResult; + } + }); + } + when(taskClient.batchPollTasksInDomain( + TEST_TASK_DEF_NAME, null, workerName, threadCount, 1000)) + .thenReturn(tasks); + when(taskClient.ack(any(), any())).thenReturn(true); + + TaskPollExecutor taskPollExecutor = + new TaskPollExecutor( + null, taskClient, 1, new HashMap<>(), "test-worker-", taskThreadCount); + + CountDownLatch latch = new CountDownLatch(threadCount); + doAnswer( + new Answer() { + public TaskResult answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + TaskResult result = (TaskResult) args[0]; + assertEquals(COMPLETED, result.getStatus()); + assertEquals("value", result.getOutputData().get("key")); + latch.countDown(); + return null; + } + }) + .when(taskClient) + .updateTask(any()); + + Executors.newSingleThreadScheduledExecutor() + .scheduleAtFixedRate( + () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); + latch.await(); + + // execute() is called 10 times on the worker (once for each task) + verify(worker, times(threadCount)).execute(any()); + verify(taskClient, times(threadCount)).updateTask(any()); + } + + private Task testTask() { + Task task = new Task(); + task.setTaskId(UUID.randomUUID().toString()); + task.setStatus(Task.Status.IN_PROGRESS); + task.setTaskDefName(TEST_TASK_DEF_NAME); + return task; + } + */ +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java new file mode 100644 index 000000000..d6d269a71 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java @@ -0,0 +1,230 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.automator; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import com.netflix.conductor.client.exception.ConductorClientException; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; + +import static com.netflix.conductor.common.metadata.tasks.TaskResult.Status.COMPLETED; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TaskRunnerConfigurerTest { + + private static final String TEST_TASK_DEF_NAME = "test"; + + private TaskClient client; + + @Before + public void setup() { + client = Mockito.mock(TaskClient.class); + } + + @Test(expected = NullPointerException.class) + public void testNoWorkersException() { + new TaskRunnerConfigurer.Builder(null, null).build(); + } + + @Test(expected = ConductorClientException.class) + public void testInvalidThreadConfig() { + Worker worker1 = Worker.create("task1", TaskResult::new); + Worker worker2 = Worker.create("task2", TaskResult::new); + Map taskThreadCount = new HashMap<>(); + taskThreadCount.put(worker1.getTaskDefName(), 2); + taskThreadCount.put(worker2.getTaskDefName(), 3); + new TaskRunnerConfigurer.Builder(client, Arrays.asList(worker1, worker2)) + .withThreadCount(10) + .withTaskThreadCount(taskThreadCount) + .build(); + } + + @Test + public void testMissingTaskThreadConfig() { + Worker worker1 = Worker.create("task1", TaskResult::new); + Worker worker2 = Worker.create("task2", TaskResult::new); + Map taskThreadCount = new HashMap<>(); + taskThreadCount.put(worker1.getTaskDefName(), 2); + TaskRunnerConfigurer configurer = + new TaskRunnerConfigurer.Builder(client, Arrays.asList(worker1, worker2)) + .withTaskThreadCount(taskThreadCount) + .build(); + + assertFalse(configurer.getTaskThreadCount().isEmpty()); + assertEquals(2, configurer.getTaskThreadCount().size()); + assertEquals(2, configurer.getTaskThreadCount().get("task1").intValue()); + assertEquals(1, configurer.getTaskThreadCount().get("task2").intValue()); + } + + @Test + public void testPerTaskThreadPool() { + Worker worker1 = Worker.create("task1", TaskResult::new); + Worker worker2 = Worker.create("task2", TaskResult::new); + Map taskThreadCount = new HashMap<>(); + taskThreadCount.put(worker1.getTaskDefName(), 2); + taskThreadCount.put(worker2.getTaskDefName(), 3); + TaskRunnerConfigurer configurer = + new TaskRunnerConfigurer.Builder(client, Arrays.asList(worker1, worker2)) + .withTaskThreadCount(taskThreadCount) + .build(); + configurer.init(); + assertEquals(-1, configurer.getThreadCount()); + assertEquals(2, configurer.getTaskThreadCount().get("task1").intValue()); + assertEquals(3, configurer.getTaskThreadCount().get("task2").intValue()); + } + + @Test + public void testSharedThreadPool() { + Worker worker = Worker.create(TEST_TASK_DEF_NAME, TaskResult::new); + TaskRunnerConfigurer configurer = + new TaskRunnerConfigurer.Builder(client, Arrays.asList(worker, worker, worker)) + .build(); + configurer.init(); + assertEquals(3, configurer.getThreadCount()); + assertEquals(500, configurer.getSleepWhenRetry()); + assertEquals(3, configurer.getUpdateRetryCount()); + assertEquals(10, configurer.getShutdownGracePeriodSeconds()); + assertFalse(configurer.getTaskThreadCount().isEmpty()); + assertEquals(1, configurer.getTaskThreadCount().size()); + assertEquals(3, configurer.getTaskThreadCount().get(TEST_TASK_DEF_NAME).intValue()); + + configurer = + new TaskRunnerConfigurer.Builder(client, Collections.singletonList(worker)) + .withThreadCount(100) + .withSleepWhenRetry(100) + .withUpdateRetryCount(10) + .withShutdownGracePeriodSeconds(15) + .withWorkerNamePrefix("test-worker-") + .build(); + assertEquals(100, configurer.getThreadCount()); + configurer.init(); + assertEquals(100, configurer.getThreadCount()); + assertEquals(100, configurer.getSleepWhenRetry()); + assertEquals(10, configurer.getUpdateRetryCount()); + assertEquals(15, configurer.getShutdownGracePeriodSeconds()); + assertEquals("test-worker-", configurer.getWorkerNamePrefix()); + assertFalse(configurer.getTaskThreadCount().isEmpty()); + assertEquals(1, configurer.getTaskThreadCount().size()); + assertEquals(100, configurer.getTaskThreadCount().get(TEST_TASK_DEF_NAME).intValue()); + } + + @Test + public void testMultipleWorkersExecution() throws Exception { + String task1Name = "task1"; + Worker worker1 = mock(Worker.class); + when(worker1.getPollingInterval()).thenReturn(3000); + when(worker1.getTaskDefName()).thenReturn(task1Name); + when(worker1.getIdentity()).thenReturn("worker1"); + when(worker1.execute(any())) + .thenAnswer( + invocation -> { + // Sleep for 2 seconds to simulate task execution + Thread.sleep(2000); + TaskResult taskResult = new TaskResult(); + taskResult.setStatus(COMPLETED); + return taskResult; + }); + + String task2Name = "task2"; + Worker worker2 = mock(Worker.class); + when(worker2.getPollingInterval()).thenReturn(3000); + when(worker2.getTaskDefName()).thenReturn(task2Name); + when(worker2.getIdentity()).thenReturn("worker2"); + when(worker2.execute(any())) + .thenAnswer( + invocation -> { + // Sleep for 2 seconds to simulate task execution + Thread.sleep(2000); + TaskResult taskResult = new TaskResult(); + taskResult.setStatus(COMPLETED); + return taskResult; + }); + + Task task1 = testTask(task1Name); + Task task2 = testTask(task2Name); + TaskClient taskClient = Mockito.mock(TaskClient.class); + TaskRunnerConfigurer configurer = + new TaskRunnerConfigurer.Builder(taskClient, Arrays.asList(worker1, worker2)) + .withThreadCount(2) + .withSleepWhenRetry(100000) + .withUpdateRetryCount(1) + .withWorkerNamePrefix("test-worker-") + .build(); + when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) + .thenAnswer( + invocation -> { + Object[] args = invocation.getArguments(); + String taskName = args[0].toString(); + if (taskName.equals(task1Name)) { + return Arrays.asList(task1); + } else if (taskName.equals(task2Name)) { + return Arrays.asList(task2); + } else { + return Collections.emptyList(); + } + }); + when(taskClient.ack(any(), any())).thenReturn(true); + + AtomicInteger task1Counter = new AtomicInteger(0); + AtomicInteger task2Counter = new AtomicInteger(0); + CountDownLatch latch = new CountDownLatch(2); + doAnswer( + invocation -> { + Object[] args = invocation.getArguments(); + TaskResult result = (TaskResult) args[0]; + assertEquals(COMPLETED, result.getStatus()); + if (result.getWorkerId().equals("worker1")) { + task1Counter.incrementAndGet(); + } else if (result.getWorkerId().equals("worker2")) { + task2Counter.incrementAndGet(); + } + latch.countDown(); + return null; + }) + .when(taskClient) + .updateTask(any()); + configurer.init(); + latch.await(); + + assertEquals(1, task1Counter.get()); + assertEquals(1, task2Counter.get()); + } + + private Task testTask(String taskDefName) { + Task task = new Task(); + task.setTaskId(UUID.randomUUID().toString()); + task.setStatus(Task.Status.IN_PROGRESS); + task.setTaskDefName(taskDefName); + return task; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java new file mode 100644 index 000000000..732e3dc54 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java @@ -0,0 +1,72 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.config; + +import org.junit.Test; + +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.metadata.tasks.TaskResult; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class TestPropertyFactory { + + @Test + public void testIdentity() { + Worker worker = Worker.create("Test2", TaskResult::new); + assertNotNull(worker.getIdentity()); + boolean paused = worker.paused(); + assertFalse("Paused? " + paused, paused); + } + + @Test + public void test() { + + int val = PropertyFactory.getInteger("workerB", "pollingInterval", 100); + assertEquals("got: " + val, 2, val); + assertEquals( + 100, PropertyFactory.getInteger("workerB", "propWithoutValue", 100).intValue()); + + assertFalse( + PropertyFactory.getBoolean( + "workerB", "paused", true)); // Global value set to 'false' + assertTrue( + PropertyFactory.getBoolean( + "workerA", "paused", false)); // WorkerA value set to 'true' + + assertEquals( + 42, + PropertyFactory.getInteger("workerA", "batchSize", 42) + .intValue()); // No global value set, so will return the default value + // supplied + assertEquals( + 84, + PropertyFactory.getInteger("workerB", "batchSize", 42) + .intValue()); // WorkerB's value set to 84 + + assertEquals("domainA", PropertyFactory.getString("workerA", "domain", null)); + assertEquals("domainB", PropertyFactory.getString("workerB", "domain", null)); + assertNull(PropertyFactory.getString("workerC", "domain", null)); // Non Existent + } + + @Test + public void testProperty() { + Worker worker = Worker.create("Test", TaskResult::new); + boolean paused = worker.paused(); + assertTrue("Paused? " + paused, paused); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/Main.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/Main.java new file mode 100644 index 000000000..00e64a3ea --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/Main.java @@ -0,0 +1,44 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.sample; + +import java.util.Arrays; + +import com.netflix.conductor.client.automator.TaskRunnerConfigurer; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.worker.Worker; + +public class Main { + + public static void main(String[] args) { + + TaskClient taskClient = new TaskClient(); + taskClient.setRootURI("http://localhost:8080/api/"); // Point this to the server API + + int threadCount = + 2; // number of threads used to execute workers. To avoid starvation, should be + // same or more than number of workers + + Worker worker1 = new SampleWorker("task_1"); + Worker worker2 = new SampleWorker("task_5"); + + // Create TaskRunnerConfigurer + TaskRunnerConfigurer configurer = + new TaskRunnerConfigurer.Builder(taskClient, Arrays.asList(worker1, worker2)) + .withThreadCount(threadCount) + .build(); + + // Start the polling and execution of tasks + configurer.init(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java new file mode 100644 index 000000000..f05bd6629 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java @@ -0,0 +1,45 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.sample; + +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.common.metadata.tasks.TaskResult.Status; + +public class SampleWorker implements Worker { + + private final String taskDefName; + + public SampleWorker(String taskDefName) { + this.taskDefName = taskDefName; + } + + @Override + public String getTaskDefName() { + return taskDefName; + } + + @Override + public TaskResult execute(Task task) { + TaskResult result = new TaskResult(task); + result.setStatus(Status.COMPLETED); + + // Register the output of the task + result.getOutputData().put("outputKey1", "value"); + result.getOutputData().put("oddEven", 1); + result.getOutputData().put("mod", 4); + + return result; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java new file mode 100644 index 000000000..de6cdc592 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java @@ -0,0 +1,134 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.testing; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.MetadataClient; +import com.netflix.conductor.client.http.WorkflowClient; +import com.netflix.conductor.common.config.ObjectMapperProvider; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.common.run.WorkflowTestRequest; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public abstract class AbstractWorkflowTests { + + protected static ObjectMapper objectMapper = new ObjectMapperProvider().getObjectMapper(); + + protected static TypeReference>> mockType = + new TypeReference>>() {}; + + protected MetadataClient metadataClient; + + protected WorkflowClient workflowClient; + + @BeforeAll + public void setup() { + String baseURL = "http://localhost:8080/api/"; + ConductorClient apiClient = new ConductorClient(baseURL); + metadataClient = new MetadataClient(apiClient); + workflowClient = new WorkflowClient(apiClient); + } + + protected WorkflowTestRequest getWorkflowTestRequest(WorkflowDef def) throws IOException { + WorkflowTestRequest testRequest = new WorkflowTestRequest(); + testRequest.setInput(new HashMap<>()); + testRequest.setName(def.getName()); + testRequest.setVersion(def.getVersion()); + testRequest.setWorkflowDef(def); + + Map> taskRefToMockOutput = new HashMap<>(); + for (WorkflowTask task : def.collectTasks()) { + List taskRuns = new LinkedList<>(); + WorkflowTestRequest.TaskMock mock = new WorkflowTestRequest.TaskMock(); + mock.setStatus(TaskResult.Status.COMPLETED); + Map output = new HashMap<>(); + + output.put("response", Map.of()); + mock.setOutput(output); + taskRuns.add(mock); + taskRefToMockOutput.put(task.getTaskReferenceName(), taskRuns); + + if (task.getType().equals(TaskType.SUB_WORKFLOW.name())) { + Object inlineSubWorkflowDefObj = task.getSubWorkflowParam().getWorkflowDefinition(); + if (inlineSubWorkflowDefObj != null) { + // If not null, it represents WorkflowDef object + WorkflowDef inlineSubWorkflowDef = (WorkflowDef) inlineSubWorkflowDefObj; + WorkflowTestRequest subWorkflowTestRequest = + getWorkflowTestRequest(inlineSubWorkflowDef); + testRequest + .getSubWorkflowTestRequest() + .put(task.getTaskReferenceName(), subWorkflowTestRequest); + } else { + // Inline definition is null + String subWorkflowName = task.getSubWorkflowParam().getName(); + // Load up the sub workflow from the JSON + WorkflowDef subWorkflowDef = + getWorkflowDef("/workflows/" + subWorkflowName + ".json"); + assertNotNull(subWorkflowDef); + WorkflowTestRequest subWorkflowTestRequest = + getWorkflowTestRequest(subWorkflowDef); + testRequest + .getSubWorkflowTestRequest() + .put(task.getTaskReferenceName(), subWorkflowTestRequest); + } + } + } + testRequest.setTaskRefToMockOutput(taskRefToMockOutput); + return testRequest; + } + + protected WorkflowDef getWorkflowDef(String path) throws IOException { + InputStream inputStream = AbstractWorkflowTests.class.getResourceAsStream(path); + if (inputStream == null) { + throw new IOException("No file found at " + path); + } + return objectMapper.readValue(new InputStreamReader(inputStream), WorkflowDef.class); + } + + protected Workflow getWorkflow(String path) throws IOException { + InputStream inputStream = AbstractWorkflowTests.class.getResourceAsStream(path); + if (inputStream == null) { + throw new IOException("No file found at " + path); + } + return objectMapper.readValue(new InputStreamReader(inputStream), Workflow.class); + } + + protected Map> getTestInputs(String path) + throws IOException { + InputStream inputStream = AbstractWorkflowTests.class.getResourceAsStream(path); + if (inputStream == null) { + throw new IOException("No file found at " + path); + } + return objectMapper.readValue(new InputStreamReader(inputStream), mockType); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java new file mode 100644 index 000000000..5aa857ad4 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.testing; + +import java.math.BigDecimal; + +public class LoanWorkflowInput { + + private String userEmail; + + private BigDecimal loanAmount; + + public String getUserEmail() { + return userEmail; + } + + public void setUserEmail(String userEmail) { + this.userEmail = userEmail; + } + + public BigDecimal getLoanAmount() { + return loanAmount; + } + + public void setLoanAmount(BigDecimal loanAmount) { + this.loanAmount = loanAmount; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java new file mode 100644 index 000000000..1d8b66de3 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java @@ -0,0 +1,108 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.testing; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.common.run.WorkflowTestRequest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** Unit test a workflow with inputs read from a file. */ +public class LoanWorkflowTest extends AbstractWorkflowTests { + + /** Uses mock inputs to verify the workflow execution and input/outputs of the tasks */ + // Tests are commented out since it requires a running server + // @Test + public void verifyWorkflowExecutionWithMockInputs() throws IOException { + WorkflowDef def = getWorkflowDef("/workflows/calculate_loan_workflow.json"); + assertNotNull(def); + Map> testInputs = + getTestInputs("/test_data/loan_workflow_input.json"); + assertNotNull(testInputs); + + WorkflowTestRequest testRequest = new WorkflowTestRequest(); + testRequest.setWorkflowDef(def); + + LoanWorkflowInput workflowInput = new LoanWorkflowInput(); + workflowInput.setUserEmail("user@example.com"); + workflowInput.setLoanAmount(new BigDecimal(11_000)); + testRequest.setInput(objectMapper.convertValue(workflowInput, Map.class)); + + testRequest.setTaskRefToMockOutput(testInputs); + testRequest.setName(def.getName()); + testRequest.setVersion(def.getVersion()); + + Workflow execution = workflowClient.testWorkflow(testRequest); + assertNotNull(execution); + + // Assert that the workflow completed successfully + assertEquals(Workflow.WorkflowStatus.COMPLETED, execution.getStatus()); + + // Ensure the inputs were captured correctly + assertEquals( + workflowInput.getLoanAmount().toString(), + String.valueOf(execution.getInput().get("loanAmount"))); + assertEquals(workflowInput.getUserEmail(), execution.getInput().get("userEmail")); + + // A total of 3 tasks were executed + assertEquals(3, execution.getTasks().size()); + + Task fetchUserDetails = execution.getTasks().get(0); + Task getCreditScore = execution.getTasks().get(1); + Task calculateLoanAmount = execution.getTasks().get(2); + + // fetch user details received the correct input from the workflow + assertEquals( + workflowInput.getUserEmail(), fetchUserDetails.getInputData().get("userEmail")); + + // And that the task produced the right output + int userAccountNo = 12345; + assertEquals(userAccountNo, fetchUserDetails.getOutputData().get("userAccount")); + + // get credit score received the right account number from the output of the fetch user + // details + assertEquals(userAccountNo, getCreditScore.getInputData().get("userAccountNumber")); + int expectedCreditRating = 750; + + // The task produced the right output + assertEquals(expectedCreditRating, getCreditScore.getOutputData().get("creditRating")); + + // Calculate loan amount gets the right loan amount from workflow input + assertEquals( + workflowInput.getLoanAmount().toString(), + String.valueOf(calculateLoanAmount.getInputData().get("loanAmount"))); + + // Calculate loan amount gets the right credit rating from the previous task + assertEquals(expectedCreditRating, calculateLoanAmount.getInputData().get("creditRating")); + + int authorizedLoanAmount = 10_000; + assertEquals( + authorizedLoanAmount, + calculateLoanAmount.getOutputData().get("authorizedLoanAmount")); + + // Finally, lets verify the workflow outputs + assertEquals(userAccountNo, execution.getOutput().get("accountNumber")); + assertEquals(expectedCreditRating, execution.getOutput().get("creditRating")); + assertEquals(authorizedLoanAmount, execution.getOutput().get("authorizedLoanAmount")); + + System.out.println(execution); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java new file mode 100644 index 000000000..207c26e10 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.testing; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.common.run.WorkflowTestRequest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * This test demonstrates how to use execution data from the previous executed workflows as golden + * input and output and use them to regression test the workflow definition. + * + *

Regression tests are useful ensuring any changes to the workflow definition does not change + * the behavior. + */ +public class RegressionTest extends AbstractWorkflowTests { + + // @Test + // Tests are commented out since it requires a running server + // Uses a previously executed successful run to verify the workflow execution, and it's output. + public void verifyWorkflowOutput() + throws IOException, ExecutionException, InterruptedException, TimeoutException { + // Workflow Definition + WorkflowDef def = getWorkflowDef("/workflows/workflow1.json"); + + // Golden output to verify against + Workflow workflow = getWorkflow("/test_data/workflow1_run.json"); + + WorkflowTestRequest testRequest = new WorkflowTestRequest(); + testRequest.setInput(new HashMap<>()); + testRequest.setName(def.getName()); + testRequest.setVersion(def.getVersion()); + testRequest.setWorkflowDef(def); + + Map> taskRefToMockOutput = new HashMap<>(); + for (Task task : workflow.getTasks()) { + List taskRuns = new ArrayList<>(); + WorkflowTestRequest.TaskMock mock = new WorkflowTestRequest.TaskMock(); + mock.setStatus(TaskResult.Status.valueOf(task.getStatus().name())); + mock.setOutput(task.getOutputData()); + taskRuns.add(mock); + taskRefToMockOutput.put(def.getTasks().get(0).getTaskReferenceName(), taskRuns); + } + + testRequest.setTaskRefToMockOutput(taskRefToMockOutput); + Workflow execution = workflowClient.testWorkflow(testRequest); + assertNotNull(execution); + assertEquals(workflow.getTasks().size(), execution.getTasks().size()); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java new file mode 100644 index 000000000..937b750ab --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.testing; + +import java.io.IOException; +import java.util.List; + +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.common.run.WorkflowTestRequest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** Demonstrates how to test workflows that contain sub-workflows */ +public class SubWorkflowTest extends AbstractWorkflowTests { + + // @Test + // Tests are commented out since it requires a running server + public void verifySubWorkflowExecutions() throws IOException { + WorkflowDef def = getWorkflowDef("/workflows/kitchensink.json"); + assertNotNull(def); + + WorkflowDef subWorkflowDef = getWorkflowDef("/workflows/PopulationMinMax.json"); + metadataClient.registerWorkflowDef(subWorkflowDef); + + WorkflowTestRequest testRequest = getWorkflowTestRequest(def); + + // The following are the dynamic tasks which are not present in the workflow definition but + // are created by dynamic fork + testRequest + .getTaskRefToMockOutput() + .put("_x_test_worker_0_0", List.of(new WorkflowTestRequest.TaskMock())); + testRequest + .getTaskRefToMockOutput() + .put("_x_test_worker_0_1", List.of(new WorkflowTestRequest.TaskMock())); + testRequest + .getTaskRefToMockOutput() + .put("_x_test_worker_0_2", List.of(new WorkflowTestRequest.TaskMock())); + testRequest + .getTaskRefToMockOutput() + .put("simple_task_1__1", List.of(new WorkflowTestRequest.TaskMock())); + testRequest + .getTaskRefToMockOutput() + .put("simple_task_5", List.of(new WorkflowTestRequest.TaskMock())); + + Workflow execution = workflowClient.testWorkflow(testRequest); + assertNotNull(execution); + + // Verfiy that the workflow COMPLETES + assertEquals(Workflow.WorkflowStatus.COMPLETED, execution.getStatus()); + + // That the workflow executes a wait task + assertTrue( + execution.getTasks().stream() + .anyMatch(t -> t.getReferenceTaskName().equals("wait"))); + + // That the call_made variable was set to True + assertEquals(true, execution.getVariables().get("call_made")); + + // Total number of tasks executed are 17 + assertEquals(17, execution.getTasks().size()); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java new file mode 100644 index 000000000..72ffb7f36 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java @@ -0,0 +1,75 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.worker; + +import java.io.InputStream; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import com.netflix.conductor.common.config.ObjectMapperProvider; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class TestWorkflowTask { + + private ObjectMapper objectMapper; + + @Before + public void setup() { + objectMapper = new ObjectMapperProvider().getObjectMapper(); + } + + @Test + public void test() throws Exception { + WorkflowTask task = new WorkflowTask(); + task.setType("Hello"); + task.setName("name"); + + String json = objectMapper.writeValueAsString(task); + + WorkflowTask read = objectMapper.readValue(json, WorkflowTask.class); + assertNotNull(read); + assertEquals(task.getName(), read.getName()); + assertEquals(task.getType(), read.getType()); + + task = new WorkflowTask(); + task.setWorkflowTaskType(TaskType.SUB_WORKFLOW); + task.setName("name"); + + json = objectMapper.writeValueAsString(task); + + read = objectMapper.readValue(json, WorkflowTask.class); + assertNotNull(read); + assertEquals(task.getName(), read.getName()); + assertEquals(task.getType(), read.getType()); + assertEquals(TaskType.SUB_WORKFLOW.name(), read.getType()); + } + + @SuppressWarnings("unchecked") + @Test + public void testObjectMapper() throws Exception { + try (InputStream stream = TestWorkflowTask.class.getResourceAsStream("/tasks.json")) { + List tasks = objectMapper.readValue(stream, List.class); + assertNotNull(tasks); + assertEquals(1, tasks.size()); + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/config.properties b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/config.properties new file mode 100644 index 000000000..93fd67347 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/config.properties @@ -0,0 +1,11 @@ +conductor.worker.pollingInterval=2 +conductor.worker.paused=false +conductor.worker.workerA.paused=true +conductor.worker.workerA.domain=domainA +conductor.worker.workerB.batchSize=84 +conductor.worker.workerB.domain=domainB +conductor.worker.Test.paused=true +conductor.worker.domainTestTask2.domain=visinghDomain +conductor.worker.task_run_always.pollOutOfDiscovery=true +conductor.worker.task_explicit_do_not_run_always.pollOutOfDiscovery=false +conductor.worker.task_ignore_override.pollOutOfDiscovery=true \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/tasks.json b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/tasks.json new file mode 100644 index 000000000..424b4880e --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/tasks.json @@ -0,0 +1,70 @@ +[ + { + "taskType": "task_1", + "status": "IN_PROGRESS", + "inputData": { + "mod": null, + "oddEven": null + }, + "referenceTaskName": "task_1", + "retryCount": 0, + "seq": 1, + "pollCount": 1, + "taskDefName": "task_1", + "scheduledTime": 1539623183131, + "startTime": 1539623436841, + "endTime": 0, + "updateTime": 1539623436841, + "startDelayInSeconds": 0, + "retried": false, + "executed": false, + "callbackFromWorker": true, + "responseTimeoutSeconds": 0, + "workflowInstanceId": "2d525ed8-d0e5-44c8-a2df-a110b25c09ac", + "workflowType": "kitchensink", + "taskId": "bc5d9deb-cf86-443d-a1f6-59c36d2464f7", + "callbackAfterSeconds": 0, + "workerId": "test", + "workflowTask": { + "name": "task_1", + "taskReferenceName": "task_1", + "inputParameters": { + "mod": "${workflow.input.mod}", + "oddEven": "${workflow.input.oddEven}" + }, + "type": "SIMPLE", + "startDelay": 0, + "optional": false, + "taskDefinition": { + "ownerApp": "falguni-test", + "createTime": 1534274994644, + "createdBy": "CPEWORKFLOW", + "name": "task_1", + "description": "Test Task 01", + "retryCount": 0, + "timeoutSeconds": 5, + "inputKeys": [ + "mod", + "oddEven" + ], + "outputKeys": [ + "someOutput" + ], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 0, + "responseTimeoutSeconds": 0, + "concurrentExecLimit": 0, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1 + } + }, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 0, + "taskDefinition": { + "present": true + }, + "queueWaitTime": 253710, + "taskStatus": "IN_PROGRESS" + } +] \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/test_data/loan_workflow_input.json b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/test_data/loan_workflow_input.json new file mode 100644 index 000000000..d9bd04d57 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/test_data/loan_workflow_input.json @@ -0,0 +1,20 @@ +{ + "fetch_user_details": [{ + "status": "COMPLETED", + "output": { + "userAccount": 12345 + } + }], + "get_credit_score": [{ + "status": "COMPLETED", + "output": { + "creditRating": 750 + } + }], + "calculate_loan_amount": [{ + "status": "COMPLETED", + "output": { + "authorizedLoanAmount": 10000 + } + }] +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/test_data/workflow1_run.json b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/test_data/workflow1_run.json new file mode 100644 index 000000000..0e29164fd --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/test_data/workflow1_run.json @@ -0,0 +1,190 @@ +{ + "createTime": 1675903039613, + "updateTime": 1675903040396, + "createdBy": "", + "updatedBy": "", + "status": "COMPLETED", + "endTime": 1675903040396, + "workflowId": "e90bd2d6-a811-11ed-bd43-f84d89b1eac3", + "parentWorkflowId": "", + "parentWorkflowTaskId": "", + "tasks": [ + { + "taskType": "HTTP", + "status": "COMPLETED", + "inputData": { + "asyncComplete": false, + "http_request": { + "method": "GET", + "readTimeOut": 3000, + "uri": "https://catfact.ninja/fact", + "connectionTimeOut": 3000 + } + }, + "referenceTaskName": "get_random_fact", + "retryCount": 0, + "seq": 1, + "pollCount": 1, + "taskDefName": "get_random_fact", + "scheduledTime": 1675903039616, + "startTime": 1675903039623, + "endTime": 1675903040391, + "updateTime": 1675903039623, + "startDelayInSeconds": 0, + "retried": false, + "executed": false, + "callbackFromWorker": true, + "responseTimeoutSeconds": 0, + "workflowInstanceId": "e90bd2d6-a811-11ed-bd43-f84d89b1eac3", + "workflowType": "test_http", + "taskId": "e90c4807-a811-11ed-bd43-f84d89b1eac3", + "callbackAfterSeconds": 0, + "workerId": "n33l3", + "outputData": { + "response": { + "headers": { + "Transfer-Encoding": [ + "chunked" + ], + "Server": [ + "nginx" + ], + "X-Ratelimit-Remaining": [ + "99" + ], + "Access-Control-Allow-Origin": [ + "*" + ], + "X-Content-Type-Options": [ + "nosniff" + ], + "Connection": [ + "keep-alive" + ], + "Date": [ + "Thu, 09 Feb 2023 00:37:20 GMT" + ], + "X-Frame-Options": [ + "SAMEORIGIN" + ], + "X-Ratelimit-Limit": [ + "100" + ], + "Cache-Control": [ + "no-cache, private" + ], + "Vary": [ + "Accept-Encoding" + ], + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IjNNRUJ4ellWaU1HSWdGMzNWWlEzdXc9PSIsInZhbHVlIjoiYUozemozNWJJUERFVzZ2QU5TTFdGdS9oY3krclg2dWlKa0oza3gwUFlrNTd4L2YydWFSYjFKTjNzdFArRmlIL2lHbGEvU2tnbm0vWjZGZDVuMWx6UEVaT1AwNlM5REp1b0dMaWFTZldYN1FSblJQSFZSalREWXhiVUVrNUpMYXAiLCJtYWMiOiI1MjFjOWY2MDFhNWZkMDFlOGNjYjE0MmU1YmU1MGEwODQ3ZTBjNTdkMzRiZWMzYWQyMjk3NzFkNGYwYTU5NWVlIiwidGFnIjoiIn0%3D; expires=Thu, 09-Feb-2023 02:37:20 GMT; path=/; samesite=lax", + "catfacts_session=eyJpdiI6IjRkMThJVWZFRnREdWExWHZ5Q0k0cEE9PSIsInZhbHVlIjoiOXZFYzJsb3IvUGZFV2tQSUVNTEVzMDJjTGRzczl2bXhtRW1PUytxTERITHp3b3dNQlBtdXlwdENMcThXTU82S1JBOHlJMU01ZlBoYUVPeG1ETmhRZEFaUDFOU0pxdHFXQ0xEWUhTQXFpcSt0SC82dmNsSDFWdmxpUFFyUjM1c3EiLCJtYWMiOiIwNzRhYTRiZjA5Nzg5M2NmMGE1NjIxMDk0NGYwNjE3MDYyZmJmMTRmYzExZGMzYWI1MTQwOWYyZjMzZGFjOGZiIiwidGFnIjoiIn0%3D; expires=Thu, 09-Feb-2023 02:37:20 GMT; path=/; httponly; samesite=lax" + ], + "X-XSS-Protection": [ + "1; mode=block" + ], + "Content-Type": [ + "application/json" + ] + }, + "reasonPhrase": "OK", + "body": { + "fact": "Cats' hearing stops at 65 khz (kilohertz); humans' hearing stops at 20 khz.", + "length": 75 + }, + "statusCode": 200 + } + }, + "workflowTask": { + "name": "get_random_fact", + "taskReferenceName": "get_random_fact", + "inputParameters": { + "asyncComplete": false, + "http_request": { + "method": "GET", + "readTimeOut": 3000, + "uri": "https://catfact.ninja/fact", + "connectionTimeOut": 3000 + } + }, + "type": "HTTP", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "rateLimited": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 0, + "workflowPriority": 0, + "iteration": 0, + "subworkflowChanged": false, + "loopOverTask": false, + "taskDefinition": null, + "queueWaitTime": 7 + } + ], + "input": { + "_X-Request-Id": "9aceedeb-3b3c-4074-8cad-79edaac3809b", + "_X-Host-Id": "localhost" + }, + "output": { + "data": "Cats' hearing stops at 65 khz (kilohertz); humans' hearing stops at 20 khz." + }, + "taskToDomain": {}, + "failedReferenceTaskNames": [], + "workflowDefinition": { + "name": "test_http", + "description": "v1", + "version": 1, + "tasks": [ + { + "name": "get_random_fact", + "taskReferenceName": "get_random_fact", + "inputParameters": { + "asyncComplete": false, + "http_request": { + "method": "GET", + "readTimeOut": 3000, + "uri": "https://catfact.ninja/fact", + "connectionTimeOut": 3000 + } + }, + "type": "HTTP", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "rateLimited": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [], + "outputParameters": { + "data": "${get_random_fact.output.response.body.fact}" + }, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "ownerEmail": "user@orkes.io", + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "variables": {}, + "inputTemplate": {} + }, + "priority": 0, + "variables": {}, + "lastRetriedTime": 0, + "startTime": 1675903039613, + "workflowVersion": 1, + "workflowName": "test_http" +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/PopulationMinMax.json b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/PopulationMinMax.json new file mode 100644 index 000000000..a44aae1dc --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/PopulationMinMax.json @@ -0,0 +1,38 @@ +{ + "createTime": 1670136356629, + "updateTime": 1670136356636, + "name": "PopulationMinMax", + "description": "Edit or extend this sample workflow. Set the workflow name to get started", + "version": 1, + "tasks": [ + { + "name": "set_variable_task_jqc56h_ref", + "taskReferenceName": "set_variable_task_jqc56h_ref", + "inputParameters": { + "name": "Orkes" + }, + "type": "SET_VARIABLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [], + "outputParameters": { + "data": "${get_random_fact.output.response.body.fact}" + }, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "ownerEmail": "user@orkes.io", + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "variables": {}, + "inputTemplate": {} +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/calculate_loan_workflow.json b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/calculate_loan_workflow.json new file mode 100644 index 000000000..a4c542b91 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/calculate_loan_workflow.json @@ -0,0 +1,41 @@ +{ + "name": "test_workflow", + "description": "Edit or extend this sample workflow. Set the workflow name to get started", + "version": 1, + "tasks": [ + { + "name": "fetch_user_details", + "taskReferenceName": "fetch_user_details", + "type": "SIMPLE", + "inputParameters": { + "userEmail": "${workflow.input.userEmail}" + } + }, + { + "name": "get_credit_score", + "taskReferenceName": "get_credit_score", + "type": "SIMPLE", + "inputParameters": { + "userAccountNumber": "${fetch_user_details.output.userAccount}" + } + }, + { + "name": "calculate_loan_amount", + "taskReferenceName": "calculate_loan_amount", + "type": "SIMPLE", + "inputParameters": { + "creditRating": "${get_credit_score.output.creditRating}", + "loanAmount": "${workflow.input.loanAmount}" + } + } + ], + "inputParameters": [ + "userEmail" + ], + "outputParameters": { + "accountNumber": "${fetch_user_details.output.userAccount}", + "creditRating": "${get_credit_score.output.creditRating}", + "authorizedLoanAmount": "${calculate_loan_amount.output.authorizedLoanAmount}" + }, + "schemaVersion": 2 +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/kitchensink.json b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/kitchensink.json new file mode 100644 index 000000000..893c9685a --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/kitchensink.json @@ -0,0 +1,465 @@ +{ + "createTime": 1670136330055, + "updateTime": 1670176591044, + "name": "kitchensink", + "version": 1, + "tasks": [ + { + "name": "x_test_worker_2", + "taskReferenceName": "simple_task_0", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "jq", + "taskReferenceName": "jq", + "inputParameters": { + "key1": { + "value1": [ + "a", + "b" + ] + }, + "queryExpression": "{ key3: (.key1.value1 + .key2.value2) }", + "value2": [ + "d", + "e" + ] + }, + "type": "JSON_JQ_TRANSFORM", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "wait", + "taskReferenceName": "wait", + "inputParameters": { + "duration": "1 s" + }, + "type": "WAIT", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "set_state", + "taskReferenceName": "set_state", + "inputParameters": { + "call_made": true, + "number": "${simple_task_0.output.number}" + }, + "type": "SET_VARIABLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "sub_flow", + "taskReferenceName": "sub_flow", + "inputParameters": {}, + "type": "SUB_WORKFLOW", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "subWorkflowParam": { + "name": "PopulationMinMax" + }, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "dynamic_fork", + "taskReferenceName": "dynamic_fork", + "inputParameters": { + "forkTaskName": "x_test_worker_0", + "forkTaskInputs": [ + 1, + 2, + 3 + ] + }, + "type": "FORK_JOIN_DYNAMIC", + "decisionCases": {}, + "dynamicForkTasksParam": "forkedTasks", + "dynamicForkTasksInputParamName": "forkedTasksInputs", + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "dynamic_fork_join", + "taskReferenceName": "dynamic_fork_join", + "inputParameters": {}, + "type": "JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "fork", + "taskReferenceName": "fork", + "inputParameters": {}, + "type": "FORK_JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [ + [ + { + "name": "loop_until_success", + "taskReferenceName": "loop_until_success", + "inputParameters": { + "loop_count": 2 + }, + "type": "DO_WHILE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": true, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopCondition": "if ( $.loop_count['iteration'] < $.loop_until_success ) { true; } else { false; }", + "loopOver": [ + { + "name": "fact_length", + "taskReferenceName": "fact_length", + "description": "Fail if the fact is too short", + "inputParameters": { + "number": "${get_data.output.number}" + }, + "type": "SWITCH", + "decisionCases": { + "LONG": [ + { + "name": "x_test_worker_1", + "taskReferenceName": "simple_task_1", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "SHORT": [ + { + "name": "too_short", + "taskReferenceName": "too_short", + "inputParameters": { + "terminationReason": "value too short", + "terminationStatus": "FAILED" + }, + "type": "TERMINATE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ] + }, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "evaluatorType": "javascript", + "expression": "$.number < 15 ? 'LONG':'LONG'" + } + ] + }, + { + "name": "sub_flow_inline", + "taskReferenceName": "sub_flow_inline", + "inputParameters": {}, + "type": "SUB_WORKFLOW", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "subWorkflowParam": { + "name": "inline_sub", + "version": 1, + "workflowDefinition": { + "name": "inline_sub", + "version": 1, + "tasks": [ + { + "name": "x_test_worker_2", + "taskReferenceName": "simple_task_0", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "fact_length2", + "taskReferenceName": "fact_length2", + "description": "Fail if the fact is too short", + "inputParameters": { + "number": "${get_data.output.number}" + }, + "type": "SWITCH", + "decisionCases": { + "LONG": [ + { + "name": "x_test_worker_1", + "taskReferenceName": "simple_task_1", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "SHORT": [ + { + "name": "too_short", + "taskReferenceName": "too_short", + "inputParameters": { + "terminationReason": "value too short", + "terminationStatus": "FAILED" + }, + "type": "TERMINATE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ] + }, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "evaluatorType": "javascript", + "expression": "$.number < 15 ? 'LONG':'LONG'" + }, + { + "name": "sub_flow_inline_lvl2", + "taskReferenceName": "sub_flow_inline_lvl2", + "inputParameters": {}, + "type": "SUB_WORKFLOW", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "subWorkflowParam": { + "name": "inline_sub", + "version": 1, + "workflowDefinition": { + "name": "inline_sub", + "version": 1, + "tasks": [ + { + "name": "x_test_worker_2", + "taskReferenceName": "simple_task_0", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [], + "outputParameters": {}, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "variables": {}, + "inputTemplate": {} + } + }, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "taskDefinition": { + "name": "sub_flow_inline", + "description": "sub_flow_inline", + "retryCount": 0, + "timeoutSeconds": 3000, + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 20, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "pollTimeoutSeconds": 3600, + "backoffScaleFactor": 1 + } + } + ], + "inputParameters": [], + "outputParameters": {}, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "variables": {}, + "inputTemplate": {} + } + }, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "taskDefinition": { + "name": "sub_flow_inline", + "description": "sub_flow_inline", + "retryCount": 0, + "timeoutSeconds": 3000, + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 20, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "pollTimeoutSeconds": 3600, + "backoffScaleFactor": 1 + } + } + ], + [ + { + "name": "x_test_worker_1", + "taskReferenceName": "simple_task_5", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ] + ], + "startDelay": 0, + "joinOn": ["sub_flow_inline","simple_task_5"], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "fork_join", + "taskReferenceName": "fork_join", + "inputParameters": {}, + "type": "JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": ["simple_task_5","sub_flow_inline"], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [], + "outputParameters": {}, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "ownerEmail": "user@orkes.io", + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "variables": {}, + "inputTemplate": {} +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/workflow1.json b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/workflow1.json new file mode 100644 index 000000000..92a822bd0 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/resources/workflows/workflow1.json @@ -0,0 +1,43 @@ +{ + "createTime": 1674453020104, + "updateTime": 1674453020105, + "name": "test_http", + "description": "v1", + "version": 1, + "tasks": [ + { + "name": "get_random_fact", + "taskReferenceName": "get_random_fact", + "inputParameters": { + "http_request": { + "uri": "https://catfact.ninja/fact", + "method": "GET", + "connectionTimeOut": 3000, + "readTimeOut": 3000 + } + }, + "type": "HTTP", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [], + "outputParameters": { + "data": "${get_random_fact.output.response.body.fact}" + }, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "ownerEmail": "user@orkes.io", + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "variables": {}, + "inputTemplate": {} +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/examples/build.gradle b/conductor-clients/java/conductor-java-sdk/examples/build.gradle new file mode 100644 index 000000000..6ac27d88b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'java' +} + +group 'io.orkes.conductor' +version '3.0.0' + +repositories { + mavenCentral() +} + +dependencies { + implementation project(':conductor-client') + implementation project(':sdk') + implementation project(':orkes-client') + implementation "ch.qos.logback:logback-classic:1.5.6" + implementation 'io.micrometer:micrometer-registry-prometheus:1.10.5' + + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRegistration.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRegistration.java new file mode 100644 index 000000000..b7ccb6cb7 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRegistration.java @@ -0,0 +1,57 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples; + +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.MetadataClient; +import com.netflix.conductor.common.metadata.tasks.TaskDef; + +import io.orkes.conductor.sdk.examples.util.ClientUtil; + + +public class TaskRegistration { + + public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException { + ConductorClient client = ClientUtil.getClient(); + MetadataClient metadataClient = new MetadataClient(client); + + TaskDef taskDef = new TaskDef(); + taskDef.setName("task_with_retries"); + taskDef.setDescription("Example task definition"); + taskDef.setRetryCount(3); + taskDef.setRetryLogic(TaskDef.RetryLogic.FIXED); + + //only allow 3 tasks at a time to be in the IN_PROGRESS status + taskDef.setConcurrentExecLimit(3); + + //timeout the task if not polled within 60 seconds of scheduling + taskDef.setPollTimeoutSeconds(60); + + //timeout the task if the task does not COMPLETE in 2 minutes + taskDef.setTimeoutSeconds(120); + + //for the long running tasks, timeout if the task does not get updated in COMPLETED or IN_PROGRESS status in 60 seconds after the last update + taskDef.setResponseTimeoutSeconds(60); + + //only allow 100 executions in a 10-second window! -- Note, this is complementary to concurrent_exec_limit + taskDef.setRateLimitPerFrequency(100); + taskDef.setRateLimitFrequencyInSeconds(10); + taskDef.setOwnerEmail("exampes@conductor-oss.org"); + + metadataClient.registerTaskDefs(List.of(taskDef)); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRunner.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRunner.java new file mode 100644 index 000000000..7ae856624 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRunner.java @@ -0,0 +1,150 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples; + +import java.util.*; + +import com.netflix.conductor.client.automator.TaskRunnerConfigurer; +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; + +import io.orkes.conductor.sdk.examples.util.ClientUtil; + + +/** + * Task worker example. Shows how you can create a worker that polls and executes a SIMPLE task in + * the conductor workflow. + */ +public class TaskRunner { + public static void main(String[] args) { + ConductorClient client = ClientUtil.getClient(); + TaskClient taskClient = new TaskClient(client); + + // Add a list with Worker implementations + List workers = Arrays.asList(new TaskWorker(), new LongRunningTaskWorker()); + + TaskRunnerConfigurer.Builder builder = + new TaskRunnerConfigurer.Builder(taskClient, workers); + + // Map of task type to domain when polling for task from specific domains + Map taskToDomains = new HashMap<>(); + + // No. of threads for each task type. Used to configure taskType specific thread count for + // execution + Map taskThreadCount = new HashMap<>(); + + TaskRunnerConfigurer taskRunner = + builder.withThreadCount( + 10) // Default thread count if not specified in taskThreadCount + .withTaskToDomain(taskToDomains) + .withTaskThreadCount(taskThreadCount) + .withSleepWhenRetry( + 500) // Time in millis to sleep when retrying for a task update. + // Default is 500ms + // .withTaskPollTimeout(100) // Poll timeout for long-poll. Default is + // 100ms + .withWorkerNamePrefix( + "worker-") // Thread name prefix for the task worker executor. + // Useful for logging + .withUpdateRetryCount( + 3) // No. of times to retry if task update fails. defaults to 3 + .build(); + + // Start Polling for tasks and execute them + taskRunner.init(); + + // Optionally, use the shutdown method to stop polling + //taskRunner.shutdown(); + } + + /** Example Task Worker */ + private static class TaskWorker implements Worker { + + private static final String TASK_NAME = "simple_task"; + + @Override + public String getTaskDefName() { + return TASK_NAME; + } + + @Override + public TaskResult execute(Task task) { + // execute method is called once the task is scheduled in the workflow + // The method implements the business logic for the task. + + // Conductor provides at-least once guarantees + // The task can be rescheduled in (rare) case of transient failures on Network + // To handle such cases, the implementation should be idempotent + + // BUSINESS LOGIC GOES HERE + + // Create a TaskResult object to hold the result of the execution and status + TaskResult result = new TaskResult(task); + + // Capture the output of the task execution as output + result.getOutputData().put("key", "value"); + result.getOutputData().put("amount", 123.45); + + // The values can be a map or an array as well + result.getOutputData().put("key2", new HashMap<>()); + + // Set the status COMPLETED. + result.setStatus(TaskResult.Status.COMPLETED); + + return result; + } + } + + /** + * An example worker that is a long-running and periodically gets polled to check the status of + * a long-running task. A use case for such worker is when the actual work is happening via + * separate process that could take longer to execute (anywhere from minutes to days or months), + * e.g. database backup, running a background job that could take hours. In such cases, such + * tasks are polled every so often (e.g. hourly or daily) to check the status and remains in + * progress for much longer without holding up threads. + */ + private static class LongRunningTaskWorker implements Worker { + + private static final String TASK_NAME = "long_running_task"; + + @Override + public String getTaskDefName() { + return TASK_NAME; + } + + @Override + public TaskResult execute(Task task) { + // 1. Create a TaskResult object to hold the result of the execution and status + TaskResult result = new TaskResult(task); + + // Check the status of the long-running process + boolean isCompleted = isJobCompleted(); + if (isCompleted) { + // Let's complete the task + // Set the status COMPLETED. + result.setStatus(TaskResult.Status.COMPLETED); + } else { + // still in progress, let's check back in an hour + result.setCallbackAfterSeconds(60 * 60); + } + return result; + } + + private boolean isJobCompleted() { + return true; + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/events/EventListenerExample.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/events/EventListenerExample.java new file mode 100644 index 000000000..916d1260b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/events/EventListenerExample.java @@ -0,0 +1,105 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.events; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.List; + +import com.netflix.conductor.client.automator.TaskRunnerConfigurer; +import com.netflix.conductor.client.automator.events.PollCompleted; +import com.netflix.conductor.client.automator.events.PollFailure; +import com.netflix.conductor.client.automator.events.TaskExecutionFailure; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; + +import io.orkes.conductor.sdk.examples.util.ClientUtil; + +import com.sun.net.httpserver.HttpServer; +import io.micrometer.prometheus.PrometheusConfig; +import io.micrometer.prometheus.PrometheusMeterRegistry; +import lombok.extern.slf4j.Slf4j; + +/** + * Shows how you can create a worker that polls and executes a SIMPLE task + * and register listeners to expose metrics with micrometer. + */ +@Slf4j +public class EventListenerExample { + + private static final PrometheusMeterRegistry prometheusRegistry = + new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + + public static void main(String[] args) throws IOException { + var client = ClientUtil.getClient(); + var taskClient = new TaskClient(client); + var runnerConfigurer = new TaskRunnerConfigurer + .Builder(taskClient, List.of(new SimpleWorker())) + .withThreadCount(2) + .withListener(PollCompleted.class, (e) -> { + log.info("Poll Completed {}", e); + var timer = prometheusRegistry.timer("poll_completed", "type", e.getTaskType()); + timer.record(e.getDuration()); + }) + .withListener(PollFailure.class, (e) -> { + log.error("Poll Failure {}", e); + var counter = prometheusRegistry.counter("poll_failure", "type", e.getTaskType()); + counter.increment(); + }) + .withListener(TaskExecutionFailure.class, (e) -> { + log.error("Task execution Failure {}", e); + var counter = prometheusRegistry.counter("execution_failure", "type", e.getTaskType(), "id", e.getTaskId()); + counter.increment(); + }) + .build(); + runnerConfigurer.init(); + + // Expose a /metrics endpoint that will be scrapped by Prometheus + var server = HttpServer.create(new InetSocketAddress(9991), 0); + server.createContext("/metrics", (exchange -> { + var body = prometheusRegistry.scrape(); + exchange.getResponseHeaders().set("Content-Type", "text/plain"); + exchange.sendResponseHeaders(200, body.getBytes().length); + try (var os = exchange.getResponseBody()) { + os.write(body.getBytes()); + } + })); + + server.start(); + } + + private static class SimpleWorker implements Worker { + + private static final String TASK_NAME = "simple_task"; + + @Override + public String getTaskDefName() { + return TASK_NAME; + } + + @Override + public TaskResult execute(Task task) { + // BUSINESS LOGIC GOES HERE + var result = new TaskResult(task); + result.setStatus(TaskResult.Status.COMPLETED); + return result; + } + + @Override + public int getPollingInterval() { + return 500; + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/Main.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/Main.java new file mode 100644 index 000000000..1342c6e88 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/Main.java @@ -0,0 +1,46 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.helloworld; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import com.netflix.conductor.sdk.examples.helloworld.workflowdef.GreetingsWorkflow; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; + +import io.orkes.conductor.sdk.examples.util.ClientUtil; + +public class Main { + + public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException { + var workflowExecutor = new WorkflowExecutor(ClientUtil.getClient(), 10); + workflowExecutor.initWorkers("com.netflix.conductor.sdk.examples.helloworld.workers"); + var workflowCreator = new GreetingsWorkflow(workflowExecutor); + var simpleWorkflow = workflowCreator.create(); + + var input = new GreetingsWorkflow.Input(); + input.setName("Conductor User 42"); + + var workflowExecution = simpleWorkflow.executeDynamic(input); + try { + var workflowRun = workflowExecution.get(10, TimeUnit.SECONDS); + System.out.println("Workflow execution status: " + workflowRun.getStatus()); + } catch (Exception e) { + System.out.println("Error while executing workflow: " + e.getMessage()); + } + + // Shutdown workers gracefully + workflowExecutor.shutdown(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workers/Workers.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workers/Workers.java new file mode 100644 index 000000000..4c16f0609 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workers/Workers.java @@ -0,0 +1,23 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.helloworld.workers; + +import com.netflix.conductor.sdk.workflow.task.InputParam; +import com.netflix.conductor.sdk.workflow.task.WorkerTask; + +public class Workers { + @WorkerTask("greet") + public String greeting(@InputParam("name") String name) { + return ("Hello " + name); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workflowdef/GreetingsWorkflow.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workflowdef/GreetingsWorkflow.java new file mode 100644 index 000000000..0052baa50 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workflowdef/GreetingsWorkflow.java @@ -0,0 +1,41 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.helloworld.workflowdef; + +import com.netflix.conductor.sdk.workflow.def.ConductorWorkflow; +import com.netflix.conductor.sdk.workflow.def.tasks.SimpleTask; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; + +import lombok.Data; + +public class GreetingsWorkflow { + private final WorkflowExecutor executor; + public GreetingsWorkflow(WorkflowExecutor executor) { + this.executor = executor; + } + public ConductorWorkflow create() { + ConductorWorkflow workflow = new ConductorWorkflow<>(executor); + workflow.setName("greetings"); + workflow.setVersion(1); + SimpleTask greetingsTask = new SimpleTask("greet", "greet_ref"); + greetingsTask.input("name", "${workflow.input.name}"); + workflow.add(greetingsTask); + workflow.setOwnerEmail("exampes@conductor-oss.org"); + return workflow; + } + + @Data + public static class Input { + private String name; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/Main.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/Main.java new file mode 100644 index 000000000..45c0c6b09 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/Main.java @@ -0,0 +1,47 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.sendemail; + +import java.util.concurrent.TimeUnit; + +import com.netflix.conductor.sdk.examples.sendemail.workflowdef.SendEmailWorkflow; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; + +import io.orkes.conductor.sdk.examples.util.ClientUtil; + +import lombok.SneakyThrows; + +public class Main { + + @SneakyThrows + public static void main(String[] args){ + var workflowExecutor = new WorkflowExecutor(ClientUtil.getClient(), 10); + var workflowCreator = new SendEmailWorkflow(workflowExecutor); + var workflow = workflowCreator.create(); + var input = new SendEmailWorkflow.Input(); + input.setUserId("conductor-user-42"); + // Init workers + workflowExecutor.initWorkers("com.netflix.conductor.sdk.examples.sendemail.workers"); + // Wait for AT MOST 10 seconds for the workflow to reach a terminal state + var workflowExecution = workflow.executeDynamic(input); + try { + var workflowRun = workflowExecution.get(10, TimeUnit.SECONDS); + System.out.println("Workflow execution status: " + workflowRun.getStatus()); + } catch (Exception e) { + System.out.println("Error while executing workflow: " + e.getMessage()); + } + + // Shutdown workers gracefully + workflowExecutor.shutdown(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workers/Workers.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workers/Workers.java new file mode 100644 index 000000000..144049e41 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workers/Workers.java @@ -0,0 +1,44 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.sendemail.workers; + +import com.netflix.conductor.sdk.workflow.task.InputParam; +import com.netflix.conductor.sdk.workflow.task.WorkerTask; + +import lombok.Data; + +public class Workers { + + @WorkerTask("send_email") + public void sendEmail(@InputParam("email") String email) { + System.out.println("Sending email to " + email); + } + + @WorkerTask("get_user_info") + public UserInfo getUserInfo(@InputParam("userId") String userId) { + UserInfo userInfo = new UserInfo(); + userInfo.setId(userId); + userInfo.setName("User X"); + userInfo.setEmail(userId + "@example.com"); + userInfo.setPhoneNumber("555-555-5555"); + return userInfo; + } + + @Data + public static class UserInfo { + private String name; + private String id; + private String email; + private String phoneNumber; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workflowdef/SendEmailWorkflow.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workflowdef/SendEmailWorkflow.java new file mode 100644 index 000000000..602f18c78 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workflowdef/SendEmailWorkflow.java @@ -0,0 +1,52 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.sendemail.workflowdef; + +import com.netflix.conductor.sdk.workflow.def.ConductorWorkflow; +import com.netflix.conductor.sdk.workflow.def.tasks.SimpleTask; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; + +import lombok.Data; + +public class SendEmailWorkflow { + private final WorkflowExecutor executor; + + public SendEmailWorkflow(WorkflowExecutor executor) { + this.executor = executor; + } + + public ConductorWorkflow create() { + ConductorWorkflow workflow = new ConductorWorkflow<>(executor); + workflow.setName("send_email_workflow"); + workflow.setVersion(1); + + SimpleTask getUserDetails = new SimpleTask("get_user_info", "get_user_info"); + getUserDetails.input("userId", "${workflow.input.userId}"); + + // send email + SimpleTask sendEmail = new SimpleTask("send_email", "send_email"); + // get user details user info, which contains the email field + sendEmail.input("email", "${get_user_info.output.email}"); + + workflow.add(getUserDetails); + workflow.add(sendEmail); + + workflow.setOwnerEmail("exampes@conductor-oss.org"); + return workflow; + } + + @Data + public static class Input { + private String userId; + } +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Main.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Main.java new file mode 100644 index 000000000..2675ac0bd --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Main.java @@ -0,0 +1,62 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.shipment; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.sdk.workflow.def.ConductorWorkflow; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; + +import io.orkes.conductor.sdk.examples.util.ClientUtil; + +public class Main { + public static void main(String[] args) { + ConductorClient client = ClientUtil.getClient(); + WorkflowExecutor executor = new WorkflowExecutor(client, 100); + + // Create the new shipment workflow + ShipmentWorkflow shipmentWorkflow = new ShipmentWorkflow(executor); + + // Create two workflows + + // 1. Order flow that ships an individual order + // 2. Shipment Workflow that tracks multiple orders in a shipment + shipmentWorkflow.createOrderFlow(); + ConductorWorkflow workflow = shipmentWorkflow.createShipmentWorkflow(); + + // Execute the workflow and wait for it to complete + try { + Shipment workflowInput = new Shipment("userA", "order123"); + + // Execute returns a completable future. + CompletableFuture executionFuture = workflow.execute(workflowInput); + + // Wait for a maximum of a minute for the workflow to complete. + Workflow run = executionFuture.get(1, TimeUnit.MINUTES); + + System.out.println("Workflow Id: " + run); + System.out.println("Workflow Status: " + run.getStatus()); + System.out.println("Workflow Output: " + run.getOutput()); + + } catch (Exception e) { + e.printStackTrace(); + } finally { + executor.shutdown(); + } + + System.out.println("Done"); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Order.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Order.java new file mode 100644 index 000000000..89d53d0b0 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Order.java @@ -0,0 +1,103 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.shipment; + +import java.math.BigDecimal; + +public class Order { + + public enum ShippingMethod { + GROUND, + NEXT_DAY_AIR, + SAME_DAY + } + + private String orderNumber; + + private String sku; + + private int quantity; + + private BigDecimal unitPrice; + + private String zipCode; + + private String countryCode; + + private ShippingMethod shippingMethod; + + public Order(String orderNumber, String sku, int quantity, BigDecimal unitPrice) { + this.orderNumber = orderNumber; + this.sku = sku; + this.quantity = quantity; + this.unitPrice = unitPrice; + } + + public Order() {} + + public String getOrderNumber() { + return orderNumber; + } + + public void setOrderNumber(String orderNumber) { + this.orderNumber = orderNumber; + } + + public String getSku() { + return sku; + } + + public void setSku(String sku) { + this.sku = sku; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + public BigDecimal getUnitPrice() { + return unitPrice; + } + + public void setUnitPrice(BigDecimal unitPrice) { + this.unitPrice = unitPrice; + } + + public String getZipCode() { + return zipCode; + } + + public void setZipCode(String zipCode) { + this.zipCode = zipCode; + } + + public String getCountryCode() { + return countryCode; + } + + public void setCountryCode(String countryCode) { + this.countryCode = countryCode; + } + + public ShippingMethod getShippingMethod() { + return shippingMethod; + } + + public void setShippingMethod(ShippingMethod shippingMethod) { + this.shippingMethod = shippingMethod; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Shipment.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Shipment.java new file mode 100644 index 000000000..f297613ce --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Shipment.java @@ -0,0 +1,43 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.shipment; + +public class Shipment { + + private String userId; + + private String orderNo; + + public Shipment(String userId, String orderNo) { + this.userId = userId; + this.orderNo = orderNo; + } + + public Shipment() {} + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getOrderNo() { + return orderNo; + } + + public void setOrderNo(String orderNo) { + this.orderNo = orderNo; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentState.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentState.java new file mode 100644 index 000000000..c5fd59a16 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentState.java @@ -0,0 +1,56 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.shipment; + +public class ShipmentState { + + private boolean paymentCompleted; + + private boolean emailSent; + + private boolean shipped; + + private String trackingNumber; + + public boolean isPaymentCompleted() { + return paymentCompleted; + } + + public void setPaymentCompleted(boolean paymentCompleted) { + this.paymentCompleted = paymentCompleted; + } + + public boolean isEmailSent() { + return emailSent; + } + + public void setEmailSent(boolean emailSent) { + this.emailSent = emailSent; + } + + public boolean isShipped() { + return shipped; + } + + public void setShipped(boolean shipped) { + this.shipped = shipped; + } + + public String getTrackingNumber() { + return trackingNumber; + } + + public void setTrackingNumber(String trackingNumber) { + this.trackingNumber = trackingNumber; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkers.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkers.java new file mode 100644 index 000000000..b39639251 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkers.java @@ -0,0 +1,146 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.shipment; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.UUID; + +import com.netflix.conductor.sdk.workflow.def.tasks.DynamicForkInput; +import com.netflix.conductor.sdk.workflow.def.tasks.SubWorkflow; +import com.netflix.conductor.sdk.workflow.def.tasks.Task; +import com.netflix.conductor.sdk.workflow.task.InputParam; +import com.netflix.conductor.sdk.workflow.task.OutputParam; +import com.netflix.conductor.sdk.workflow.task.WorkerTask; + +public class ShipmentWorkers { + + @WorkerTask(value = "generateDynamicFork", threadCount = 3) + public DynamicForkInput generateDynamicFork( + @InputParam("orderDetails") List orderDetails, + @InputParam("userDetails") User userDetails) { + DynamicForkInput input = new DynamicForkInput(); + List> tasks = new ArrayList<>(); + Map inputs = new HashMap<>(); + + for (int i = 0; i < orderDetails.size(); i++) { + Order detail = orderDetails.get(i); + String referenceName = "order_flow_sub_" + i; + tasks.add( + new SubWorkflow(referenceName, "order_flow", null) + .input("orderDetail", detail) + .input("userDetails", userDetails)); + inputs.put(referenceName, new HashMap<>()); + } + input.setInputs(inputs); + input.setTasks(tasks); + return input; + } + + @WorkerTask(value = "get_order_details", threadCount = 5) + public List getOrderDetails(@InputParam("orderNo") String orderNo) { + int lineItemCount = new Random().nextInt(10); + List orderDetails = new ArrayList<>(); + for (int i = 0; i < lineItemCount; i++) { + Order orderDetail = new Order(orderNo, "sku_" + i, 2, BigDecimal.valueOf(20.5)); + orderDetail.setOrderNumber(UUID.randomUUID().toString()); + orderDetail.setCountryCode(i % 2 == 0 ? "US" : "CA"); + if (i % 3 == 0) { + orderDetail.setCountryCode("UK"); + } + + if (orderDetail.getCountryCode().equals("US")) + orderDetail.setShippingMethod(Order.ShippingMethod.SAME_DAY); + else if (orderDetail.getCountryCode().equals("CA")) + orderDetail.setShippingMethod(Order.ShippingMethod.NEXT_DAY_AIR); + else orderDetail.setShippingMethod(Order.ShippingMethod.GROUND); + + orderDetails.add(orderDetail); + } + return orderDetails; + } + + @WorkerTask("get_user_details") + public User getUserDetails(@InputParam("userId") String userId) { + User user = + new User( + "User Name", + userId + "@example.com", + "1234 forline street", + "mountain view", + "95030", + "US", + "Paypal", + "biling_001"); + + return user; + } + + @WorkerTask("calculate_tax_and_total") + public @OutputParam("total_amount") BigDecimal calculateTax( + @InputParam("orderDetail") Order orderDetails) { + BigDecimal preTaxAmount = + orderDetails.getUnitPrice().multiply(new BigDecimal(orderDetails.getQuantity())); + BigDecimal tax = BigDecimal.valueOf(0.2).multiply(preTaxAmount); + if (!"US".equals(orderDetails.getCountryCode())) { + tax = BigDecimal.ZERO; + } + return preTaxAmount.add(tax); + } + + @WorkerTask("ground_shipping_label") + public @OutputParam("reference_number") String prepareGroundShipping( + @InputParam("name") String name, + @InputParam("address") String address, + @InputParam("orderNo") String orderNo) { + + return "Ground_" + orderNo; + } + + @WorkerTask("air_shipping_label") + public @OutputParam("reference_number") String prepareAirShipping( + @InputParam("name") String name, + @InputParam("address") String address, + @InputParam("orderNo") String orderNo) { + + return "Air_" + orderNo; + } + + @WorkerTask("same_day_shipping_label") + public @OutputParam("reference_number") String prepareSameDayShipping( + @InputParam("name") String name, + @InputParam("address") String address, + @InputParam("orderNo") String orderNo) { + + return "SameDay_" + orderNo; + } + + @WorkerTask("charge_payment") + public @OutputParam("reference") String chargePayment( + @InputParam("amount") BigDecimal amount, + @InputParam("billingId") String billingId, + @InputParam("billingType") String billingType) { + + return UUID.randomUUID().toString(); + } + + @WorkerTask("send_email") + public void sendEmail( + @InputParam("name") String name, + @InputParam("email") String email, + @InputParam("orderNo") String orderNo) {} +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkflow.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkflow.java new file mode 100644 index 000000000..4a0321c0b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkflow.java @@ -0,0 +1,185 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.shipment; + + +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.sdk.workflow.def.ConductorWorkflow; +import com.netflix.conductor.sdk.workflow.def.WorkflowBuilder; +import com.netflix.conductor.sdk.workflow.def.tasks.DynamicFork; +import com.netflix.conductor.sdk.workflow.def.tasks.ForkJoin; +import com.netflix.conductor.sdk.workflow.def.tasks.SetVariable; +import com.netflix.conductor.sdk.workflow.def.tasks.SimpleTask; +import com.netflix.conductor.sdk.workflow.def.tasks.Switch; +import com.netflix.conductor.sdk.workflow.def.tasks.Task; +import com.netflix.conductor.sdk.workflow.def.tasks.Terminate; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; + + +public class ShipmentWorkflow { + + private final WorkflowExecutor executor; + + public ShipmentWorkflow(WorkflowExecutor executor) { + this.executor = executor; + this.executor.initWorkers(ShipmentWorkflow.class.getPackageName()); + } + + public ConductorWorkflow createOrderFlow() { + WorkflowBuilder builder = new WorkflowBuilder<>(executor); + builder.name("order_flow") + .version(1) + .ownerEmail("user@example.com") + .timeoutPolicy(WorkflowDef.TimeoutPolicy.TIME_OUT_WF, 60) // 1 day max + .description("Workflow to track shipment") + .add( + new SimpleTask("calculate_tax_and_total", "calculate_tax_and_total") + .input("orderDetail", ConductorWorkflow.input.get("orderDetail"))) + .add( + new SimpleTask("charge_payment", "charge_payment") + .input( + "billingId", + ConductorWorkflow.input + .map("userDetails") + .get("billingId"), + "billingType", + ConductorWorkflow.input + .map("userDetails") + .get("billingType"), + "amount", "${calculate_tax_and_total.output.total_amount}")) + .add( + new Switch("shipping_label", "${workflow.input.orderDetail.shippingMethod}") + .switchCase( + Order.ShippingMethod.GROUND.toString(), + new SimpleTask( + "ground_shipping_label", + "ground_shipping_label") + .input( + "name", + ConductorWorkflow.input + .map("userDetails") + .get("name"), + "address", + ConductorWorkflow.input + .map("userDetails") + .get("addressLine"), + "orderNo", + ConductorWorkflow.input + .map("orderDetail") + .get("orderNumber"))) + .switchCase( + Order.ShippingMethod.NEXT_DAY_AIR.toString(), + new SimpleTask("air_shipping_label", "air_shipping_label") + .input( + "name", + ConductorWorkflow.input + .map("userDetails") + .get("name"), + "address", + ConductorWorkflow.input + .map("userDetails") + .get("addressLine"), + "orderNo", + ConductorWorkflow.input + .map("orderDetail") + .get("orderNumber"))) + .switchCase( + Order.ShippingMethod.SAME_DAY.toString(), + new SimpleTask( + "same_day_shipping_label", + "same_day_shipping_label") + .input( + "name", + ConductorWorkflow.input + .map("userDetails") + .get("name"), + "address", + ConductorWorkflow.input + .map("userDetails") + .get("addressLine"), + "orderNo", + ConductorWorkflow.input + .map("orderDetail") + .get("orderNumber"))) + .defaultCase( + new Terminate( + "unsupported_shipping_type", + Workflow.WorkflowStatus.FAILED, + "Unsupported Shipping Method"))) + .add( + new SimpleTask("send_email", "send_email") + .input( + "name", + ConductorWorkflow.input + .map("userDetails") + .get("name"), + "email", + ConductorWorkflow.input + .map("userDetails") + .get("email"), + "orderNo", + ConductorWorkflow.input + .map("orderDetail") + .get("orderNumber"))); + ConductorWorkflow conductorWorkflow = builder.build(); + conductorWorkflow.registerWorkflow(true, true); + return conductorWorkflow; + } + + public ConductorWorkflow createShipmentWorkflow() { + + WorkflowBuilder builder = new WorkflowBuilder<>(executor); + + SimpleTask getOrderDetails = + new SimpleTask("get_order_details", "get_order_details") + .input("orderNo", ConductorWorkflow.input.get("orderNo")); + + SimpleTask getUserDetails = + new SimpleTask("get_user_details", "get_user_details") + .input("userId", ConductorWorkflow.input.get("userId")); + + ConductorWorkflow conductorWorkflow = + builder.name("shipment_workflow") + .version(1) + .ownerEmail("user@example.com") + .variables(new ShipmentState()) + .timeoutPolicy(WorkflowDef.TimeoutPolicy.TIME_OUT_WF, 60) // 30 days + .description("Workflow to track shipment") + .add( + new ForkJoin( + "get_in_parallel", + new Task[] {getOrderDetails}, + new Task[] {getUserDetails})) + + // For all the line items in the order, run in parallel: + // (calculate tax, charge payment, set state, prepare shipment, send + // shipment, set state) + .add( + new DynamicFork( + "process_order", + new SimpleTask("generateDynamicFork", "generateDynamicFork") + .input( + "orderDetails", + getOrderDetails.taskOutput.get("result")) + .input("userDetails", getUserDetails.taskOutput))) + + // Update the workflow state with shipped = true + .add(new SetVariable("update_state").input("shipped", true)) + .build(); + + conductorWorkflow.registerWorkflow(true, true); + + return conductorWorkflow; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/User.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/User.java new file mode 100644 index 000000000..24c3f93e8 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/User.java @@ -0,0 +1,117 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.shipment; + +public class User { + + private String name; + + private String email; + + private String addressLine; + + private String city; + + private String zipCode; + + private String countryCode; + + private String billingType; + + private String billingId; + + public User( + String name, + String email, + String addressLine, + String city, + String zipCode, + String countryCode, + String billingType, + String billingId) { + this.name = name; + this.email = email; + this.addressLine = addressLine; + this.city = city; + this.zipCode = zipCode; + this.countryCode = countryCode; + this.billingType = billingType; + this.billingId = billingId; + } + + public User() {} + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getAddressLine() { + return addressLine; + } + + public void setAddressLine(String addressLine) { + this.addressLine = addressLine; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getZipCode() { + return zipCode; + } + + public void setZipCode(String zipCode) { + this.zipCode = zipCode; + } + + public String getCountryCode() { + return countryCode; + } + + public void setCountryCode(String countryCode) { + this.countryCode = countryCode; + } + + public String getBillingType() { + return billingType; + } + + public void setBillingType(String billingType) { + this.billingType = billingType; + } + + public String getBillingId() { + return billingId; + } + + public void setBillingId(String billingId) { + this.billingId = billingId; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Main.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Main.java new file mode 100644 index 000000000..94e34ff9d --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Main.java @@ -0,0 +1,79 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.taskdomains; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.netflix.conductor.client.automator.TaskRunnerConfigurer; +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.sdk.workflow.executor.task.AnnotatedWorkerExecutor; +import com.netflix.conductor.sdk.workflow.executor.task.WorkerConfiguration; + +import io.orkes.conductor.sdk.examples.util.ClientUtil; + +public class Main { + + public static void main(String[] args) throws IOException { + setSystemProperties(); + + ConductorClient client = ClientUtil.getClient(); + TaskClient taskClient = new TaskClient(client); + AnnotatedWorkerExecutor workerExecutor = new AnnotatedWorkerExecutor( + taskClient, new WorkerConfiguration() + ); + workerExecutor.initWorkers("com.netflix.conductor.sdk.examples.taskdomains"); + workerExecutor.startPolling(); + workerExecutor.shutdown(); + + startTaskRunnerWorkers(taskClient); + } + + private static void startTaskRunnerWorkers(TaskClient taskClient) { + List workers = List.of(new Workers.TaskWorker()); + TaskRunnerConfigurer.Builder builder = new TaskRunnerConfigurer.Builder(taskClient, workers); + + // test-domain-common should be picked up as Task Domain if conductor.worker.all.domain is populated + // For test-domain-runner to be picked up, conductor.worker.all.domain shouldn't be populated + Map taskToDomains = new HashMap<>(); + taskToDomains.put("task-domain-runner-simple-task", "test-domain-runner"); + Map taskThreadCount = new HashMap<>(); + + TaskRunnerConfigurer taskRunner = + builder.withThreadCount(2) + .withTaskToDomain(taskToDomains) + .withTaskThreadCount(taskThreadCount) + .withSleepWhenRetry(500) + .withWorkerNamePrefix("task-domain") + .withUpdateRetryCount(3) + .build(); + + // Start Polling for tasks and execute them + taskRunner.init(); + + // Optionally, use the shutdown method to stop polling + // taskRunner.shutdown(); + } + + + public static void setSystemProperties() { + // This is in lieu of setting properties in application.properties + System.setProperty("conductor.worker.task-domain-property-simple-task.domain", "test-domain-prop"); + // If below line is un-commented, test-domain-common trumps test-domain-runner and test-domain-annotated + // System.setProperty("conductor.worker.all.domain", "test-domain-common"); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Workers.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Workers.java new file mode 100644 index 000000000..fe17af0c7 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Workers.java @@ -0,0 +1,101 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.examples.taskdomains; + +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.sdk.workflow.task.WorkerTask; + + +/** + * This program demonstrates different mechanisms for setting task domains during worker initialization + *

+ * 1. Using task specific property in application.properties + * 2. Using task agnostic property in application.properties + * 3. Passing taskToDomains as constructor parameter when using TaskRunner + * 4. Passing domain argument when using worker annotator + *

+ * Sample workflow can be created using the definition task_domain_wf.json in the resources folder + *

+ * Example Task to Domain Mapping for workflow creation when property, conductor.worker.all.domain, is set + * { + * "task-domain-property-simple-task": "test-domain-prop", + * "task-domain-all-simple-task": "test-domain-common", + * "task-domain-runner-simple-task": "test-domain-common", + * "task-domain-annotated-simple-task": "test-domain-common" + * } + *

+ * Example Task to Domain Mapping for workflow creation when property, conductor.worker.all.domain, is not set + * { + * "task-domain-property-simple-task": "test-domain-prop", + * "task-domain-runner-simple-task": "test-domain-runner", + * "task-domain-annotated-simple-task": "test-domain-annotated", + * } + */ +public class Workers { + + @WorkerTask(value = "task-domain-property-simple-task", pollingInterval = 200) + public TaskResult sendPropertyTaskDomain(Task task) { + // test-domain-prop should be picked up as Task Domain + TaskResult result = new TaskResult(task); + + result.getOutputData().put("key", "value2"); + result.getOutputData().put("amount", 167.12); + result.setStatus(TaskResult.Status.COMPLETED); + + return result; + } + + @WorkerTask(value = "task-domain-all-simple-task", pollingInterval = 200) + public TaskResult sendAllTaskDomain(Task task) { + // test-domain-common should be picked up as Task Domain + TaskResult result = new TaskResult(task); + result.getOutputData().put("key", "value3"); + result.getOutputData().put("amount", 400); + result.setStatus(TaskResult.Status.COMPLETED); + + return result; + } + + @WorkerTask(value = "task-domain-annotated-simple-task", domain = "test-domain-annotated", pollingInterval = 200) + public TaskResult sendAnnotatedTaskDomain(Task task) { + // test-domain-common should be picked up as Task Domain if conductor.worker.all.domain is populated + // For test-domain-annotated to be picked up, conductor.worker.all.domain shouldn't be populated + TaskResult result = new TaskResult(task); + + result.getOutputData().put("key", "value"); + result.getOutputData().put("amount", 123.45); + result.setStatus(TaskResult.Status.COMPLETED); + + return result; + } + + static class TaskWorker implements Worker { + + @Override + public String getTaskDefName() { + return "task-domain-runner-simple-task"; + } + + public TaskResult execute(Task task) { + TaskResult result = new TaskResult(task); + + result.getOutputData().put("key2", "value2"); + result.getOutputData().put("amount", 145); + result.setStatus(TaskResult.Status.COMPLETED); + + return result; + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/AuthorizationManagement.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/AuthorizationManagement.java new file mode 100644 index 000000000..a23aff7a1 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/AuthorizationManagement.java @@ -0,0 +1,123 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.sdk.examples; + +import java.util.List; +import java.util.UUID; + +import io.orkes.conductor.client.AuthorizationClient; +import io.orkes.conductor.client.OrkesClients; +import io.orkes.conductor.client.model.AuthorizationRequest; +import io.orkes.conductor.client.model.SubjectRef; +import io.orkes.conductor.client.model.TargetRef; +import io.orkes.conductor.client.model.UpsertGroupRequest; +import io.orkes.conductor.client.model.UpsertUserRequest; +import io.orkes.conductor.sdk.examples.util.ClientUtil; + +/** + * Examples for managing user authorization in Orkes Conductor + * + * 1. upsertUser - Add user + * 2. upsertUser - Add group + * 3. addUserToGroup - Add user to group + * 4. removeUserFromGroup - Remove user from group + * 5. grantPermissions - Grant permission to user via tag or group. + */ +public class AuthorizationManagement { + + private static AuthorizationClient authorizationClient; + + public static void main(String[] a) { + OrkesClients orkesClients = ClientUtil.getOrkesClients(); + createMetadata(); + authorizationClient = orkesClients.getAuthorizationClient(); + AuthorizationManagement authorizationManagement = new AuthorizationManagement(); + authorizationManagement.userAndGroupOperations(); + } + + private void userAndGroupOperations() { + // Create users + String userId = createUser("user1"); + String userId2 = createUser("user2"); + String userId3 = createUser("user3"); + // Create groups + String group1 = "group1"; + String group2 = "group2"; + createGroup(group1, "group to perform"); + createGroup(group2, "group to perform action"); + // Add users to group 1 + authorizationClient.addUserToGroup(group1, userId); + authorizationClient.addUserToGroup(group1, userId2); + authorizationClient.addUserToGroup(group2, userId2); + authorizationClient.addUserToGroup(group2, userId3); + + // Remove user from group + authorizationClient.removeUserFromGroup(group1, userId2); + + // Add workflow execution permissions to the group + AuthorizationRequest authorizationRequest = new AuthorizationRequest(); + authorizationRequest.setAccess(List.of(AuthorizationRequest.AccessEnum.EXECUTE)); + SubjectRef subjectRef = new SubjectRef(); + subjectRef.setId(userId); + subjectRef.setType(SubjectRef.TypeEnum.USER); + // Grant workflow execution permission to user + authorizationRequest.setSubject(subjectRef); + TargetRef targetRef = new TargetRef(); + targetRef.setId("org:engineering"); + targetRef.setType(TargetRef.TypeEnum.WORKFLOW_DEF); + authorizationRequest.setTarget(targetRef); + authorizationClient.grantPermissions(authorizationRequest); + + // Grant workflow execution permission to tag + targetRef = new TargetRef(); + targetRef.setId("customer:abc"); + targetRef.setType(TargetRef.TypeEnum.TASK_DEF); + authorizationRequest.setTarget(targetRef); + authorizationClient.grantPermissions(authorizationRequest); + + // Add read only permission to tag in group + authorizationRequest = new AuthorizationRequest(); + authorizationRequest.setAccess(List.of(AuthorizationRequest.AccessEnum.READ)); + subjectRef = new SubjectRef(); + subjectRef.setId(group1); + subjectRef.setType(SubjectRef.TypeEnum.GROUP); + authorizationRequest.setSubject(subjectRef); + targetRef = new TargetRef(); + targetRef.setId("org:engineering"); + targetRef.setType(TargetRef.TypeEnum.WORKFLOW_DEF); + authorizationRequest.setTarget(targetRef); + authorizationClient.grantPermissions(authorizationRequest); + } + + private String createUser(String name) { + String userId = UUID.randomUUID().toString(); + UpsertUserRequest upsertUserRequest = new UpsertUserRequest(); + upsertUserRequest.setName(name); + upsertUserRequest.setRoles(List.of(UpsertUserRequest.RolesEnum.USER)); + authorizationClient.upsertUser(upsertUserRequest, userId); + return userId; + } + + private void createGroup(String name, String description) { + UpsertGroupRequest upsertGroupRequest = new UpsertGroupRequest(); + upsertGroupRequest.setDescription(description); + upsertGroupRequest.setRoles(List.of(UpsertGroupRequest.RolesEnum.USER)); + authorizationClient.upsertGroup(upsertGroupRequest, name); + } + + private static void createMetadata() { + MetadataManagement metadataManagement = new MetadataManagement(); + metadataManagement.createTaskDefinitions(); + metadataManagement.createWorkflowDefinitions(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/MetadataManagement.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/MetadataManagement.java new file mode 100644 index 000000000..b74960f45 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/MetadataManagement.java @@ -0,0 +1,180 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.sdk.examples; + +import java.util.Arrays; +import java.util.Map; + +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +import io.orkes.conductor.client.OrkesClients; +import io.orkes.conductor.client.http.OrkesMetadataClient; +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.sdk.examples.util.ClientUtil; + +/** + * Examples for managing Metadata (Tasks, Workflows) in Conductor + * + * 1. registerWorkflowDef - Register workflow definition + * 2. registerTaskDefs - Register task definition + * 3. addTaskTag - Add tag to taks + * 4. addWorkflowTag - Add tag to workflow + */ +public class MetadataManagement { + + public static String taskName = "test11_task"; + public static String taskName2 = "test11_task1"; + public static String taskName3 = "test11_task2"; + private static TaskDef taskDef; + private static TaskDef taskDef2; + private static TaskDef taskDef3; + public static WorkflowDef workflowDef; + + private static OrkesMetadataClient metadataClient; + + public static void main(String[] args) { + MetadataManagement metadataManagement = new MetadataManagement(); + metadataManagement.createTaskDefinitions(); + metadataManagement.tagTasks(); + metadataManagement.createWorkflowDefinitions(); + metadataManagement.tagWorkflow(); + } + + public void createTaskDefinitions() { + OrkesClients orkesClients = ClientUtil.getOrkesClients(); + metadataClient = orkesClients.getMetadataClient(); + + // Create task definitions + taskDef = new TaskDef(taskName, "task to update database", "test@orkes.io", 3, 4, 3); + + taskDef2 = new TaskDef(); + taskDef2.setName(taskName2); + taskDef2.setDescription("task to notify users"); + taskDef2.setOwnerEmail("test@orkes.io"); + taskDef2.setResponseTimeoutSeconds(10); + taskDef2.setRetryCount(3); + // At any given time, max 10 executions of this task will be allowed. Tasks to be scheduled + // after reaching max + // limit will be put into queue. + // For more information + // https://orkes.io/content/docs/how-tos/Tasks/creating-tasks#task-rate-limits + taskDef2.setConcurrentExecLimit(10); + taskDef2.setInputKeys( + Arrays.asList( + "${workflow.input.value}", + "${some_other_task.output.response.data.value}")); + + taskDef3 = new TaskDef(); + taskDef3.setName(taskName3); + taskDef3.setDescription("task to compress image"); + taskDef3.setOwnerEmail("test@orkes.io"); + // In 60 seconds at max 5 execution of this task wil be allowed. + // For more information + // https://orkes.io/content/docs/how-tos/Tasks/creating-tasks#task-rate-limits + taskDef3.setRateLimitPerFrequency(5); + taskDef3.setRateLimitFrequencyInSeconds(60); + + // Register task definitions + metadataClient.registerTaskDefs(Arrays.asList(taskDef, taskDef2, taskDef3)); + } + + public void tagTasks() { + // Tagging a task + TagObject tagObject = new TagObject(); + tagObject.setType(TagObject.TypeEnum.METADATA); + tagObject.setKey("department"); + tagObject.setValue("accounts"); + metadataClient.addTaskTag(tagObject, taskName); + + // Tagging another task + TagObject tagObject2 = new TagObject(); + tagObject2.setType(TagObject.TypeEnum.METADATA); + tagObject2.setKey("department"); + tagObject2.setValue("engineering"); + metadataClient.addTaskTag(tagObject2, taskName2); + + // Tagging another task + TagObject tagObject3 = new TagObject(); + tagObject3.setType(TagObject.TypeEnum.METADATA); + tagObject3.setKey("env"); + tagObject3.setValue("dev"); + metadataClient.addTaskTag(tagObject3, taskName3); + } + + public void createWorkflowDefinitions() { + // Create workflowTask + WorkflowTask workflowTask = new WorkflowTask(); + workflowTask.setTaskReferenceName(taskName); + workflowTask.setName(taskName); + workflowTask.setTaskReferenceName(taskName); + workflowTask.setWorkflowTaskType(TaskType.SIMPLE); + workflowTask.setInputParameters(Map.of("value", "${workflow.input.value}", "order", "123")); + workflowTask.setTaskDefinition(taskDef); + + // Create INLINE workflowTask + WorkflowTask workflowTask2 = new WorkflowTask(); + workflowTask2.setName(taskName3); + workflowTask2.setTaskReferenceName(taskName2); + workflowTask2.setTaskReferenceName(taskName2); + workflowTask2.setWorkflowTaskType(TaskType.INLINE); + workflowTask2.setInputParameters( + Map.of( + "inlineValue", + "${workflow.input.inlineValue}", + "evaluatorType", + "javascript", + "expression", + "function scriptFun(){if ($.inlineValue == 1){ " + + "return {testvalue: true} } else { return {testvalue: false} }} scriptFun();")); + workflowTask2.setTaskDefinition(taskDef2); + + workflowDef = new WorkflowDef(); + workflowDef.setName("test11_workflow"); + workflowDef.setOwnerEmail("test@orkes.io"); + workflowDef.setTimeoutSeconds(600); + workflowDef.setTimeoutPolicy(WorkflowDef.TimeoutPolicy.TIME_OUT_WF); + workflowDef.setInputParameters(Arrays.asList("value", "inlineValue")); + workflowDef.setDescription("Workflow to monitor order state"); + workflowDef.setTasks(Arrays.asList(workflowTask, workflowTask2)); + metadataClient.registerWorkflowDef(workflowDef); + } + + public void tagWorkflow() { + TagObject tagObject = new TagObject(); + // Workflow level rate limit. At max 5 instance of workflow will be getting executed that + // are triggered with + // same correlationId. + // For more information + // https://orkes.io/content/docs/how-tos/retries-failures-rate_limits#rate-limiting-your-workflow + tagObject.setType(TagObject.TypeEnum.RATE_LIMIT); + tagObject.setKey("${workflow.correlationId}"); + tagObject.setValue(5); + + TagObject tagObject1 = new TagObject(); + tagObject1.setType(TagObject.TypeEnum.METADATA); + tagObject1.setKey("customer"); + tagObject1.setValue("xyz"); + + TagObject tagObject2 = new TagObject(); + tagObject2.setType(TagObject.TypeEnum.METADATA); + tagObject2.setKey("customer"); + tagObject2.setValue("abc"); + + metadataClient.addWorkflowTag(tagObject, workflowDef.getName()); + metadataClient.addWorkflowTag(tagObject1, workflowDef.getName()); + metadataClient.addWorkflowTag(tagObject2, workflowDef.getName()); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/SchedulerManagement.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/SchedulerManagement.java new file mode 100644 index 000000000..c108c03c6 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/SchedulerManagement.java @@ -0,0 +1,106 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.sdk.examples; + +import java.util.List; + +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; + +import io.orkes.conductor.client.OrkesClients; +import io.orkes.conductor.client.SchedulerClient; +import io.orkes.conductor.client.model.SaveScheduleRequest; +import io.orkes.conductor.client.model.WorkflowSchedule; +import io.orkes.conductor.sdk.examples.util.ClientUtil; + +/** + * Examples for managing Schedules in Orkes Conductor + * + * 1. saveSchedule - Create workflow schedule + * 2. pauseSchedule - Pause workflow schedule + * 3. getSchedule - Get schedule for workflow + * 4. getNextFewSchedules - Get next few schedule runs for schedule + * 5. pauseAllSchedules - Pause all the schedules + * 6. resumeAllSchedules - Resume all the schedules + * 7. searchV2 - Get Schedule executions + */ +public class SchedulerManagement { + + private static SchedulerClient schedulerClient; + + private static final String scheduleName = "sample_schedule"; + public static final long NANO = 1_000_000_000; // nano-seconds. + String cron = "0 0 * ? * *"; // Every hour + + public static void main(String[] args) { + OrkesClients orkesClients = ClientUtil.getOrkesClients(); + createMetadata(); + SchedulerManagement schedulerManagement = new SchedulerManagement(); + schedulerClient = orkesClients.getSchedulerClient(); + schedulerManagement.createSchedule(); + schedulerManagement.scheduleOperations(); + } + + private static void createMetadata() { + MetadataManagement metadataManagement = new MetadataManagement(); + metadataManagement.createTaskDefinitions(); + metadataManagement.createWorkflowDefinitions(); + } + + private void createSchedule() { + // Create save schedule request + SaveScheduleRequest saveScheduleRequest = new SaveScheduleRequest(); + saveScheduleRequest.createdBy("test@orkes.io"); + saveScheduleRequest.cronExpression(cron); + saveScheduleRequest.setName(scheduleName); + // Create start workflow request + StartWorkflowRequest startWorkflowRequest = new StartWorkflowRequest(); + startWorkflowRequest.setName(MetadataManagement.workflowDef.getName()); + startWorkflowRequest.setVersion(MetadataManagement.workflowDef.getVersion()); + startWorkflowRequest.setCorrelationId("testing"); + saveScheduleRequest.setStartWorkflowRequest(startWorkflowRequest); + + // Save schedule + schedulerClient.saveSchedule(saveScheduleRequest); + + // Verify that schedule is saved + WorkflowSchedule workflowSchedule = schedulerClient.getSchedule(scheduleName); + assert cron.equals(workflowSchedule.getCronExpression()); + } + + private void scheduleOperations() { + // Pause Schedule + schedulerClient.pauseSchedule(scheduleName); + + // Verify the schedule is paused + WorkflowSchedule workflowSchedule = schedulerClient.getSchedule(scheduleName); + System.out.println(workflowSchedule.isPaused()); + + /// Resume schedule + schedulerClient.resumeSchedule(scheduleName); + // Verify the schedule is resumed + WorkflowSchedule workflowSchedule1 = schedulerClient.getSchedule(scheduleName); + System.out.println(!workflowSchedule1.isPaused()); + + // Example to get schedule, pause, resume, find next schedules, list scheduled executions + + // Find the next run + List schedules = + schedulerClient.getNextFewSchedules( + cron, System.nanoTime(), System.nanoTime() + 6 * 60 * 60 * NANO, 5); + System.out.println(schedules.size() == 5); + schedulerClient.pauseSchedule(scheduleName); + + // Get Scheduled executions + // TODO + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement.java new file mode 100644 index 000000000..18c6c5f58 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement.java @@ -0,0 +1,88 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.sdk.examples; + +import java.util.Arrays; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; + +import io.orkes.conductor.client.http.OrkesWorkflowClient; +import io.orkes.conductor.sdk.examples.util.ClientUtil; + +import static io.orkes.conductor.sdk.examples.MetadataManagement.workflowDef; + +/** + * Examples for managing Workflow operations in Conductor + * + * 1. startWorkflow - Start a new workflow + * 2. getWorkflow - Get workflow execution status + * 3. pauseWorkflow - Pause workflow + * 4. resumeWorkflow - Resume workflow + * 5. terminateWorkflow - Terminate workflow + * 6. deleteWorkflow - Delete workflow + * + */ +public class WorkflowManagement { + + private static OrkesWorkflowClient workflowClient; + + public static void main(String[] args) { + ConductorClient client = ClientUtil.getClient(); + createMetadata(); + WorkflowManagement workflowManagement = new WorkflowManagement(); + workflowClient = new OrkesWorkflowClient(client); + workflowManagement.workflowOperations(); + client.shutdown(); + } + + private static void createMetadata() { + MetadataManagement metadataManagement = new MetadataManagement(); + metadataManagement.createTaskDefinitions(); + metadataManagement.createWorkflowDefinitions(); + } + + private void workflowOperations() { + StartWorkflowRequest startWorkflowRequest = new StartWorkflowRequest(); + startWorkflowRequest.setName(workflowDef.getName()); + startWorkflowRequest.setVersion(workflowDef.getVersion()); + startWorkflowRequest.setCorrelationId("test_workflow"); + + // Start the workflow + String workflowId = workflowClient.startWorkflow(startWorkflowRequest); + // Get the workflow execution status + workflowClient.getWorkflow(workflowId, true); + // Pause the workflow + workflowClient.pauseWorkflow(workflowId); + // Resume the workflow + workflowClient.resumeWorkflow(workflowId); + // Terminate the workflow + workflowClient.terminateWorkflow(workflowId, "Terminated"); + // Retry workflow + workflowClient.retryWorkflow(Arrays.asList(workflowId)); + // Terminate the workflow + workflowClient.terminateWorkflow(workflowId, "Terminated"); + // Restart workflow + workflowClient.restartWorkflow(Arrays.asList(workflowId), false); + // Terminate the workflow + workflowClient.terminateWorkflow(workflowId, "Terminated"); + // Restart workflow using latest workflow definitions + workflowClient.restartWorkflow(Arrays.asList(workflowId), true); + // Terminate the workflow + workflowClient.terminateWorkflow(workflowId, "Terminated"); + // Delete the workflow without archiving + workflowClient.deleteWorkflow(workflowId, false); + // Delete the workflow with archiving to persistent store. + workflowClient.deleteWorkflow(workflowId, true); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement2.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement2.java new file mode 100644 index 000000000..20988816c --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement2.java @@ -0,0 +1,118 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.sdk.examples; + +import java.time.Duration; +import java.util.Map; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.http.WorkflowClient; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; +import com.netflix.conductor.common.run.SearchResult; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.common.run.WorkflowSummary; +import com.netflix.conductor.sdk.workflow.def.ConductorWorkflow; +import com.netflix.conductor.sdk.workflow.def.tasks.Http; +import com.netflix.conductor.sdk.workflow.def.tasks.Wait; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; + +import io.orkes.conductor.sdk.examples.util.ClientUtil; + +//TODO review this example +public class WorkflowManagement2 { + private final WorkflowClient workflowClient; + private final TaskClient taskClient; + private final WorkflowExecutor workflowExecutor; + + public static void main(String[] args) { + new WorkflowManagement2().execute(); + } + + public WorkflowManagement2() { + ConductorClient client = ClientUtil.getClient(); + this.workflowClient = new WorkflowClient(client); + this.taskClient = new TaskClient(client); + this.workflowExecutor = new WorkflowExecutor(client, 100); + } + + public String startWorkflow() { + ConductorWorkflow workflow = new ConductorWorkflow<>(workflowExecutor); + workflow.setName("workflow_signals_demo"); + workflow.setVersion(1); + Wait waitForTwoSec = new Wait("wait_for_2_sec", Duration.ofSeconds(2)); + Http httpCall = new Http("call_remote_api"); + httpCall.url("https://orkes-api-tester.orkesconductor.com/api"); + + Wait waitForSignal = new Wait("wait_for_signal"); + + workflow.add(waitForTwoSec); + workflow.add(waitForSignal); + workflow.add(httpCall); + + workflow.registerWorkflow(true); + StartWorkflowRequest request = new StartWorkflowRequest(); + request.setVersion(1); + request.setName(workflow.getName()); + request.setInput(Map.of()); + + return workflowClient.startWorkflow(request); + } + + public void execute() { + String workflowId = startWorkflow(); + System.out.println("Started workflow with id " + workflowId); + + try { + Thread.sleep(3000); // Wait for 3 seconds + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + Workflow workflow = workflowClient.getWorkflow(workflowId, true); + Task lastTask = workflow.getTasks().get(workflow.getTasks().size() - 1); + System.out.println("Workflow status is " + workflow.getStatus() + " and currently running task is " + lastTask.getReferenceTaskName()); + + workflowClient.terminateWorkflow(workflowId, "testing termination"); + + // Other operations like retry, update tasks, etc. + + // Example of task completion + TaskResult taskResult = new TaskResult(); + taskResult.setWorkflowInstanceId(workflowId); + taskResult.setTaskId(lastTask.getTaskId()); + taskResult.setStatus(TaskResult.Status.COMPLETED); + taskResult.setOutputData(Map.of("greetings", "hello from Orkes")); + taskClient.updateTask(taskResult); + + // Handling workflow lifecycle: terminate, restart, pause, resume + workflowClient.terminateWorkflow(workflowId, "terminating so we can do a restart"); + workflowClient.restart(workflowId, true); + workflowClient.pauseWorkflow(workflowId); + + try { + Thread.sleep(3000); // Simulating a wait + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + workflowClient.resumeWorkflow(workflowId); + + // Search workflow examples + SearchResult searchResults = workflowClient.search(0, 100, "", "*", "correlationId = 'correlation_123'"); + System.out.println("Found " + searchResults.getTotalHits() + " executions with correlation_id 'correlation_123'"); + workflowExecutor.shutdown(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/util/ClientUtil.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/util/ClientUtil.java new file mode 100644 index 000000000..35315adf4 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/util/ClientUtil.java @@ -0,0 +1,57 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.sdk.examples.util; + +import com.netflix.conductor.client.http.ConductorClient; + +import io.orkes.conductor.client.OrkesClients; +import io.orkes.conductor.client.http.OrkesAuthentication; + +import com.google.common.base.Preconditions; + +import static java.lang.System.getenv; + +public class ClientUtil { + private static final String ENV_ROOT_URI = "CONDUCTOR_SERVER_URL"; + private static final String ENV_KEY_ID = "CONDUCTOR_SERVER_AUTH_KEY"; + private static final String ENV_SECRET = "CONDUCTOR_SERVER_AUTH_SECRET"; + private static final ConductorClient CLIENT = getClient(); + + public static OrkesClients getOrkesClients() { + return new OrkesClients(CLIENT); + } + + public static ConductorClient getClient() { + if (CLIENT != null) { + return CLIENT; + } + + var basePath = getenv(ENV_ROOT_URI); + Preconditions.checkNotNull(basePath, ENV_ROOT_URI + " env not set"); + + ConductorClient.Builder builder = ConductorClient.builder() + .basePath(basePath) + .readTimeout(10_000) + .connectTimeout(10_000) + .writeTimeout(10_000); + + var keyId = getenv(ENV_KEY_ID); + var keySecret = getenv(ENV_SECRET); + + if (keyId != null && keySecret != null) { + builder.addHeaderSupplier(new OrkesAuthentication(keyId, keySecret)); + } + + return builder.build(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/Main.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/Main.java new file mode 100644 index 000000000..f05e3899b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/Main.java @@ -0,0 +1,107 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.sdk.examples.workflowops; + +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; + +import io.orkes.conductor.client.http.OrkesWorkflowClient; +import io.orkes.conductor.sdk.examples.util.ClientUtil; +import io.orkes.conductor.sdk.examples.workflowops.workflowdef.GreetingsWorkflow; + +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + + +public class Main { + + public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException { + //Initialise Conductor Client + var client = ClientUtil.getClient(); + var orkesWorkflowClient = new OrkesWorkflowClient(client); + + //Initialise WorkflowExecutor and Conductor Workers + var workflowExecutor = new WorkflowExecutor(client, 10); + workflowExecutor.initWorkers("com.netflix.conductor.sdk.examples.helloworld.workers"); + + //Create the workflow with input + var workflowCreator = new GreetingsWorkflow(workflowExecutor); + var simpleWorkflow = workflowCreator.create(); + var input = new GreetingsWorkflow.Input(); + input.setName("Orkes User"); + input.setPersons(List.of(Person.builder() + .id("1") + .name("John") + .last("Doe") + .email("john@orkes.io") + .build(), + Person.builder() + .id("2") + .name("Jane") + .last("Doe") + .email("jane@orkes.io") + .build())); + var workflowId = simpleWorkflow.startDynamic(input); + var summary = orkesWorkflowClient.getWorkflowStatusSummary(workflowId, false, false); + + //Workflow Execution Started + System.out.println("Your workflow started with id " + workflowId); + System.out.println("Workflow status is " + summary.getStatus()); + + var workflow = orkesWorkflowClient.getWorkflow(workflowId, true); + var lastTask = workflow.getTasks(); + var lastTaskId = lastTask.get(lastTask.size() - 1).getTaskDefName(); + System.out.println("Workflow status is " + workflow.getStatus() + " and current or last task is " + lastTaskId); + + //Test Termination + orkesWorkflowClient.terminateWorkflow(workflowId, "Testing Termination"); + workflow = orkesWorkflowClient.getWorkflow(workflowId, false); + System.out.println("Workflow status is " + workflow.getStatus()); + + var workflowIds = List.of(workflowId); + + //Restart Workflow + var bulkResponse = orkesWorkflowClient.restartWorkflow(workflowIds, false); + + workflow = orkesWorkflowClient.getWorkflow(workflowId, false); + System.out.println("Workflow status is " + workflow.getStatus()); + + //Pause Workflow + orkesWorkflowClient.pauseWorkflow(workflowId); + workflow = orkesWorkflowClient.getWorkflow(workflowId, true); + System.out.println("Workflow status is " + workflow.getStatus()); + + //Resume Workflow + orkesWorkflowClient.resumeWorkflow(workflowIds); + workflow = orkesWorkflowClient.getWorkflow(workflowId, true); + System.out.println("Workflow status is " + workflow.getStatus()); + + //Shutdown workflowClient and taskrunner + orkesWorkflowClient.close(); + System.exit(0); + } + + @Builder + @RequiredArgsConstructor + @Getter + public static class Person { + private final String id; + private final String name; + private final String last; + private final String email; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/workflowdef/GreetingsWorkflow.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/workflowdef/GreetingsWorkflow.java new file mode 100644 index 000000000..e66ce916f --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/workflowdef/GreetingsWorkflow.java @@ -0,0 +1,85 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.sdk.examples.workflowops.workflowdef; + +import java.time.Duration; +import java.util.List; + +import com.netflix.conductor.sdk.workflow.def.ConductorWorkflow; +import com.netflix.conductor.sdk.workflow.def.tasks.Http; +import com.netflix.conductor.sdk.workflow.def.tasks.JQ; +import com.netflix.conductor.sdk.workflow.def.tasks.Javascript; +import com.netflix.conductor.sdk.workflow.def.tasks.SetVariable; +import com.netflix.conductor.sdk.workflow.def.tasks.SimpleTask; +import com.netflix.conductor.sdk.workflow.def.tasks.Switch; +import com.netflix.conductor.sdk.workflow.def.tasks.Wait; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; + +import io.orkes.conductor.sdk.examples.workflowops.Main; + +import lombok.Data; + +public class GreetingsWorkflow { + private final WorkflowExecutor executor; + + public GreetingsWorkflow(WorkflowExecutor executor) { + this.executor = executor; + } + + public ConductorWorkflow create() { + ConductorWorkflow workflow = new ConductorWorkflow<>(executor); + workflow.setName("greetings"); + workflow.setVersion(1); + //Simple Task + SimpleTask greetingsTask = new SimpleTask("greet", "greet_ref"); + greetingsTask.input("name", "IDXC" + "${workflow.input.name}"); + workflow.add(greetingsTask); + //JS Task + String script = "function greetings(name){return {\"text\": \"Your email is \" + name+\"@workflow.io\",\"url\": \"https://orkes-api-tester.orkesconductor.com/api\"}}greetings(\"'${workflow.input.name}'\");"; + Javascript jstask = new Javascript("hello_script", script); + workflow.add(jstask); + //Wait Task + Wait waitTask = new Wait("wait_for_1_sec", Duration.ofMillis(1000)); + workflow.add(waitTask);//workflow is an object of ConductorWorkflow + //Set Variable + SetVariable setVariable = new SetVariable("set_name"); + setVariable.input("Name", "${workflow.input.name}"); + workflow.add(setVariable); +// SubWorkflow +// SubWorkflow subWorkflow = new SubWorkflow("persist_inDB", "insertintodb", 1); +// subWorkflow.input("name", "{workflow.input.name}"); +// workflow.add(subWorkflow); + //Switch Case + Wait waitTask2 = new Wait("wait_for_2_sec", Duration.ofSeconds(2)); + Javascript jstask2 = new Javascript("hello_script2", script); + Switch switchTask = new Switch("Version_switch", "${workflow.input.name}").switchCase("Orkes", jstask2).switchCase("Others", waitTask2); + workflow.add(switchTask); + //JQ Task + JQ jqtask = new JQ("jq_task", ".persons | map({user:{email, name}})"); + jqtask.input("persons", "${workflow.input.persons}"); + workflow.add(jqtask); + //HTTP Task + Http httptask = new Http("HTTPtask"); + httptask.url("https://orkes-api-tester.orkesconductor.com/api"); + workflow.add(httptask); + Wait waitTask10 = new Wait("wait_for_10_sec", Duration.ofSeconds(10)); + workflow.add(waitTask10); + return workflow; + } + + @Data + public static class Input { + private String name; + private List persons; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/resources/logback.xml b/conductor-clients/java/conductor-java-sdk/examples/src/main/resources/logback.xml new file mode 100644 index 000000000..9df2403dd --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + + + %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{}): %msg%n%throwable + + + + + + + + + diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/resources/script.js b/conductor-clients/java/conductor-java-sdk/examples/src/main/resources/script.js new file mode 100644 index 000000000..af6d42c43 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/resources/script.js @@ -0,0 +1,11 @@ +function e() { + if ($.value > 1){ + return { + "key": "value", + "key2": 42 + }; + } else { + return {}; + } +} +e(); \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/resources/task_domain_wf.json b/conductor-clients/java/conductor-java-sdk/examples/src/main/resources/task_domain_wf.json new file mode 100644 index 000000000..f48ab5c27 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/resources/task_domain_wf.json @@ -0,0 +1,85 @@ +{ + "createTime": 1688142904648, + "updateTime": 1688491115511, + "name": "task_domain_wf", + "description": "Workflow for workers with task domains", + "version": 1, + "tasks": [ + { + "name": "task-domain-annotated-simple-task", + "taskReferenceName": "task-domain-annotated-simple-task", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + }, + { + "name": "task-domain-property-simple-task", + "taskReferenceName": "task-domain-property-simple-task", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + }, + { + "name": "task-domain-all-simple-task", + "taskReferenceName": "task-domain-all-simple-task", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + }, + { + "name": "task-domain-runner-simple-task", + "taskReferenceName": "task-domain-runner-simple-task", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + } + ], + "inputParameters": [], + "outputParameters": {}, + "failureWorkflow": "", + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "ownerEmail": "tester@email.com", + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "variables": {}, + "inputTemplate": {}, + "onStateChange": {} +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/resources/workflow.json b/conductor-clients/java/conductor-java-sdk/examples/src/main/resources/workflow.json new file mode 100644 index 000000000..33f1625f0 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/resources/workflow.json @@ -0,0 +1,17 @@ +{ + "name": "hello", + "description": "hello workflow", + "version": 1, + "tasks": [ + { + "name": "greet", + "taskReferenceName": "greet_ref", + "type": "SIMPLE", + "inputParameters": { + "name": "${workflow.input.name}" + } + } + ], + "timeoutPolicy": "TIME_OUT_WF", + "timeoutSeconds": 60 + } \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/gradle.properties b/conductor-clients/java/conductor-java-sdk/gradle.properties new file mode 100644 index 000000000..ffca90162 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/gradle.properties @@ -0,0 +1 @@ +version=3.0.0-alpha15-SNAPSHOT diff --git a/conductor-clients/java/conductor-java-sdk/gradle/wrapper/gradle-wrapper.jar b/conductor-clients/java/conductor-java-sdk/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f3d88b1c2faf2fc91d853cd5d4242b5547257070 GIT binary patch literal 58695 zcma&OV~}Oh(k5J8>Mq;vvTfV8ZQE5{wr$(iDciPf+tV}m-if*I+;_h3N1nY;M6TF7 zBc7A_WUgl&IY|&uNFbnJzkq;%`2QLZ5b*!{1OkHidzBVe;-?mu5upVElKVGD>pC88 zzP}E3wRHBgaO?2nzdZ5pL;m-xf&RU>buj(E-s=DK zf%>P9se`_emGS@673tqyT^;o8?2H}$uO&&u^TlmHfPgSSfPiTK^AZ7DTPH`Szw4#- z&21E&^c|dx9f;^@46XDX9itS+ZRYuqx#wG*>5Bs&gxwSQbj8grds#xkl;ikls1%(2 zR-`Tn(#9}E_aQ!zu~_iyc0gXp2I`O?erY?=JK{M`Ew(*RP3vy^0=b2E0^PSZgm(P6 z+U<&w#)I=>0z=IC4 zh4Q;eq94OGttUh7AGWu7m){;^Qk*5F6eTn+Ky$x>9Ntl~n0KDzFmB0lBI6?o!({iX zQt=|-9TPjAmCP!eA{r|^71cIvI(1#UCSzPw(L2>8OG0O_RQeJ{{MG)tLQ*aSX{AMS zP-;|nj+9{J&c9UV5Ww|#OE*Ah6?9WaR?B04N|#`m0G-IqwdN~Z{8)!$@UsK>l9H81 z?z`Z@`dWZEvuABvItgYLk-FA(u-$4mfW@2(Eh(9fe`5?WUda#wQa54 z3dXE&-*@lsrR~U#4NqkGM7Yu4#pfGqAmxmGr&Ep?&MwQ9?Z*twtODbi;vK|nQ~d_N z;T5Gtj_HZKu&oTfqQ~i`K!L||U1U=EfW@FzKSx!_`brOs#}9d(!Cu>cN51(FstP_2dJh>IHldL~vIwjZChS-*KcKk5Gz zyoiecAu;ImgF&DPrY6!68)9CM-S8*T5$damK&KdK4S6yg#i9%YBH>Yuw0f280eAv3 za@9e0+I>F}6&QZE5*T8$5__$L>39+GL+Q(}j71dS!_w%B5BdDS56%xX1~(pKYRjT; zbVy6V@Go&vbd_OzK^&!o{)$xIfnHbMJZMOo``vQfBpg7dzc^+&gfh7_=oxk5n(SO3 zr$pV6O0%ZXyK~yn++5#x`M^HzFb3N>Vb-4J%(TAy#3qjo2RzzD*|8Y} z7fEdoY5x9b3idE~-!45v?HQ$IQWc(c>@OZ>p*o&Om#YU904cMNGuEfV=7=&sEBWEO z0*!=GVSv0>d^i9z7Sg{z#So+GM2TEu7$KXJ6>)Bor8P5J(xrxgx+fTLn1?Jlotz*U z(ekS*a2*ml5ft&R;h3Gc2ndTElB!bdMa>UptgIl{pA+&b+z_Y&aS7SWUlwJf-+PRv z$#v|!SP92+41^ppe}~aariwztUtwKA8BBLa5=?j3@~qHfjxkvID8CD`t5*+4s|u4T zLJ9iEfhO4YuAl$)?VsWcln|?(P=CA|!u}ab3c3fL8ej9fW;K|@3-c@y4I;^8?K!i0 zS(5Cm#i85BGZov}qp+<-5!Fh+KZev3(sA2D_4Z~ZLmB5B$_Yw2aY{kA$zuzggbD{T zE>#yd3ilpjM4F^dmfW#p#*;@RgBg{!_3b6cW?^iYcP!mjj!}pkNi{2da-ZCD2TKKz zH^x^+YgBb=dtg@_(Cy33D|#IZ&8t?w8$E8P0fmX#GIzq~w51uYmFs{aY76e0_~z2M z(o%PNTIipeOIq(H5O>OJ*v8KZE>U@kw5(LkumNrY>Rv7BlW7{_R9v@N63rK)*tu|S zKzq|aNs@81YUVZ5vm>+pc42CDPwQa>oxrsXkRdowWP!w?=M(fn3y6frEV*;WwfUV$s31D!S_;_~E@MEZ>|~wmIr05#z2J+& zBme6rnxfCp&kP@sP)NwG>!#WqzG>KN7VC~Gdg493So%%-P%Rk!<|~-U|L3VASMj9K zk(Pfm1oj~>$A>MFFdAC8M&X0i9-cV7Q($(R5C&nR5RH$T&7M=pCDl`MpAHPOha!4r zQnYz$7B1iLK$>_Ai%kZQaj-9)nH$)tESWUSDGs2|7plF4cq1Oj-U|+l4Ga}>k!efC z*ecEudbliG+%wI8J#qI!s@t%0y9R$MBUFB)4d47VmI`FjtzNd_xit&l1T@drx z&4>Aj<2{1gUW8&EihwT1mZeliwrCN{R|4@w4@@Btov?x5ZVzrs&gF0n4jGSE33ddUnBg_nO4Zw)yB$J-{@a8 z);m%fvX2fvXxogriNb}}A8HxA)1P-oK+Da4C3pofK3>U_6%DsXFpPX}3F8O`uIpLn zdKjq(QxJTJ4xh->(=lxWO#^XAa~<7UxQl8~8=izS!TcPmAiBP5Et7y?qEbFd9Q=%IJ;%Kn$lto-~3`}&`x=AVS+Uo7N*hbUxhqVH_w^sn!74z{Ka#*U6s z=8jIrHpUMBC@@9Jn~GS<$lse*EKuX%3Swl5&3~GiK_$vn8Vjqe{mjhBlH}m4I8qK+ ztU50COh7)d-gXpq-|}T;biGa^e=VjxjjFuoGIA8`2jJ}wNBRcsx24?7lJ7W4ksNPv zA7|gcXT@~7KTID#0|EX#OAXvgaBJ8Jg!7X#kc1^Tvl;I(=~(jtn-(5bhB=~J^w5bw z8^Hifeupm;nwsSDkT{?x?E(DgLC~Nh8HKQGv`~2jMYrz9PwS^8qs3@nz4ZBCP5}%i z=w}jr2*$X-f(zDhu%D8(hWCpix>TQpi{e`-{p^y?x4?9%)^wWc?L}UMcfp~lL|;g) zmtkcXGi9#?cFOQQi_!Z8b;4R%4y{$SN~fkFedDJ&3eBfHg|DRSx09!tjoDHgD510Z z_aJLHdS&7;Dl;X|WBVyl_+d+2_MK07^X1JEi_)v$Z*ny-()VrD6VWx|Un{)gO0*FQ zX{8Ss3JMrV15zXyfCTsVO@hs49m&mN(QMdL3&x@uQqOyh2gnGJYocz0G=?BX7qxA{ zXe0bn4ij^;wfZfnRlIYkWS^usYI@goI9PccI>}Ih*B!%zv6P$DoXsS%?G)|HHevkG z>`b#vtP=Lx$Ee(t??%_+jh(nuc0Q&mCU{E3U z1NqNK!XOE#H2Pybjg0_tYz^bzX`^RR{F2ML^+<8Q{a;t(#&af8@c6K2y2m zP|parK=qf`I`#YxwL=NTP>tMiLR(d|<#gEu=L-c!r&(+CpSMB5ChYW1pUmTVdCWw|!Ao?j&-*~50S`=) z9#Knf7GPA19g%Y7wip@`nj$aJcV|SakXZ*Q2k$_SZlNMx!eY8exF;navr&R)?NO9k z#V&~KLZ0c9m|Mf4Gic}+<=w9YPlY@|Pw*z?70dwOtb<9-(0GOg>{sZaMkZc9DVk0r zKt%g5B1-8xj$Z)>tWK-Gl4{%XF55_Ra3}pSY<@Y&9mw`1jW8|&Zm{BmHt^g=FlE{` z9Lu7fI2v3_0u~apyA;wa|S4NaaG>eHEw&3lNFVd_R9E=Y? zgpVQxc9{drFt2pP#ZiN~(PL%9daP4pWd*5ABZYK{a@e&Vb`TYiLt$1S>KceK36Ehz z;;MI%V;I`#VoSVAgK3I%-c>ViA>nt=5EZ zjr$Jv~$_vg<$q<@CpZ1gdqP_3v^)uaqZ`?RS_>f(pWx3(H;gWpjR?W8L++YPW;)Vw3)~tozdySrB3A2;O<%1F8?Il4G|rO0mEZYHDz!?ke!$^bEiWRC1B%j~ws0+hHS;B8l5Wh)e+Ms7f4M4CbL%Q_*i~cP}5-B(UkE&f7*pW6OtYk5okQCEoN4v|7;(+~~nyViqo5 z(bMGQi$)KN6EmfVHv4pf2zZMJbcAKyYy>jY@>LB5eId|2Vsp{>NMlsee-tmh({;@b z@g;wiv8@a1qrDf-@7$(MR^M^*dKYBewhIDFX%;*8s zR#u?E;DJO;VnTY6IfbO=dQ61V0DisUAs4~t|9`9ZE(jG}ax#-xikDhsO_4^RaK ziZ?9AJQP_{9WuzVk^s_U+3V8gOvVl5(#1>}a|RL>};+uJB%nQM-J>M4~yK)cioytFXtnmOaJZSiE+3g}C`Im~6H z*+-vjI>ng5w>>Y!L(+DwX2gs0!&-BFEaDie4i5ln*NGP$te7$F9iUlJl4`XpkAsPm z0l?GQ17uN^=g~u1*$)S`30xL%!`LW*flwT*#svAtY(kHXFfvA`dj*pDfr0pBZ`!La zWmX$Z@qyv|{nNsRS|+CzN-Pvb>47HEDeUGFhpp5C_NL0Vp~{Wc{bsm_5J!#tuqW@? z)Be zb&Gj&(l*bHQDq7w-b`F9MHEH*{Dh~0`Gn8t`pz}!R+q~4u$T@cVaUu`E^%0f-q*hM z1To6V31UGJN7a-QW5;nhk#C26vmHyjTVZkdV zqYMI9jQY)3oZt=V0L7JZQ=^c2k){Y_lHp&V_LIi*iX^Ih3vZ_K<@Di(hY<&g^f?c$wwF-wX1VLj>ZC4{0#e`XhbL_$a9uXS zKph*4LupSV2TQBCJ4AfOXD8fs2;bAGz-qU4=Qj$^1ZJX z2TtaVdq>OjaWGvv9)agwV)QW9eTZ-xv`us2!yXSARnD5DwX_Vg*@g4w!-zT|5<}-7 zsnllGRQz>k!LwdU`|i&!Bw^W7CTUU3x`Zg8>XgHj=bo!cd<#pI8*pa*1N`gg~I0ace!wzZoJ)oGScm~D_Sc;#wFed zUo;-*0LaWVCC2yqr6IbeW3`hvXyMfAH94qP2|cN``Z%dSuz8HcQ!WT0k38!X34<6l zHtMV%4fH5<6z-lYcK;CTvzzT6-^xSP>~a*8LfbByHyp$|X*#I6HCAi){gCu1nvN%& zvlSbNFJRCc&8>f`$2Qa`fb@w!C11v1KCn)P9<}ei0}g*cl~9A9h=7(}FO!=cVllq3 z7nD)E%gt;&AYdo{Ljb2~Fm5jy{I><%i*GUlU8crR4k(zwQf#nima@xb%O71M#t-4< z(yjX(m^mp_Y;5()naqt2-VibylPS)Oof9uBp$3Gj`>7@gjKwnwRCc>rx%$esn);gI z5B9;~uz57n7Rpm8K^o=_sFPyU?>liHM&8&#O%f)}C5F7gvj#n#TLp@!M~Q?iW~lS}(gy%d&G3p?iBP z(PZQUv07@7!o3~1_l|m5m;Xr)^QK_JaVAY3v1UREC*6>v;AT$BO`nA~KZa1x3kV2F z%iwG7SaaAcT8kalCa^Hg&|eINWmBQA_d8$}B+-Q_@6j_{>a- zwT3CMWG!A}Ef$EvQsjK>o)lJ;q!~#F%wo`k-_mT=+yo%6+`iGe9(XeUl;*-4(`G;M zc@+ep^Xv&<3e7l4wt48iwaLIC1RhSsYrf6>7zXfVD zNNJ1#zM;CjKgfqCabzacX7#oEN{koCnq1-stV+-CMQ=ZX7Fpd*n9`+AEg9=p&q7mTAKXvcbo?$AVvOOp{F>#a;S?joYZl_f}BECS%u&0x!95DR;|QkR9i}`FEAsPb=)I z8nb=4iwjiLRgAF}8WTwAb^eA>QjL4Srqb#n zTwx^-*Z38Uzh@bX$_1tq>m{o8PBX*t3Lqaf$EBqiOU*2NFp{LJX#3}p9{|v{^Hg4f zlhllKI>F+>*%mu6i9V7TT*Wx-zdK z(p8faUOwGOm5mBC%UGA1jO0@IKkG;i&+6Ur8XR2ZuRb$*a}R^-H6eKxcYodlXsF`& z{NkO+;_Yh-Ni@vV9iyzM43Yibn;oC7hPAzC24zs&+RYdY&r`3&&fg2hs62ysV^G`N zHMfBEFo8E3S$0C_m({bL8QCe$B@M{n1dLsaJYIU;(!n*V?0I1OvBB=iYh&`?u8 z&~n-$nbVIhO3mMhCQRlq%XRr1;Hvl=9E_F0sc9!VLnM>@mY~=Cx3K5}wxHKEZF9pC zIdyu1qucM!gEiomw7bW0-RwbX7?o=FE#K0l4`U2KhC8*kMWaEWJyVNZVu_tY2e&4F zb54Lh=Oz>(3?V$!ArXFXh8Cb3i;%KQGCrW$W#;kvx$YA2gofNeu?@nt>Yq8?2uJQp zUTo14hS%&dHF3Uhm~Z1>W)yb%&HoM!3z?%a%dmKT#>}}kKy2B=V3{Nu=bae%V%wU$ zb4%^m?&qn==QeHo`nAs3H}wtiK~!!&i|iBLfazh6!y9F)ToKNyE0B385!zq{p)5vB zvu`R#ULIS|2{3w52c*c$4}Pe>9Fw&U^>Bb_LUWn!xPx3X-uQsv(b1XFvFzn#voq0* z5~o`V_G805QXdgAOwOjoqmZ?uzwBVYSNP0Ie8FL`P0VK1J4CzV@t&%0duHB{;yIL$FZ9 zz#s#%ZG6ya&AwE;0_~^$1K

Hnj76Oym1QVh(3qRgs)GmgnEt-KxP|nCFY3uezZn zmtR0CZ$Z_-+f07?lu_tr~IC{&U6+QOth>ZgYk4V2FI$B2V3`M`Jk zsr>>lupymPeK129PfpDt9?GA2;I>03Ktz8NxwvTroqu8oaRB&bXT}G=^2UyOW}(4H z;9sG^YwV8K7pC&&viM^X_pfeFoN!cIhrE>OPQ5E<4KKDyPhRV^BGb_^Y6GO6#w}c= zu`0fC-@F4qXQtnB^nPmfI7Uw0bLhY^09TCO+H2(nvg8jdPjMAi4oSX%GP3oeo0`ks z%DoV|waU-Q7_libJCwnnOL9~LoapKqFPpZx?5FygX zsA~*ZR7X=@i{smf?fgxbcY6Y`JvD50P=R;Xv^sANPRp-Hc8n~Wb*gLIaoZJ2Q^CFe z_=G}y&{_NXT|Ob??}$cF7)$oPQMaeN_va1f%>C>V2E01uDU=h~<_fQKjtnl_aho2i zmI|R9jrNdhtl+q*X@}>l08Izz&UJygYkbsqu?4OOclV{GI5h98vfszu2QPiF?{Tvh19u_-C^+NjdAq!tq&Rd`ejXw#` z@U15c$Nmylco)Yj4kctX{L+lz$&CqTT5~}Q>0r-Xe!m5+?du6R&XY|YD5r5C-k*`s zOq-NOg%}RJr5ZWV4)?EO%XzZg&e8qVFQ?40r=8BI-~L%9T7@_{1X@<7RjboXqMzsV z8FiSINMjV*vC^FCv_;`jdJ-{U1<_xjZg4g?ek z4FtsapW_vFGqiGcGHP%?8US~Dfqi8^ZqtHx!}0%dqZFg%nQB)8`mE$~;1)Fb76nFk z@rK#&>2@@)4vO&gb{9&~R8-_{8qz6Rmw`4zeckD(L9xq}{r(fUO0Zh-R(d#x{<0j| z?6xZ2sp3mWnC}40B~g2QinHs1CZqZH&`+x2yBLT8hF7oWNIs_#YK2cyHO6AoGRG|RM>Hyn(ddpXFPAOGh~^0zcat`%&WoEQf9)!@l*3Tt@m>Lb z6$+$c!zsy_=%L9!_;jfd`?VXDd*^Vn%G>n~V9Vr6+_D@#E+dWB#&zAE+6xJeDMr1j zV+Tp~ht!M%^6f?)LBf8U1O4G#CutR07SB>8C&_&;g3TdIR#~e~qRtwd>&)|-ztJJ#4y0|UMjhJZlS8gA zAA260zUh+!$+xMfWKs|Lr23bcy#)JNnY|?WOka&wTS7_u%*N7PrMl1Lp9gxJY%CF? zz4IA@VVxX{knZPlNF+$9)>YIj#+(|$aflt=Wnforgn6`^3T+vaMmbshBjDi&tR(a7 zky~xCa77poRXPPam)@_UCwPdha^X~Aum=c0I@yTyD&Z!3pkA7LKr%Y6g%;~0<`{2& zS7W$AY$Kd}3Tg9CJgx=_gKR59zTMROsos?PU6&ocyCwCs8Qx1R%2#!&5c%~B+APu( z<1EXfahbm{XtOBK%@2a3&!cJ6R^g|2iLIN1)C2|l=;uj%tgSHoq2ojec6_4@6b<8BYG1h-Pm_V6dkRB!{T?jwVIIj&;~b7#%5Ew=0Fx zc(p7D1TT&e=hVt4spli}{J6tJ^}WL>sb`k}&gz+6It`Yz6dZdI53%$TR6!kSK2CfT*Q$`P30 z;$+G$D*C$U(^kkeY!OWn$j@IUu0_a{bZQ=TCbHD1EtmZ0-IBR<_3=tT%cz$>EE!V}pvfn7EMWs^971+XK}~kxSc_ATJJD$?)1Gz^Jq!>Hz#KkdCJ~jb-Y*Xv01_}}=T_V-A1<3O!V9Ezf z%Lnjihb3>=ZV}jSeqNu5AAdVbe|`;|p<%W#-<$s1oDYrB;C({psqV>ENkhadsC{cfEx=teVSB`?FOs+}d#pssxP z(ihudAVu3%%!*vOIWY11fn1M0&W|(|<2lEShz|#%W|wV2qM%#+P9NOy1x8jytHpfU zh;_L^uiL<<$L@~NpRXSrkJgdC>9R=>FmVu3^#C?3H>P{ue=mcv7lBmnfA?mB|L)EF zHv%Nl|D}0Tb~JVnv$ZysvbD8zw)>|5NpW3foe!QHipV9>Zy`|<5?O+rsBr*nZ4OE} zUytv%Rw7>^moSMsSU?@&a9+OdVgzWZnD>QXcUd{dd7vad+=0Hy)4|0A`}rpCx6cu!Ee5AM=iJ?|6=pG^>q(ExotyZP3(2PGhgg6-FkkQHS?nHX(yU0NG;4foCV|&)7 z1YK!bnv%#5n<25|CZ>4r1nK=D39qMzLAja*^#CN(aBbMx${?Iur3t=g2EMK|KwOF?I@W~0y`al&TGqJ zwf#~(?!>@#|JbDjQV9ct%+51l%q|lcY&f{FV&ACRVW*%VY6G5DzTpC!e%=T30mvav zRk$JOTntNoxRv>PDlJG1X=uep&???K00ep|l_#7=YZPuRHYoM46Z$O=ZZuGy_njgC z>P@gd+zKH5SjpWQ!h_r*!ol1s{9DS@sD4}xgFxaw>|av!xrKzg?rGnhZ#uZeU~iod z3-i*Hl@7cge0);y{DCVU(Ni1zg{yE&CxYT7)@zJ%ZZABj-Fh}0au^)*aw`vpmym;( z5|JZ!EACYenKNXH%=Md{my$sI3!8^FgtqkMcUR%w_)EBdP5DZ64aCIR%K99tId6SU ziT8Ef)K%7{XuIpPi}N+&FCm$elE>oKY;3c$x+*mXy?~wt6~?ss$HGqCm=YL2xzVTQ zr>*2_F;7j{5}NUPQ(aY0+h~rOKN|IA28L7^4XjX!L0C^vFB+3R5*1+s@k7;4d#U=5 zXTy8JN^_BCx1a4O3HMa9rf@?Fz>>dq}uvkY7!c?oksgs~xrpCo1{}^PD?w}Ug z3MbfBtRi z$ze~eRSLW^6bDJJeAt^5El{T*i1*v9wX{T7`a2wAVA z%j>3m*g^lc*~GOHFNy?h7>f7mPU*)3J>yPosaGkok}2#?wX5d$9moM~{NTzLznVhX zKa}bFQt#De`atoWzj4Lb@ZCud_T9rA@6VcmvW(+X?oIaH-FDbEg#0Slwf|7f!zUO( z7EUzpBOODL&w~(tNt0z|<9}Filev&4y;SQPp+?kIvJgnpc!^eYmsWz1)^n`LmP&Ui z-Oi1J2&O|$I<^V@g2Z91l3OArSbCkYAD0Tuw-O(INJJ>t%`DfIj}6%zmO+=-L{b!P zLRKvZHBT=^`60YuZon~D$;8UDlb-5l8J=1erf$H(r~ryWFN)+yY@a;=CjeUGNmexR zN)@)xaHmyp$SJcl>9)buKst5_+XomJu34&QMyS zQR(N@C$@%EmfWB8dFN(@Z%xmRma@>QU}!{3=E`wrRCQ~W=Dwb}*CW8KxAJ;v@TAs3 zW}Pq5JPc)(C8Rths1LR}Bgcf6dPOX<#X08^QHkznM-S>6YF(siF;pf~!@)O{KR4q1_c`T9gxSEf`_;a-=bg6=8W zQ&t`BK^gsK-E0Jp{^gW&8F9k?L4<#}Y0icYT2r+Dvg!bnY;lNNCj_3=N=yd9cM9kY zLFg|R0X;NRMY%zD*DbAmFV`(V@IANtz4^_32CH*)XCc$A>P-v49$k@!o$8%Ug>3-- z$#Fpo9J>eUMKg>Cn+T0H!n0Hf#avZX4pp54cv}YcutP+CmKC~a745-zhZp`KNms;J zS3S49WEyS8gCRAY|B~6yDh*cehY52jOSA#MZmk2dzu`_XpBXx9jDf!H3~!`n zaGe=)1VkfIz?*$T3t>-Pwhrw447idZxrsi;ks;(NF>uVl12}zI(N~2Gxi)8yDv-TLgbZ;L&{ax&TBv;m@z6RcbakF^el{!&)<___n#_|XR%jedxzfXG!a2Eyi)4g zYAWkYK{bQzhm|=>4+*SLTG2<#7g-{oB48b05=?PeW;Jo3ebWlo5y5|cl?p8)~PVZqiT^A~w-V*st8kV%%Et1(}x(mE0br-#hyPspVehofF`{gjFXla1lrqXJqQKE9M)8Xe0ZO&s$}Q zBTPjH>N!UU%bRFqaX(O9KMoG$Zy|xt-kCDjz(E*VDaI={%q? zURR{qi>G^wNteX|?&ZfhK-93KZlPXmGMsPd1o?*f_ej~TkoQ#no}~&#{O=>RadgtR zvig@~IZMsm3)vOr`>TGKD&fbRoB*0xhK7|R?Jh-NzkmR}H6lJiAZTIM1#AXE1LOGx zm7j;4b(Lu6d6GwtnsCvImB8%KJD+8z?W{_bDEB$ulcKP*v;c z*Ymsd)aP+t$dAfC-XnbwDx3HXKrB{91~O}OBx)fsb{s-qXkY<@QK7p-q-aaX&F?GS z2};`CqoNJ$<0DuM2!NCbtIpJ9*1a8?PH#bnF#xf~AYOIc4dx1Bw@K=)9bRX;ehYs; z$_=Ro(1!iIM=kZDlHFB>Ef46#rUwLM%)(#oAG(gYp>0tc##V{#aBl!q``!iIe1GBn z+6^G^5)(nr z8h#bm1ZzI450T?!EL)>RWX8VwT1X`2f;dW!{b~S>#$Pa~D6#Hp!;85XzluH%v5325 z730-aW?rY1!EAt;j7d23qfbMEyRZqxP};uID8xmG@mGw~3#2T^B~~14K5?&dP&H@r zL|aXJsEcAAXEXfu2d-!otZTV=if~^EQD*!NkUFQaheV&b-?-zH6JfjKO)aYN=Do*5 zYZ-@m#)5U0c&sUqu_%-Editr5#%Ne&bs)DxOj2_}`f;I_ReEY9U&Cf3rb>A3LK(ZD zid0_-3RfsS*t&g!zw}C_9u(_ze-vc1L59CdBl(IS^yrvsksfvjXfm>(lcol%L3))Q z@ZT;aumO3Q#8R!-)U697NBM@11jQ>lWBPs#?M4_(w=V_73rsiZh8awEm>q1phn1Ks ze@D|zskeome3uilE8-dgG(EojlI(@Yhfm}Xh_AgueHV`SL##I@?VR+bEHH=sh21A_ zhs&pIN7YTLcmJiyf4lZ;`?pN0`8@QbzDpmT`$m0CTrTMiCq%dE&Cd_{-h`I~f8Kps zAuZt4z)}@T>w$9V@iLi=mh({yiCl}}d>JN)z;*G<6&mgl(CYhJHCAPl=PYK2D>*F zy;YK=xS@1JW7i=C)T04(2P#|fowalY=`Y`G8?eRMAKt|ddG9UF^0M5 zW=ZGZ5qb-z@}iS`4RKXvuPIfzUHT)rv<8a|b?bgB3n=ziCiX4m2~CdVBKHWxw2+Hz zLvqoAij9(0moKoo2$`dqS0?5-(?^RXfcsQB6hU2SAgq8wyeasuyFGcK+@An?8ZzVw zW8wwbZB@i=<<4fA7JKPkki6y>>qO3_bW>-uQ*>9g+g7M0U^`RV)YTrGu2Q=2K>fiI zY0dFs>+}xuOZE^efLK2K6&X@>+y10Oqejnnq^NjfXt9JpK4K_E=cl29 z(t2P;kl4AK_Jg9v{1(z)ESpyo_(Z`74D&J1A#J?l5&J^Ad1sm5;Po@s9v7wOs(=_T zkutjt`BaxT09G{-r>yzyKLlM(k`GZl5m+Tgvq=IN|VjtJ*Zu66@#Rw;qdfZqi15A@fr^vz?071F5!T`s>Lx5!TszI%UK|7dDU;rUCwrRcLh!TZZ9$UMfo z@Qzjw>tKS3&-pyWS^p4mMtx`AvwxVc?g?#8aj@jQ#YKDG0aCx{pU+36?ctAiz=f$k z05S(b&VPQgA(Sm`oP&M^eiHvBe&PcTb+j$!!Yx(j3iI5zcQLOn(QqfX5OElbSsQBUw7);5C92onieJyx`p{V!iwXk)+1v zA6vStRZo0hc>m5yz-pkby#9`iG5+qJ{x>6I@qeAK zSBFylj8{FU*0YbFd2FZ6zdt^2p?V;3F~kap`UQgf@}c33+6xP)hK)fmDo@mm=`47* z9S6rnwCSL&aqgZs959!lhEZZp`*>V8ifNmL;cqajMuaJ~t`;jLPB?X~Ylk_Z#Q;%} zV+sAJ=4505-DdnIR=@D_a`Gy#RxtSX+i-zInO@LVDOd*p>M-|X(qRrZ3S(>(=Oj>} z89d75&n?m^j>;SOXM=)vNoum|3YmzxjYx%^AU*V|5v@SjBYtESp^yz?eQ#>5pnCj} zJ_WCw23wGd2AA-iBve8Hq8`%B3K4@9q@a}sf$49IA^IPsX@QK)36mrzqOv?R_n9K@ zw3=^_m#j{gNR0;&+F~wlS(i8IQN8mIvIO)mkx|e)u*y+xDie}%mkZ*m)BQM^$R@-g z1FrP0{8A?EcxtxxxX&J;393ljwwG?2A2?y-1M0-tw$?5ssoEsbPi?sd2!s~TrwPLF zYo-5XYV7AU-c|Vb-v;>pVi^CwX(Rpt<9{Ic?@<9SrNu>F(gwij%?dC9^!Xo90o1-| z&_aPKo%+xyw64e&v<}F^-7sO0Cz-VOF@7**i@v&(Oy4Q8PbV+4&rKwmYyokM z48OZ|^%*mC_Q)RJ31D#b4o4Jzr{~BX4D#swW<31;qCil2qlim;e=9ymJAEXfv-|h3 z)>uqQ5~S+8IgiWW28Fqbq+@ukCLy+k7eGa1i5#G_tAUquw$FjFvQt6~kWa69KXvAj z-knF`5yWMEJvCbTX!K{L)VeNF?(+s?eNjtE5ivg^-#937-l()2nKr#cHShB&Pl^l8 zVYws26D^7nXPlm<_DYU{iDS>6Bq0@QsN%6n>XHVvP<^rDWscC!c+LFrK#)T@$%_0{ zob%f&oaq>1_Z8Ata@Y2K6n?GYg|l8SgUr(}hi4D!@KL~hjRv<}ZZ`tCD^ev=H&^0pP%6q2e+t=Ua`ag8xqWvNnIvCU|6ZA^L5v{DD)!mcQ@n6{=; z#Z)PrAz>*+h-|IV!&J*f@{xb!L7h3{?FEs*ifw5z2U9$&OkYseI68yb=V4xv*VK3- zVxGhtmedujX32y-kC{5ej-Wy#JvB~4oxTb{|1H825_B(A0#?CjUTc=PrGh6jAgK9h zoLAe`+NBdStZE@Y8UH^Rd*|R-|7Ke}wr$(CZQHhO+upHlCp)%n+fH_}S8%^%xqhu%20_1p=x#Dl9ia`c3iM+9Vh5?gyY8M9c$tJ5>}V_sidHN zoMl%rSgSK!7+Y8tQkYq|;Vh`4by2uMsUfnxkk2{S@a>V#d}fv}Yud*>paVi_~T zU!GoYwWbnG%92!Cte(zhZX-i9#KJ;b{$(aZs|{MerP#6||UUx$=y)4XOb zihyKn`_QhJ#~@_peJ*8yD4>I7wQyKkZG%#FTKZfb(@G+9x7-3@hG}+ZC&$7DwbaB$ zC)jLj7yituY&WpOWlG7Z4Tuxzdwo6k!3lgwhh7BYMyB? zO9Q5nvn77~g~c623b`Pe5efNzYD#2Sfmg>aMB5s?4NC|-0pIXy%%`J;+E{(irb!Szc8M8A@!}0zqJLoG4SJ5$~1*yRo0^Z`uObA+= zV?1sYNvzvWbP%AsMzoIo3Cwx~y%i8rHF(BgLS>tH5Ab|1wp$X_3o2_VB(pFxgQ5QQ zk@)Vy95$b%HVf4@ppX(wrv^Jwfrsu+9N_OUm}nD7Ch_7STj66EYsZR#`9k|Tf^@p& ziHwnO$p{TB#R(Q{Os>Un~0!r$JO zLZ&F%SP|%$TuG)mFeOhKr1?S!aa0jTV$2XIeZb_fgO&n{8HTe9s`L&(tKoy?OaS^$ zLHNrgYgq920EI~M>LyU7gK70$7*`nFKD^d>MoEAhsBU0%@*RW@%T(J z?+wVbz=mcN%4#7qlCpl_^Ay7VB%?+uW1WSNnQOj^tALyqTpV zkEN2C;qO_W)MYl^Ow5I;t3;z#iG82F(qe}#QeE;AjA=wM==dB(Gu+ez*5|RVxO4}l zt`o?*B;);-0`vR(#+Q^L4WH_9wklh-S-L-_zd%Q0LZ%|H5=>Z)-x#Z+m%p&6$2ScV zEBneIGo)r0oT)xjze*Q~AIqhB%lOM5Id}^eKwS!?b_;B&TouZsemyL&y`)#FX}ZKp zp)ZnB*^)1P@2bCoe+Z|#KhTBNrT)UN@WIuudw})fwHl)re1|b~E1F=xpH?7L77p>5 zei$aD@KO0<+zo1<&7OuZatNsPq24Whu%0jD_ z$ZZy6MzayYgTJulNEy8D$F%JDYgx|d6{6kpDg#s170<15bM#4tzvrDU$6bvu-hH@6 zgcjq&3aR3k(23$FaUA|iuoy*bO{2F6W0<+ZdsYvXjc?d@ZT8kM!GD}r@qr;TF@0Hb z2Dz-A!HZ$-qJ?F%w6_`t`8xk$f$MNBfjqwvJiVdD+pf7NVFGh?O=qp2vh%UcYvc{rFldib~rkIlo`seU%pO_6hmBWGMcUhsBSWiQYYPMX<-Cjp49@7U==iS57bG zw3T9Nbm`)m9<<4e$U74`t~zRo0JSfi}=GdQXGLLPyW zlT^I}y=t$j{Vx!wN^z8X4l0|@RNrC#)G>bK)7IT7Qop>YdS^NnI3gfP>vtp)pXkr2WSVcAAv8uN>@ z`6)kICvNYU$DA8pnkl4sQopDC6<_M8zGJ^@ANXJL(yd#n1XFj9pH;rld*gwY8om_I zdB55w@FUQ_2k}d%HtQsmUx_7Mzftky&o2X2yDQrgGcehmrDDDtUJj5``AX$gzEbMc zUj2Qzp)Lo>y-O*@HJ|g9$GR2-jgjKfB68J6OlIg;4F2@2?FlW zqj|lO7A2Ts-Kd!SO|r9XLbPt_B~pBpF40xcr0h=a&$bg(cwjp>v%d~Uk-7GUWom?1 z92p+C0~)Og*-N~daT#gQdG{&dPRZso(#{jGeDb1G`N)^nFSB`{2-UQ&!fkPyK`m03 z_Di94`{-(%3nE4}7;4MZ)Pmawf#{}lyTSs5f(r;r1Dp4<;27K=F}Oga^VsUs3*NIn zOsYstpqpRF&rq^9>m50LRORj>=;{CV2&#C$-{M5{oY9biBSoQyXvugVcwyT-19S;pf!`GSNqb4**TI%Y z*zyV)XN3Fdp3RNNr9FU+cV*tt?4L8>D@kJp^rkf_rJ~DPYL}oJngd1^l!4ITQN`0RTT^iq4xMg|S6;d}lznE$Ip^8pW-CHu zP*^!U>Lcd3*shqa)pswq;y<|ISM1g1RG#`|MSPNAsw*XH1IAD(e(Kgqp6aDHgv>fI z!P67$z{#()Pdo3;4dUoy*Xor(O?+YTRPe=g*FfRj*9q9!8p%1l>g3e^rQ_nm{(@4t z?^nMDC2J8@my5q0QyCljCSp_@)No+6bZ*y)lSdrkLFcR6YOHu*vZ-q(C);5$MmM_z z1WT>Gc8g%`Rt~6*!}JhWi0=Rc_z5c8GR9YXW+cdoK~Ea(@wyXf|89HagNuFAO-V7k zUb|9zaCCWH3^Fz(m7$8K$|0ZOP!SNpgP!ql<)!z8w$Z$?9gq2f<~koe3|zD=imLfD z>IV5?SkRZ;7JlOG%z%Tlze$GXr0A}ResyF63ZGZVDLv2k4HWtoqoCaq+Z&GaVKuLA z>@zhNjYYc=sexH?;DTe4&2vnQE}C@UFo&|qcLddvH0FwswdRUc(p*X&IT^Zu>xLpG zn(@C%3ig(l2ZPm#Fc){+0b+%O7nt4zbOt+3@GQVm|1t70=-U(>yo3VY2`FnXFHUyi zwiqf(akt0kEE5_Pa-a*VCS}Pi6?`~P%bvX6UT~r-tUAY%I4XF3^nC+tf3alyL{M`w zv?aVQ#usdwpZmkrfv19O39}tQPQM+oY**a{X?@3Qe>r$+G!>r#?Id&U&m^HU(f= zjVpSi9M||1FyNQA&PO`*94&(qTTMQv3-z`bpCXs-3bX}#Ovqec<>omYhB*VrwxqjY zF3#OXFsj`h#G?F}UAilxTQ|78-edHc-Uc-LHaH*Y(K%R#dVw>_gz}kRD4s#+U&Pq= zps)kMf_t9`GHR7CO4zI8WVj0%qiSqy50N{e_5o#GrvNhMpJf5_sCPrEa%a@ltFnss ziaWh26vEW4fQp}qa4oP(l4xIMpA)~VHD9!lP%;Tm`(HD$jYMM-5Ag>S(gC35J35$%?^gk(r|`4Ewi-W z;f&;B*fO=kC@N=r<-#nGW|yXE;`zb0Y3TJOAkw1a$SQgoTawHZTck+V%T=spmP`^BHihc(jc+S1ObX%6AYQ6LVVc+BfM*P{2s0T2z zVIs*5{ql%#CKAzv0?@S+%||z;`dpfj0Y(VtA51n$j%sG5I%A|h98VU}PkVZFrk1*G zaw75v3(N50lanvr&ND4=7Db;HS4fpi)2vTME7aD2-8N5+kcOXmYCrLE?*5&dWhvB` zbD5)ADuIwwpS*Ms;1qyns(8&tZ*)0*&_lNa`_(phwqkL}h#WdX_ zyKg%+7vP>*&Fus9E4SqIN*Ms`QLB(YOnJ|md%U|X`r#tVN$#q6nEH1|blQ?9e(3|3 z`i#;GUl~v?I6&I6%YvkvmR?*l%&z)Pv8irzVQsWrZSr%aoYuPJa#EjK|4NmiuswK= zlKP2v&;yXv3>LQ$P){aYWrb)5GICwbj;ygw>*amKP;Z{xb^cF}O@IeQ^hB-OjEK{l z>#PNyLuVkeDroL9SK2*ChHmJJSkv@YRn7)E49fy!3tqhq`HtHs_(DK|2Lyv(%9L&f zSy+H}Uk{nE2^5h7zN7;{tP3)$1GK9Xcv^L48Sodg0}ZST@}x607yJo2O*XCfs7*wT@d?G^Q6QQRb!kVn?}iZLUVoyh8M4A^ElaHD*Nn2= zkfCS=(Bg9-Mck6K{ z%ZM59Rs4(j1tSG1B#wS=$kQfXSvw6V>A(IC@>F;5RrCos`N{>Oyg|o*qR2EJ>5Gpe ze~a4CB{mmDXC7C>uS@VL&t%X#&4k<`nDx;Zjmo%?A4fV3KOhBr;VuO!cvM8s2;pG5 zcAs!j?nshFQhNA`G3HMS z?8bfRyy1LwSYktu+I7Hurb-AIU9r|rl5nMd!S&!()6xYNJ1EqJd9BkjgDH@F*! zzjtj4ezywvlkV7X@dG^oOB}T76eK=y!YZB#53LhYsZuP&HdmVL>6kH8&xwa zxv8;t-AE>D5K<{`-({E0O4%fGiLVI8#GfZ0aXR6SfYiPUJKnujMoTI5El<1ZO9w|u zS3lJFx<7XUoUD(@)$pDcs3taMb*(v2yj#G)=Mz-1M1q@Tf4o{s9}Uj9Yo?8refJwV zJ;b+7kf0M}fluzHHHS!Ph8MGJxJNks7C$58^EmlaJcp`5nx+O7?J)4}1!Y>-GHf9o zk}oTyPa>+YC$)(Qm8|MhEWbj?XEq}R=0NFH@F3ymW>&KS!e&k5*05>V@O*~my_Th; zlP05~S5@q+XG>0EuSH!~gZe_@5Dbj}oNIiPJpEOip+3l!gyze@%qOkmjmx=?FWJLF zj?b}f8Vet*yYd16KmM43rVfZo?rz3u|L6Foi*GQe4+{REUv9*}d?%a{%=8|i;I!aT z7Wxm}QJC`?cEt9+$@kSkB!@`TKZz1|yrA1^*7geq zD5Kx-zf|pvWA+8s$egLrb=kY385v2WCGL{y4I15NCz5NMnyXP_^@rsP#LN$%`2+AL zJaUyV<5;B^7f+pLzTN50Z~6KC0WI<|#bMfv+JiP3RTN^2!a7*oi+@v3w*sm5#|7zz zosF*{&;fHBXn2@uguQ1IDsh(oJzH#i4%pk;Qh^T zfQLyOW;E*NqU!Fki*f-T4j(?C$lY2CT{e!uW}8E(evb3!S%>v^NtNy@BTYAD;DkVo zn9ehVGaO7s?PQBP{p%b#orGi6Y&~<;D%XLWdUi}`Nu-(U$wBBTt*|N4##sm2JSuWc)TRoYg57cM*VDGj~ka<=&JF zo8=4>Z8F`wA?AUHtoi$_hHoK!3v?l*P0$g^yipOWlcex4?N2?Ewb1U=lu}0`QICA4 zef61j-^1p}hkA*0_(esa!p%dX6%-1e-eMfQsIp6wRgtE=6=hDe`&jel{y=6x5;78s z?5^{J|t!#x1aS8<3C`v%E%u{*wZwSXr$0Owl5_ zmXh>D>C_SjOCL^CyGZpBpM5`eymt{*rf~9`%F&&o7*S!H%3X)7~QFgn^J>6 zD+yV}u{HN-x9*_$R;a+k?4k*1f)rE~K|QvcC3dlr>!nftB?gE-cfcPMj&9mRl>|Lg zQyCe|&SuZopU0>IfRmcV3^_mhueN5oQ=J+H4%UsSIum4r4!`^DJqZr?1j3BU)Ttzg z6LwM)W&UEMIe*H2T6|{rQ;x9qGbp7ca#-!Egm4|ECNTMN);`>2Q&%|BpOdIJ4l|fp zk!qEhl;n(Y7~R1YNt7FnY10bQZXRna2X`E_D1f*}v1bW^lJorDD0_p2Rkr32n}hY! zCDB(t$)4YOd)97R60gfg3|wrlsVs#4=poh4JS7Ykg$H)vE#B|YFrxU-$Ae^~62e;! zK9mwxK?dV4(|0_sv(zY&mzkf{x@!T8@}Z6Bf)#sfGy#XyRS1{$Bl(6&+db=>uy-@y z$Eq~9fYX$06>PSKAs#|7RqJ3GFb;@(^e`jpo-14%^{|%}&|6h{CD(w@8(bu-m=dVl zoWmYtxTjwKlI!^nwJ}^+ql`&fE#pcj*3I|_Z>#y##e@AvnlSN4po#4N#}WT)V5oNP zkG+h_Yb=fB$)i`e2Fd28kS$;$*_sI;o0Xoj#uVAtsB6CjX&|;Bk}HzQ*hJ!HDQ&qZ z^qf{}c`l^h5sg-i(pEg#_9aW(yTi?#WH=48?2Hfl_X+(SfW)_c48bG5Bf+MDNp>Y#Mpil%{IzCXD&azAq4&1U10=$#ETJzev$)C*S;Pr9papU3OabRQk_toRZ!Ge(4-=Ki8Db?eSBq~ZT#ufL6SKaXZ+9rA~ zQwyTQTI7*NXOhn?^$QOU>Y6PyCFP|pg;wi8VZ5Z$)7+(I_9cy--(;T#c9SO;Hk~|_ z0tEQ)?geu8C(E$>e1wy%f@o;Ar2e#3HZP$I#+9ar9bDa(RUOA+y!oB;NEBQ`VMb@_ zLFj{syU4mN%9GF;zCwNbx@^)jkv$|vFtbtbi7_odG)9s=q(-PtOnIVcwy(FxnEZm&O^y`vwRfhB z7Urcums9SQS6(swAgl?S|WDGUTFQu51yG$8069U zviuZ=@J&7tQ8DZG<(a->RzV+sUrmH$WG+QvZmUJhT*IoR3#3{ugW%XG0s?_ycS6V6 zS)019<_Rl@DN~8K4#w3g_lvRm4mK3&jmI$mwROr0>D`mX+228Dw4r;mvx7df zy~$zP8NjVX?xkGFaV>|BLuXMQ+BN+MMrIB4S6X)p&5l$;6=S8oI9qi&1iQbs?TroDMfCmIeJ}pbVVtVqHhS(zutEy6#UjTk29-+3@W0`KfehW`@np zhhu#)O&g%r)hTj4b$CY41NYp_)7!bYyG;v(rts z^}YDJt2W88H^H;e$LSm3dh=~yi@)mzJtEfW8=4avbeOE&;Oc>-6OHO+MW`XBZ4rO6 zS;nAi**w3Yso4&Ty+8f$uvT?Z)eaLe$KW1I~9YM2zeTIT}C%_G6FPH-s5Wi3r`=I&juGTfl zZ;4qFZV|6V0c&>t!Y>mvGx#1WWL0N5evV=u28K9**dv`}U3tJ$W?>3InXiwyc)SA% zcnH}(zb0@&wmE>J07n#DOs7~lw>5qUY0(JDQszC~KAAM}Bmd-2tGIzUpO@|yGBrJyXGJk3d+7 zJBN0$?Se(rEb0-z2m%CBd;~_4aH04%9UnSc4KP!FDAM5F_EFujJZ!KDR-fn181GX` z8A?8BUYV}D9bCE0eV~M>9SPag%iVCLWOYQJDzC4~B~Ct0{H7x|kOmVcTQ;esvyHJC zi$H0R73Z8+Z!9^3|2tNut#&MVKbm`8?65s)UM8rg6uE(|e^DYqvoc15-f;u8c=>3;Viz*T# zN%!T+Hex0>>_gUKs%+lgY9jo6CnxL6qnQ>C*RseLWRpipqI;AQE7;LUwL`zM%b`Vu z%Sa-+?a#+=)HaD|k2%_(b;pHRF96(c;QyPl6XHL8IqGQKC$M8R=US-c8;hUe?LKo&l!{V)8d&55sUXEu z5uITcO~`ipddh+Nr{7ibp^Wd{bU)^3##<5`lkuqfckxEU*9{pgNpTB2=ku1c-|3dK z|LIQF=ld@I7swq^4|G1VA}BK85&>2p#*P95W`I1FF(8G9vfNJ6MoN$+C^M89u!X=< zJSS%l?Qj>$J%9?0#0&S6#*h*(-9Z$}q*G#hP?cX7cAvM0eiVFhJJ~$`iZM!N5NhDb zi<1u_m#?jzpIaOe7h|Kiap#mHA`L|)ATnPJ7du{^ybuNx@1jA+V1l8ux#{LJ#teM(6=%gZcMq24J$2p z`wcC!qRssmwUv4H6Psw{(YdDNOv$!sq&O1SvIS}fCKZa+`T=Ayt@uZjQqEC{@Uj+| z!;i3W+p~=@fqEEhW@gT^JtCR<`m`i|Htg<TSJ&v`p;55ed zt@a|)70mq;#RP@=%76*iz>fAr7FKd|X8*@?9sWOFf$gbH$XFG zcUNu#=_+ovUd>FW*twO`+NSo*bcea=nbQ_gu^C7iR*dZtYbMkXL5mB@4a3@0wnwH! z(fZKLy+yfQRd%}-!aPC z4GB%OvPHXl(^H(BwVr6u6s=I;`SHQ1um7GPCdP-BjO%OQUH!_UKbEGvHCY}{OL`8FU$GZ;Y$SlS$-0VjK%lCP?U0shcadt4x7lN4%V}wBrLEbiEcK-OHl+pcBNSqN#mftpRj2A4Q z+av@-<#t_Dj_FN^O2~wq(ij1O*+=RVl+6gNV^~CI1UED- zn^zN@UOq8?q58b^4RA>lV}x;jA2OE=SqMYV9P#RsUlI+pp!y*jpwHgp-w3i$V)%?L z>irn1pnRc|P@r|Z0pCeMZ*k$}$`1GVGCT&QtJ`V%Mq!TXoge?8Fjn$bz}NqDn*2ZQ z$p3@F_^(}IVS76>OLNzs`O5!pF=LZ$<&gyuM$HQzHx8ww^FVxnP%Yv2i=m*1ASF~~ zP=!H}b`xl`k0pL5byku2QOS~!_1po!6vQyQL#LQ#rIRr?G5^W?yuNvw-PP{}%m35i$i+I?DJ%RGRcqekT#X~CxOjkV1UQrd&m_bbJ+gsSGbPwKS{F& zU-`QNw!*yq#Co#{)2JvP-6>lY$J$2u+e=r0&kEc#j#jh@4Tp;l*s<28wU%r= zezVPG^r*a?&Fn_(M|A7^xTPD998E-)-A4agNwT?=>FbrHz8w~w?hWBeHVYM()|buJ zvGv4j<%!U_Rh^ZKi~2(h1vk-?o9;`*Zc}m5#o@a1ncp)}rO2SDD9y!nT$_Eb%h`>% zDmssJ8Dl=gDn<-7Ug$~nTaRzd?CJh;?}nCco$7Pz<#J8;YL40#VFbAG|4nA$co;l^byBOT2Ki@gAO!{xU7-TY|rujdYTaWV(Rr{Jwu?(_TA zDR1|~ExJBfJ?MAReMF47u!oEw>JHVREmROknZUs2>yaboEyVs$Pg1f6vs06gCQp$b z?##4PWI#BxjCAVl>46V_dm4?uw=Y@h#}ER4|ACU{lddiweg`vq>gmB25`XuhNai1- zjt{?&%;TRFE+2Y_Gn;p^&&|bU44M=`9!Mc%NbHv|2E4!2+dUL z>6be$Kh|Duz}+)(R7WXsh!m`+#t^Its($x`pqDaN-^E z?*a=0Ck^rZBLQV~jY-SBliN&7%-y3s@FB;X)z(t&D=~@U0vT%xfcu`Lix=W#WVE{{ z2=C~L$>`~@JCIg8RAyk= zYG`(@w4H95n0@Fqv16~nlDU!+QZw&#w@K)hv!V>zA!ZOL$1Iykd&Su3rEln@(gxO| zxWc++T-rQEIL+j7i`TeatMfp4z7Ir31(TE4+_Ds@M|-+cwQg(z>s=S}gsSz{X*Wm+ ziKJWgOd`5^o|5a#i%?Gvw~8e?Rpi7C>nQ5dvPHVTO$PI^mnJ*7?gd3RD{|c_a>WrXT#Es3d}(k z$wpmA#$Q^zFclx{-GUL_M$i0&mRQMd4J#xq-5es)yD{kYCP1s!An(~K5JDRkv6DUSKgo^s@lVM5|V4mWjNZp zsuw^##l%rbRDKglQyj?YT!nk$lNUzh%kH705HWhiMuv(5a<~yoRDM&oCqm+1#S~|8 zA$g2Xr=}p_FX%Eaq{tUO9i*Q1i!>$+1JYZCL}flWRvF0y1=#D#y-JQTwx6uP-(bC} z_uP7)c;Xd`C6k#JVW?#Id7-|`uW+hN0>OM=C2Ta^4?G zr;EvxJ{%l|8D-heRYRM%f*LBC)krHZJ@%&CL0)FADWh14&7KV<9km6gE=o9(7keg~^rIQtthK^_8%Jk&aZLY_bc6SbY>IcwDK9{sV*t1GfKwf8aCo8t za)yALEi^-WXb!k6n>W-62Z^n8hO|eRYr&uZiW5d_URi??nl*aGu?ioQ+9RF9u8kwD z6UZ6HVd(G%l9>y7E)uyn?gAJMKeki0@tG*jdcE-}K?8(D-&n=Ld1i=A1AI<1z>u5p=B z<1}|q3@2jNxW-}Q4z~s|j&^Qc;nXIdS3K8caP_07#ig} z#KAD&ue2jXc&K#Q`Hy#x+LeT4HHUCzi1e?*3w{tK+5Tij(#2l2%p#YGI-b~{5{aS8 z!jABC*n6y~W|h;P!kn(a4$Ri2G118!?0WHDNn((QDJP^I{{wPf<^efQWW?zS>VS?X zfIUgCS{7oV$|7z2hJBt+pp1CPx4L{B_yC3oWdE)d)20WG6m5qknl}8@;kjPJE@!xP zV(Nkv^-Vz>DuwBXmKT(z>57*D<$u=Blt)IS-RK0j89omD{5Ya*ULWkoO)qeM_*)jF zIn87l{kXPp=}4ufM1h7t(lAL?-kEq>_DE-in8-!@+>E1+gCV9Fq)5V3SY?**;AKq0 zIpQ(1u*3MVh#tHRu5E5=B{W-QOI34plm`#uH(mk*;9&Re%?|v-=fvb;?qvVL@gc|l z8^L?2_0ZrVFS-stRY(E>UiQeG_sMrw5UiO znGFLOP-GO{JtBM@!)Q37k3G_p&JhdwPwtJS6@R4_($Ut^b!8HP{52-tkue8MG=Zwr z7u6WaFranJq4oNadY)>_6d~?pKVxg$2Uz`zZPnZVHOh-;M|H7qbV0OF8}z;ZPoI+| z(`e}bn6u*kJpRLC>OZ}gX#eHCMEk#d8y$XzSU;QZ|An$pQ%uZC$=Ki!h@&m8$5(xCtGaY3X1FsU?l5w^Fr{Q-?+EbUBxx+b?D z80o*@qg0juG;aZhj=tO=YHjfo=1+-NqLME~Kw7Y1A*?}M7#cOyT(vd$1tVPKKd@U! z&oV!RzZcK6gPWj`*8FIAy2I&x``h_sXPe*O{|ih(Y+V3|o68MWq~2Iy^iQ8RqK76f zC$1+hXqd^jsz`U{+EFo^VQNrLZt#R`qE*>2-Ip&(@6FmtAngx@+YnG}b5B9Y)^wg#oc z24KlT2s!H_4ZR^1_nDX#UH4(UTgl603&Q3g{G4!?6Sl9Om=Sy|8CjWO>d@e9?Q%s- z-OS3*W_H7*LW|Ne{b+^#LqQ}UKDmiZDma@no2!ydO^jcm>+z379K%=Ifs{20mT|xh zP$e7P=?N(tW4PMHJOQ`a8?n}>^&@<`1Rgo`aRevPp^1n7ibeS6sc8^GPe>c&{Kc+R z^2_F~K=HVI45Pf|<3)^;I{?H}vU7-QK3L1nHpcn3!1_)<$V;e0d_b8^d1T==rVpky zZTn~UvKrjdr11k}UO@o>aR2wn{jX5`KQQM1J1A?^wAFvi&A#NA#`_qKksu`sQ0tdM ziif17TO<{wDq_Q;OM}+1xMji^5X=syK=$QdZnS#dwe$;JYC7JozV8KpwfV}?As|^! zFlln0UitprIpuzLd$`<{_XoUV>rrHgc{cUQH-Px#(_Ul%=#ENrfJe@MRP_$E@FLMa zI`(J)Imw$o427@Oc^3(U&vz}<3Lfmy7diVpJJJ@gA>e;q-&gj zcGcBC_luF%_;**EB?o--G?AkaruJ%-b*8aX$4E+-?V@RWMnjHJ;hx27Vd7l0nUUY( z6OQb&8g8cvN3LZ%^xvIav*X|Epqm@yrTZk9U{GSZXAUJt8Lh(%7?Eaf&AzmXOVvU| zmz<@l1oMe#^POR38KT6q3@c`{%eYNu4ccurv`q?b5DzLxENjSfYOJHAI$MbSNgB*D zJsP>i*BgrFlIn?x&DH9x~UbPBtMFj{_vJ#CaAF>1$oE&k`EF&L@HCa@mN>Q7~!RU>7 zW%fv84aCKSgBacmuvg}r@)YKqO$U{D5|!`vG-Gp%An}raz2gESWm0Exhux4C)zE}} z_@kn z3t}bvm?L+@@az@<*jG>(Xopq&c*;^mttlJ!mv;5k6o%Ac<_`o`4G3qzzo(GO{!&F8 zW+~bF?S;7gO1dQ@>gwZ?iIHjE#^@;Ix!Z`R6{RYLlGB&v4A)ha(2hc`RGV-8`LcvSf+Y@lhT%(Z7$tWEF;cZs2{B|9k#&C}sPyr; zd-g~${TqY7E$9X+h4_(yMxQ%q;tm(h(lKzK)2FQ%k#b2}aMy+a=LHYgk?1|1VQ=&e z9)olOA5H}UD{%nu+!3^HsrBoX^D9Iy0pw!xNGXB6bPSpKDAaun{!fT~Z~`xp&Ii~k zdac?&*lkM+k_&+4oc6=KJ6RwIkB|st@DiQ!4`sI;@40>%zAG^!oG2@ z@eBM$2PJ@F&_3_}oc8A*7mp-0bWng^he9UYX#Ph*JL+<>y+moP^xvQF!MD_)h@b}c2GVX8Ez`x!kjAIV>y9h;2EgwMhDc~tn<2~`lf9j8-Q~yL zM=!Ahm|3JL3?@Tt(OuDDfljlbbN@nIgn#k+7VC+Ko;@iKi>~ovA)(M6rz5KP(yiH| z#iwJqOB7VmFZ#6qI~93C`&qTxT(*Q@om-Xb%ntm_?E;|58Ipd1F!r>^vEjy}*M^E(WslbfLE z<+71#sY~m$gZvoRX@=^FY}X?5qoU|Vg8(o`Om5RM6I(baU^6HmB<+n9rBl@N$CmP41^s?s1ey}wu3r3 z4~1dkyi%kA#*pLQy0phlXa-u(oK2Dwzhuex$YZv=*t*Tg5=n~H=}fJA!p2L78y3D2 zimkqC1gTU(0q||k9QM#><$b-Ilw#Ut2>JF=T^qN34^qcBEd={! zB)rxUbM2IwvMo?S;Id^aglw}-t9et}@TP;!QlFoqqcs(-HfNt9VqGFJ4*Ko*Kk#*B zGpJ>tA9(=t|4#M!kBaf%{$Kfj3-uf|ZFgiU`Bo>%k_OuAp~vnE^_Tg8*% z*?)4JdzyMTzvNDy{r$c``zBw=Vr)6c4}CBIv#mw()3h7`?V-;LF?J&N5a>kjpy;9n zQyXvuu`n?+W84QV=(i`JEJY=}Ak+u4>!Lyt2P!$nBl}T=^|pG*z@)_l!)OKB{tIV&&E@hj=OIhSBHgPV~X=R3NrTMh?VzDm?1yW^IJ&zzAn2{8rE~MRX5EE)a(-T&oE)1J4pGXBYi+nexX-?5! z{EZ4Ju=Y8MQ87=uNc2t^7@X)?85KeSoc`?BmCD;Uv_cwQaLyc}vvnJKHV zuK)H_d)xhGKB!_pRXv{$XgfZ_(8G%N3o$ZI#_ zixQj~so0*m^iuA!bT>&8R@>b%#B~zbIlwt4Ba0v&>B(`*Z;~?6!>-aQ zal+Qt4^dCcjZZMd4b4Khg~(GP#8$3BeB8j!-6l?*##)H?J$PeUy)cA_I26#0aggao zaM5PweS_Sb@{OZ@Uw*(!DNV)KTQU+BTRi?AUAv0Vowth`7mr9)ZVC+TI?@; zWGL&zydnsuE3+D7#U~P%PrxpD3nTc9#mm621iX*?ZMS_Q#n9SzOJ~Hg@`rX{d?qJ; zt}`76!H)MX#=VKifJZP$3<8@}0-llthFpq3FV;(UP$-k63MkHHq~J&}d?C<+c~*Zk z<#G&>AD7EoiAVO38TO2TOBKN>6N|JS*{+`}V-)T0j(bAzGlEUWEvWLrMOIItYexh) z?he>SJk*#bywgDF6+*&%>n%0`-3tOY72+n&Q1NJ`A-bX*2tJV(@;%b6&RxMcUd7+# z@UzOmc9DolSHc-D$5(GouinaE%&uOVMyD&CTdKaEB{Qap4_wU7_=23CULKQ;jmZuV;+Y$(`#Gh0@}s7-!qk-^&#IG>7B{yft?UoA)H5 z|B0u3Tu0TF{AB0jpT|E&RsYB$3WiQU^5p*|f)^Si_#^j+Ao^|5(gNjn+!0|NtXDt* z5fwxpajl@e0FrdEuj2s#Pg>gUvJdko9RBwEe_4@?aEM?SiA2nvm^tsLML{-AvBWM7 z_bm7%tu*MaJkUWd#?GWVrqaQ0>B%Azkxj+Yidvc$XdG1{@$U~uF|1oovneldx`h;9 zB1>H;;n1_5(h`2ECl?bu-sSY@d!QTa`3DrNj_F@vUIdW5{R7$|K{fN11_l7={h7@D z4}I;wCCq>QR6(;JbVbb4$=OBO)#zVu|0iK~SnW~{SrOq&j*_>YRzU&bHUhPPwiy($ zK0qin8U;#F@@}_P_flw`bW_v^G;ct?Pb65%=%egDBgS#YF3?E36$9xzdvYqjAZoK#hcjctJu~MF^S*$q3`o2;!L|jPnM1x*Q~qF%BH(5UDFYglsJwO zEdEuB7NihnTXK6$)F~``nmSQNFP7x7hE{WuOjTAhEjGw#XxvL@S;aZYuyu9)!yZ~X zo35D6Cwb8`shRXCCR;xlR`n`cs4aie!SSM`0)x3ykwM*k zK~w^4x2u#=jEEi`3Q9AU!wE)Zpn#)0!*~)(T^SEjIJveav(d1$RaSMC0|}<)?}nSG zRC2xEBN_YAsuKyl_3yDt%W^F`J-TyeGrcfboC_0Ta=KcW_?~RLb>xbqIVI6`%iWz; zM8Kq9QzwO8w!TntqcB;gNuV$gd+N|(4?6A9GEzYs z5f4(*N5}&ObeYA~I28r;?pKUj4N6}iloE=ok%1|X()Ahdwir?xf6QJfY7owe>pPj)Me*}c^%W-pP6`dnX1&6 z`b#*_P0PeM+1FR)t)Rnr22f!@UFBW!TxgjV)u0%_C~gIbb_D3aPhZ~Wmex0)Lj`VoZKjoW)dUoKY6*| z0|V)|XyjiKgZ}s5(SN?te*muif87vD_(wYOiOjOKNI4L*aK||2$~;s25HS#iY6r=)WW8a^dkd0Y|pPc1-9jmy&wqoCbL84`C94At6$lm_o!8m*did^?o$m?ozIp{RmZ*M%YMX_i$KYkz_Q)QK?Fdm)REqf*f=@>C-SnW{Lb;yYfk&2nAC~b}&B@@^fY7g;n(FVh_hy zW}ifIO9T7nSBHBQP5%-&GF8@A-!%wJAjDn{gAg=lV6IJv!|-QEXT+O>3yoZNCSD3V zG$B?5Xl20xQT?c%cCh?mParFHBsMGB=_5hl#!$W@JHM-vKkiwYqr8kZJ06n%w|-bS zE?p&12hR2B+YB$0GQd;40fJd6#37-qd1}xc1mNCeC%PDxb zlK=X|WE*qn2fROb4{oXtJZSyjOFleI3i8RBZ?2u?EEL1W-~L%7<`H6Vp0;cz5vv`7jlTXf-7XGwp}3|Xl6tNaII3GC z9y1w*@jFLl2iFA!<5AQ~e@S|uK4WL9<$R^??V^aM?Bgy=#|wl$D2P$o;06>{f)P+X z91};NrzVV+)b}k2#rYLF0X0-A+eRul=opDju)g0+vd79B%i!Y}*&a^L$_|C&jQN^j z9q#4<(4)3qNst^+ZYpyVF2hP;DN|OMxM9w(+)%kFQRcYVI zO-frej9x6a%-D%Xuwedcw9#3VSVkOjNF!BYRoY1KD3wFJ%?ML*3QwcarMK)@v`o%s z$w=NLrO>og`nRJpZZ(%~*hNJU#Y~k;_Ci3~gc=4UQO!Ydje^?=W^DgCKyO;Zz4LgQ zKtm($MdY;UZ((U_g5*pMY+dYGyyT1ERkaj`U#S-2yyJ47wMonCpV+2rI8zPNHDfo& zc59dFz*2#^A-R?P6Np}jhDLi4&vP%$NW#8J>=CLj1mlf$XzmQezH*F1jNOiPgXl2j zzD07AKLT*h$CA*OsOba2etPLU%|p?=XhplXo?vOu@q0{QBo++)@6U?YKv_)GFK(^Y zm&uFBbrQyzJm;c49O00PIt;|{&ei%VSS%Y3m3#~L#(3%Gso^a4#9AaB$w@vnAvdr6 z%!2#)YS0HFt%o)q6~BelT;?%oUjX%9qQCn#-~+TM(a^s%Y>&aBkL(UY{+?a9@&Q+a;t%c_6u^6_r@>MEAN9ir5q=Yo|R8z4lKYd1sv^LyTozFn$KqaJ>? zoH&+`AX>E03Gv=71+NZK2>!-NasKeCfMp;@5rZ z*m<}q2!$AgKUwWRXTVHs!E>`FcMT|fzJo30W551|6RoE#Q0WPD$fdA>IRD-C=ae&$=Fuzc6q1CNF>b3z_c<9!;))OViz@ zP58XOt`WOQS)r@tD0IiEIo4Umc(5f%J1p{y4F(1&3AzeAP%V)e#}>2%8W9~x^l}S4 zUOc9^;@m{eUDGL={35TN0+kQbN$X~)P>~L?3FD>s;=PIq9f{Xsl)b7D@8JW{!WVi=s?aqGVKrSJB zO-V&R>_|3@u=MEV1AF%!V*;mZS=ZK9u5OVbETOE$9JhOs!YRxgwRS9XMQ0TArkAi< zu1EC{6!O{djvwxWk_cF`2JgB zE{oo?Cyjy5@Et}<6+>vsYWY3T7S-EcO?8lrm&3!318GR}f~VZMy+(GQ#X9yLEXnnX z7)UaEJSIHQtj5?O(ZJQ{0W{^JrD=EqH_h`gxh^HS!~)?S)s<7ox3eeb7lS!XiKNiWDj5!S1ZVr8m*Vm(LX=PFO>N%y7l+73j-eS1>v0g}5&G zp?qu*PR0C>)@9!mP#acrxNj`*gh}21yrvqyhpQQK)U6|hk1wt3`@h^0-$GQCE z^f#SJiU zb@27$QZ^SVuNSI7qoRcwiH6H(ax|Xx!@g__4i%NN5wu0;mM`CSTZjJw96htSu%C7? z#pPQ9o4xEOJ#DT#KRu9mzu!GH0jb{vhP$nkD}v`n1`tnnNls#^_AN-c~PD;MVeGMBhLT0Ce2O2nwYOlg39xtI24v>pzQ zanl2Vr$77%weA<>>iVZQ&*K9_hfmv=tXiu#PVzNA;M@2}l&vaQsh84GX_+hrIfZC= z0Se*ilv-%zoXRHyvAQW9nOI2C$%DlFH1%zP-4r8bEfHjB3;8{WH`gOYt zg+fX)HIleuMKewYtjg+cSVRUIxAD9xCn+MT zs`DA7)Wx;B`ycL8Q&dR8+8mfhK;a^Rw9 zh9tC~qa>%5T{^8THrj^VEl5Do4j4h@nkrBG6+k8CDD~KB=57m@BL-)vXGkKIuVO9v z7t_L5rpY^0y=uu5iNw0v&Ca-zWk>v;fLJ=+SaV&V#C-o^}8 zp&Xp$v?~ccnfR=&5Df)32^d6QJLg*iuF#s|0M4zJF@Hza1p`q|f}~K)q;HC*I1_9t zQ&1jr9-kdUi8)DGxiwdqU|rPxYWDQPWY&SI&Rxkhxobp~C=Y*`d?HD4JW?WjU7dBPeuIE`ABLq95b#lfKS52IB^6KoHmm60$R}TESplQt59#mboJj+Na!P)V{ic@$yQ-&Z za^JU0T+n0Lf2VdusoNr0?g~1DMsY)zdY-63yH!Ii#aWe|;0TO>L7#YlaDrH}xvYXn zh-NYa>O>f_NTTBG=|k0qWH+X?d5@+INsQ}WcI_3z1Z4-%Gj#_{P$0A~cAye`?j0cW z8)hd(V}7rattLUSMvgZ4g96P7n` z^{55A&&29;-P992{yhkGWa3v_Z6iB4a&~NmL)IpC&dsSwe$9jS(4RVJGt=Y!b-O~1 zSCl@wlaba_cA*yt(QvulMcLUuK z>(ys_!{vqKy{%%~d#4ibQ5$yKn6|4Ky0_ngH>x-}h3pHzRt;iqs}KzajS!i!Pqs8c zCP%xI*d=F=6za_0g`{ZO^mAwRk0iwkzKB7D)SaLR0h|ovGF2w9C9g8;f#EtDN*vBP9yl;n=;B2a7#E8(%Bw()z(M$_pu zQ+9uFnlJ!5&$kk^S_+kJ>r9y8MFPpSf9;o8v;ZxsMA!p>eaAIwt5xNiQ|2_ydGkbi zkggG;Xp&I7C8R{>ten^j@MsN#V5JPs1Ezc!74->Nh0a}U){OK@j=OIoY}C7IYYd8-V9 zQ6s?v=Y7(?Y$7=P#Wwub-*0DLqli?I%kT-D^jqK?c2~HEx<2(poRWAUoC}!~6$1=I z*M(IfPmdID8i+5l@=1(+`?i`G_ew=1Y!gF?tFbdgtW2etKLOFoNozkH(i!Qa7(h^| zF`9!VeqQQwM+yO6J`;oWUWq@9l6hP~FiG8-{Pj*T`XI3~s@FfjW2Tl(llpa901$&y`F}K1uZuHEo;=mr+_8d(o z2Be#yWHEN@euC$=VUSB+3A}khJdF$)0r#<5(f3n`kx>ZT8ifaKyX*OhffeHH1?6OM z*-19$j5tMNYQoB)>cGpz@11>J%q4KW`GLNj?uB>LcNg$0G@}XN#Tqf2F5@jv<`|~p zqB^l!%v!g{R_+0GX5z0>3Q~O``%T$NFc==dsPsTj-;{b$XUS0TGoJs2BUA*H;4S?w z|Nigt|F@9hf7QLSo}JPEK#CPgYgTjrdCSChx0yJeRdbXipF(OwV)ZvghYba)5NZxS zm=L8k_7Lb?f8`=vpv(@m%gzsCs9^E$D5Jn+sf}1lep*zz&5V?~qi_@B?-$Vd1ti(rCi*I0}c}slKv@H_+g?#yarVzpYZN zIk21Bz9Z#WOF`JG&TC&C%a*3*`)GJx9I!U8+!#J4}@5rm8*jK%Xg2VLjP-a;H zFydWO;nxOZ&|{yOW;ta$ZU^6*4vFP)idD6M*M0+9buB#hK4z%YTGBdSva?Pvxim2` zF-?QVGuRQ2-1eYzd1Y%}w^`t1S7|{{8=Es#ApC0<;pc$|NJ)IU%WVK+4gnTWA7-t1 z0K{DCESXb}!y_tzrycr^%%|G4T4)`$BC8+qm|n1lS?CO=`V`1T#ykY#5g5$dc$lGt zqGHyw-*Av%C;33nEiU(rU?w^3F46!dEz#cHd3IF<(XCq)>JG?Bi)4v26MQr1A-g5RqhFoPy%^TD3sa|D^9aS>>_2-X2i#? ztVp@ZkyMB;Uo#9s!R!@G#CCaFVaxx*8YYu$kGFk4g3|9t!1nKqOaDBAe;w!(6#w)0 z?{&F2BgctT1=Z;TvjOGL_!}Vlt=kaLA7#W`mv1h%hUg983!wA*K@_r6_cd6o z6LHiCE6qwlt2H&|Ica~%b9C?Z@$dreBNR_!NKcfL)%8kGr7!IVq|^&6PKYK%EhcKu z6+uR*%EOw=rF6Q42Mx|a> z$2XrM*NV2x9ci6|X^eh1UAbJ9Ky!#*Q5w7)#o#%}d!#-^k8To=n8{UU*LmFsS-wRj zi6-p76V6g?If3S&Bj~GW&QI_WtyPY0@u3hjKtqf9`8S!wn{@P&Tc8uu8cf)YmrX7+ zrC+O3V{9}JG6ihA&^2Q7@)Kq)j(Y_oTzsoBUYQDG!}`Ame`bbcr>J-6E%gaBPEDCU zflX#1-)Ih^HJV*lew*N_SdG-4!b2}G8%U&9_V0~Qt?ZS z@H3L&5ybV8X}A@KQADl93H`}0qkNm!jGHkCJUM%r8`mP1nV?Oo%^l;yDnU6IJtbuY z`X2Sf8|r00mB_f)Q0;S{FqS1Yq?otd-BVbw`#@SDd5}n5X4lqdDi1*vtVv8-Zi10q zexCj0eyngrp`UxjEOrdzUt`?%jRlj7zSU-V-%R?y+_w7P7f1ge%t1ozmN+&)%3xQW zT3u@)))(_a<6`lTJd`DIYw>(pkb=PMKvCNEG~zza+LVNqkY^}QoGMVdS0K;gS*A3f z;6Ua!^sSV-try(M^pB6D9dsX}c>$Da#NHucp9vr(fg4pbBR*uPhYq+N>q1X4RSOCl znIQj4=A+y+8{?LQ$3L@(!Yy~~Cu4Sx72*%@dW>eP%Br7=uaynV6Mqa-49A9) z|L&5r=4K5SClwc`!2J|>(#n$4y1>lmR~2Om8q6HkcpK>d(Fk!T^NO?hM4Fc+(5J{` z&K|vrBz;;zWlNO%=a~JkMxMiZa%wYz#G901lw#+2SUaMMHrebb&|1L8tKoGJK*QhJ zU9|WkDy^-4F6U&VYSc3ScHDk@kV^0801#I|-pSK%az5=DwI}gMm)@s2O+-ESTk?QY z;y9gyucaXO(Cc+cd{B>2)euMHFT71$a6DssWU>>oLw4E-7>FC-YgZH1QAbRwmdahD zO4KAeuA^0q&yWS|zLTx%(P4VOqZv-^BO`0OFAXdBNt9>LAXmPALi3b|gt{b?e-$z0 z4n7H$eg6y_zs(c>*4FT!kN*$H`43~1p!g;IZ8-mYbUPTejaLW#BZnAPFES?ApM{TQ zE*TC%O8)apqcX|PrNjIZE-z{q`I(LwIE0kf=PLjExEX>)oIu><<@lt>-Ng9i$Lrk( znGXl|i4dP;Mt^-IbEp7K0e#*c7By@gCo@VQIW$93ujLL`)lMbA9R?C_5u~7^KopaAMj#6&>n-SOWlup_@{4 zcJ?w_!9JKPM=&Bd#IQ37F*x39y!azm$;~IRlkm>bHdABcNwW-TdDKD$pkD{j6A8d* z{vP~|<}bj_Oz#83K$ieRtsA4a@4a5cRjJ}A01{PgxXn3;fx)5ElMEPwDX_mW9)9oB z*;scve~v#HHqUj3KdC$tdV3&0)Whkp-=hKKz{SzD7g0@N!wyv;ZAime7AjB7&)!)5 zp_iVblaf)%agwJqOG2e7WTCM1&khq`{b>fN4n8hOJbvO?Y;60>LIwagLXWC@@0RSR zo%lPo1cUU=g$ahJ8D=;`v~ORUSl(1-&a@yTAC5Y8E892@{P@MM=GXUGpBSXSbSs!N z;L~0D_s7{+^F6c!WW+^yz5~o7eWtsOE}8{hKaFlHgnyBeUJ8Zz2$k7Lrh?NuMU|No zVvsq@57)8zin;&ckR1;*Z%(xH2lBw z`x%N;|H1En8au588bPDxP^$kfpO!bIzz>K=5Jiq9Rg(NGde0g!rKagLa+&yC)jg7y zq}~2IH)N*FJC31qrIH-2;%3^F?=bDD^U2Y;%ftN(v71oY;od+vh!!2z^}GHR$43rg z0In@ki}TglIsMU^O1(SiLK#oiuyw zB>-@z?&uW`ILoPupw0_cs?C|2YoX&87~us+ny%eo{A!3M<-7O7mHUBCgA~{yR!Dc^ zb= z8}s4Ly!GdxEQj7HHr<}iu@%Lu+-bV>EZ6MnB~{v7U59;q<9$h}&0WT;SKRpf2IId ztAjig0@{@!ab z{yVt$e@uJ{3R~8*vfrL03KVF2pS5`oR75rm?1c`@a8e{G$zfx^mA*~d>1x`8#dRm) zFESmEnSSsupfB>h7MipTeE!t>BayDVjH~pu&(FI%bRUpZ*H615?2(_6vNmYwbc^KX4HqSi!&mY9$w zpf%C6vy@O30&3N5#0s_!jDk|6qjb-7wE3YT3DA7q3D`Q&Y*y>XbgE7=g#rPx1hnf8 zTWd{IC!Iysq*vZup5VGrO)UM<3)6raR`rOwk(!ikf3XPp!n|gz0hS*P=VDXAyMW(s zL??-`&IusEuOMrz>m(A1W5Q~>9xJwCExAcMkOBD` zD5BJSadd{0u}%z4r!9qA`FW4;Ka_Qk>FcHxiucGw4L9qhtoge|ag8jbr`7LHSbVQz z6|xUo*^LV1SLxS>?D`m=g{8IC&1YF$e}VRGD#ZOc_15QW%J@FbEj8tE-nGxo4?X02 z@|q#k*G4xMW>q84Xc09pRj@>Hz8t^fMm3n&G;Al6KU*;=W`7Q{$^|=bnZiJ7?(s)@ zB`vW>#zJ{}!8=*|?p(~fcXSanO^j8+q7V!q16*ic!HLRdz0TzNI6}m+=OKd2b8KX< zAcDTj*%~vQlcO+%@H01gjv-1zZaOXVoM*t-+KXTR#NoTf-#{dQAm?GqK6q8Ta zu3xW?t=NE$EfYa#=0HofLn5~c#m-U#Ct_r6~X-pg6k*F zYIP7De52BBwcAnK?O(j?YEs1;q60!-!hTuKzw3T;XcA_w5HvU;tO~}byLA^cggu8i z-IP@pxFjTy&ie28m}j66dm@g78xK7aG{QSR^bAcY+W*xWu;G~I08sf(GK4>K-cbfJ z-%v9DGR77He<291M~=fg>>9&NFQlboP)pC6fT;{>_!lM`A&&HWIMd)Y6e@IL;nvRdBE*Tn({&3{-XJ9helJa{G51Ck}-_Y=5C|fEo z)7fZlsHxN&SY&ZLTdYuBBZnwIh0#VTzmyK>U0|r&SXb&GP0m)1dGV8z(^x6s5yQ-z zEyniK${#U@Y7p@Yxx}E+jA?1@{=|e6UM;iyai=0=aItVvqieogZUq@sio2#9NLW~L z{w@^H!HEGU;>;T0lu{Ad20Hr6u;?-9YHKvkjEc)}wsb4Y-ArRK8`24uBT8N)8m%Ee zYJX21)|e{peL26}VUUKYQ3L@NSe8rEbN#AIo$tjJm-$B|IJU?mu(h$Sq`XNY0@NhY z0?WeMtPwP)sUdk}dWA4qBUV^x>P|is-kPgVe)*WV>dKDL>gOq1 zUYw(nU|N#dw>97A_(c3?VA_zDfF{^A1eE#8Bucd^ON(sv-{tc@&i)Y)3V~o7U~+AA zOwnXB5`WN^z$z<9^@(?LY%7?y5X_C(j1ip-Ug^f7Tt6suI3&a=&~#EJegG4r2^tKz zJoEXCVOc1QdOSNHp2d;t&smxL%CfK@mSl)Ky}`!6kCsi#7s5&G2Q!sM9S6o)&mdx% zz|2M~pav2;Th=DTN5yB@6HFAO!pl-y+tEJsh}(? z!tIyg01O*w@mWxsFhHMi7%Gqz!v(Osc5WxK+^1PGfsozw)FE}VIxk9GexmAohPNAF*SAjxG3Al#(xQoYXdI}TR zoCHAFS6+LDqsP8L1SZH{RxJjFK_=vy4nNH^?M!OsQWe^qC~$c1r&y`H9n5;D z2F$t-Htc%2@K(>opJHE{NytI2<_J<6Kz*p$wtKUTEH}zITx?H0L%!5%i@!rLphSBrkFs>jscP6?HVQovX8!~b~ZY|0h%&souT7e5nD@OxuSgC zVW*eo0B|1POwg7;6fJSUC`g+`1%XQvwpRc*&|AtV*h!#5nQM(@m!K)-Qop!Rt3F`a z9HUO zF3w{uI_==EpjFQWV4boF^A?wc@@@U+KrKPjn6sK{OLu-~1UloSqt-aHYo*^@kQy2+ zH(9*-mFz?YV4cL7EW)9hsdmG{5jaYXLvm*&3PZ4y?8z`$9z6`q9fgsJm@*W$-QSzu zut}57hroSbTd=&RJpuy#?K?A6!-;_MowpK8eb~5T-^eye%3O-T^ktSMbd%PT0j-B?#yAKr37u%gB z*2)WJMw6Y)6BvY$JjD`(06ci7u;u$hv}gN5oS&Q^*y$J6L)0#BD<>XL|;pZgtZaxp3~$0zxA(;6Qr_AP$?8l@S)C^Hoaz#rQFK^lA}3&)Gr}Fsca? zK>9BkVcl;c*E2P9UMppEIB&38dL9R?Xg9N{Nl~4*w!qsZJElz}Xc9gz#}cwnP4u{+ z6VNTEx*>u67?3bn{sWk*P`1_$YfsB+)Ax0+jt|)0p&VS?N0k8IAp2KH_#eY3I#{Hw zB$vObUDtXyZX)*wVh*@BefnUej#jv@%uiA=>ngX0kQXaz>8(WM)fX~v__@I}7|!Il z@J%r#I!JqqFwGd4JPhmDmL>1Bh}nn_BE;hgKUesNOf9zQhiuhn%4B}O8jnxEwJiQFDaiiuXw2sb?*8a}Lr;_#7+IPfIjhVDhazSpbQZECL+4)p8lO;)!y>Rt=0X*;O# zX{s(p-*d{#{Y3gVhL;A{4a(Z5sIfpk;WMCqdFA&Mb7mp;YMXhBF@p`}$ShAug+bo`;<9fm!~F z-;1yCj$GQ^mzucrfuatilXrYLr)`izjn_m(f~);txN?D7d?Kg4wDuPXilVyeVwjzf z=4Kewf=u}X_H*viVfPWZW?Sqa3G#h3|;b!Q7>BRc7-Wox0}&>}Lqo=0v;T_i~% zqB&h;14|~nK{W0N=$obGP@O%(c8SraYS^qiu%Q`B zBHdA!`Vk7#Bz*@_3eE#bizLzjBV;F0vfSA~+7@8+F{$7Y?fwI~Pp_X`2ORgqW6g@2 z{cQV!niSsMEVr1IaeRAj8~|*4yW~X5$6o`crw4uTHhgPs^qAk?9UPu;xy5wh2^jZ; z)@27Q=QKa?8w7_C0|u`@k=%b9Ce$D7x42CdLsckF2<$wLuV2kpik8PXex2^Co$n2o z)l#H*;#>?yrPw0x6LI@x(X$nezCBa0Obi%|I5ZV|4bJSPtNHjDkS|3S?fiv(i_(n* zFbve0g!B0!MMmakRsgg_if8nwImb=kk%|s+08xGQ)J?vpkdaya3UD|RJK+LQ72|g> zc4LnwInx!2pN-5Yvp7rvRF#B=(ZO8gyVB^0Dh#ZdHA2BjjppfV<=2Nm#w_t{%6O$W z`-?7N?LwL0DWgK0Y7L#ChSHfa{=DOpJpl8L@V70cd%ei)n%SQO;Z+Xw#li#%LUfbs z&hP%UzN(qM3cw#bWQS6_B@>1^ea-AqNA12xoiQeb_Zdtf>yHljqeIHqlyC^gzH)h1 zstXTFEb0r=l9;><<$a}YWlscH7VW_xeKVZ#*#v#HiuUOs7PPj8ml4#!BiGEK)kDpO zX=2mU0ZuIDDnhfV7v_Rs)0R#ff6I6_|MrzV(R$3Nt#S7D?GQy6?a^WRvA@r2~?7f~s99*9;fuqJ(843U`hRl2O|sk>J@WMsR2O zwyZt$@J)DnSUNkF@B3MPNz|<@`72{M*S5d<1Vkg+G=q~u{8OP84Yh6VCE5pNC*#m> z*jzHy5Tc82sBVw+6W7DoR5@LXZ|+>;)Q%czg%8pyMyeE2-)R^oHg~SrO~#I8MxNc> z6pWT&F&H1mX7#2@mBY>#rRoFKszT z(gvV#j3x|7sF|Dt0*CgsJTdH1R!>inYZWp*2RDbjjQCP98L_ds!$x&{t85NRYk4ii ztJ3HyC8h2A2&`kq^Cfci>N*r&btHg_|v6=s|v=(-MQ zK4kjqoI^~y`j9poC2r{Izdlehm8!AcMP^+SwDUce1Zon(%YvxK)x|rXsJRlO?-K91 zMsmHgI&PmqT_W}C0mdA_6L!EEjgJzidRvTN;vQRJ-uBl#{dEeN?24PRwx)7c5kF^ut=M0)e@zr?z_vpYf=%;;@UYF9>9-->Qf2FW*# z5*#VFB$$-k(zphh4sAElMiLbp`$+SKm*{l6qX;Q8GZ7b|J>OhC!yg$}8dt$dx3E8b z$FlaM*K@6mSsYCoe#*QjLEB3|_Vs4GbZI#!>Ya}dzh%uMn}sw0gFQQ{+V+e|_`q)M3nK27)nAqQ-viJoPHUKdr9HN`v0 z+tZo0ORLuv_d)x}gO|~s(H!12RM(aMfqLG>KSH#kGxC{sUUj>FUC(6;ds1cOjeDYu zOrd>q@bNFq5?0s&@5nbF3-rw{{V&YYf3o_9|K-X4k861UwZ&C2bH+A7^%7nizU>b? zC2@*VlrqprJiv$rx{+^+Op9i3RM;IHq@a;34=Gn%B+rXMZi=UsHC@TEFk4{*fs96p z)wNUY?AhVkdLGQmPESuh@-!iqSZrnxIT~Mon)J+i+B~9VdL8QE`^4=2@lNaKluUVx z_^i7~5E4dN4&gVMi%;7ast@WIY21Q`+^iTC*Gx@IMVYB`BLFHzPh{Fpc6LKZTk@>P zquo2E*Pgq(0MX>h>4)YaJYbIK&V?-W}JfL@&R0I2)TOA!Teg zNa4DBO&)`Nn0$Inb|d8ea|)qqOLYVbQIBRC4T4E<5#Nzc2 z57|Bq7mYsW8y?uLA$XMj%OeK+1|DAKcLYB98-vDP<3*+SKYcPcOkm&}H|!{9l*9%L zbiYJYJ^)Cql-&wPwABGD>Ai7SUXe15m zIr^wNEU$9)D6@atm z(w(1~GuLpHi?JGgIBj`Ovy;j4M`XjrCNs?JsGh1zKsZ{8 z@%G?i>LaU7#uSQLpypocm*onI)$8zFgVWc7_8PVuuw>u`j-<@R$Of}T`glJ!@v*N^ zc(T~+N+M!ZczPSXN&?Ww(<@B=+*jZ+KmcpB8* zDY_1bZ3fwTw|urH{LLWB;DCGzz$jD|VX#Af@HC%BktA8F7VJSy&!5iTt};#U^e0_q zh6j7KCTInKqriZ1`BiF3iq2LWk;gyt0ORIFc4Mi3Bx`7WEuFq{u^C49-SYVjnv!_40m1>7x*+<8~Xkq?056 z!RBfE@osP%SxzOw>cLAQ$bioAOC0V!OzIXIc};)8HjfPtc~8tnah$PtoAz`4k)7$FDUc2O@D)g_uAo&nXMymK$##V?gYUPt^l zj{6NFDL(l-Rh(xkAHP%bBa=($r%3Y~jB!eQ1Smuq2iuQ|>n%Y=p(26SE5gFu11*Q< zaPN5G^d;Iovf`VY&Gh58z~%JpGzaeUz6QoBL^J%+U4|30w7Q&g9i}}@l61eKEfCgo zST6qMxF_Eaj7;0OC)TSU{4_m}%FOa6B{AxS$QIcmmG~IVjjf;7Uk!HBtHfm{%LsLb zu8~5VQFyOZk&!VY(wxL__haJ;>Bj?g&n`+i&=X{unJmv&0whCitWfGlOr6+Tc-lMZ z(ZRXqC-=O+GAvTXKViA9vdwu{aifhk$tYh~-9BScg!Yr*M2zw&9`pHMxHGh`dUH-1;~^6lF@ep;X9PjQ!rqmXNWJ?#P-qb%*TB%xe&3 zX*5V>xuW7)$3!Yc$y>cwBqd8+p+u>WS7p7~O80ipG{(a*#=NJ`^Ld6k-`|;Y&htFy zIi2(Sm)4eD=o+CGo~M3%qF|O9P0+ahmc%EklI?NgX05W3+OdS`_Rd#wg-}hd1&txU5wXy zy`x)05?WVZvELw`XWetIAg6$|(^4ntaE;=f$Wcpwbxm7?bLDnPs-1!bRoMcy!EeOh zpIv8ewDzcIU}mv1NxV!&(Wf7~_kqGAk=2=j&O5FA)z2!APCcDQPnIaiqMkVT4fUyX z))R|WvOJyzcU6d=z0q8JDt42*`js4g+_t{YP7lVguX+vhEejJ3TAIo*Z6jizHm#S- zZT_}-STQAa-0Gn8+RmR7V}{Ns1@jJ{^Sb!9&RSXXP;^ep)r6;&PW++~XYXC9a=zSF z?sp(JQo&MROb~b1Y*Xw4!P)>PHT>Z<)*U=Ax_75^OUw97pNudbxS1XPtNrIg zQ5YB77E@i7$2Ia}(^JcCi@OX`9a|m}PY%-th2m~y+)eCl>fTVjCP^lDOBLyhg1DZ+ z)~G{&OkDc$!;t~`gq(wz@qW3lh9B^ic$>-h#nV!H8d#l+>C(M%g}u2g=I#&W|L!VD zqHYoQkBW;`r|fW02u{7X!X;}T7X4iAaWzkeOh}7&o!F1qt4#$1|BDF;(2VlgEqJ$F zy8Ba-y(%fs`MzpvyXlQLEhS^ed$7Va2hO%?$-D>^*f$b)2Hx;}Ao$UqFt7l26<7eP z!{!C7PVrq>=794Zqmc z%LKkzIBZq@%Ja8EkH}?>c5ILG(EAMS*JHu?#9_7TsELw)8LZzN>f2Y6YN{AJC?34> zh42sPa1%2JpCeS9&E1URm+Pb}B>A1M`R{+O+2~}c(@^1Rf&J9p(4QqHl;E^4w5;I5 zM{?(A^eg*6DY_kI*-9!?If^HaNBfuh*u==X1_a?8$EQ3z!&;v2iJ``O7mZh%G)(O8 ze<4wX?N94(Ozf9`j+=TZpCbH>KVjWyLUe*SCiYO=rFZ4}S~Tq|ln75Jz7$AcKl$=hub=-0RM1s(0WMmE`(OPtAj>7_2I5&76hu2KPIA0y;9{+8yKa;9-m??hIE5t`5DrZ8DzRsQ+{p1jk-VFL9U z2NK_oIeqvyze>1K%b|V?-t;Wv`nY~?-t;tMC4ozyk8CR(hoZTno3!*8ZTc15`?MFf zDI892&g&3lshOEv4E@w-*_%)8C_<&HhV`0D5lN$WT4Q^UWHNSAE+RZe(o z%bqR^hp1IsDr47e^AajFtlppT)2F6yPcrWO9{Kw{o=P6y^HOW$Wqd_)_fwzn`ikZl zOGVc0+S(*=xZ_KbL0Nr`Sx$$CWEbw$52udl1f=X6CZEcFMA*nl>`0gn4&tc5^`!!)tGw<}^Q>P7E}$ zialDUofH*XcB3r9@tA@lnS}dA(@nK_xuw0b;FPUnNGD0;MIySCw=cSzB#=3>F37V-nni3UNB)-;;Gkk;3l9fh6FIjSZU zk=Eo2a`6i7@i*4>ym5`R?i-uZFv6+iX*Gi^I}ZU1OrLAX8aGiT@`*YnjeF>}$U}ORP`+EY5`eqVC_&4yG z;Tp>+2QbZ?lt1GB+D}q14W3dWP8lWnN zf(nlT6+XW&(zme{FbyDpP^NakA<~TK=Y}H^eS%2rt0v8Lr)B}@B!cTvC=9FM;7q4@ zf*;vb4HG>RFpY5?vFCp27VEnVIGx~-na6biU4{+UoYe=}^R#_My6wT$5d&r*=kpAA zu;=-c0|~yqi(N8&*H;aNfhyey+HHQ7J_qae*_CgG2V8j=Tq936S0DC8r3BXBql3Gz z0pLo_`|4Q+oY3rPBNaLmL{QM};9dke>ujP^j@z-N;fNlKb|edn>)YaafDaJ>GWKP$ z5}l&#$QFhN!CMT;WH&z-5E)kvM|36lV!^#3z{@2FF>HsgUO4PMqO#U$X%+U>K!xJ@ zBFs|+woG_9HZQs_Tw*vnCPGhlXG@>y|6pJT$I67!aP&b0o$AF2JwFy9OoapQAk>k7 z**+$_5L;5fKof<;NBX%_;vP@eyD=Z0(QW)5AF7 zp|=tk3p?5)*e~Inuydz-U?%Kuj4%zToS5I|lolPT!B)ZuRVkVa>f*-2aPeV3R79xh zB)3A$>X~szg#}>uNkpLPG#3IKyeMHM*pUuV5=-Jji7S6PSQ9oCLo{oXxzOZfF$PP) zrYwlmSQ-~n94uO3CD{K0QTmj@g%Yzn7_xQ4fTduU0Yqvln`e_`CdXH5iQ5qRr1 zBC;}%YZ2!4I>*=sR)O~jBPx6sxmIEBnq)s-fHz_y0z8-gPl2Us4BiBXNR5CIF!YR@ zb9B305SilU*@4|+ x6JBtc8JSt5M0pkooaq!^FqtuD_KdXXTo>Mw54>`rP&>h&58!3a6l6r9{sG7g--!SK literal 0 HcmV?d00001 diff --git a/conductor-clients/java/conductor-java-sdk/gradle/wrapper/gradle-wrapper.properties b/conductor-clients/java/conductor-java-sdk/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..a59520664 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/conductor-clients/java/conductor-java-sdk/gradlew b/conductor-clients/java/conductor-java-sdk/gradlew new file mode 100755 index 000000000..2fe81a7d9 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/conductor-clients/java/conductor-java-sdk/gradlew.bat b/conductor-clients/java/conductor-java-sdk/gradlew.bat new file mode 100644 index 000000000..9618d8d96 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/conductor-clients/java/conductor-java-sdk/licenseheader.txt b/conductor-clients/java/conductor-java-sdk/licenseheader.txt new file mode 100644 index 000000000..8145074ab --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/licenseheader.txt @@ -0,0 +1,12 @@ +/* + * Copyright $YEAR Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/build.gradle b/conductor-clients/java/conductor-java-sdk/orkes-client/build.gradle new file mode 100644 index 000000000..d8d212e4a --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/build.gradle @@ -0,0 +1,108 @@ +plugins { + id 'java-library' + id 'idea' + id 'maven-publish' + id 'signing' + id 'com.github.johnrengelman.shadow' version '8.1.1' + id 'groovy' +} + +dependencies { + implementation project(':conductor-client') + implementation "com.squareup.okhttp3:okhttp:${versions.okHttp}" + + // test dependencies + testImplementation "org.junit.jupiter:junit-jupiter-api:${versions.junit}" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${versions.junit}" + + testImplementation "org.powermock:powermock-module-junit4:2.0.9" + testImplementation "org.powermock:powermock-api-mockito2:2.0.9" + + testImplementation 'org.spockframework:spock-core:2.3-groovy-3.0' + testImplementation 'org.codehaus.groovy:groovy:3.0.15' + testImplementation 'ch.qos.logback:logback-classic:1.5.6' +} + +java { + withSourcesJar() + withJavadocJar() +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + pom { + name = 'Orkes Conductor Client' + description = 'OSS & Orkes Conductor client (http)' + url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + scm { + connection = 'scm:git:git://github.com/conductor-oss/conductor-java-sdk.git' + developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor-java-sdk.git' + url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + } + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + organization = 'Orkes' + organizationUrl = 'https://orkes.io' + name = 'Orkes Development Team' + email = 'developers@orkes.io' + } + } + } + } + } + + repositories { + maven { + if (project.hasProperty("mavenCentral")) { + println "Publishing to Sonatype Repository" + url = "https://s01.oss.sonatype.org/${project.version.endsWith('-SNAPSHOT') ? "content/repositories/snapshots/" : "service/local/staging/deploy/maven2/"}" + credentials { + username project.properties.username + password project.properties.password + } + } else { + url = "s3://orkes-artifacts-repo/${project.version.endsWith('-SNAPSHOT') ? "snapshots" : "releases"}" + authentication { + awsIm(AwsImAuthentication) + } + } + } + } +} + +signing { + def signingKeyId = findProperty('signingKeyId') + if (signingKeyId) { + println 'Signing the artifact with keys' + signing { + def signingKey = findProperty('signingKey') + def signingPassword = findProperty('signingPassword') + if (signingKeyId && signingKey && signingPassword) { + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + } + + sign publishing.publications + } + } +} + +test { + useJUnitPlatform() +} + +shadowJar { + archiveFileName = "orkes-conductor-client-$version-all.jar" + mergeServiceFiles() +} + +tasks.build { + dependsOn shadowJar +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/ApiClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/ApiClient.java new file mode 100644 index 000000000..76b7127c0 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/ApiClient.java @@ -0,0 +1,157 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.NotNull; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.Param; + +import io.orkes.conductor.client.http.ApiCallback; +import io.orkes.conductor.client.http.ApiException; +import io.orkes.conductor.client.http.ApiResponse; +import io.orkes.conductor.client.http.OrkesAuthentication; +import io.orkes.conductor.client.http.Pair; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +/** + * This class exists to maintain backward compatibility and facilitate the migration for + * users of orkes-conductor-client v2 to v3. + */ +@Deprecated +public final class ApiClient extends ConductorClient { + + public ApiClient(String rootUri, String keyId, String secret) { + super(ConductorClient.builder() + .basePath(rootUri) + .addHeaderSupplier(new OrkesAuthentication(keyId, secret))); + } + + public ApiClient(String rootUri, String keyId, String secret, Consumer configurer) { + super(ConductorClient.builder() + .basePath(rootUri) + .configureOkHttp(configurer) + .addHeaderSupplier(new OrkesAuthentication(keyId, secret))); + } + + public ApiClient(String rootUri) { + super(rootUri); + } + + @Deprecated + public Call buildCall( + String path, + String method, + List pathParams, + List queryParams, + Object body, + Map headers) { + Request request = buildRequest(method, path, toParamList(pathParams), toParamList(queryParams), headers, body); + return okHttpClient.newCall(request); + } + + private List toParamList(List pairList) { + List params = new ArrayList<>(); + if (pairList != null) { + params.addAll(pairList.stream() + .map(it -> new Param(it.getName(), it.getValue())) + .collect(Collectors.toList())); + } + + return params; + } + + /** + * {@link #executeAsync(Call, Type, ApiCallback)} + * + * @param Type + * @param call An instance of the Call object + * @param callback ApiCallback<T> + */ + @Deprecated + public void executeAsync(Call call, ApiCallback callback) { + executeAsync(call, null, callback); + } + + /** + * Execute HTTP call asynchronously. + * + * @param Type + * @param call The callback to be executed when the API call finishes + * @param returnType Return type + * @param callback ApiCallback + */ + @SuppressWarnings("unchecked") + @Deprecated + public void executeAsync(Call call, final Type returnType, final ApiCallback callback) { + call.enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + T result; + try { + result = (T) handleResponse(response, returnType); + } catch (ApiException e) { + callback.onFailure(e, response.code(), response.headers().toMultimap()); + return; + } + callback.onSuccess( + result, response.code(), response.headers().toMultimap()); + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + callback.onFailure(new ApiException(e), 0, null); + } + }); + } + + @Deprecated + public ApiResponse execute(Call call) throws ApiException { + return execute(call, null); + } + + /** + * Execute HTTP call and deserialize the HTTP response body into the given return type. + * + * @param returnType The return type used to deserialize HTTP response body + * @param The return type corresponding to (same with) returnType + * @param call Call + * @return ApiResponse object containing response status, headers and data, which is a Java + * object deserialized from response body and would be null when returnType is null. + * @throws ApiException If fail to execute the call + */ + @Deprecated + public ApiResponse execute(Call call, Type returnType) throws ApiException { + try { + Response response = call.execute(); + T data = handleResponse(response, returnType); + return new ApiResponse(response.code(), response.headers().toMultimap(), data); + } catch (IOException e) { + throw new ApiException(e); + } + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/AuthorizationClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/AuthorizationClient.java new file mode 100644 index 000000000..2c6f94b02 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/AuthorizationClient.java @@ -0,0 +1,99 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client; + +import java.util.List; +import java.util.Map; + +import io.orkes.conductor.client.model.AccessKeyResponse; +import io.orkes.conductor.client.model.AuthorizationRequest; +import io.orkes.conductor.client.model.ConductorApplication; +import io.orkes.conductor.client.model.ConductorUser; +import io.orkes.conductor.client.model.CreateAccessKeyResponse; +import io.orkes.conductor.client.model.CreateOrUpdateApplicationRequest; +import io.orkes.conductor.client.model.GrantedAccessResponse; +import io.orkes.conductor.client.model.Group; +import io.orkes.conductor.client.model.Subject; +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.UpsertGroupRequest; +import io.orkes.conductor.client.model.UpsertUserRequest; + +public interface AuthorizationClient { + + // Permissions + + Map> getPermissions(String type, String id); + + void grantPermissions(AuthorizationRequest authorizationRequest); + + void removePermissions(AuthorizationRequest authorizationRequest); + + // Users + void deleteUser(String id); + + GrantedAccessResponse getGrantedPermissionsForUser(String userId); + + ConductorUser getUser(String id); + + List listUsers(Boolean apps); + + void sendInviteEmail(String email); + + ConductorUser upsertUser(UpsertUserRequest upsertUserRequest, String id); + + // Groups + void addUserToGroup(String groupId, String userId); + + void deleteGroup(String id); + + GrantedAccessResponse getGrantedPermissionsForGroup(String groupId); + + Group getGroup(String id); + + List getUsersInGroup(String id); + + List listGroups(); + + void removeUserFromGroup(String groupId, String userId); + + Group upsertGroup(UpsertGroupRequest upsertGroupRequest, String id); + + // Applications + void addRoleToApplicationUser(String applicationId, String role); + + CreateAccessKeyResponse createAccessKey(String id); + + ConductorApplication createApplication(CreateOrUpdateApplicationRequest createOrUpdateApplicationRequest); + + void deleteAccessKey(String applicationId, String keyId); + + void deleteApplication(String id); + + List getAccessKeys(String id); + + ConductorApplication getApplication(String id); + + List listApplications(); + + void removeRoleFromApplicationUser(String applicationId, String role); + + AccessKeyResponse toggleAccessKeyStatus(String applicationId, String keyId); + + ConductorApplication updateApplication(CreateOrUpdateApplicationRequest createOrUpdateApplicationRequest, String id); + + void setApplicationTags(List body, String applicationId); + + List getApplicationTags(String applicationId); + + void deleteApplicationTags(List body, String applicationId); +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/IntegrationClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/IntegrationClient.java new file mode 100644 index 000000000..bc8eb2ec8 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/IntegrationClient.java @@ -0,0 +1,77 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client; + +import java.util.List; +import java.util.Map; + +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.integration.Integration; +import io.orkes.conductor.client.model.integration.IntegrationApi; +import io.orkes.conductor.client.model.integration.IntegrationApiUpdate; +import io.orkes.conductor.client.model.integration.IntegrationUpdate; +import io.orkes.conductor.client.model.integration.ai.PromptTemplate; + +public interface IntegrationClient { + /** + * Client for managing integrations with external systems. Some examples of integrations are: + * 1. AI/LLM providers (e.g. OpenAI, HuggingFace) + * 2. Vector DBs (Pinecone, Weaviate etc.) + * 3. Kafka + * 4. Relational databases + * + * Integrations are configured as integration -> api with 1->N cardinality. + * APIs are the underlying resources for an integration and depending on the type of integration they represent underlying resources. + * Examples: + * LLM integrations + * The integration specifies the name of the integration unique to your environment, api keys and endpoint used. + * APIs are the models (e.g. text-davinci-003, or text-embedding-ada-002) + * + * Vector DB integrations, + * The integration represents the cluster, specifies the name of the integration unique to your environment, api keys and endpoint used. + * APIs are the indexes (e.g. pinecone) or class (e.g. for weaviate) + * + * Kafka + * The integration represents the cluster, specifies the name of the integration unique to your environment, api keys and endpoint used. + * APIs are the topics that are configured for use within this kafka cluster + */ + + void associatePromptWithIntegration(String aiIntegration, String modelName, String promptName); + + void deleteIntegrationApi(String apiName, String integrationName); + + void deleteIntegrationProvider(String integrationName); + + IntegrationApi getIntegrationApi(String apiName, String integrationName); + + List getIntegrationApis(String integrationName); + + Integration getIntegrationProvider(String integrationName); + + List getIntegrationProviders(String category, Boolean activeOnly); + + List getPromptsWithIntegration(String aiIntegration, String modelName); + + int getTokenUsageForIntegration(String name, String integrationName); + + Map getTokenUsageForIntegrationProvider(String name); + + void saveIntegrationApi(String integrationName, String apiName, IntegrationApiUpdate apiDetails); + + void saveIntegration(String integrationName, IntegrationUpdate integrationDetails); + + // Tags + void deleteTagForIntegrationProvider(List tags, String name); + void saveTagForIntegrationProvider(List tags, String name); + List getTagsForIntegrationProvider(String name); +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/OrkesClients.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/OrkesClients.java new file mode 100644 index 000000000..2d38038e5 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/OrkesClients.java @@ -0,0 +1,70 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client; + +import com.netflix.conductor.client.http.ConductorClient; + +import io.orkes.conductor.client.http.OrkesAuthorizationClient; +import io.orkes.conductor.client.http.OrkesEventClient; +import io.orkes.conductor.client.http.OrkesIntegrationClient; +import io.orkes.conductor.client.http.OrkesMetadataClient; +import io.orkes.conductor.client.http.OrkesPromptClient; +import io.orkes.conductor.client.http.OrkesSchedulerClient; +import io.orkes.conductor.client.http.OrkesSecretClient; +import io.orkes.conductor.client.http.OrkesTaskClient; +import io.orkes.conductor.client.http.OrkesWorkflowClient; + +public class OrkesClients { + + private final ConductorClient client; + + public OrkesClients(ConductorClient client) { + this.client = client; + } + + public OrkesWorkflowClient getWorkflowClient() { + return new OrkesWorkflowClient(client); + } + + public AuthorizationClient getAuthorizationClient() { + return new OrkesAuthorizationClient(client); + } + + public OrkesEventClient getEventClient() { + return new OrkesEventClient(client); + } + + public OrkesMetadataClient getMetadataClient() { + return new OrkesMetadataClient(client); + } + + public OrkesSchedulerClient getSchedulerClient() { + return new OrkesSchedulerClient(client); + } + + public OrkesSecretClient getSecretClient() { + return new OrkesSecretClient(client); + } + + public OrkesTaskClient getTaskClient() { + return new OrkesTaskClient(client); + } + + public IntegrationClient getIntegrationClient() { + return new OrkesIntegrationClient(client); + } + + public PromptClient getPromptClient() { + return new OrkesPromptClient(client); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/PromptClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/PromptClient.java new file mode 100644 index 000000000..cbee63f7c --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/PromptClient.java @@ -0,0 +1,51 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client; + +import java.util.List; +import java.util.Map; + +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.integration.ai.PromptTemplate; + +public interface PromptClient { + + void savePrompt(String promptName, String description, String promptTemplate); + + PromptTemplate getPrompt(String promptName); + + List getPrompts(); + + void deletePrompt(String promptName); + + List getTagsForPromptTemplate(String promptName); + + void updateTagForPromptTemplate(String promptName, List tags); + + void deleteTagForPromptTemplate(String promptName, List tags); + + /** + * Tests a prompt template by substituting variables and processing through the specified AI model. + * + * @param promptText the text of the prompt template + * @param variables a map containing variables to be replaced in the template + * @param aiIntegration the AI integration context + * @param textCompleteModel the AI model used for completing text + * @param temperature the randomness of the output (optional, default is 0.1) + * @param topP the probability mass to consider from the output distribution (optional, default is 0.9) + * @param stopWords a list of words to stop generating further (can be null) + * @return the processed prompt text + */ + String testPrompt(String promptText, Map variables, String aiIntegration, + String textCompleteModel, float temperature, float topP, List stopWords); +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SchedulerClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SchedulerClient.java new file mode 100644 index 000000000..c206fc9cd --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SchedulerClient.java @@ -0,0 +1,51 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client; + +import java.util.List; + +import io.orkes.conductor.client.model.SaveScheduleRequest; +import io.orkes.conductor.client.model.SearchResultWorkflowScheduleExecution; +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.WorkflowSchedule; + +public interface SchedulerClient { + void deleteSchedule(String name); + + List getAllSchedules(String workflowName); + + List getNextFewSchedules(String cronExpression, Long scheduleStartTime, Long scheduleEndTime, Integer limit); + + WorkflowSchedule getSchedule(String name); + + void pauseAllSchedules(); + + void pauseSchedule(String name); + + void requeueAllExecutionRecords(); + + void resumeAllSchedules(); + + void resumeSchedule(String name); + + void saveSchedule(SaveScheduleRequest saveScheduleRequest); + + SearchResultWorkflowScheduleExecution search(Integer start, Integer size, String sort, String freeText, String query); + + void setSchedulerTags(List body, String name); + + void deleteSchedulerTags(List body, String name); + + List getSchedulerTags(String name); + + } diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SecretClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SecretClient.java new file mode 100644 index 000000000..69314d106 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SecretClient.java @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client; + +import java.util.List; +import java.util.Set; + +import io.orkes.conductor.client.model.TagObject; + +public interface SecretClient { + void deleteSecret(String key); + + String getSecret(String key); + + Set listAllSecretNames(); + + List listSecretsThatUserCanGrantAccessTo(); + + void putSecret(String value, String key); + + boolean secretExists(String key); + void setSecretTags(List tags, String key); + + void deleteSecretTags(List body, String key); + List getSecretTags(String key); +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiCallback.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiCallback.java new file mode 100644 index 000000000..7c9608e97 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiCallback.java @@ -0,0 +1,60 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Map; + +@Deprecated +/** + * Callback for asynchronous API call. + * + * @param The return type + */ +public interface ApiCallback { + /** + * This is called when the API call fails. + * + * @param e The exception causing the failure + * @param statusCode Status code of the response if available, otherwise it would be 0 + * @param responseHeaders Headers of the response if available, otherwise it would be null + */ + void onFailure(ApiException e, int statusCode, Map> responseHeaders); + + /** + * This is called when the API call succeeded. + * + * @param result The result deserialized from response + * @param statusCode Status code of the response + * @param responseHeaders Headers of the response + */ + void onSuccess(T result, int statusCode, Map> responseHeaders); + + /** + * This is called when the API upload processing. + * + * @param bytesWritten bytes Written + * @param contentLength content length of request body + * @param done write end + */ + void onUploadProgress(long bytesWritten, long contentLength, boolean done); + + /** + * This is called when the API downlond processing. + * + * @param bytesRead bytes Read + * @param contentLength content lenngth of the response + * @param done Read end + */ + void onDownloadProgress(long bytesRead, long contentLength, boolean done); +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiException.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiException.java new file mode 100644 index 000000000..c1e5cef1e --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import com.netflix.conductor.client.exception.ConductorClientException; + + +/** + * This class exists to maintain backward compatibility and facilitate the migration + * for users of orkes-conductor-client v2 to v3. + */ +@Deprecated +public class ApiException extends ConductorClientException { + + public ApiException() { + } + + public ApiException(Throwable throwable) { + super(throwable); + } + + public ApiException(String message) { + super(message); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiResponse.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiResponse.java new file mode 100644 index 000000000..0a746c942 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiResponse.java @@ -0,0 +1,62 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Map; + +/** + * API response returned by API call. + *

+ * This class exists to maintain backward compatibility and facilitate the migration for users + * of orkes-conductor-client v2 to v3. + * + * @param The type of data that is deserialized from response body + */ +@Deprecated +public class ApiResponse { + private final int statusCode; + private final Map> headers; + private final T data; + + /** + * @param statusCode The status code of HTTP response + * @param headers The headers of HTTP response + */ + public ApiResponse(int statusCode, Map> headers) { + this(statusCode, headers, null); + } + + /** + * @param statusCode The status code of HTTP response + * @param headers The headers of HTTP response + * @param data The object deserialized from response bod + */ + public ApiResponse(int statusCode, Map> headers, T data) { + this.statusCode = statusCode; + this.headers = headers; + this.data = data; + } + + public int getStatusCode() { + return statusCode; + } + + public Map> getHeaders() { + return headers; + } + + public T getData() { + return data; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApplicationResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApplicationResource.java new file mode 100644 index 000000000..639c1f4f3 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApplicationResource.java @@ -0,0 +1,194 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Objects; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.client.http.ConductorClientResponse; + +import io.orkes.conductor.client.model.AccessKeyResponse; +import io.orkes.conductor.client.model.ConductorApplication; +import io.orkes.conductor.client.model.CreateAccessKeyResponse; +import io.orkes.conductor.client.model.CreateOrUpdateApplicationRequest; +import io.orkes.conductor.client.model.TagObject; + +import com.fasterxml.jackson.core.type.TypeReference; + +class ApplicationResource { + + private final ConductorClient client; + + ApplicationResource(ConductorClient client) { + this.client = client; + } + + void addRoleToApplicationUser(String applicationId, String role) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/applications/{applicationId}/roles/{role}") + .addPathParam("applicationId", applicationId) + .addPathParam("role", role) + .build(); + client.execute(request); + } + + CreateAccessKeyResponse createAccessKey(String applicationId) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/applications/{applicationId}/accessKeys") + .addPathParam("applicationId", applicationId) + .build(); + ConductorClientResponse response = client.execute(request, new TypeReference<>() { + }); + + return response.getData(); + } + + ConductorApplication upsertApplication(CreateOrUpdateApplicationRequest body, String id) { + Objects.requireNonNull(body, "CreateOrUpdateApplicationRequest cannot be null"); + ConductorClientRequest.Builder builder = ConductorClientRequest.builder() + .body(body); + + if (id == null) { + builder.method(Method.POST).path("/applications"); + } else { + builder.method(Method.PUT) + .path("/applications/{id}") + .addPathParam("id", id); + } + + ConductorClientResponse response = client.execute(builder.build(), new TypeReference<>() { + }); + + return response.getData(); + } + + void deleteAccessKey(String applicationId, String keyId) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/applications/{applicationId}/accessKeys/{keyId}") + .addPathParam("applicationId", applicationId) + .addPathParam("keyId", keyId) + .build(); + client.execute(request); + } + + void deleteApplication(String applicationId) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/applications/{applicationId}") + .addPathParam("applicationId", applicationId) + .build(); + client.execute(request); + } + + List getAccessKeys(String applicationId) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/applications/{applicationId}/accessKeys") + .addPathParam("applicationId", applicationId) + .build(); + ConductorClientResponse> response = client.execute(request, new TypeReference<>() { + }); + + return response.getData(); + } + + ConductorApplication getApplication(String applicationId) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/applications/{applicationId}") + .addPathParam("applicationId", applicationId) + .build(); + ConductorClientResponse response = client.execute(request, new TypeReference<>() { + }); + + return response.getData(); + } + + List listApplications() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/applications") + .build(); + ConductorClientResponse> response = client.execute(request, new TypeReference<>() { + }); + + return response.getData(); + } + + void removeRoleFromApplicationUser(String applicationId, String role) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/applications/{applicationId}/roles/{role}") + .addPathParam("applicationId", applicationId) + .addPathParam("role", role) + .build(); + client.execute(request); + } + + AccessKeyResponse toggleAccessKeyStatus(String applicationId, String keyId) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/applications/{applicationId}/accessKeys/{keyId}/status") + .addPathParam("applicationId", applicationId) + .addPathParam("keyId", keyId) + .build(); + + ConductorClientResponse response = client.execute(request, new TypeReference<>() { + }); + + return response.getData(); + } + + void putTags(List body, String applicationId) { + Objects.requireNonNull(body, "List cannot be null"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/applications/{id}/tags") + .addPathParam("applicationId", applicationId) + .body(body) + .build(); + + client.execute(request); + } + + List getTags(String applicationId) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/applications/{id}/tags") + .addPathParam("applicationId", applicationId) + .build(); + + ConductorClientResponse> response = client.execute(request, new TypeReference<>() { + }); + + return response.getData(); + } + + void deleteTags(List body, String applicationId) { + Objects.requireNonNull(body, "List cannot be null"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/applications/{id}/tags") + .addPathParam("applicationId", applicationId) + .body(body) + .build(); + + client.execute(request); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/AuthorizationResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/AuthorizationResource.java new file mode 100644 index 000000000..c9a4b3dbc --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/AuthorizationResource.java @@ -0,0 +1,72 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.client.http.ConductorClientResponse; + +import io.orkes.conductor.client.model.AuthorizationRequest; +import io.orkes.conductor.client.model.Subject; + +import com.fasterxml.jackson.core.type.TypeReference; + +class AuthorizationResource { + + private final ConductorClient client; + + AuthorizationResource(ConductorClient client) { + this.client = client; + } + + Map> getPermissions(String type, String id) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/auth/authorization/{type}/{id}") + .addPathParam("type", type) + .addPathParam("id", id) + .build(); + + ConductorClientResponse>> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + void grantPermissions(AuthorizationRequest body) { + Objects.requireNonNull(body, "AuthorizationRequest cannot be null"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/auth/authorization") + .body(body) + .build(); + + client.execute(request); + } + + void removePermissions(AuthorizationRequest body) { + Objects.requireNonNull(body, "AuthorizationRequest cannot be null"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/auth/authorization") + .body(body) + .build(); + + client.execute(request); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/EventResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/EventResource.java new file mode 100644 index 000000000..f71000731 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/EventResource.java @@ -0,0 +1,100 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.client.http.ConductorClientResponse; +import com.netflix.conductor.common.metadata.events.EventHandler; + +import com.fasterxml.jackson.core.type.TypeReference; + +class EventResource { + + private final ConductorClient client; + + EventResource(ConductorClient client) { + this.client = client; + } + + List getEventHandlers() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/event") + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + void handleIncomingEvent(Map body) { + Objects.requireNonNull(body, "EventHandler cannot be null"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/event/handleIncomingEvent") + .body(body) + .build(); + client.execute(request); + } + + void removeEventHandlerStatus(String name) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/event/{name}") + .addPathParam("name", name) + .build(); + client.execute(request); + } + + Map getQueueConfig(String queueType, String queueName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path( "/event/queue/config/{queueType}/{queueName}") + .addPathParam("queueType", queueType) + .addPathParam("queueName", queueName) + .build(); + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + void deleteQueueConfig(String queueType, String queueName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path( "/event/queue/config/{queueType}/{queueName}") + .addPathParam("queueType", queueType) + .addPathParam("queueName", queueName) + .build(); + + client.execute(request); + } + + void putQueueConfig(String queueType, String queueName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path( "/event/queue/config/{queueType}/{queueName}") + .addPathParam("queueType", queueType) + .addPathParam("queueName", queueName) + .build(); + + client.execute(request); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/GroupResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/GroupResource.java new file mode 100644 index 000000000..534eeb023 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/GroupResource.java @@ -0,0 +1,133 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.client.http.ConductorClientResponse; + +import io.orkes.conductor.client.model.ConductorUser; +import io.orkes.conductor.client.model.GrantedAccessResponse; +import io.orkes.conductor.client.model.Group; +import io.orkes.conductor.client.model.UpsertGroupRequest; + +import com.fasterxml.jackson.core.type.TypeReference; + +class GroupResource { + + private final ConductorClient client; + + GroupResource(ConductorClient client) { + this.client = client; + } + + public void addUserToGroup(String groupId, String userId) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/groups/{groupId}/users/{userId}") + .addPathParam("groupId", groupId) + .addPathParam("userId", userId) + .build(); + + client.execute(request); + } + + public void deleteGroup(String id) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/groups/{id}") + .addPathParam("id", id) + .build(); + + client.execute(request); + } + + public GrantedAccessResponse getGrantedPermissions(String groupId) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/groups/{groupId}/permissions") + .addPathParam("groupId", groupId) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public Group getGroup(String id) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/groups/{id}") + .addPathParam("id", id) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public List getUsersInGroup(String id) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/groups/{id}/users") + .addPathParam("id", id) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public List listGroups() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/groups") + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public void removeUserFromGroup(String groupId, String userId) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/groups/{groupId}/users/{userId}") + .addPathParam("groupId", groupId) + .addPathParam("userId", userId) + .build(); + + client.execute(request); + } + + public Group upsertGroup(UpsertGroupRequest upsertGroupRequest, String id) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/groups/{id}") + .addPathParam("id", id) + .body(upsertGroupRequest) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/IntegrationResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/IntegrationResource.java new file mode 100644 index 000000000..89d7e9d76 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/IntegrationResource.java @@ -0,0 +1,226 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.client.http.ConductorClientResponse; + +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.integration.Integration; +import io.orkes.conductor.client.model.integration.IntegrationApi; +import io.orkes.conductor.client.model.integration.IntegrationApiUpdate; +import io.orkes.conductor.client.model.integration.IntegrationUpdate; +import io.orkes.conductor.client.model.integration.ai.PromptTemplate; + +import com.fasterxml.jackson.core.type.TypeReference; + + +class IntegrationResource { + + private final ConductorClient client; + + IntegrationResource(ConductorClient client) { + this.client = client; + } + + void associatePromptWithIntegration(String integrationProvider, String integrationName, String promptName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/integrations/provider/{integrationProvider}/integration/{integrationName}/prompt/{promptName}") + .addPathParam("integrationProvider", integrationProvider) + .addPathParam("integrationName", integrationName) + .addPathParam("promptName", promptName) + .build(); + + client.execute(request); + } + + void deleteIntegrationApi(String integrationProvider, String integrationName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/integrations/provider/{integrationProvider}/integration/{integrationName}") + .addPathParam("integrationProvider", integrationProvider) + .addPathParam("integrationName", integrationName) + .build(); + + client.execute(request); + } + + void deleteIntegrationProvider(String integrationProvider) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/integrations/provider/{integrationProvider}") + .addPathParam("integrationProvider", integrationProvider) + .build(); + + client.execute(request); + } + + void deleteTagForIntegrationProvider(List body, String integrationProvider) { + Objects.requireNonNull(body, "List cannot be null"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/integrations/provider/{integrationProvider}/tags") + .addPathParam("integrationProvider", integrationProvider) + .body(body) + .build(); + + client.execute(request); + } + + IntegrationApi getIntegrationApi(String integrationProvider, String integrationName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/integrations/provider/{integrationProvider}/integration/{integrationName}") + .addPathParam("integrationProvider", integrationProvider) + .addPathParam("integrationName", integrationName) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + return resp.getData(); + } + + List getIntegrationApis(String name, Boolean activeOnly) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/integrations/provider/{name}/integration") + .addPathParam("name", name) + .addQueryParam("activeOnly", activeOnly) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + Integration getIntegrationProvider(String integrationProvider) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/integrations/provider/{integrationProvider}") + .addPathParam("integrationProvider", integrationProvider) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + List getIntegrationProviders(String category, Boolean activeOnly) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/integrations/provider") + .addQueryParam("category", category) + .addQueryParam("activeOnly", activeOnly) + .build(); + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + List getPromptsWithIntegration(String integrationProvider, String integrationName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/integrations/provider/{integrationProvider}/integration/{integrationName}/prompt") + .addQueryParam("integrationProvider", integrationProvider) + .addQueryParam("integrationName", integrationName) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + List getTagsForIntegrationProvider(String integrationProvider) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/integrations/provider/{integrationProvider}/tags") + .addPathParam("integrationProvider", integrationProvider) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + Integer getTokenUsageForIntegration(String integrationProvider, String integrationName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/integrations/provider/{integrationProvider}/integration/{integrationName}/metrics") + .addPathParam("integrationProvider", integrationProvider) + .addPathParam("integrationName", integrationName) + .build(); + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + Map getTokenUsageForIntegrationProvider(String integrationProvider) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/integrations/provider/{integrationProvider}/metrics") + .addPathParam("integrationProvider", integrationProvider) + .build(); + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + void putTagForIntegrationProvider(List body, String integrationProvider) { + Objects.requireNonNull(body, "List cannot be null"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/integrations/provider/{integrationProvider}/tags") + .addPathParam("integrationProvider", integrationProvider) + .body(body) + .build(); + + client.execute(request); + } + + void saveIntegrationApi(IntegrationApiUpdate body, String integrationProvider, String integrationName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/integrations/provider/{integrationProvider}/integration/{integrationName}") + .addPathParam("integrationProvider", integrationProvider) + .addPathParam("integrationName", integrationName) + .body(body) + .build(); + + client.execute(request); + } + + void saveIntegrationProvider(IntegrationUpdate body, String integrationProvider) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/integrations/provider/{integrationProvider}") + .addPathParam("integrationProvider", integrationProvider) + .body(body) + .build(); + + client.execute(request); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/MetadataResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/MetadataResource.java new file mode 100644 index 000000000..9d02e176d --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/MetadataResource.java @@ -0,0 +1,168 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Objects; + +import org.apache.commons.lang3.StringUtils; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.client.http.ConductorClientResponse; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; + +import com.fasterxml.jackson.core.type.TypeReference; + +public class MetadataResource { + + private final ConductorClient client; + + public MetadataResource(ConductorClient client) { + this.client = client; + } + + public void registerWorkflowDef(WorkflowDef workflowDef, Boolean overwrite) { + Objects.requireNonNull(workflowDef, "WorkflowDef cannot be null"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/metadata/workflow") + .addQueryParam("overwrite", overwrite) + .body(workflowDef) + .build(); + + client.execute(request); + } + + public WorkflowDef getWorkflow(String name, Integer version, Boolean metadata) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/metadata/workflow/{name}") + .addPathParam("name", name) + .addQueryParam("version", version) + .addQueryParam("metadata", metadata) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public List getAllWorkflows( + String access, Boolean metadata, String tagKey, String tagValue) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/metadata/workflow") + .addQueryParam("access", access) + .addQueryParam("metadata", metadata) + .addQueryParam("tagKey", tagKey) + .addQueryParam("tagValue", tagValue) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public TaskDef getTaskDef(String taskType, Boolean metadata) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/metadata/taskdefs/{taskType}") + .addPathParam("taskType", taskType) + .addQueryParam("metadata", metadata) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public List getTaskDefs(String access, Boolean metadata, String tagKey, String tagValue) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/metadata/taskdefs") + .addQueryParam("access", access) + .addQueryParam("metadata", metadata) + .addQueryParam("tagKey", tagKey) + .addQueryParam("tagValue", tagValue) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public void registerTaskDef(List taskDefs) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/metadata/taskdefs") + .body(taskDefs) + .build(); + + client.execute(request); + } + + public void unregisterTaskDef(String taskType) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/metadata/taskdefs/{taskType}") + .addPathParam("taskType", taskType) + .build(); + + client.execute(request); + } + + public void unregisterWorkflowDef(String name, Integer version) { + if (StringUtils.isBlank(name)) { + throw new IllegalArgumentException("name cannot be null or blank"); + } + + Objects.requireNonNull(version, "version cannot be null"); + + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/metadata/workflow/{name}/{version}") + .addPathParam("name", name) + .addPathParam("version", Integer.toString(version)) + .build(); + + client.execute(request); + } + + public void updateWorkflows(List workflowDefs, Boolean overwrite) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/metadata/workflow") + .addQueryParam("overwrite", overwrite) + .body(workflowDefs) + .build(); + + client.execute(request); + } + + public void updateTaskDef(TaskDef taskDef) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/metadata/taskdefs") + .body(taskDef) + .build(); + + client.execute(request); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthentication.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthentication.java new file mode 100644 index 000000000..63fa54f8a --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthentication.java @@ -0,0 +1,129 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.time.Instant; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.HeaderSupplier; + +import io.orkes.conductor.client.model.GenerateTokenRequest; +import io.orkes.conductor.client.model.TokenResponse; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +public class OrkesAuthentication implements HeaderSupplier { + + public static final String PROP_TOKEN_REFRESH_INTERVAL = "CONDUCTOR_SECURITY_TOKEN_REFRESH_INTERVAL"; + private static final Logger LOGGER = LoggerFactory.getLogger(OrkesAuthentication.class); + private static final String TOKEN_CACHE_KEY = "TOKEN"; + private final ScheduledExecutorService tokenRefreshService = Executors.newSingleThreadScheduledExecutor( + new BasicThreadFactory.Builder() + .namingPattern("OrkesAuthenticationSupplier Token Refresh %d") + .daemon(true) + .build()); + + private final Cache tokenCache; + private final String keyId; + private final String keySecret; + private final long tokenRefreshInSeconds; + + private TokenResource tokenResource; + + public OrkesAuthentication(String keyId, String keySecret) { + this(keyId, keySecret, 0); + } + + public OrkesAuthentication(String keyId, String keySecret, long tokenRefreshInSeconds) { + this.keyId = keyId; + this.keySecret = keySecret; + this.tokenRefreshInSeconds = getTokenRefreshInSeconds(tokenRefreshInSeconds); + this.tokenCache = CacheBuilder.newBuilder().expireAfterWrite(this.tokenRefreshInSeconds, TimeUnit.SECONDS).build(); + LOGGER.info("Setting token refresh interval to {} seconds", this.tokenRefreshInSeconds); + } + + private long getTokenRefreshInSeconds(long tokenRefreshInSeconds) { + if (tokenRefreshInSeconds == 0) { + String refreshInterval = System.getenv(PROP_TOKEN_REFRESH_INTERVAL); + if (refreshInterval == null) { + refreshInterval = System.getProperty(PROP_TOKEN_REFRESH_INTERVAL); + } + + if (refreshInterval != null) { + try { + return Integer.parseInt(refreshInterval); + } catch (Exception ignored) { + } + } + + return 2700; // 45 minutes + } + + return tokenRefreshInSeconds; + } + + @Override + public void init(ConductorClient client) { + this.tokenResource = new TokenResource(client); + scheduleTokenRefresh(); + try { + getToken(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + @Override + public Map get(String method, String path) { + if ("/token".equalsIgnoreCase(path)) { + return Map.of(); + } + + return Map.of("X-Authorization", getToken()); + } + + public String getToken() { + try { + return tokenCache.get(TOKEN_CACHE_KEY, this::refreshToken); + } catch (ExecutionException e) { + return null; + } + } + + private void scheduleTokenRefresh() { + long refreshInterval = Math.max(30, tokenRefreshInSeconds - 30); // why? + LOGGER.info("Starting token refresh thread to run at every {} seconds", refreshInterval); + tokenRefreshService.scheduleAtFixedRate(this::refreshToken, refreshInterval, refreshInterval, TimeUnit.SECONDS); + } + + private String refreshToken() { + LOGGER.debug("Refreshing token @ {}", Instant.now()); + if (keyId == null || keySecret == null) { + throw new RuntimeException("KeyId and KeySecret must be set in order to get an authentication token"); + } + + GenerateTokenRequest generateTokenRequest = new GenerateTokenRequest(keyId, keySecret); + TokenResponse response = tokenResource.generate(generateTokenRequest).getData(); + return response.getToken(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthorizationClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthorizationClient.java new file mode 100644 index 000000000..4782560f3 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthorizationClient.java @@ -0,0 +1,202 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Map; + +import com.netflix.conductor.client.http.ConductorClient; + +import io.orkes.conductor.client.AuthorizationClient; +import io.orkes.conductor.client.model.AccessKeyResponse; +import io.orkes.conductor.client.model.AuthorizationRequest; +import io.orkes.conductor.client.model.ConductorApplication; +import io.orkes.conductor.client.model.ConductorUser; +import io.orkes.conductor.client.model.CreateAccessKeyResponse; +import io.orkes.conductor.client.model.CreateOrUpdateApplicationRequest; +import io.orkes.conductor.client.model.GrantedAccessResponse; +import io.orkes.conductor.client.model.Group; +import io.orkes.conductor.client.model.Subject; +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.UpsertGroupRequest; +import io.orkes.conductor.client.model.UpsertUserRequest; + +public class OrkesAuthorizationClient implements AuthorizationClient { + + private final ApplicationResource applicationResource; + private final AuthorizationResource authorizationResource; + private final GroupResource groupResource; + private final UserResource userResource; + + public OrkesAuthorizationClient(ConductorClient apiClient) { + this.applicationResource = new ApplicationResource(apiClient); + this.authorizationResource = new AuthorizationResource(apiClient); + this.groupResource = new GroupResource(apiClient); + this.userResource = new UserResource(apiClient); + } + + @Override + public Map> getPermissions(String type, String id) { + return authorizationResource.getPermissions(type, id); + } + + @Override + public void grantPermissions(AuthorizationRequest authorizationRequest) { + authorizationResource.grantPermissions(authorizationRequest); + } + + @Override + public void removePermissions(AuthorizationRequest authorizationRequest) { + authorizationResource.removePermissions(authorizationRequest); + } + + @Override + public void addUserToGroup(String groupId, String userId) { + groupResource.addUserToGroup(groupId, userId); + } + + @Override + public void deleteGroup(String id) { + groupResource.deleteGroup(id); + } + + @Override + public GrantedAccessResponse getGrantedPermissionsForGroup(String groupId) { + return groupResource.getGrantedPermissions(groupId); + } + + @Override + public Group getGroup(String id) { + return groupResource.getGroup(id); + } + + @Override + public List getUsersInGroup(String id) { + return groupResource.getUsersInGroup(id); + } + + @Override + public List listGroups() { + return groupResource.listGroups(); + } + + @Override + public void removeUserFromGroup(String groupId, String userId) { + groupResource.removeUserFromGroup(groupId, userId); + } + + @Override + public Group upsertGroup(UpsertGroupRequest upsertGroupRequest, String id) { + return groupResource.upsertGroup(upsertGroupRequest, id); + } + + @Override + public void deleteUser(String id) { + userResource.deleteUser(id); + } + + @Override + public GrantedAccessResponse getGrantedPermissionsForUser(String userId) { + return userResource.getGrantedPermissions(userId); + } + + @Override + public ConductorUser getUser(String id) { + return userResource.getUser(id); + } + + @Override + public List listUsers(Boolean apps) { + return userResource.listUsers(apps); + } + + @Override + public void sendInviteEmail(String email) { + userResource.sendInviteEmail(email); + } + + @Override + public ConductorUser upsertUser(UpsertUserRequest upsertUserRequest, String id) { + return userResource.upsertUser(upsertUserRequest, id); + } + + @Override + public void addRoleToApplicationUser(String applicationId, String role) { + applicationResource.addRoleToApplicationUser(applicationId, role); + } + + @Override + public CreateAccessKeyResponse createAccessKey(String id) { + return applicationResource.createAccessKey(id); + } + + @Override + public ConductorApplication createApplication(CreateOrUpdateApplicationRequest createOrUpdateApplicationRequest) { + return applicationResource.upsertApplication(createOrUpdateApplicationRequest, null); + } + + @Override + public void deleteAccessKey(String applicationId, String keyId) { + applicationResource.deleteAccessKey(applicationId, keyId); + } + + @Override + public void deleteApplication(String id) { + applicationResource.deleteApplication(id); + } + + @Override + public List getAccessKeys(String id) { + return applicationResource.getAccessKeys(id); + } + + @Override + public ConductorApplication getApplication(String id) { + return applicationResource.getApplication(id); + } + + @Override + public List listApplications() { + return applicationResource.listApplications(); + } + + @Override + public void removeRoleFromApplicationUser(String applicationId, String role) { + applicationResource.removeRoleFromApplicationUser(applicationId, role); + } + + @Override + public AccessKeyResponse toggleAccessKeyStatus(String applicationId, String keyId) { + return applicationResource.toggleAccessKeyStatus(applicationId, keyId); + } + + @Override + public ConductorApplication updateApplication(CreateOrUpdateApplicationRequest createOrUpdateApplicationRequest, String id) { + return applicationResource.upsertApplication(createOrUpdateApplicationRequest, id); + } + + @Override + public void setApplicationTags(List tags, String applicationId) { + applicationResource.putTags(tags, applicationId); + } + + @Override + public List getApplicationTags(String applicationId) { + return applicationResource.getTags(applicationId); + } + + @Override + public void deleteApplicationTags(List tags, String applicationId) { + applicationResource.deleteTags(tags, applicationId); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesEventClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesEventClient.java new file mode 100644 index 000000000..2092e6b5f --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesEventClient.java @@ -0,0 +1,71 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + + +import java.util.List; +import java.util.Map; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.EventClient; +import com.netflix.conductor.common.metadata.events.EventHandler; + +import io.orkes.conductor.client.model.event.QueueConfiguration; + +public class OrkesEventClient { + + private final EventResource eventResource; + + private final EventClient eventClient; + + public OrkesEventClient(ConductorClient client) { + this.eventResource = new EventResource(client); + this.eventClient = new EventClient(client); + } + + public List getEventHandlers() { + return eventResource.getEventHandlers(); + } + + public void handleIncomingEvent(Map payload) { + eventResource.handleIncomingEvent(payload); + } + + public Map getQueueConfig(QueueConfiguration queueConfiguration) { + return eventResource.getQueueConfig(queueConfiguration.getQueueType(), queueConfiguration.getQueueName()); + } + + public void deleteQueueConfig(QueueConfiguration queueConfiguration) { + eventResource.deleteQueueConfig(queueConfiguration.getQueueType(), queueConfiguration.getQueueName()); + } + + public void putQueueConfig(QueueConfiguration queueConfiguration) { + eventResource.putQueueConfig(queueConfiguration.getQueueType(), queueConfiguration.getQueueName()); + } + + public void registerEventHandler(EventHandler eventHandler) { + eventClient.registerEventHandler(eventHandler); + } + + public void updateEventHandler(EventHandler eventHandler) { + eventClient.updateEventHandler(eventHandler); + } + + public List getEventHandlers(String event, boolean activeOnly) { + return eventClient.getEventHandlers(event, activeOnly); + } + + public void unregisterEventHandler(String name) { + eventClient.unregisterEventHandler(name); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesIntegrationClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesIntegrationClient.java new file mode 100644 index 000000000..c059da7cc --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesIntegrationClient.java @@ -0,0 +1,113 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Map; + +import com.netflix.conductor.client.exception.ConductorClientException; +import com.netflix.conductor.client.http.ConductorClient; + +import io.orkes.conductor.client.IntegrationClient; +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.integration.Integration; +import io.orkes.conductor.client.model.integration.IntegrationApi; +import io.orkes.conductor.client.model.integration.IntegrationApiUpdate; +import io.orkes.conductor.client.model.integration.IntegrationUpdate; +import io.orkes.conductor.client.model.integration.ai.PromptTemplate; + +public class OrkesIntegrationClient implements IntegrationClient { + private final IntegrationResource integrationResource; + + public OrkesIntegrationClient(ConductorClient apiClient) { + this.integrationResource = new IntegrationResource(apiClient); + } + + public void associatePromptWithIntegration(String integrationProvider, String integrationName, String promptName) { + integrationResource.associatePromptWithIntegration(integrationProvider, integrationName, promptName); + } + + public void deleteIntegrationApi(String integrationProvider, String integrationName) { + integrationResource.deleteIntegrationApi(integrationProvider, integrationName); + } + + public void deleteIntegrationProvider(String integrationName) { + integrationResource.deleteIntegrationProvider(integrationName); + } + + public IntegrationApi getIntegrationApi(String integrationProvider, String integrationName) { + try { + return integrationResource.getIntegrationApi(integrationProvider, integrationName); + } catch (ConductorClientException e) { + //FIXME WHY? this creates inconsistency. Should all 404 be a null? + if (e.getStatus() == 404) { + return null; + } + + throw e; + } + } + + public List getIntegrationApis(String integrationName) { + return integrationResource.getIntegrationApis(integrationName, true); + } + + public Integration getIntegrationProvider(String integrationProvider) { + try { + return integrationResource.getIntegrationProvider(integrationProvider); + } catch (ConductorClientException e) { + if (e.getStatus() == 404) { + return null; + } + throw e; + } + } + + public List getIntegrationProviders(String category, Boolean activeOnly) { + return integrationResource.getIntegrationProviders(category, activeOnly); + } + + public List getPromptsWithIntegration(String aiIntegration, String modelName) { + return integrationResource.getPromptsWithIntegration(aiIntegration, modelName); + } + + public void saveIntegrationApi(String integrationName, String apiName, IntegrationApiUpdate integrationApiUpdate) { + integrationResource.saveIntegrationApi(integrationApiUpdate, integrationName, apiName); + } + + public void saveIntegration(String integrationName, IntegrationUpdate integrationDetails) { + integrationResource.saveIntegrationProvider(integrationDetails, integrationName); + } + + public int getTokenUsageForIntegration(String integrationProvider, String integrationName) { + return integrationResource.getTokenUsageForIntegration(integrationProvider, integrationName); + } + + public Map getTokenUsageForIntegrationProvider(String integrationProvider) { + return integrationResource.getTokenUsageForIntegrationProvider(integrationProvider); + } + + // Tags - Implementations are assumed to be placeholders + + public void deleteTagForIntegrationProvider(List tags, String name) { + integrationResource.deleteTagForIntegrationProvider(tags, name); + } + + public void saveTagForIntegrationProvider(List tags, String name) { + integrationResource.putTagForIntegrationProvider(tags, name); + } + + public List getTagsForIntegrationProvider(String name) { + return integrationResource.getTagsForIntegrationProvider(name); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesMetadataClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesMetadataClient.java new file mode 100644 index 000000000..e7203c1dc --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesMetadataClient.java @@ -0,0 +1,127 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.MetadataClient; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; + +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.TagString; + + +public class OrkesMetadataClient { + + private final MetadataResource metadataResource; + private final TagsResource tagsResource; + + private final MetadataClient metadataClient; + + public OrkesMetadataClient(ConductorClient client) { + this.metadataResource = new MetadataResource(client); + this.tagsResource = new TagsResource(client); + this.metadataClient = new MetadataClient(client); + } + + public void registerWorkflowDef(WorkflowDef workflowDef) { + metadataResource.registerWorkflowDef(workflowDef, true); + } + + public void registerWorkflowDef(WorkflowDef workflowDef, boolean overwrite) { + metadataResource.registerWorkflowDef(workflowDef, overwrite); + } + + public void updateWorkflowDefs(List workflowDefs) { + metadataResource.updateWorkflows(workflowDefs, true); + } + + public void updateWorkflowDefs(List workflowDefs, boolean overwrite) { + metadataResource.updateWorkflows(workflowDefs, overwrite); + } + + public WorkflowDef getWorkflowDef(String name, Integer version) { + return metadataResource.getWorkflow(name, version, false); + } + + public WorkflowDef getWorkflowDefWithMetadata(String name, Integer version) { + return metadataResource.getWorkflow(name, version, true); + } + + public void unregisterWorkflowDef(String name, Integer version) { + metadataClient.unregisterWorkflowDef(name, version); + } + + public List getAllTaskDefs() { + return metadataClient.getAllTaskDefs(); + } + + public void registerTaskDefs(List taskDefs) { + metadataResource.registerTaskDef(taskDefs); + } + + public void updateTaskDef(TaskDef taskDef) { + metadataClient.updateTaskDef(taskDef); + } + + public TaskDef getTaskDef(String taskType) { + return metadataResource.getTaskDef(taskType, true); + } + + public void unregisterTaskDef(String taskType) { + metadataClient.unregisterTaskDef(taskType); + } + + public void addTaskTag(TagObject tagObject, String taskName) { + tagsResource.addTaskTag(tagObject, taskName); + } + + public void addWorkflowTag(TagObject tagObject, String name) { + tagsResource.addWorkflowTag(tagObject, name); + } + + public void deleteTaskTag(TagString tagString, String taskName) { + tagsResource.deleteTaskTag(tagString, taskName); + } + + public void deleteWorkflowTag(TagObject tagObject, String name) { + tagsResource.deleteWorkflowTag(tagObject, name); + } + + public List getTags() { + return tagsResource.getTags(); + } + + public List getTaskTags(String taskName) { + return tagsResource.getTaskTags(taskName); + } + + public List getWorkflowTags(String name) { + return tagsResource.getWorkflowTags(name); + } + + public void setTaskTags(List tagObjects, String taskName) { + tagsResource.setTaskTags(tagObjects, taskName); + } + + public void setWorkflowTags(List tagObjects, String name) { + tagsResource.setWorkflowTags(tagObjects, name); + } + + public List getAllWorkflowDefs() { + return metadataResource.getAllWorkflows(null, false, null, null); + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesPromptClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesPromptClient.java new file mode 100644 index 000000000..76d5aebd0 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesPromptClient.java @@ -0,0 +1,149 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Map; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.client.http.ConductorClientResponse; + +import io.orkes.conductor.client.PromptClient; +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.integration.PromptTemplateTestRequest; +import io.orkes.conductor.client.model.integration.ai.PromptTemplate; + +import com.fasterxml.jackson.core.type.TypeReference; + + +public class OrkesPromptClient implements PromptClient { + + private final ConductorClient client; + + public OrkesPromptClient(ConductorClient client) { + this.client = client; + } + + @Override + public void savePrompt(String promptName, String description, String promptTemplate) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/prompts/{name}") + .addPathParam("name", promptName) + .addQueryParam("description", description) + .build(); + client.execute(request); + } + + @Override + public PromptTemplate getPrompt(String promptName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/prompts/{name}") + .addPathParam("name", promptName) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + @Override + public List getPrompts() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/prompts") + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + @Override + public void deletePrompt(String promptName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/prompts/{name}") + .addPathParam("name", promptName) + .build(); + + client.execute(request); + } + + @Override + public List getTagsForPromptTemplate(String promptName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/prompts/{name}/tags") + .addPathParam("name", promptName) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + @Override + public void updateTagForPromptTemplate(String promptName, List tags) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/prompts/{name}/tags") + .addPathParam("name", promptName) + .body(tags) + .build(); + + client.execute(request); + } + + @Override + public void deleteTagForPromptTemplate(String promptName, List tags) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/prompts/{name}/tags") + .addPathParam("name", promptName) + .body(tags) + .build(); + + client.execute(request); + } + + @Override + public String testPrompt(String promptText, Map variables, String aiIntegration, String textCompleteModel, float temperature, float topP, + List stopWords) { + PromptTemplateTestRequest body = new PromptTemplateTestRequest(); + body.setPrompt(promptText); + body.setLlmProvider(aiIntegration); + body.setModel(textCompleteModel); + body.setTemperature((double) temperature); + body.setTopP((double) topP); + body.setStopWords(stopWords == null ? List.of() : stopWords); + body.setPromptVariables(variables); + + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/prompts/test") + .body(body) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSchedulerClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSchedulerClient.java new file mode 100644 index 000000000..1b0599a01 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSchedulerClient.java @@ -0,0 +1,106 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; + +import com.netflix.conductor.client.http.ConductorClient; + +import io.orkes.conductor.client.SchedulerClient; +import io.orkes.conductor.client.model.SaveScheduleRequest; +import io.orkes.conductor.client.model.SearchResultWorkflowScheduleExecution; +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.WorkflowSchedule; + +public class OrkesSchedulerClient implements SchedulerClient { + + private final SchedulerResource schedulerResource; + + public OrkesSchedulerClient(ConductorClient apiClient) { + this.schedulerResource = new SchedulerResource(apiClient); + } + + @Override + public void deleteSchedule(String name) { + schedulerResource.deleteSchedule(name); + } + + @Override + public List getAllSchedules(String workflowName) { + return schedulerResource.getAllSchedules(workflowName); + } + + @Override + public List getNextFewSchedules(String cronExpression, + Long scheduleStartTime, + Long scheduleEndTime, + Integer limit) { + return schedulerResource.getNextFewSchedules( + cronExpression, scheduleStartTime, scheduleEndTime, limit); + } + + @Override + public WorkflowSchedule getSchedule(String name) { + return schedulerResource.getSchedule(name); + } + + @Override + public void pauseAllSchedules() { + schedulerResource.pauseAllSchedules(); + } + + @Override + public void pauseSchedule(String name) { + schedulerResource.pauseSchedule(name); + } + + @Override + public void requeueAllExecutionRecords() { + schedulerResource.requeueAllExecutionRecords(); + } + + @Override + public void resumeAllSchedules() { + schedulerResource.resumeAllSchedules(); + } + + @Override + public void resumeSchedule(String name) { + schedulerResource.resumeSchedule(name); + } + + @Override + public void saveSchedule(SaveScheduleRequest saveScheduleRequest) { + schedulerResource.saveSchedule(saveScheduleRequest); + } + + @Override + public SearchResultWorkflowScheduleExecution search(Integer start, Integer size, String sort, String freeText, String query) { + return schedulerResource.search(start, size, sort, freeText, query); + } + + @Override + public void setSchedulerTags(List body, String name) { + schedulerResource.putTagForSchedule(name, body); + } + + @Override + public void deleteSchedulerTags(List body, String name) { + schedulerResource.deleteTagForSchedule(name, body); + } + + @Override + public List getSchedulerTags(String name) { + return schedulerResource.getTagsForSchedule(name); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSecretClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSecretClient.java new file mode 100644 index 000000000..400a6cb43 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSecretClient.java @@ -0,0 +1,75 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Set; + +import com.netflix.conductor.client.http.ConductorClient; + +import io.orkes.conductor.client.SecretClient; +import io.orkes.conductor.client.model.TagObject; + +public class OrkesSecretClient implements SecretClient { + + private final SecretResource secretResource; + + public OrkesSecretClient(ConductorClient apiClient) { + this.secretResource = new SecretResource(apiClient); + } + + @Override + public void deleteSecret(String key) { + secretResource.deleteSecret(key); + } + + @Override + public String getSecret(String key) { + return secretResource.getSecret(key); + } + + @Override + public Set listAllSecretNames() { + return secretResource.listAllSecretNames(); + } + + @Override + public List listSecretsThatUserCanGrantAccessTo() { + return secretResource.listSecretsThatUserCanGrantAccessTo(); + } + + @Override + public void putSecret(String value, String key) { + secretResource.putSecret(value, key); + } + + @Override + public boolean secretExists(String key) { + return secretResource.secretExists(key); + } + + @Override + public void setSecretTags(List tags, String key) { + secretResource.putTagForSecret(key, tags); + } + + @Override + public void deleteSecretTags(List tags, String key) { + secretResource.deleteTagForSecret(tags, key); + } + + @Override + public List getSecretTags(String key) { + return secretResource.getTags(key); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesTaskClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesTaskClient.java new file mode 100644 index 000000000..62bbeb26a --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesTaskClient.java @@ -0,0 +1,204 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.commons.lang3.Validate; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientResponse; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.common.config.ObjectMapperProvider; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskExecLog; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.common.run.SearchResult; +import com.netflix.conductor.common.run.TaskSummary; +import com.netflix.conductor.common.run.Workflow; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class OrkesTaskClient { + + private final ObjectMapper objectMapper; + private final TaskClient taskClient; + private final ConductorClient client; + + public OrkesTaskClient(ConductorClient client) { + this.client = client; + this.taskClient = new TaskClient(client); + this.objectMapper = new ObjectMapperProvider().getObjectMapper(); + } + + /** + * Update the task status and output based given workflow id and task reference name + * + * @param workflowId Workflow Id + * @param taskReferenceName Reference name of the task to be updated + * @param status Status of the task + * @param output Output for the task + */ + public void updateTask(String workflowId, String taskReferenceName, TaskResult.Status status, Object output) { + updateTaskByRefName(getOutputMap(output), workflowId, taskReferenceName, status.toString(), getWorkerId()); + } + + /** + * Update the task status and output based given workflow id and task reference name and return back the updated workflow status + * + * @param workflowId Workflow Id + * @param taskReferenceName Reference name of the task to be updated + * @param status Status of the task + * @param output Output for the task + * @return Status of the workflow after updating the task + */ + public Workflow updateTaskSync(String workflowId, String taskReferenceName, TaskResult.Status status, Object output) { + return updateTaskSync(getOutputMap(output), workflowId, taskReferenceName, status.toString(), getWorkerId()); + } + + private Map getOutputMap(Object output) { + try { + return objectMapper.convertValue(output, new TypeReference<>() { + }); + } catch (Exception e) { + Map outputMap = new HashMap<>(); + outputMap.put("result", output); + return outputMap; + } + } + + //TODO extract this to a strategy that will be used by TaskResource + private String getWorkerId() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + return System.getenv("HOSTNAME"); + } + } + + private String updateTaskByRefName(Map output, + String workflowId, + String taskRefName, + String status, + String workerId) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(ConductorClientRequest.Method.POST) + .path("/tasks/{workflowId}/{taskRefName}/{status}") + .addPathParam("workflowId", workflowId) + .addPathParam("taskRefName", taskRefName) + .addPathParam("status", status) + .addQueryParam("workerid", workerId) + .body(output) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + private Workflow updateTaskSync(Map output, + String workflowId, + String taskRefName, + String status, + String workerId) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(ConductorClientRequest.Method.POST) + .path("/tasks/{workflowId}/{taskRefName}/{status}/sync") + .addPathParam("workflowId", workflowId) + .addPathParam("taskRefName", taskRefName) + .addPathParam("status", status) + .addQueryParam("workerid", workerId) + .body(output) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + // Delegate only methods which are supported + + public Task pollTask(String taskType, String workerId, String domain) { + return taskClient.pollTask(taskType, workerId, domain); + } + + public List batchPollTasksByTaskType(String taskType, String workerId, int count, int timeoutInMillisecond) { + return taskClient.batchPollTasksByTaskType(taskType, workerId, count, timeoutInMillisecond); + } + + public List batchPollTasksInDomain(String taskType, String domain, String workerId, int count, int timeoutInMillisecond) { + return taskClient.batchPollTasksInDomain(taskType, domain, workerId, count, timeoutInMillisecond); + } + + public void updateTask(TaskResult taskResult) { + taskClient.updateTask(taskResult); + } + + public Optional evaluateAndUploadLargePayload(Map taskOutputData, String taskType) { + return taskClient.evaluateAndUploadLargePayload(taskOutputData, taskType); + } + + public void logMessageForTask(String taskId, String logMessage) { + taskClient.logMessageForTask(taskId, logMessage); + } + + public List getTaskLogs(String taskId) { + return taskClient.getTaskLogs(taskId); + } + + public Task getTaskDetails(String taskId) { + return taskClient.getTaskDetails(taskId); + } + + public int getQueueSizeForTask(String taskType) { + Validate.notBlank(taskType, "Task type cannot be blank"); + ConductorClientRequest request = ConductorClientRequest.builder() + .method(ConductorClientRequest.Method.GET) + .path("/tasks/queue/sizes") + .addQueryParams("taskType", List.of(taskType)) + .build(); + ConductorClientResponse> response = client.execute(request, new TypeReference<>() { + }); + + Integer queueSize = response.getData().get(taskType); + return queueSize != null ? queueSize : 0; + } + + public int getQueueSizeForTask(String taskType, String domain, String isolationGroupId, String executionNamespace) { + // This is not supported by Orkes Conductor + // taskClient.getQueueSizeForTask(taskType, domain, isolationGroupId, executionNamespace); + return getQueueSizeForTask(taskType); + } + + public String requeuePendingTasksByTaskType(String taskType) { + return taskClient.requeuePendingTasksByTaskType(taskType); + } + + public SearchResult search(Integer start, Integer size, String sort, String freeText, String query) { + return taskClient.search(start, size, sort, freeText, query); + } + + public SearchResult searchV2(Integer start, Integer size, String sort, String freeText, String query) { + return taskClient.searchV2(start, size, sort, freeText, query); + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesWorkflowClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesWorkflowClient.java new file mode 100644 index 000000000..4c2e844fa --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesWorkflowClient.java @@ -0,0 +1,237 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.apache.commons.lang3.Validate; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.WorkflowClient; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; +import com.netflix.conductor.common.metadata.workflow.UpgradeWorkflowRequest; +import com.netflix.conductor.common.model.BulkResponse; +import com.netflix.conductor.common.run.SearchResult; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.common.run.WorkflowSummary; + +import io.orkes.conductor.client.model.CorrelationIdsSearchRequest; +import io.orkes.conductor.client.model.WorkflowRun; +import io.orkes.conductor.client.model.WorkflowStateUpdate; +import io.orkes.conductor.client.model.WorkflowStatus; + +//TODO should this extend or not WorkflowClient? +public class OrkesWorkflowClient implements AutoCloseable { + + private final WorkflowResource workflowResource; + + private final WorkflowBulkResource bulkResource; + + private final WorkflowClient workflowClient; + + private final ExecutorService executorService; + + public OrkesWorkflowClient(ConductorClient client) { + this(client, 0); + } + + public OrkesWorkflowClient(ConductorClient client, int executorThreadCount) { + this.workflowResource = new WorkflowResource(client); + this.bulkResource = new WorkflowBulkResource(client); + this.workflowClient = new WorkflowClient(client); + ThreadFactory factory = new BasicThreadFactory.Builder() + .namingPattern("WorkflowClient Executor %d") + .build(); + + if (executorThreadCount < 1) { + this.executorService = Executors.newCachedThreadPool(factory); + } else { + this.executorService = Executors.newFixedThreadPool(executorThreadCount, factory); + } + } + + public CompletableFuture executeWorkflow(StartWorkflowRequest request, String waitUntilTask) { + return executeWorkflowHttp(request, waitUntilTask); + } + + public CompletableFuture executeWorkflow(StartWorkflowRequest request, String waitUntilTask, Integer waitForSeconds) { + return executeWorkflowHttp(request, waitUntilTask, waitForSeconds); + } + + public CompletableFuture executeWorkflow(StartWorkflowRequest request, List waitUntilTasks, Integer waitForSeconds) { + String waitUntilTask = String.join(",", waitUntilTasks); + return executeWorkflowHttp(request, waitUntilTask, waitForSeconds); + } + + public WorkflowRun executeWorkflow(StartWorkflowRequest request, String waitUntilTask, Duration waitTimeout) throws ExecutionException, InterruptedException, TimeoutException { + CompletableFuture future = executeWorkflow(request, waitUntilTask); + return future.get(waitTimeout.get(ChronoUnit.MILLIS), TimeUnit.MILLISECONDS); + } + + private CompletableFuture executeWorkflowHttp(StartWorkflowRequest startWorkflowRequest, String waitUntilTask) { + CompletableFuture future = new CompletableFuture<>(); + String requestId = UUID.randomUUID().toString(); + executorService.submit( + () -> { + try { + WorkflowRun response = workflowResource.executeWorkflow( + startWorkflowRequest, + startWorkflowRequest.getName(), + startWorkflowRequest.getVersion(), + waitUntilTask, + requestId); + future.complete(response); + } catch (Throwable t) { + future.completeExceptionally(t); + } + }); + + return future; + } + + private CompletableFuture executeWorkflowHttp(StartWorkflowRequest startWorkflowRequest, String waitUntilTask, Integer waitForSeconds) { + CompletableFuture future = new CompletableFuture<>(); + String requestId = UUID.randomUUID().toString(); + executorService.submit( + () -> { + try { + WorkflowRun response = workflowResource.executeWorkflow( + startWorkflowRequest, + startWorkflowRequest.getName(), + startWorkflowRequest.getVersion(), + waitUntilTask, + requestId, + waitForSeconds); + future.complete(response); + } catch (Throwable t) { + future.completeExceptionally(t); + } + }); + + return future; + } + + public void terminateWorkflowWithFailure(String workflowId, String reason, boolean triggerFailureWorkflow) { + Validate.notBlank(workflowId, "workflow id cannot be blank"); + workflowResource.terminateWithAReason(workflowId, reason, triggerFailureWorkflow); + } + + public BulkResponse pauseWorkflow(List workflowIds) { + return bulkResource.pauseWorkflows(workflowIds); + } + + public BulkResponse restartWorkflow(List workflowIds, Boolean useLatestDefinitions) { + return bulkResource.restartWorkflows(workflowIds, useLatestDefinitions); + } + + public BulkResponse resumeWorkflow(List workflowIds) { + return bulkResource.resumeWorkflows(workflowIds); + } + + public BulkResponse retryWorkflow(List workflowIds) { + return bulkResource.retryWorkflows(workflowIds); + } + + public BulkResponse terminateWorkflowsWithFailure(List workflowIds, String reason, boolean triggerFailureWorkflow) { + return bulkResource.terminateWorkflows(workflowIds, reason, triggerFailureWorkflow); + } + + public WorkflowStatus getWorkflowStatusSummary(String workflowId, Boolean includeOutput, Boolean includeVariables) { + return workflowResource.getWorkflowStatusSummary(workflowId, includeOutput, includeVariables); + } + + public void uploadCompletedWorkflows() { + workflowResource.uploadCompletedWorkflows(); + } + + public Map> getWorkflowsByNamesAndCorrelationIds( + List correlationIds, List workflowNames, Boolean includeClosed, Boolean includeTasks) { + CorrelationIdsSearchRequest request = new CorrelationIdsSearchRequest(correlationIds, workflowNames); + return workflowResource.getWorkflowsByNamesAndCorrelationIds(request, includeClosed, includeTasks); + } + + public Workflow updateVariables(String workflowId, Map variables) { + return workflowResource.updateVariables(workflowId, variables); + } + + public void upgradeRunningWorkflow(String workflowId, UpgradeWorkflowRequest upgradeWorkflowRequest) { + workflowResource.upgradeRunningWorkflow(upgradeWorkflowRequest, workflowId); + } + + public WorkflowRun updateWorkflow(String workflowId, List waitUntilTaskRefNames, Integer waitForSeconds, WorkflowStateUpdate updateRequest) { + String joinedReferenceNames = ""; + if (waitUntilTaskRefNames != null) { + joinedReferenceNames = String.join(",", waitUntilTaskRefNames); + } + return workflowResource.updateWorkflowState(updateRequest, UUID.randomUUID().toString(), workflowId, joinedReferenceNames, waitForSeconds); + } + + public String startWorkflow(StartWorkflowRequest startWorkflowRequest) { + return workflowClient.startWorkflow(startWorkflowRequest); + } + + public Workflow getWorkflow(String workflowId, boolean includeTasks) { + return workflowClient.getWorkflow(workflowId, includeTasks); + } + + public void pauseWorkflow(String workflowId) { + workflowClient.pauseWorkflow(workflowId); + } + + public void resumeWorkflow(String workflowId) { + workflowClient.resumeWorkflow(workflowId); + } + + public void terminateWorkflow(String workflowId, String reason) { + workflowClient.terminateWorkflow(workflowId, reason); + } + + public void deleteWorkflow(String workflowId, boolean archiveWorkflow) { + workflowClient.deleteWorkflow(workflowId, archiveWorkflow); + } + + public void retryLastFailedTask(String workflowId) { + workflowClient.retryLastFailedTask(workflowId); + } + + public void skipTaskFromWorkflow(String workflowId, String taskReferenceName) { + workflowClient.skipTaskFromWorkflow(workflowId, taskReferenceName); + } + + public SearchResult search(String query) { + return workflowClient.search(query); + } + + public SearchResult search(Integer start, Integer size, String sort, String freeText, String query) { + return workflowClient.search(start, size, sort, freeText, query); + } + + @Override + public void close() { + if (executorService != null) { + executorService.shutdown(); + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/Pair.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/Pair.java new file mode 100644 index 000000000..031e68c64 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/Pair.java @@ -0,0 +1,55 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +/** + * This class exists to maintain backward compatibility and facilitate the migration for + * users of orkes-conductor-client v2 to v3. + */ +@Deprecated +public class Pair { + private String name = ""; + private String value = ""; + + public Pair(String name, String value) { + setName(name); + setValue(value); + } + + private void setName(String name) { + if (!isValidString(name)) return; + + this.name = name; + } + + private void setValue(String value) { + if (!isValidString(value)) return; + + this.value = value; + } + + public String getName() { + return this.name; + } + + public String getValue() { + return this.value; + } + + private boolean isValidString(String arg) { + if (arg == null) return false; + if (arg.trim().isEmpty()) return false; + + return true; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SchedulerResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SchedulerResource.java new file mode 100644 index 000000000..30ad749ae --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SchedulerResource.java @@ -0,0 +1,212 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Map; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.client.http.ConductorClientResponse; + +import io.orkes.conductor.client.model.SaveScheduleRequest; +import io.orkes.conductor.client.model.SearchResultWorkflowScheduleExecution; +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.WorkflowSchedule; + +import com.fasterxml.jackson.core.type.TypeReference; + + +public class SchedulerResource { + + private final ConductorClient client; + + public SchedulerResource(ConductorClient client) { + this.client = client; + } + + public void deleteSchedule(String name) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/scheduler/schedules/{name}") + .addPathParam("name", name) + .build(); + + client.execute(request); + } + + public List getAllSchedules(String workflowName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/scheduler/schedules") + .addQueryParam("workflowName", workflowName) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public List getNextFewSchedules(String cronExpression, + Long scheduleStartTime, + Long scheduleEndTime, + Integer limit) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/scheduler/nextFewSchedules") + .addQueryParam("cronExpression", cronExpression) + .addQueryParam("scheduleStartTime", scheduleStartTime) + .addQueryParam("scheduleEndTime", scheduleEndTime) + .addQueryParam("limit", limit) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public WorkflowSchedule getSchedule(String name) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/scheduler/schedules/{name}") + .addPathParam("name", name) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public Map pauseAllSchedules() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/scheduler/admin/pause") + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public void pauseSchedule(String name) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/scheduler/schedules/{name}/pause") + .addPathParam("name", name) + .build(); + + client.execute(request); + } + + public Map requeueAllExecutionRecords() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/scheduler/admin/requeue") + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public Map resumeAllSchedules() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/scheduler/admin/resume") + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public void resumeSchedule(String name) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/scheduler/schedules/{name}/resume") + .addPathParam("name", name) + .build(); + + client.execute(request); + } + + public void saveSchedule(SaveScheduleRequest saveScheduleRequest) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/scheduler/schedules") + .body(saveScheduleRequest) + .build(); + + client.execute(request); + } + + public SearchResultWorkflowScheduleExecution search( + Integer start, Integer size, String sort, String freeText, String query) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/scheduler/search/executions") + .addQueryParam("start", start) + .addQueryParam("size", size) + .addQueryParam("sort", sort) + .addQueryParam("freeText", freeText) + .addQueryParam("query", query) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public void deleteTagForSchedule(String name, List body) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/scheduler/schedules/{name}/tags") + .addPathParam("name", name) + .body(body) + .build(); + + client.execute(request); + } + + public void putTagForSchedule(String name, List body) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/scheduler/schedules/{name}/tags") + .addPathParam("name", name) + .body(body) + .build(); + + client.execute(request); + } + + public List getTagsForSchedule(String name) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/scheduler/schedules/{name}/tags") + .addPathParam("name", name) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SecretResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SecretResource.java new file mode 100644 index 000000000..0a78fd849 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SecretResource.java @@ -0,0 +1,142 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Set; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientResponse; + +import io.orkes.conductor.client.model.TagObject; + +import com.fasterxml.jackson.core.type.TypeReference; + +public class SecretResource { + private final ConductorClient client; + + public SecretResource(ConductorClient client) { + this.client = client; + } + + public void deleteSecret(String key) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(ConductorClientRequest.Method.DELETE) + .path("/secrets/{key}") + .addPathParam("key", key) + .build(); + + client.execute(request); + } + + public String getSecret(String key) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(ConductorClientRequest.Method.GET) + .path("/secrets/{key}") + .addPathParam("key", key) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + + } + + public Set listAllSecretNames() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(ConductorClientRequest.Method.GET) + .path("/secrets") + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + + public List listSecretsThatUserCanGrantAccessTo() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(ConductorClientRequest.Method.GET) + .path("/secrets") + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public void putSecret(String body, String key) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(ConductorClientRequest.Method.PUT) + .path("/secrets/{key}") + .addPathParam("key", key) + .body(body) + .build(); + + client.execute(request); + } + + public Boolean secretExists(String key) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(ConductorClientRequest.Method.GET) + .path("/secrets/{key}/exists") + .addPathParam("key", key) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public void putTagForSecret(String key, List body) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(ConductorClientRequest.Method.PUT) + .path("/secrets/{key}/tags") + .addPathParam("key", key) + .body(body) + .build(); + + client.execute(request); + + } + + public void deleteTagForSecret(List body, String key) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(ConductorClientRequest.Method.DELETE) + .path("/secrets/{key}/tags") + .addPathParam("key", key) + .body(body) + .build(); + + client.execute(request); + } + + + public List getTags(String key) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(ConductorClientRequest.Method.GET) + .path("/secrets/{key}/tags") + .addPathParam("key", key) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TagsResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TagsResource.java new file mode 100644 index 000000000..4daff987b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TagsResource.java @@ -0,0 +1,139 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.client.http.ConductorClientResponse; + +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.TagString; + +import com.fasterxml.jackson.core.type.TypeReference; + + +public class TagsResource { + + private final ConductorClient client; + + public TagsResource(ConductorClient client) { + this.client = client; + } + + public void addTaskTag(TagObject tagObject, String taskName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/metadata/task/{taskName}/tags") + .addPathParam("taskName", taskName) + .body(tagObject) + .build(); + + client.execute(request); + } + + public void addWorkflowTag(TagObject tagObject, String workflow) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/metadata/workflow/{workflow}/tags") + .addPathParam("workflow", workflow) + .body(tagObject) + .build(); + + client.execute(request); + } + + public void deleteTaskTag(TagString tagString, String taskName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/metadata/task/{taskName}/tags") + .addPathParam("taskName", taskName) + .body(tagString) + .build(); + + client.execute(request); + } + + public void deleteWorkflowTag(TagObject tagObject, String workflow) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/metadata/workflow/{workflow}/tags") + .addPathParam("workflow", workflow) + .body(tagObject) + .build(); + + client.execute(request); + } + + public List getTags() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/metadata/tags") + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public List getTaskTags(String taskName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/metadata/task/{taskName}/tags") + .addPathParam("taskName", taskName) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public List getWorkflowTags(String name) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/metadata/workflow/{name}/tags") + .addPathParam("name", name) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public void setTaskTags(List tagObjects, String taskName) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/metadata/task/{taskName}/tags") + .addPathParam("taskName", taskName) + .body(tagObjects) + .build(); + + client.execute(request); + } + + public void setWorkflowTags(List tagObjects, String workflow) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/metadata/workflow/{workflow}/tags") + .addPathParam("workflow", workflow) + .body(tagObjects) + .build(); + + client.execute(request); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TokenResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TokenResource.java new file mode 100644 index 000000000..17c8a5e40 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TokenResource.java @@ -0,0 +1,55 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.client.http.ConductorClientResponse; + +import io.orkes.conductor.client.model.ConductorUser; +import io.orkes.conductor.client.model.GenerateTokenRequest; +import io.orkes.conductor.client.model.TokenResponse; + +import com.fasterxml.jackson.core.type.TypeReference; + + +public class TokenResource { + + private final ConductorClient client; + + public TokenResource(ConductorClient client) { + this.client = client; + } + + public ConductorClientResponse generate(GenerateTokenRequest body) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/token") + .body(body) + .build(); + + return client.execute(request, new TypeReference<>() { + }); + } + + public ConductorClientResponse getUserInfo() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/token/userInfo") + .build(); + + return client.execute(request, new TypeReference<>() { + }); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/UserResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/UserResource.java new file mode 100644 index 000000000..671aba75d --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/UserResource.java @@ -0,0 +1,109 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.client.http.ConductorClientResponse; + +import io.orkes.conductor.client.model.ConductorUser; +import io.orkes.conductor.client.model.GrantedAccessResponse; +import io.orkes.conductor.client.model.UpsertUserRequest; + +import com.fasterxml.jackson.core.type.TypeReference; + + +class UserResource { + private final ConductorClient client; + + public UserResource(ConductorClient client) { + this.client = client; + } + + public void deleteUser(String id) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.DELETE) + .path("/users/{id}") + .addPathParam("id", id) + .build(); + + client.execute(request); + } + + public GrantedAccessResponse getGrantedPermissions(String userId) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/users/{userId}/permissions") + .addPathParam("userId", userId) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public ConductorUser getUser(String id) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/users/{id}") + .addPathParam("id", id) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + + public List listUsers(Boolean apps) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/users") + .addQueryParam("apps", apps) + .build(); + + ConductorClientResponse> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public void sendInviteEmail(String email) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/users/{email}/sendInviteEmail") + .addPathParam("email", email) + .build(); + + client.execute(request); + } + + public ConductorUser upsertUser(UpsertUserRequest upsertUserRequest, String id) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/users/{id}") + .addPathParam("id", id) + .body(upsertUserRequest) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowBulkResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowBulkResource.java new file mode 100644 index 000000000..856382a31 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowBulkResource.java @@ -0,0 +1,101 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.client.http.ConductorClientResponse; +import com.netflix.conductor.common.model.BulkResponse; + +import com.fasterxml.jackson.core.type.TypeReference; + + +class WorkflowBulkResource { + + private final ConductorClient client; + + WorkflowBulkResource(ConductorClient client) { + this.client = client; + } + + BulkResponse pauseWorkflows(List workflowIds) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/workflow/bulk/pause") + .body(workflowIds) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + BulkResponse restartWorkflows(List workflowIds, Boolean useLatestDefinitions) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/bulk/restart") + .addQueryParam("useLatestDefinitions", useLatestDefinitions) + .body(workflowIds) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + BulkResponse resumeWorkflows(List workflowIds) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.PUT) + .path("/workflow/bulk/resume") + .body(workflowIds) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + BulkResponse retryWorkflows(List workflowIds) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/bulk/retry") + .body(workflowIds) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public BulkResponse terminateWorkflows(List workflowIds, String reason, boolean triggerFailureWorkflow) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/bulk/terminate") + .addQueryParam("reason", reason) + .addQueryParam("triggerFailureWorkflow", triggerFailureWorkflow) + .body(workflowIds) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowResource.java new file mode 100644 index 000000000..f6869c926 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowResource.java @@ -0,0 +1,171 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Map; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.ConductorClientRequest; +import com.netflix.conductor.client.http.ConductorClientRequest.Method; +import com.netflix.conductor.client.http.ConductorClientResponse; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; +import com.netflix.conductor.common.metadata.workflow.UpgradeWorkflowRequest; +import com.netflix.conductor.common.run.Workflow; + +import io.orkes.conductor.client.model.CorrelationIdsSearchRequest; +import io.orkes.conductor.client.model.WorkflowRun; +import io.orkes.conductor.client.model.WorkflowStateUpdate; +import io.orkes.conductor.client.model.WorkflowStatus; + +import com.fasterxml.jackson.core.type.TypeReference; + + +class WorkflowResource { + private final ConductorClient client; + + WorkflowResource(ConductorClient client) { + this.client = client; + } + + WorkflowRun executeWorkflow(StartWorkflowRequest req, + String name, + Integer version, + String waitUntilTaskRef, + String requestId) { + return executeWorkflow(req, name, version, waitUntilTaskRef, requestId, null); + } + + WorkflowRun executeWorkflow(StartWorkflowRequest req, + String name, + Integer version, + String waitUntilTaskRef, + String requestId, + Integer waitForSeconds) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/execute/{name}/{version}") + .addPathParam("name", name) + .addPathParam("version", version) + .addQueryParam("requestId", requestId) + .addQueryParam("waitUntilTaskRef", waitUntilTaskRef) + .addQueryParam("waitForSeconds", waitForSeconds) + .body(req) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + WorkflowStatus getWorkflowStatusSummary(String workflowId, Boolean includeOutput, Boolean includeVariables) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.GET) + .path("/workflow/{workflowId}/status") + .addPathParam("workflowId", workflowId) + .addQueryParam("includeOutput", includeOutput) + .addQueryParam("includeVariables", includeVariables) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + Map> getWorkflowsByNamesAndCorrelationIds(CorrelationIdsSearchRequest searchRequest, + Boolean includeClosed, + Boolean includeTasks) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/correlated/batch") + .addQueryParam("includeClosed", includeClosed) + .addQueryParam("includeTasks", includeTasks) + .body(searchRequest) + .build(); + + ConductorClientResponse>> resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + void uploadCompletedWorkflows() { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/document-store/upload") + .build(); + + client.execute(request); + } + + Workflow updateVariables(String workflowId, Map variables) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/{workflowId}/variables") + .addPathParam("workflowId", workflowId) + .body(variables) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + + } + + void upgradeRunningWorkflow(UpgradeWorkflowRequest body, String workflowId) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/{workflowId}/upgrade") + .addPathParam("workflowId", workflowId) + .body(body) + .build(); + + client.execute(request); + } + + WorkflowRun updateWorkflowState(WorkflowStateUpdate updateRequest, + String requestId, + String workflowId, + String waitUntilTaskRef, + Integer waitForSeconds) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(Method.POST) + .path("/workflow/{workflowId}/state") + .addPathParam("workflowId", workflowId) + .addQueryParam("requestId", requestId) + .addQueryParam("waitUntilTaskRef", waitUntilTaskRef) + .addQueryParam("waitForSeconds", waitForSeconds) + .body(updateRequest) + .build(); + + ConductorClientResponse resp = client.execute(request, new TypeReference<>() { + }); + + return resp.getData(); + } + + public void terminateWithAReason(String workflowId, String reason, boolean triggerFailureWorkflow) { + ConductorClientRequest request = ConductorClientRequest.builder() + .method(ConductorClientRequest.Method.DELETE) + .path("/workflow/{workflowId}") + .addPathParam("workflowId", workflowId) + .addQueryParam("reason", reason) + .addQueryParam("triggerFailureWorkflow", triggerFailureWorkflow) + .build(); + + client.execute(request); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyResponse.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyResponse.java new file mode 100644 index 000000000..775ac9d5a --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyResponse.java @@ -0,0 +1,22 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import lombok.Data; + +@Data +public class AccessKeyResponse { + private String id; + private Long createdAt; + private AccessKeyStatus status; +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyStatus.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyStatus.java new file mode 100644 index 000000000..55578c5af --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyStatus.java @@ -0,0 +1,18 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +public enum AccessKeyStatus { + ACTIVE, + INACTIVE +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AuthorizationRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AuthorizationRequest.java new file mode 100644 index 000000000..3db2b1579 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AuthorizationRequest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.ArrayList; +import java.util.List; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class AuthorizationRequest { + /** + * The set of access which is granted or removed + */ + public enum AccessEnum { + CREATE("CREATE"), + READ("READ"), + UPDATE("UPDATE"), + DELETE("DELETE"), + EXECUTE("EXECUTE"); + + private final String value; + + AccessEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static AccessEnum fromValue(String input) { + for (AccessEnum b : AccessEnum.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + } + + private List access = new ArrayList<>(); + + private SubjectRef subject = null; + + private TargetRef target = null; + + public AuthorizationRequest access(List access) { + this.access = access; + return this; + } + + public AuthorizationRequest addAccessItem(AccessEnum accessItem) { + this.access.add(accessItem); + return this; + } + + /** + * The set of access which is granted or removed + * + * @return access + */ + public List getAccess() { + return access; + } + + public void setAccess(List access) { + this.access = access; + } + + public AuthorizationRequest subject(SubjectRef subject) { + this.subject = subject; + return this; + } + + public SubjectRef getSubject() { + return subject; + } + + public void setSubject(SubjectRef subject) { + this.subject = subject; + } + + public AuthorizationRequest target(TargetRef target) { + this.target = target; + return this; + } + + public TargetRef getTarget() { + return target; + } + + public void setTarget(TargetRef target) { + this.target = target; + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorApplication.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorApplication.java new file mode 100644 index 000000000..064093213 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorApplication.java @@ -0,0 +1,66 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class ConductorApplication { + + private String createdBy = null; + + private String id = null; + + private String name = null; + + public ConductorApplication createdBy(String createdBy) { + this.createdBy = createdBy; + return this; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public ConductorApplication id(String id) { + this.id = id; + return this; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public ConductorApplication name(String name) { + this.name = name; + return this; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorUser.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorUser.java new file mode 100644 index 000000000..b3a67e8e1 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorUser.java @@ -0,0 +1,130 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.ArrayList; +import java.util.List; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class ConductorUser { + + private Boolean applicationUser = null; + + private List groups = null; + + private String id = null; + + private String name = null; + + private List roles = null; + + private String uuid = null; + + public ConductorUser applicationUser(Boolean applicationUser) { + this.applicationUser = applicationUser; + return this; + } + + public Boolean isApplicationUser() { + return applicationUser; + } + + public void setApplicationUser(Boolean applicationUser) { + this.applicationUser = applicationUser; + } + + public ConductorUser groups(List groups) { + this.groups = groups; + return this; + } + + public ConductorUser addGroupsItem(Group groupsItem) { + if (this.groups == null) { + this.groups = new ArrayList<>(); + } + this.groups.add(groupsItem); + return this; + } + + public List getGroups() { + return groups; + } + + public void setGroups(List groups) { + this.groups = groups; + } + + public ConductorUser id(String id) { + this.id = id; + return this; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public ConductorUser name(String name) { + this.name = name; + return this; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ConductorUser roles(List roles) { + this.roles = roles; + return this; + } + + public ConductorUser addRolesItem(Role rolesItem) { + if (this.roles == null) { + this.roles = new ArrayList<>(); + } + this.roles.add(rolesItem); + return this; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + public ConductorUser uuid(String uuid) { + this.uuid = uuid; + return this; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CorrelationIdsSearchRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CorrelationIdsSearchRequest.java new file mode 100644 index 000000000..3da10251f --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CorrelationIdsSearchRequest.java @@ -0,0 +1,29 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class CorrelationIdsSearchRequest { + + private List correlationIds; + + private List workflowNames; +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateAccessKeyResponse.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateAccessKeyResponse.java new file mode 100644 index 000000000..ff610b8bb --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateAccessKeyResponse.java @@ -0,0 +1,34 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +public class CreateAccessKeyResponse { + private String id; + private String secret; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateOrUpdateApplicationRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateOrUpdateApplicationRequest.java new file mode 100644 index 000000000..de3fe84d3 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateOrUpdateApplicationRequest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class CreateOrUpdateApplicationRequest { + private String name = null; + + public CreateOrUpdateApplicationRequest name(String name) { + this.name = name; + return this; + } + + /** + * Application's name e.g.: Payment Processors + * + * @return name + */ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ExternalStorageLocation.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ExternalStorageLocation.java new file mode 100644 index 000000000..e410835e6 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ExternalStorageLocation.java @@ -0,0 +1,50 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class ExternalStorageLocation { + private String path = null; + + private String uri = null; + + public ExternalStorageLocation path(String path) { + this.path = path; + return this; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public ExternalStorageLocation uri(String uri) { + this.uri = uri; + return this; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GenerateTokenRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GenerateTokenRequest.java new file mode 100644 index 000000000..ebfb21ea3 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GenerateTokenRequest.java @@ -0,0 +1,27 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +@RequiredArgsConstructor +@EqualsAndHashCode +@ToString +@Getter +public final class GenerateTokenRequest { + private final String keyId; + private final String keySecret; +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccess.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccess.java new file mode 100644 index 000000000..0d4b936db --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccess.java @@ -0,0 +1,96 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.ArrayList; +import java.util.List; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class GrantedAccess { + /** + * Gets or Sets access + */ + public enum AccessEnum { + CREATE("CREATE"), + READ("READ"), + UPDATE("UPDATE"), + DELETE("DELETE"), + EXECUTE("EXECUTE"); + + private final String value; + + AccessEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static AccessEnum fromValue(String input) { + for (AccessEnum b : AccessEnum.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + } + + private List access = null; + + private TargetRef target = null; + + public GrantedAccess access(List access) { + this.access = access; + return this; + } + + public GrantedAccess addAccessItem(AccessEnum accessItem) { + if (this.access == null) { + this.access = new ArrayList<>(); + } + this.access.add(accessItem); + return this; + } + + public List getAccess() { + return access; + } + + public void setAccess(List access) { + this.access = access; + } + + public GrantedAccess target(TargetRef target) { + this.target = target; + return this; + } + + public TargetRef getTarget() { + return target; + } + + public void setTarget(TargetRef target) { + this.target = target; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccessResponse.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccessResponse.java new file mode 100644 index 000000000..26003584c --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccessResponse.java @@ -0,0 +1,47 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.ArrayList; +import java.util.List; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + + +@EqualsAndHashCode +@ToString +public class GrantedAccessResponse { + private List grantedAccess = null; + + public GrantedAccessResponse grantedAccess(List grantedAccess) { + this.grantedAccess = grantedAccess; + return this; + } + + public GrantedAccessResponse addGrantedAccessItem(GrantedAccess grantedAccessItem) { + if (this.grantedAccess == null) { + this.grantedAccess = new ArrayList<>(); + } + this.grantedAccess.add(grantedAccessItem); + return this; + } + + public List getGrantedAccess() { + return grantedAccess; + } + + public void setGrantedAccess(List grantedAccess) { + this.grantedAccess = grantedAccess; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Group.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Group.java new file mode 100644 index 000000000..0ccc406e9 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Group.java @@ -0,0 +1,158 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class Group { + /** Gets or Sets inner */ + public enum InnerEnum { + CREATE("CREATE"), + READ("READ"), + UPDATE("UPDATE"), + DELETE("DELETE"), + EXECUTE("EXECUTE"); + + private final String value; + + InnerEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static InnerEnum fromValue(String input) { + for (InnerEnum b : InnerEnum.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + } + + private Map> defaultAccess = null; + + private String description = null; + + private String id = null; + + private List roles = null; + + public Group defaultAccess(Map> defaultAccess) { + this.defaultAccess = defaultAccess; + return this; + } + + public Group putDefaultAccessItem(String key, List defaultAccessItem) { + if (this.defaultAccess == null) { + this.defaultAccess = new HashMap<>(); + } + this.defaultAccess.put(key, defaultAccessItem); + return this; + } + + /** + * Get defaultAccess + * + * @return defaultAccess + */ + + public Map> getDefaultAccess() { + return defaultAccess; + } + + public void setDefaultAccess(Map> defaultAccess) { + this.defaultAccess = defaultAccess; + } + + public Group description(String description) { + this.description = description; + return this; + } + + /** + * Get description + * + * @return description + */ + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Group id(String id) { + this.id = id; + return this; + } + + /** + * Get id + * + * @return id + */ + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Group roles(List roles) { + this.roles = roles; + return this; + } + + public Group addRolesItem(Role rolesItem) { + if (this.roles == null) { + this.roles = new ArrayList<>(); + } + this.roles.add(rolesItem); + return this; + } + + /** + * Get roles + * + * @return roles + */ + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Permission.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Permission.java new file mode 100644 index 000000000..87a8a2b91 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Permission.java @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class Permission { + + private String name = null; + + public Permission name(String name) { + this.name = name; + return this; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Role.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Role.java new file mode 100644 index 000000000..450bf8861 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Role.java @@ -0,0 +1,75 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.ArrayList; +import java.util.List; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + + +@EqualsAndHashCode +@ToString +public class Role { + + private String name = null; + + private List permissions = null; + + public Role name(String name) { + this.name = name; + return this; + } + + /** + * Get name + * + * @return name + */ + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Role permissions(List permissions) { + this.permissions = permissions; + return this; + } + + public Role addPermissionsItem(Permission permissionsItem) { + if (this.permissions == null) { + this.permissions = new ArrayList<>(); + } + this.permissions.add(permissionsItem); + return this; + } + + /** + * Get permissions + * + * @return permissions + */ + + public List getPermissions() { + return permissions; + } + + public void setPermissions(List permissions) { + this.permissions = permissions; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SaveScheduleRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SaveScheduleRequest.java new file mode 100644 index 000000000..bf6f669f4 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SaveScheduleRequest.java @@ -0,0 +1,216 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class SaveScheduleRequest { + + private String createdBy = null; + + private String cronExpression = null; + + private String name = null; + + private Boolean paused = null; + + private Boolean runCatchupScheduleInstances = null; + + private Long scheduleEndTime = null; + + private Long scheduleStartTime = null; + + private StartWorkflowRequest startWorkflowRequest = null; + + private String updatedBy = null; + + private String zoneId; + + public SaveScheduleRequest createdBy(String createdBy) { + this.createdBy = createdBy; + return this; + } + + /** + * Get createdBy + * + * @return createdBy + */ + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public SaveScheduleRequest cronExpression(String cronExpression) { + this.cronExpression = cronExpression; + return this; + } + + /** + * Get cronExpression + * + * @return cronExpression + */ + + public String getCronExpression() { + return cronExpression; + } + + public void setCronExpression(String cronExpression) { + this.cronExpression = cronExpression; + } + + public SaveScheduleRequest name(String name) { + this.name = name; + return this; + } + + /** + * Get name + * + * @return name + */ + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public SaveScheduleRequest paused(Boolean paused) { + this.paused = paused; + return this; + } + + /** + * Get paused + * + * @return paused + */ + + public Boolean isPaused() { + return paused; + } + + public void setPaused(Boolean paused) { + this.paused = paused; + } + + public SaveScheduleRequest runCatchupScheduleInstances(Boolean runCatchupScheduleInstances) { + this.runCatchupScheduleInstances = runCatchupScheduleInstances; + return this; + } + + /** + * Get runCatchupScheduleInstances + * + * @return runCatchupScheduleInstances + */ + + public Boolean isRunCatchupScheduleInstances() { + return runCatchupScheduleInstances; + } + + public void setRunCatchupScheduleInstances(Boolean runCatchupScheduleInstances) { + this.runCatchupScheduleInstances = runCatchupScheduleInstances; + } + + public SaveScheduleRequest scheduleEndTime(Long scheduleEndTime) { + this.scheduleEndTime = scheduleEndTime; + return this; + } + + /** + * Get scheduleEndTime + * + * @return scheduleEndTime + */ + + public Long getScheduleEndTime() { + return scheduleEndTime; + } + + public void setScheduleEndTime(Long scheduleEndTime) { + this.scheduleEndTime = scheduleEndTime; + } + + public SaveScheduleRequest scheduleStartTime(Long scheduleStartTime) { + this.scheduleStartTime = scheduleStartTime; + return this; + } + + /** + * Get scheduleStartTime + * + * @return scheduleStartTime + */ + + public Long getScheduleStartTime() { + return scheduleStartTime; + } + + public void setScheduleStartTime(Long scheduleStartTime) { + this.scheduleStartTime = scheduleStartTime; + } + + public SaveScheduleRequest startWorkflowRequest(StartWorkflowRequest startWorkflowRequest) { + this.startWorkflowRequest = startWorkflowRequest; + return this; + } + + /** + * Get startWorkflowRequest + * + * @return startWorkflowRequest + */ + + public StartWorkflowRequest getStartWorkflowRequest() { + return startWorkflowRequest; + } + + public void setStartWorkflowRequest(StartWorkflowRequest startWorkflowRequest) { + this.startWorkflowRequest = startWorkflowRequest; + } + + public SaveScheduleRequest updatedBy(String updatedBy) { + this.updatedBy = updatedBy; + return this; + } + + public String getUpdatedBy() { + return updatedBy; + } + + public void setUpdatedBy(String updatedBy) { + this.updatedBy = updatedBy; + } + + public String getZoneId() { + return zoneId; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecution.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecution.java new file mode 100644 index 000000000..ca2fd1b76 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecution.java @@ -0,0 +1,23 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.List; + +import lombok.Data; + +@Data +public class SearchResultWorkflowScheduleExecution { + private List results; + private Long totalHits; +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecutionModel.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecutionModel.java new file mode 100644 index 000000000..795dbd7a2 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecutionModel.java @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.ArrayList; +import java.util.List; + + +public class SearchResultWorkflowScheduleExecutionModel { + + private List results = null; + + private Long totalHits = null; + + public SearchResultWorkflowScheduleExecutionModel results(List results) { + this.results = results; + return this; + } + + public SearchResultWorkflowScheduleExecutionModel addResultsItem(WorkflowScheduleExecutionModel resultsItem) { + if (this.results == null) { + this.results = new ArrayList<>(); + } + this.results.add(resultsItem); + return this; + } + + public List getResults() { + return results; + } + + public void setResults(List results) { + this.results = results; + } + + public SearchResultWorkflowScheduleExecutionModel totalHits(Long totalHits) { + this.totalHits = totalHits; + return this; + } + + public Long getTotalHits() { + return totalHits; + } + + public void setTotalHits(Long totalHits) { + this.totalHits = totalHits; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowSummary.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowSummary.java new file mode 100644 index 000000000..94c366e17 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowSummary.java @@ -0,0 +1,64 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.ArrayList; +import java.util.List; + +import com.netflix.conductor.common.run.WorkflowSummary; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class SearchResultWorkflowSummary { + + private List results = null; + + private Long totalHits = null; + + public SearchResultWorkflowSummary results(List results) { + this.results = results; + return this; + } + + public SearchResultWorkflowSummary addResultsItem(WorkflowSummary resultsItem) { + if (this.results == null) { + this.results = new ArrayList<>(); + } + this.results.add(resultsItem); + return this; + } + + public List getResults() { + return results; + } + + public void setResults(List results) { + this.results = results; + } + + public SearchResultWorkflowSummary totalHits(Long totalHits) { + this.totalHits = totalHits; + return this; + } + + public Long getTotalHits() { + return totalHits; + } + + public void setTotalHits(Long totalHits) { + this.totalHits = totalHits; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Subject.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Subject.java new file mode 100644 index 000000000..0b05dd85c --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Subject.java @@ -0,0 +1,82 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class Subject { + + private String id = null; + + /** Gets or Sets type */ + public enum TypeEnum { + USER("USER"), + ROLE("ROLE"), + GROUP("GROUP"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static TypeEnum fromValue(String input) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + } + + private TypeEnum type = null; + + public Subject id(String id) { + this.id = id; + return this; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Subject type(TypeEnum type) { + this.type = type; + return this; + } + + public TypeEnum getType() { + return type; + } + + public void setType(TypeEnum type) { + this.type = type; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SubjectRef.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SubjectRef.java new file mode 100644 index 000000000..3f3fe4208 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SubjectRef.java @@ -0,0 +1,101 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * User, group or role which is granted/removed access + */ +@EqualsAndHashCode +@ToString +public class SubjectRef { + + private String id = null; + + /** + * User, role or group + */ + public enum TypeEnum { + USER("USER"), + ROLE("ROLE"), + GROUP("GROUP"), + TAG("TAG"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static TypeEnum fromValue(String input) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + } + + + private TypeEnum type = null; + + public SubjectRef id(String id) { + this.id = id; + return this; + } + + /** + * Get id + * + * @return id + */ + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public SubjectRef type(TypeEnum type) { + this.type = type; + return this; + } + + /** + * User, role or group + * + * @return type + */ + + public TypeEnum getType() { + return type; + } + + public void setType(TypeEnum type) { + this.type = type; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagObject.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagObject.java new file mode 100644 index 000000000..7971b86e8 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagObject.java @@ -0,0 +1,100 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class TagObject { + + private String key = null; + + /** + * Gets or Sets type + */ + public enum TypeEnum { + METADATA("METADATA"), + RATE_LIMIT("RATE_LIMIT"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static TypeEnum fromValue(String input) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + } + + @Deprecated + // This is not required anymore. Type has been moved to WorkflowDef.RateLimitConfig as METADATA type + private TypeEnum type = null; + + private Object value = null; + + public TagObject key(String key) { + this.key = key; + return this; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public TagObject type(TypeEnum type) { + this.type = type; + return this; + } + + public TypeEnum getType() { + return type; + } + + public void setType(TypeEnum type) { + this.type = type; + } + + public TagObject value(Object value) { + this.value = value; + return this; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagString.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagString.java new file mode 100644 index 000000000..d82da42a3 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagString.java @@ -0,0 +1,96 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class TagString { + + private String key = null; + + public enum TypeEnum { + METADATA("METADATA"), + RATE_LIMIT("RATE_LIMIT"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static TypeEnum fromValue(String input) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + } + + private TypeEnum type = null; + + private String value = null; + + public TagString key(String key) { + this.key = key; + return this; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public TagString type(TypeEnum type) { + this.type = type; + return this; + } + + public TypeEnum getType() { + return type; + } + + public void setType(TypeEnum type) { + this.type = type; + } + + public TagString value(String value) { + this.value = value; + return this; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TargetRef.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TargetRef.java new file mode 100644 index 000000000..4a08983ad --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TargetRef.java @@ -0,0 +1,101 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * The object over which access is being granted or removed + */ +@EqualsAndHashCode +@ToString +public class TargetRef { + + private String id = null; + + public enum TypeEnum { + WORKFLOW_DEF("WORKFLOW_DEF"), + TASK_DEF("TASK_DEF"), + APPLICATION("APPLICATION"), + USER("USER"), + SECRET("SECRET_NAME"), + TAG("TAG"), + DOMAIN("DOMAIN"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static TypeEnum fromValue(String input) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + } + + + private TypeEnum type = null; + + public TargetRef id(String id) { + this.id = id; + return this; + } + + /** + * Get id + * + * @return id + **/ + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public TargetRef type(TypeEnum type) { + this.type = type; + return this; + } + + /** + * Get type + * + * @return type + **/ + + public TypeEnum getType() { + return type; + } + + public void setType(TypeEnum type) { + this.type = type; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TaskDetails.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TaskDetails.java new file mode 100644 index 000000000..fab49397d --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TaskDetails.java @@ -0,0 +1,93 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.HashMap; +import java.util.Map; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class TaskDetails { + + private Map output = null; + + private String taskId = null; + + private String taskRefName = null; + + private String workflowId = null; + + public TaskDetails output(Map output) { + this.output = output; + return this; + } + + public TaskDetails putOutputItem(String key, Object outputItem) { + if (this.output == null) { + this.output = new HashMap<>(); + } + this.output.put(key, outputItem); + return this; + } + + public Map getOutput() { + return output; + } + + public void setOutput(Map output) { + this.output = output; + } + + public TaskDetails taskId(String taskId) { + this.taskId = taskId; + return this; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public TaskDetails taskRefName(String taskRefName) { + this.taskRefName = taskRefName; + return this; + } + + public String getTaskRefName() { + return taskRefName; + } + + public void setTaskRefName(String taskRefName) { + this.taskRefName = taskRefName; + } + + public TaskDetails workflowId(String workflowId) { + this.workflowId = workflowId; + return this; + } + + public String getWorkflowId() { + return workflowId; + } + + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TerminateWorkflow.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TerminateWorkflow.java new file mode 100644 index 000000000..2c0464427 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TerminateWorkflow.java @@ -0,0 +1,52 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class TerminateWorkflow { + + private String terminationReason = null; + + private String workflowId = null; + + public TerminateWorkflow terminationReason(String terminationReason) { + this.terminationReason = terminationReason; + return this; + } + + public String getTerminationReason() { + return terminationReason; + } + + public void setTerminationReason(String terminationReason) { + this.terminationReason = terminationReason; + } + + public TerminateWorkflow workflowId(String workflowId) { + this.workflowId = workflowId; + return this; + } + + public String getWorkflowId() { + return workflowId; + } + + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TokenResponse.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TokenResponse.java new file mode 100644 index 000000000..cdaafefc2 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TokenResponse.java @@ -0,0 +1,24 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TokenResponse { + + private String token; + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertGroupRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertGroupRequest.java new file mode 100644 index 000000000..f4aa291ec --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertGroupRequest.java @@ -0,0 +1,177 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class UpsertGroupRequest { + /** + * a default Map<TargetType, Set<Access> to share permissions, allowed target types: + * WORKFLOW_DEF, TASK_DEF + */ + public enum InnerEnum { + CREATE("CREATE"), + READ("READ"), + UPDATE("UPDATE"), + DELETE("DELETE"), + EXECUTE("EXECUTE"); + + private final String value; + + InnerEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static InnerEnum fromValue(String input) { + for (InnerEnum b : InnerEnum.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + + } + + private Map> defaultAccess = null; + + private String description = null; + + /** Gets or Sets roles */ + public enum RolesEnum { + ADMIN("ADMIN"), + USER("USER"), + WORKER("WORKER"), + METADATA_MANAGER("METADATA_MANAGER"), + WORKFLOW_MANAGER("WORKFLOW_MANAGER"); + + private final String value; + + RolesEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static RolesEnum fromValue(String input) { + for (RolesEnum b : RolesEnum.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + + } + + + private List roles = null; + + public UpsertGroupRequest defaultAccess(Map> defaultAccess) { + this.defaultAccess = defaultAccess; + return this; + } + + public UpsertGroupRequest putDefaultAccessItem(String key, List defaultAccessItem) { + if (this.defaultAccess == null) { + this.defaultAccess = new HashMap<>(); + } + this.defaultAccess.put(key, defaultAccessItem); + return this; + } + + /** + * a default Map<TargetType, Set<Access> to share permissions, allowed target types: + * WORKFLOW_DEF, TASK_DEF + * + * @return defaultAccess + */ + public Map> getDefaultAccess() { + return defaultAccess; + } + + public void setDefaultAccess(Map> defaultAccess) { + this.defaultAccess = defaultAccess; + } + + public UpsertGroupRequest description(String description) { + this.description = description; + return this; + } + + /** + * A general description of the group + * + * @return description + */ + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public UpsertGroupRequest roles(List roles) { + this.roles = roles; + return this; + } + + public UpsertGroupRequest addRolesItem(RolesEnum rolesItem) { + if (this.roles == null) { + this.roles = new ArrayList<>(); + } + this.roles.add(rolesItem); + return this; + } + + /** + * Get roles + * + * @return roles + */ + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertUserRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertUserRequest.java new file mode 100644 index 000000000..53c8e6c1a --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertUserRequest.java @@ -0,0 +1,133 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.ArrayList; +import java.util.List; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + + +@EqualsAndHashCode +@ToString +public class UpsertUserRequest { + + private List groups = null; + + private String name = null; + + public enum RolesEnum { + ADMIN("ADMIN"), + USER("USER"), + WORKER("WORKER"), + METADATA_MANAGER("METADATA_MANAGER"), + WORKFLOW_MANAGER("WORKFLOW_MANAGER"); + + private final String value; + + RolesEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static RolesEnum fromValue(String input) { + for (RolesEnum b : RolesEnum.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + } + + private List roles = null; + + public UpsertUserRequest groups(List groups) { + this.groups = groups; + return this; + } + + public UpsertUserRequest addGroupsItem(String groupsItem) { + if (this.groups == null) { + this.groups = new ArrayList<>(); + } + this.groups.add(groupsItem); + return this; + } + + /** + * Ids of the groups this user belongs to + * + * @return groups + */ + public List getGroups() { + return groups; + } + + public void setGroups(List groups) { + this.groups = groups; + } + + public UpsertUserRequest name(String name) { + this.name = name; + return this; + } + + /** + * User's full name + * + * @return name + */ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public UpsertUserRequest roles(List roles) { + this.roles = roles; + return this; + } + + public UpsertUserRequest addRolesItem(RolesEnum rolesItem) { + if (this.roles == null) { + this.roles = new ArrayList<>(); + } + this.roles.add(rolesItem); + return this; + } + + /** + * Get roles + * + * @return roles + */ + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowRun.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowRun.java new file mode 100644 index 000000000..5bf424799 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowRun.java @@ -0,0 +1,49 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.List; +import java.util.Map; + +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.run.Workflow; + +import lombok.Data; + +@Data +public class WorkflowRun { + + private String workflowId; + + private Workflow.WorkflowStatus status; + + private String correlationId; + + private String requestId; + + private int priority; + + private Map input; + + private Map output; + + private Map variables; + + private List tasks; + + private String createdBy; + + private long createTime; + + private long updateTime; +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowSchedule.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowSchedule.java new file mode 100644 index 000000000..616e25169 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowSchedule.java @@ -0,0 +1,188 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class WorkflowSchedule { + + private Long createTime = null; + + private String createdBy = null; + + private String cronExpression = null; + + private String name = null; + + private Boolean paused = null; + + private Boolean runCatchupScheduleInstances = null; + + private Long scheduleEndTime = null; + + private Long scheduleStartTime = null; + + private StartWorkflowRequest startWorkflowRequest = null; + + private String updatedBy = null; + + private Long updatedTime = null; + + public WorkflowSchedule createTime(Long createTime) { + this.createTime = createTime; + return this; + } + + public Long getCreateTime() { + return createTime; + } + + public void setCreateTime(Long createTime) { + this.createTime = createTime; + } + + public WorkflowSchedule createdBy(String createdBy) { + this.createdBy = createdBy; + return this; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public WorkflowSchedule cronExpression(String cronExpression) { + this.cronExpression = cronExpression; + return this; + } + + public String getCronExpression() { + return cronExpression; + } + + public void setCronExpression(String cronExpression) { + this.cronExpression = cronExpression; + } + + public WorkflowSchedule name(String name) { + this.name = name; + return this; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public WorkflowSchedule paused(Boolean paused) { + this.paused = paused; + return this; + } + + public Boolean isPaused() { + return paused; + } + + public void setPaused(Boolean paused) { + this.paused = paused; + } + + public WorkflowSchedule runCatchupScheduleInstances(Boolean runCatchupScheduleInstances) { + this.runCatchupScheduleInstances = runCatchupScheduleInstances; + return this; + } + + public Boolean isRunCatchupScheduleInstances() { + return runCatchupScheduleInstances; + } + + public void setRunCatchupScheduleInstances(Boolean runCatchupScheduleInstances) { + this.runCatchupScheduleInstances = runCatchupScheduleInstances; + } + + public WorkflowSchedule scheduleEndTime(Long scheduleEndTime) { + this.scheduleEndTime = scheduleEndTime; + return this; + } + + public Long getScheduleEndTime() { + return scheduleEndTime; + } + + public void setScheduleEndTime(Long scheduleEndTime) { + this.scheduleEndTime = scheduleEndTime; + } + + public WorkflowSchedule scheduleStartTime(Long scheduleStartTime) { + this.scheduleStartTime = scheduleStartTime; + return this; + } + + public Long getScheduleStartTime() { + return scheduleStartTime; + } + + public void setScheduleStartTime(Long scheduleStartTime) { + this.scheduleStartTime = scheduleStartTime; + } + + public WorkflowSchedule startWorkflowRequest(StartWorkflowRequest startWorkflowRequest) { + this.startWorkflowRequest = startWorkflowRequest; + return this; + } + + public StartWorkflowRequest getStartWorkflowRequest() { + return startWorkflowRequest; + } + + public void setStartWorkflowRequest(StartWorkflowRequest startWorkflowRequest) { + this.startWorkflowRequest = startWorkflowRequest; + } + + public WorkflowSchedule updatedBy(String updatedBy) { + this.updatedBy = updatedBy; + return this; + } + + public String getUpdatedBy() { + return updatedBy; + } + + public void setUpdatedBy(String updatedBy) { + this.updatedBy = updatedBy; + } + + public WorkflowSchedule updatedTime(Long updatedTime) { + this.updatedTime = updatedTime; + return this; + } + + public Long getUpdatedTime() { + return updatedTime; + } + + public void setUpdatedTime(Long updatedTime) { + this.updatedTime = updatedTime; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowScheduleExecutionModel.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowScheduleExecutionModel.java new file mode 100644 index 000000000..582289d0b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowScheduleExecutionModel.java @@ -0,0 +1,204 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode +@ToString +public class WorkflowScheduleExecutionModel { + + private String executionId = null; + + private Long executionTime = null; + + private String reason = null; + + private String scheduleName = null; + + private Long scheduledTime = null; + + private String stackTrace = null; + + private StartWorkflowRequest startWorkflowRequest = null; + + public enum StateEnum { + POLLED("POLLED"), + FAILED("FAILED"), + EXECUTED("EXECUTED"); + + private final String value; + + StateEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static StateEnum fromValue(String input) { + for (StateEnum b : StateEnum.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + } + + private StateEnum state = null; + + private String workflowId = null; + + private String workflowName = null; + + public WorkflowScheduleExecutionModel executionId(String executionId) { + this.executionId = executionId; + return this; + } + + public String getExecutionId() { + return executionId; + } + + public void setExecutionId(String executionId) { + this.executionId = executionId; + } + + public WorkflowScheduleExecutionModel executionTime(Long executionTime) { + this.executionTime = executionTime; + return this; + } + + public Long getExecutionTime() { + return executionTime; + } + + public void setExecutionTime(Long executionTime) { + this.executionTime = executionTime; + } + + public WorkflowScheduleExecutionModel reason(String reason) { + this.reason = reason; + return this; + } + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } + + public WorkflowScheduleExecutionModel scheduleName(String scheduleName) { + this.scheduleName = scheduleName; + return this; + } + + public String getScheduleName() { + return scheduleName; + } + + public void setScheduleName(String scheduleName) { + this.scheduleName = scheduleName; + } + + public WorkflowScheduleExecutionModel scheduledTime(Long scheduledTime) { + this.scheduledTime = scheduledTime; + return this; + } + + public Long getScheduledTime() { + return scheduledTime; + } + + public void setScheduledTime(Long scheduledTime) { + this.scheduledTime = scheduledTime; + } + + public WorkflowScheduleExecutionModel stackTrace(String stackTrace) { + this.stackTrace = stackTrace; + return this; + } + + public String getStackTrace() { + return stackTrace; + } + + public void setStackTrace(String stackTrace) { + this.stackTrace = stackTrace; + } + + public WorkflowScheduleExecutionModel startWorkflowRequest( + StartWorkflowRequest startWorkflowRequest) { + this.startWorkflowRequest = startWorkflowRequest; + return this; + } + + public StartWorkflowRequest getStartWorkflowRequest() { + return startWorkflowRequest; + } + + public void setStartWorkflowRequest(StartWorkflowRequest startWorkflowRequest) { + this.startWorkflowRequest = startWorkflowRequest; + } + + public WorkflowScheduleExecutionModel state(StateEnum state) { + this.state = state; + return this; + } + + public StateEnum getState() { + return state; + } + + public void setState(StateEnum state) { + this.state = state; + } + + public WorkflowScheduleExecutionModel workflowId(String workflowId) { + this.workflowId = workflowId; + return this; + } + + public String getWorkflowId() { + return workflowId; + } + + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + + public WorkflowScheduleExecutionModel workflowName(String workflowName) { + this.workflowName = workflowName; + return this; + } + + public String getWorkflowName() { + return workflowName; + } + + public void setWorkflowName(String workflowName) { + this.workflowName = workflowName; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStateUpdate.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStateUpdate.java new file mode 100644 index 000000000..f374a35f5 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStateUpdate.java @@ -0,0 +1,26 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.Map; + +import com.netflix.conductor.common.metadata.tasks.TaskResult; + +import lombok.Data; + +@Data +public class WorkflowStateUpdate { + private String taskReferenceName; + private Map variables; + private TaskResult taskResult; +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStatus.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStatus.java new file mode 100644 index 000000000..3f89866b6 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStatus.java @@ -0,0 +1,156 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model; + +import java.util.HashMap; +import java.util.Map; + +import lombok.EqualsAndHashCode; +import lombok.ToString; + + +@EqualsAndHashCode +@ToString +public class WorkflowStatus { + + private String correlationId = null; + + private Map output = null; + + public enum StatusEnum { + RUNNING("RUNNING"), + COMPLETED("COMPLETED"), + FAILED("FAILED"), + TIMED_OUT("TIMED_OUT"), + TERMINATED("TERMINATED"), + PAUSED("PAUSED"); + + private final String value; + + StatusEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static StatusEnum fromValue(String input) { + for (StatusEnum b : StatusEnum.values()) { + if (b.value.equals(input)) { + return b; + } + } + return null; + } + } + + private StatusEnum status = null; + + private Map variables = null; + + private String workflowId = null; + + public WorkflowStatus correlationId(String correlationId) { + this.correlationId = correlationId; + return this; + } + + /** + * Get correlationId + * + * @return correlationId + */ + + public String getCorrelationId() { + return correlationId; + } + + public void setCorrelationId(String correlationId) { + this.correlationId = correlationId; + } + + public WorkflowStatus output(Map output) { + this.output = output; + return this; + } + + public WorkflowStatus putOutputItem(String key, Object outputItem) { + if (this.output == null) { + this.output = new HashMap<>(); + } + this.output.put(key, outputItem); + return this; + } + + public Map getOutput() { + return output; + } + + public void setOutput(Map output) { + this.output = output; + } + + public WorkflowStatus status(StatusEnum status) { + this.status = status; + return this; + } + + public StatusEnum getStatus() { + return status; + } + + public void setStatus(StatusEnum status) { + this.status = status; + } + + public WorkflowStatus variables(Map variables) { + this.variables = variables; + return this; + } + + public WorkflowStatus putVariablesItem(String key, Object variablesItem) { + if (this.variables == null) { + this.variables = new HashMap<>(); + } + this.variables.put(key, variablesItem); + return this; + } + + public Map getVariables() { + return variables; + } + + public void setVariables(Map variables) { + this.variables = variables; + } + + public WorkflowStatus workflowId(String workflowId) { + this.workflowId = workflowId; + return this; + } + + public String getWorkflowId() { + return workflowId; + } + + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueConfiguration.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueConfiguration.java new file mode 100644 index 000000000..1b4e9ea1c --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueConfiguration.java @@ -0,0 +1,81 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.event; + +import java.util.Map; + +import com.netflix.conductor.common.config.ObjectMapperProvider; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public abstract class QueueConfiguration { + + private final String queueName; + private final String queueType; + + private final ObjectMapper objectMapper = new ObjectMapperProvider().getObjectMapper(); + + private QueueWorkerConfiguration consumer; + private QueueWorkerConfiguration producer; + + public QueueConfiguration(String queueName, String queueType) { + this.queueName = queueName; + this.queueType = queueType; + } + + public QueueConfiguration withConsumer(QueueWorkerConfiguration consumer) { + this.consumer = consumer; + return this; + } + + public QueueConfiguration withProducer(QueueWorkerConfiguration producer) { + this.producer = producer; + return this; + } + + public String getQueueType() { + return this.queueType; + } + + public String getQueueName() { + return this.queueName; + } + + //FIXME why? explain me why? + @Deprecated + public String getConfiguration() throws Exception { + if (this.consumer == null) { + throw new RuntimeException("consumer must be set"); + } + if (this.producer == null) { + throw new RuntimeException("producer must be set"); + } + Map config = + Map.of( + "consumer", this.consumer.getConfiguration(), + "producer", this.producer.getConfiguration()); + return objectMapper.writeValueAsString(config); + } + + public Map getQueueConfiguration() throws Exception { + if (this.consumer == null) { + throw new RuntimeException("consumer must be set"); + } + if (this.producer == null) { + throw new RuntimeException("producer must be set"); + } + return Map.of( + "consumer", this.consumer.getConfiguration(), + "producer", this.producer.getConfiguration()); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueWorkerConfiguration.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueWorkerConfiguration.java new file mode 100644 index 000000000..277ec42ed --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueWorkerConfiguration.java @@ -0,0 +1,39 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.event; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public abstract class QueueWorkerConfiguration { + private final Set allowedConfigurationKeys; + private final Map config; + + public QueueWorkerConfiguration(Set allowedConfigurationKeys) { + this.allowedConfigurationKeys = allowedConfigurationKeys; + config = new HashMap<>(); + } + + public QueueWorkerConfiguration withConfiguration(String key, String value) throws Exception { + if (!allowedConfigurationKeys.contains(key)) { + throw new RuntimeException("key not valid for consumer"); + } + this.config.put(key, value); + return this; + } + + public Map getConfiguration() { + return this.config; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Category.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Category.java new file mode 100644 index 000000000..c970e1403 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Category.java @@ -0,0 +1,27 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration; + +public enum Category { + + API, + + AI_MODEL, + + VECTOR_DB, + + RELATIONAL_DB, + + MESSAGE_BROKER + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Integration.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Integration.java new file mode 100644 index 000000000..f8f33bb6e --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Integration.java @@ -0,0 +1,38 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration; + +import java.util.List; +import java.util.Map; + +import io.orkes.conductor.client.model.TagObject; + +import lombok.Data; + +@Data +public class Integration { + + private Category category; + private Map configuration; + private String createdBy; + private Long createdOn; + private String description; + private Boolean enabled; + private long modelsCount; + private String name; + private List tags; + private String type; + private String updatedBy; + private Long updatedOn; + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApi.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApi.java new file mode 100644 index 000000000..7f50d1df6 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApi.java @@ -0,0 +1,36 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration; + +import java.util.List; +import java.util.Map; + +import io.orkes.conductor.client.model.TagObject; + +import lombok.Data; + +@Data +public class IntegrationApi { + + private String api; + private Map configuration; + private String createdBy; + private Long createdOn; + private String description; + private Boolean enabled; + private String integrationName; + private List tags; + private String updatedBy; + private Long updatedOn; + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApiUpdate.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApiUpdate.java new file mode 100644 index 000000000..375c358c5 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApiUpdate.java @@ -0,0 +1,26 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration; + +import java.util.Map; + +import lombok.Data; + +@Data +public class IntegrationApiUpdate { + + private Map configuration; + private String description; + private Boolean enabled; + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationDef.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationDef.java new file mode 100644 index 000000000..75f61dbfa --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationDef.java @@ -0,0 +1,30 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration; + +import java.util.List; + +import lombok.Data; + +@Data +public class IntegrationDef { + + private Category category; + private String categoryLabel; + private String description; + private Boolean enabled; + private String iconName; + private String name; + private List tags; + private String type; +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationUpdate.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationUpdate.java new file mode 100644 index 000000000..1c65850ea --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationUpdate.java @@ -0,0 +1,28 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration; + +import java.util.Map; + +import lombok.Data; + +@Data +public class IntegrationUpdate { + + private Category category; + private Map configuration; + private String description; + private Boolean enabled; + private String type; + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/PromptTemplateTestRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/PromptTemplateTestRequest.java new file mode 100644 index 000000000..3acf1c56b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/PromptTemplateTestRequest.java @@ -0,0 +1,31 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration; + +import java.util.List; +import java.util.Map; + +import lombok.Data; + +@Data +public class PromptTemplateTestRequest { + + private String llmProvider; + private String model; + private String prompt; + private Map promptVariables; + private List stopWords; + private Double temperature; + private Double topP; + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatCompletion.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatCompletion.java new file mode 100644 index 000000000..74896dba0 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatCompletion.java @@ -0,0 +1,34 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration.ai; + +import java.util.List; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class ChatCompletion extends LLMWorkerInput { + private List messages; + + //Starting template + //e.g. start by saying: + // you are a helpful assistant, who does not deviate from the goals" + //You do not respond to questions that are not related to the topic + //Any answer you give - should be related to the following topic: weather + // + // + private String instructions; + private boolean jsonOutput; +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatMessage.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatMessage.java new file mode 100644 index 000000000..3e5fc2855 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatMessage.java @@ -0,0 +1,30 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration.ai; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ChatMessage { + + public enum Actor { + user, assistant, system, human, chatbot + } + + String role; + String message; +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/EmbeddingRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/EmbeddingRequest.java new file mode 100644 index 000000000..bb717b7b2 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/EmbeddingRequest.java @@ -0,0 +1,24 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration.ai; + +import lombok.Data; + +@Data +public class EmbeddingRequest { + private String llmProvider; + private String model; + private String text; + private Integer dimensions; + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexDocInput.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexDocInput.java new file mode 100644 index 000000000..a31ad317f --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexDocInput.java @@ -0,0 +1,55 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration.ai; + +import java.util.Map; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class IndexDocInput { + + private String llmProvider; + private String model; + private String embeddingModelProvider; + private String embeddingModel; + private String vectorDB; + private String text; + private String docId; + private String url; + private String mediaType; + private String namespace; + private String index; + private int chunkSize; + private int chunkOverlap; + private Map metadata; + private Integer dimensions; + public String getNamespace() { + if(namespace == null) { + return docId; + } + return namespace; + } + + public int getChunkSize() { + return chunkSize > 0 ? chunkSize : 12000; + } + + public int getChunkOverlap() { + return chunkOverlap > 0 ? chunkOverlap : 400; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexedDoc.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexedDoc.java new file mode 100644 index 000000000..6ac627544 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexedDoc.java @@ -0,0 +1,38 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration.ai; + +import java.util.HashMap; +import java.util.Map; + +import lombok.Data; + + +@Data +public class IndexedDoc { + private String docId; + private String parentDocId; + private String text; + private double score; + private Map metadata = new HashMap<>(); + + public IndexedDoc(String docId, String parentDocId, String text, double score) { + this.docId = docId; + this.parentDocId = parentDocId; + this.text = text; + this.score = score; + } + + public IndexedDoc() { + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMResponse.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMResponse.java new file mode 100644 index 000000000..334557613 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMResponse.java @@ -0,0 +1,26 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration.ai; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class LLMResponse { + private Object result; + private String finishReason; + private int tokenUsed; + + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMWorkerInput.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMWorkerInput.java new file mode 100644 index 000000000..c4746fbdf --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMWorkerInput.java @@ -0,0 +1,33 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration.ai; + +import java.util.List; + +import lombok.Data; + +@Data +public class LLMWorkerInput { + + private String llmProvider; + private String model; + private String embeddingModel; + private String embeddingModelProvider; + private String prompt; + private double temperature = 0.1; + private double topP = 0.9; + private List stopWords; + private int maxTokens; + private int maxResults = 1; + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplate.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplate.java new file mode 100644 index 000000000..6d0bd88dd --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplate.java @@ -0,0 +1,34 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration.ai; + +import java.util.List; + +import io.orkes.conductor.client.model.TagObject; + +import lombok.Data; + +@Data +public class PromptTemplate { + + private String createdBy; + private Long createdOn; + private String description; + private List integrations; + private String name; + private List tags; + private String template; + private String updatedBy; + private Long updatedOn; + private List variables; +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplateTestRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplateTestRequest.java new file mode 100644 index 000000000..69a56590f --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplateTestRequest.java @@ -0,0 +1,32 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration.ai; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import lombok.Data; + +@Data +public class PromptTemplateTestRequest { + + private String llmProvider; + private String model; + private String prompt; + private Map promptVariables = new HashMap<>(); + private double temperature = 0.1; + private double topP = 0.9; + private List stopWords; + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/StoreEmbeddingsInput.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/StoreEmbeddingsInput.java new file mode 100644 index 000000000..feaadc9f0 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/StoreEmbeddingsInput.java @@ -0,0 +1,31 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration.ai; + +import java.util.List; +import java.util.Map; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class StoreEmbeddingsInput extends LLMWorkerInput { + + private String vectorDB; + private String index; + private String namespace; + private List embeddings; + private String id; + private Map metadata; +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/TextCompletion.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/TextCompletion.java new file mode 100644 index 000000000..46ee461f1 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/TextCompletion.java @@ -0,0 +1,24 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration.ai; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +@ToString +@EqualsAndHashCode(callSuper = true) +public class TextCompletion extends LLMWorkerInput { + +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/VectorDBInput.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/VectorDBInput.java new file mode 100644 index 000000000..847695a30 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/VectorDBInput.java @@ -0,0 +1,32 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.model.integration.ai; + +import java.util.List; +import java.util.Map; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class VectorDBInput extends LLMWorkerInput { + + private String vectorDB; + private String index; + private String namespace; + private List embeddings; + private String query; + private Map metadata; + private Integer dimensions; +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/WorkerFn.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/WorkerFn.java new file mode 100644 index 000000000..19c3d3c7a --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/WorkerFn.java @@ -0,0 +1,21 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.worker; + +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; + +@FunctionalInterface +public interface WorkerFn { + TaskResult execute(Task task); +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/Workers.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/Workers.java new file mode 100644 index 000000000..c920be762 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/Workers.java @@ -0,0 +1,126 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.worker; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.client.automator.TaskRunnerConfigurer; +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; + +import io.orkes.conductor.client.http.OrkesAuthentication; + +public class Workers { + + private static final Logger LOGGER = LoggerFactory.getLogger(Workers.class); + + private final List workers = new ArrayList<>(); + private String rootUri; + private boolean started = false; + private String keyId; + private String secret; + private ConductorClient client; + + public Workers register(String name, WorkerFn workerFn) { + workers.add( + new Worker() { + @Override + public String getTaskDefName() { + return name; + } + + @Override + public TaskResult execute(Task task) { + return workerFn.execute(task); + } + + @Override + public int getPollingInterval() { + return 100; + } + }); + return this; + } + + public Workers rootUri(String rootUri) { + this.rootUri = rootUri; + return this; + } + + public Workers keyId(String keyId) { + this.keyId = keyId; + return this; + } + + public Workers secret(String secret) { + this.secret = secret; + return this; + } + + public Workers apiClient(ConductorClient apiClient) { + this.client = apiClient; + return this; + } + + public Workers startAll() { + if (rootUri == null) { + throw new IllegalStateException("RootUri is null"); + } + + if (!started) { + LOGGER.info("Conductor Server URL: {}", rootUri); + LOGGER.info("Starting workers : {}", workers); + + if (this.client != null) { + this.client = new ConductorClient.Builder() + .basePath(rootUri) + .addHeaderSupplier(new OrkesAuthentication(keyId, secret)) + .build(); + } + + TaskClient taskClient = new TaskClient(client); + TaskRunnerConfigurer runnerConfigurer = new TaskRunnerConfigurer.Builder(taskClient, workers) + .withThreadCount(Math.max(1, workers.size())) + .build(); + runnerConfigurer.init(); + started = true; + } else { + LOGGER.warn("Workers have already been started"); + } + + return this; + } + + public void start(String name, WorkerFn workerFn) { + workers.add( + new Worker() { + @Override + public String getTaskDefName() { + return name; + } + + @Override + public TaskResult execute(Task task) { + return workerFn.execute(task); + } + }); + startAll(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/orkes-spring/build.gradle b/conductor-clients/java/conductor-java-sdk/orkes-spring/build.gradle new file mode 100644 index 000000000..b4025341f --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-spring/build.gradle @@ -0,0 +1,78 @@ +plugins { + id 'java-library' + id 'idea' + id 'maven-publish' + id 'signing' +} + +compileJava { + sourceCompatibility = 17 + targetCompatibility = 17 +} + +repositories { + mavenCentral() +} + +dependencies { + api project(":conductor-client") + api project(":orkes-client") + api project(":sdk") + api project(":conductor-client-spring") + + implementation 'org.springframework.boot:spring-boot-starter:3.3.0' +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + pom { + name = 'Orkes Conductor Client/SDK Spring' + description = 'Spring autoconfig for Orkes Conductor Client and SDK' + url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + scm { + connection = 'scm:git:git://github.com/conductor-oss/conductor-java-sdk.git' + developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor-java-sdk.git' + url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + } + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + organization = 'Orkes' + organizationUrl = 'https://orkes.io' + name = 'Orkes Development Team' + email = 'developers@orkes.io' + } + } + } + } + } + + repositories { + maven { + if (project.hasProperty("mavenCentral")) { + println "Publishing to Sonatype Repository" + url = "https://s01.oss.sonatype.org/${project.version.endsWith('-SNAPSHOT') ? "content/repositories/snapshots/" : "service/local/staging/deploy/maven2/"}" + credentials { + username project.properties.username + password project.properties.password + } + } else { + url = "s3://orkes-artifacts-repo/${project.version.endsWith('-SNAPSHOT') ? "snapshots" : "releases"}" + authentication { + awsIm(AwsImAuthentication) + } + } + } + } +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/orkes-spring/src/main/java/io/orkes/conductor/client/spring/OrkesConductorClientAutoConfiguration.java b/conductor-clients/java/conductor-java-sdk/orkes-spring/src/main/java/io/orkes/conductor/client/spring/OrkesConductorClientAutoConfiguration.java new file mode 100644 index 000000000..2149c93cc --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/orkes-spring/src/main/java/io/orkes/conductor/client/spring/OrkesConductorClientAutoConfiguration.java @@ -0,0 +1,106 @@ +/* + * Copyright 2020 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.spring; + + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import io.orkes.conductor.client.ApiClient; +import io.orkes.conductor.client.AuthorizationClient; +import io.orkes.conductor.client.OrkesClients; +import io.orkes.conductor.client.SchedulerClient; +import io.orkes.conductor.client.SecretClient; +import io.orkes.conductor.client.http.OrkesEventClient; +import io.orkes.conductor.client.http.OrkesMetadataClient; +import io.orkes.conductor.client.http.OrkesTaskClient; +import io.orkes.conductor.client.http.OrkesWorkflowClient; + +import lombok.extern.slf4j.Slf4j; + +@Configuration(proxyBeanMethods = false) +@Slf4j +public class OrkesConductorClientAutoConfiguration { + + public static final String CONDUCTOR_SERVER_URL ="conductor.server.url"; + public static final String CONDUCTOR_SECURITY_CLIENT_KEY_ID ="conductor.security.client.key-id"; + public static final String CONDUCTOR_SECURITY_CLIENT_SECRET ="conductor.security.client.secret"; + //TODO add more properties e.g.: ssl off, timeout settings, etc. and these should be client properties!!! + public static final String CONDUCTOR_CLIENT_BASE_PATH = "conductor.client.basepath"; + public static final String CONDUCTOR_CLIENT_KEY_ID = "conductor.client.key-id"; + public static final String CONDUCTOR_CLIENT_SECRET = "conductor.client.secret"; + + @Bean + @ConditionalOnMissingBean + public ApiClient orkesConductorClient(Environment env) { + String basePath = env.getProperty(CONDUCTOR_CLIENT_BASE_PATH); + if (basePath == null) { + basePath = env.getProperty(CONDUCTOR_SERVER_URL); + } + + String keyId = env.getProperty(CONDUCTOR_CLIENT_KEY_ID); + if (keyId == null) { + keyId = env.getProperty(CONDUCTOR_SECURITY_CLIENT_KEY_ID); + } + + String secret = env.getProperty(CONDUCTOR_CLIENT_SECRET); + if (secret == null) { + secret = env.getProperty(CONDUCTOR_SECURITY_CLIENT_SECRET); + } + + return new ApiClient(basePath, keyId, secret); + } + + @Bean + public OrkesClients orkesClients(ApiClient client) { + return new OrkesClients(client); + } + + @Bean + public OrkesTaskClient orkesTaskClient(OrkesClients clients) { + return clients.getTaskClient(); + } + + @Bean + public OrkesMetadataClient orkesMetadataClient(OrkesClients clients) { + return clients.getMetadataClient(); + } + + @Bean + public OrkesWorkflowClient orkesWorkflowClient(OrkesClients clients) { + return clients.getWorkflowClient(); + } + + @Bean + public AuthorizationClient orkesAuthorizationClient(OrkesClients clients) { + return clients.getAuthorizationClient(); + } + + @Bean + public OrkesEventClient orkesEventClient(OrkesClients clients) { + return clients.getEventClient(); + } + + @Bean + public SchedulerClient orkesSchedulerClient(OrkesClients clients) { + return clients.getSchedulerClient(); + } + + @Bean + public SecretClient orkesSecretClient(OrkesClients clients) { + return clients.getSecretClient(); + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/README.md b/conductor-clients/java/conductor-java-sdk/sdk/README.md new file mode 100644 index 000000000..111470ec5 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/README.md @@ -0,0 +1,11 @@ +# SDK for Conductor +Conductor SDK allows developers to create, test and execute workflows using code. + +There are three main features of the SDK: + +1. [Create and run workflows using code](workflow_sdk.md) +2. [Create and run strongly typed workers](worker_sdk.md) +3. [Unit Testing framework for workflows and workers](testing_framework.md) + + + diff --git a/conductor-clients/java/conductor-java-sdk/sdk/build.gradle b/conductor-clients/java/conductor-java-sdk/sdk/build.gradle new file mode 100644 index 000000000..9ce4cdf3e --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/build.gradle @@ -0,0 +1,88 @@ +plugins { + id 'java-library' + id 'idea' + id 'maven-publish' + id 'signing' +} + +dependencies { + implementation project(":conductor-client") + testImplementation 'org.mockito:mockito-core:5.4.0' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' +} + +java { + withSourcesJar() + withJavadocJar() +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + pom { + name = 'Orkes Conductor SDK' + description = 'OSS & Orkes Conductor SDK' + url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + scm { + connection = 'scm:git:git://github.com/conductor-oss/conductor-java-sdk.git' + developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor-java-sdk.git' + url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + } + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + organization = 'Orkes' + organizationUrl = 'https://orkes.io' + name = 'Orkes Development Team' + email = 'developers@orkes.io' + } + } + } + } + } + + repositories { + maven { + if (project.hasProperty("mavenCentral")) { + println "Publishing to Sonatype Repository" + url = "https://s01.oss.sonatype.org/${project.version.endsWith('-SNAPSHOT') ? "content/repositories/snapshots/" : "service/local/staging/deploy/maven2/"}" + credentials { + username project.properties.username + password project.properties.password + } + } else { + url = "s3://orkes-artifacts-repo/${project.version.endsWith('-SNAPSHOT') ? "snapshots" : "releases"}" + authentication { + awsIm(AwsImAuthentication) + } + } + } + } +} + +signing { + def signingKeyId = findProperty('signingKeyId') + if (signingKeyId) { + println 'Signing the artifact with keys' + signing { + def signingKey = findProperty('signingKey') + def signingPassword = findProperty('signingPassword') + if (signingKeyId && signingKey && signingPassword) { + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + } + + sign publishing.publications + } + } +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java new file mode 100644 index 000000000..b1fa171f0 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java @@ -0,0 +1,66 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.healthcheck; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class HealthCheckClient { + + private final String healthCheckURL; + + private final ObjectMapper objectMapper; + + public HealthCheckClient(String healthCheckURL) { + this.healthCheckURL = healthCheckURL; + this.objectMapper = + new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + public boolean isServerRunning() { + try { + + BufferedReader in = + new BufferedReader(new InputStreamReader(new URL(healthCheckURL).openStream())); + StringBuilder response = new StringBuilder(); + String inputLine; + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + HealthCheckResults healthCheckResults = + objectMapper.readValue(response.toString(), HealthCheckResults.class); + return healthCheckResults.healthy; + } catch (Throwable t) { + return false; + } + } + + private static final class HealthCheckResults { + + private boolean healthy; + + public boolean isHealthy() { + return healthy; + } + + public void setHealthy(boolean healthy) { + this.healthy = healthy; + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java new file mode 100644 index 000000000..941494e1c --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java @@ -0,0 +1,206 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.testing; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.sdk.healthcheck.HealthCheckClient; + +import com.google.common.util.concurrent.Uninterruptibles; + +public class LocalServerRunner { + + private static final Logger LOGGER = LoggerFactory.getLogger(LocalServerRunner.class); + + private final HealthCheckClient healthCheck; + + private Process serverProcess; + + private final ScheduledExecutorService healthCheckExecutor = + Executors.newSingleThreadScheduledExecutor(); + + private final CountDownLatch serverProcessLatch = new CountDownLatch(1); + + private final int port; + + private final String conductorVersion; + + private final String serverURL; + + private static final Map serverInstances = new HashMap<>(); + + public LocalServerRunner(int port, String conductorVersion) { + this.port = port; + this.conductorVersion = conductorVersion; + this.serverURL = "http://localhost:" + port + "/"; + healthCheck = new HealthCheckClient(serverURL + "health"); + } + + public String getServerAPIUrl() { + return this.serverURL + "api/"; + } + + /** + * Starts the local server. Downloads the latest conductor build from the maven repo If you want + * to start the server from a specific download location, set `repositoryURL` system property + * with the link to the actual downloadable server boot jar file. + * + *

System Properties that can be set conductorVersion: when specified, uses this + * version of conductor to run tests (and downloads from maven repo) repositoryURL: full url + * where the server boot jar can be downloaded from. This can be a public repo or internal + * repository, allowing full control over the location and version of the conductor server + */ + public void startLocalServer() { + synchronized (serverInstances) { + if (serverInstances.get(port) != null) { + throw new IllegalStateException( + "Another server has already been started at port " + port); + } + serverInstances.put(port, this); + } + + try { + String downloadURL = + "https://repo1.maven.org/maven2/com/netflix/conductor/conductor-server/" + + conductorVersion + + "/conductor-server-" + + conductorVersion + + "-boot.jar"; + + String repositoryURL = + Optional.ofNullable(System.getProperty("repositoryURL")).orElse(downloadURL); + + LOGGER.info( + "Running conductor with version {} from repo url {}", + conductorVersion, + repositoryURL); + + Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown)); + installAndStartServer(repositoryURL, port); + healthCheckExecutor.scheduleAtFixedRate( + () -> { + try { + if (serverProcessLatch.getCount() > 0) { + boolean isRunning = healthCheck.isServerRunning(); + if (isRunning) { + serverProcessLatch.countDown(); + } + } + } catch (Exception e) { + LOGGER.warn( + "Caught an exception while polling for server running status {}", + e.getMessage()); + } + }, + 100, + 100, + TimeUnit.MILLISECONDS); + Uninterruptibles.awaitUninterruptibly(serverProcessLatch, 1, TimeUnit.MINUTES); + + if (serverProcessLatch.getCount() > 0) { + throw new RuntimeException("Server not healthy"); + } + healthCheckExecutor.shutdownNow(); + + } catch (IOException e) { + throw new Error(e); + } + } + + public void shutdown() { + if (serverProcess != null) { + serverProcess.destroyForcibly(); + serverInstances.remove(port); + } + } + + private synchronized void installAndStartServer(String repositoryURL, int localServerPort) + throws IOException { + if (serverProcess != null) { + return; + } + + String configFile = + LocalServerRunner.class.getResource("/test-server.properties").getFile(); + String tempDir = System.getProperty("java.io.tmpdir"); + Path serverFile = Paths.get(tempDir, "conductor-server.jar"); + if (!Files.exists(serverFile)) { + Files.copy(new URL(repositoryURL).openStream(), serverFile); + } + + String command = + "java -Dserver.port=" + + localServerPort + + " -DCONDUCTOR_CONFIG_FILE=" + + configFile + + " -jar " + + serverFile; + LOGGER.info("Running command {}", command); + + serverProcess = Runtime.getRuntime().exec(command); + BufferedReader error = + new BufferedReader(new InputStreamReader(serverProcess.getErrorStream())); + BufferedReader op = + new BufferedReader(new InputStreamReader(serverProcess.getInputStream())); + + // This captures the stream and copies to a visible log for tracking errors asynchronously + // using a separate thread + Executors.newSingleThreadScheduledExecutor() + .execute( + () -> { + String line = null; + while (true) { + try { + if ((line = error.readLine()) == null) break; + } catch (IOException e) { + LOGGER.error("Exception reading input stream:", e); + } + // copy to standard error + LOGGER.error("Server error stream - {}", line); + } + }); + + // This captures the stream and copies to a visible log for tracking errors asynchronously + // using a separate thread + Executors.newSingleThreadScheduledExecutor() + .execute( + () -> { + String line = null; + while (true) { + try { + if ((line = op.readLine()) == null) break; + } catch (IOException e) { + LOGGER.error("Exception reading input stream:", e); + } + // copy to standard out + LOGGER.trace("Server input stream - {}", line); + } + }); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java new file mode 100644 index 000000000..28bd41ad3 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java @@ -0,0 +1,65 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.testing; + + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; +import com.netflix.conductor.sdk.workflow.executor.task.AnnotatedWorkerExecutor; + + +public class WorkflowTestRunner { + + private LocalServerRunner localServerRunner; + + private final AnnotatedWorkerExecutor annotatedWorkerExecutor; + + private final WorkflowExecutor workflowExecutor; + + public WorkflowTestRunner(String serverApiUrl) { + TaskClient taskClient = new TaskClient(new ConductorClient.Builder() + .basePath(serverApiUrl) + .build()); + this.annotatedWorkerExecutor = new AnnotatedWorkerExecutor(taskClient); + this.workflowExecutor = new WorkflowExecutor(serverApiUrl); + } + + public WorkflowTestRunner(int port, String conductorVersion) { + localServerRunner = new LocalServerRunner(port, conductorVersion); + + String serverAPIUrl = localServerRunner.getServerAPIUrl(); + TaskClient taskClient = new TaskClient(new ConductorClient.Builder() + .basePath(serverAPIUrl) + .build()); + this.annotatedWorkerExecutor = new AnnotatedWorkerExecutor(taskClient); + this.workflowExecutor = new WorkflowExecutor(serverAPIUrl); + } + + public WorkflowExecutor getWorkflowExecutor() { + return workflowExecutor; + } + + public void init(String basePackages) { + if (localServerRunner != null) { + localServerRunner.startLocalServer(); + } + annotatedWorkerExecutor.initWorkers(basePackages); + } + + public void shutdown() { + localServerRunner.shutdown(); + annotatedWorkerExecutor.shutdown(); + workflowExecutor.shutdown(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java new file mode 100644 index 000000000..81295a469 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java @@ -0,0 +1,385 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + +import com.netflix.conductor.client.exception.ConductorClientException; +import com.netflix.conductor.common.config.ObjectMapperProvider; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.sdk.workflow.def.tasks.Task; +import com.netflix.conductor.sdk.workflow.def.tasks.TaskRegistry; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; +import com.netflix.conductor.sdk.workflow.utils.InputOutputGetter; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * @param Type of the workflow input + */ +public class ConductorWorkflow { + + public static final InputOutputGetter input = + new InputOutputGetter("workflow", InputOutputGetter.Field.input); + + public static final InputOutputGetter output = + new InputOutputGetter("workflow", InputOutputGetter.Field.output); + + private String name; + + private String description; + + private int version; + + private String failureWorkflow; + + private String ownerEmail; + + private WorkflowDef.TimeoutPolicy timeoutPolicy; + + private Map workflowOutput; + + private long timeoutSeconds; + + private boolean restartable; + + private T defaultInput; + + private Map variables; + + private final List tasks = new ArrayList<>(); + + private final ObjectMapper objectMapper = new ObjectMapperProvider().getObjectMapper(); + + private final WorkflowExecutor workflowExecutor; + + public ConductorWorkflow(WorkflowExecutor workflowExecutor) { + this.workflowOutput = new HashMap<>(); + this.workflowExecutor = workflowExecutor; + this.restartable = true; + } + + public void setName(String name) { + this.name = name; + } + + public void setVersion(int version) { + this.version = version; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setFailureWorkflow(String failureWorkflow) { + this.failureWorkflow = failureWorkflow; + } + + public void add(Task task) { + this.tasks.add(task); + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public int getVersion() { + return version; + } + + public String getFailureWorkflow() { + return failureWorkflow; + } + + public String getOwnerEmail() { + return ownerEmail; + } + + public void setOwnerEmail(String ownerEmail) { + this.ownerEmail = ownerEmail; + } + + public WorkflowDef.TimeoutPolicy getTimeoutPolicy() { + return timeoutPolicy; + } + + public void setTimeoutPolicy(WorkflowDef.TimeoutPolicy timeoutPolicy) { + this.timeoutPolicy = timeoutPolicy; + } + + public long getTimeoutSeconds() { + return timeoutSeconds; + } + + public void setTimeoutSeconds(long timeoutSeconds) { + this.timeoutSeconds = timeoutSeconds; + } + + public boolean isRestartable() { + return restartable; + } + + public void setRestartable(boolean restartable) { + this.restartable = restartable; + } + + public T getDefaultInput() { + return defaultInput; + } + + public void setDefaultInput(T defaultInput) { + this.defaultInput = defaultInput; + } + + public Map getWorkflowOutput() { + return workflowOutput; + } + + public void setWorkflowOutput(Map workflowOutput) { + this.workflowOutput = workflowOutput; + } + + public Object getVariables() { + return variables; + } + + public void setVariables(Map variables) { + this.variables = variables; + } + + /** + * Execute a dynamic workflow without creating a definition in metadata store. + * + *


+ * Note: Use this with caution - as this does not promote re-usability of the workflows + * + * @param input Workflow Input - The input object is converted a JSON doc as an input to the + * workflow + * @return + */ + public CompletableFuture executeDynamic(T input) { + return workflowExecutor.executeWorkflow(this, input); + } + + /** + * Executes the workflow using registered metadata definitions + * + * @see #registerWorkflow() + * @param input + * @return + */ + public CompletableFuture execute(T input) { + return workflowExecutor.executeWorkflow(this.getName(), this.getVersion(), input); + } + + /** + * Starts a dynamic workflow without creating a definition in metadata store. + * + *


+ * Note: Use this with caution - as this does not promote re-usability of the workflows + * + * @param input + * @return the workflow id + */ + public String startDynamic(T input) { + return workflowExecutor.startWorkflow(this, input); + } + + /** + * Starts the workflow using registered metadata definitions + * + * @see #registerWorkflow() + * @param name + * @param input + * @param version + * @return the workflow id + */ + public String start(String name, Integer version, Object input) { + return workflowExecutor.startWorkflow(name, version, input); + } + + /** + * Registers a new workflow in the server. + * + * @return true if the workflow is successfully registered. False if the workflow cannot be + * registered and the workflow definition already exists on the server with given name + + * version The call will throw a runtime exception if any of the tasks are missing + * definitions on the server. + */ + public boolean registerWorkflow() { + return registerWorkflow(false, false); + } + + /** + * @param overwrite set to true if the workflow should be overwritten if the definition already + * exists with the given name and version. Use with caution + * @return true if success, false otherwise. + */ + public boolean registerWorkflow(boolean overwrite) { + return registerWorkflow(overwrite, false); + } + + /** + * @param overwrite set to true if the workflow should be overwritten if the definition already + * exists with the given name and version. Use with caution + * @param registerTasks if set to true, missing task definitions are registered with the default + * configuration. + * @return true if success, false otherwise. + */ + public boolean registerWorkflow(boolean overwrite, boolean registerTasks) { + WorkflowDef workflowDef = toWorkflowDef(); + List missing = getMissingTasks(workflowDef); + if (!missing.isEmpty()) { + if (!registerTasks) { + throw new RuntimeException( + "Workflow cannot be registered. The following tasks do not have definitions. " + + "Please register these tasks before creating the workflow. Missing Tasks = " + + missing); + } else { + String ownerEmail = this.ownerEmail; + missing.stream().forEach(taskName -> registerTaskDef(taskName, ownerEmail)); + } + } + return workflowExecutor.registerWorkflow(workflowDef, overwrite); + } + + /** + * @return Convert to the WorkflowDef model used by the Metadata APIs + */ + public WorkflowDef toWorkflowDef() { + + WorkflowDef def = new WorkflowDef(); + def.setName(name); + def.setDescription(description); + def.setVersion(version); + def.setFailureWorkflow(failureWorkflow); + def.setOwnerEmail(ownerEmail); + def.setTimeoutPolicy(timeoutPolicy); + def.setTimeoutSeconds(timeoutSeconds); + def.setRestartable(restartable); + def.setOutputParameters(workflowOutput); + def.setVariables(variables); + def.setInputTemplate(objectMapper.convertValue(defaultInput, Map.class)); + + for (Task task : tasks) { + def.getTasks().addAll(task.getWorkflowDefTasks()); + } + return def; + } + + /** + * Generate ConductorWorkflow based on the workflow metadata definition + * + * @param def + * @return + */ + public static ConductorWorkflow fromWorkflowDef(WorkflowDef def) { + ConductorWorkflow workflow = new ConductorWorkflow<>(null); + fromWorkflowDef(workflow, def); + return workflow; + } + + public ConductorWorkflow from(String workflowName, Integer workflowVersion) { + WorkflowDef def = + workflowExecutor.getMetadataClient().getWorkflowDef(workflowName, workflowVersion); + fromWorkflowDef(this, def); + return this; + } + + private static void fromWorkflowDef(ConductorWorkflow workflow, WorkflowDef def) { + workflow.setName(def.getName()); + workflow.setVersion(def.getVersion()); + workflow.setFailureWorkflow(def.getFailureWorkflow()); + workflow.setRestartable(def.isRestartable()); + workflow.setVariables(def.getVariables()); + workflow.setDefaultInput((T) def.getInputTemplate()); + + workflow.setWorkflowOutput(def.getOutputParameters()); + workflow.setOwnerEmail(def.getOwnerEmail()); + workflow.setDescription(def.getDescription()); + workflow.setTimeoutSeconds(def.getTimeoutSeconds()); + workflow.setTimeoutPolicy(def.getTimeoutPolicy()); + + List workflowTasks = def.getTasks(); + for (WorkflowTask workflowTask : workflowTasks) { + Task task = TaskRegistry.getTask(workflowTask); + workflow.tasks.add(task); + } + } + + private List getMissingTasks(WorkflowDef workflowDef) { + List missing = new ArrayList<>(); + workflowDef.collectTasks().stream() + .filter(workflowTask -> workflowTask.getType().equals(TaskType.TASK_TYPE_SIMPLE)) + .map(WorkflowTask::getName) + .distinct() + .parallel() + .forEach( + taskName -> { + try { + workflowExecutor.getMetadataClient().getTaskDef(taskName); + } catch (ConductorClientException ex) { + if (ex.getStatus() == 404) { + missing.add(taskName); + } else { + throw ex; + } + } + }); + return missing; + } + + private void registerTaskDef(String taskName, String ownerEmail) { + TaskDef taskDef = new TaskDef(); + taskDef.setName(taskName); + taskDef.setOwnerEmail(ownerEmail); + workflowExecutor.getMetadataClient().registerTaskDefs(Arrays.asList(taskDef)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ConductorWorkflow workflow = (ConductorWorkflow) o; + return version == workflow.version && Objects.equals(name, workflow.name); + } + + @Override + public int hashCode() { + return Objects.hash(name, version); + } + + @Override + public String toString() { + try { + return objectMapper.writeValueAsString(toWorkflowDef()); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java new file mode 100644 index 000000000..23e064d33 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java @@ -0,0 +1,20 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def; + +public class ValidationError extends RuntimeException { + + public ValidationError(String message) { + super(message); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java new file mode 100644 index 000000000..5ecb604d6 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java @@ -0,0 +1,215 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.netflix.conductor.common.config.ObjectMapperProvider; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.sdk.workflow.def.tasks.Task; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; +import com.netflix.conductor.sdk.workflow.utils.InputOutputGetter; +import com.netflix.conductor.sdk.workflow.utils.MapBuilder; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * @param Input type for the workflow + */ +public class WorkflowBuilder { + + private String name; + + private String description; + + private int version; + + private String failureWorkflow; + + private String ownerEmail; + + private WorkflowDef.TimeoutPolicy timeoutPolicy; + + private long timeoutSeconds; + + private boolean restartable = true; + + private T defaultInput; + + private final Map output = new HashMap<>(); + + private Map state; + + protected List> tasks = new ArrayList<>(); + + private final WorkflowExecutor workflowExecutor; + + public final InputOutputGetter input = + new InputOutputGetter("workflow", InputOutputGetter.Field.input); + + private final ObjectMapper objectMapper = new ObjectMapperProvider().getObjectMapper(); + + public WorkflowBuilder(WorkflowExecutor workflowExecutor) { + this.workflowExecutor = workflowExecutor; + this.tasks = new ArrayList<>(); + } + + public WorkflowBuilder name(String name) { + this.name = name; + return this; + } + + public WorkflowBuilder version(int version) { + this.version = version; + return this; + } + + public WorkflowBuilder description(String description) { + this.description = description; + return this; + } + + public WorkflowBuilder failureWorkflow(String failureWorkflow) { + this.failureWorkflow = failureWorkflow; + return this; + } + + public WorkflowBuilder ownerEmail(String ownerEmail) { + this.ownerEmail = ownerEmail; + return this; + } + + public WorkflowBuilder timeoutPolicy( + WorkflowDef.TimeoutPolicy timeoutPolicy, long timeoutSeconds) { + this.timeoutPolicy = timeoutPolicy; + this.timeoutSeconds = timeoutSeconds; + return this; + } + + public WorkflowBuilder add(Task... tasks) { + Collections.addAll(this.tasks, tasks); + return this; + } + + public WorkflowBuilder defaultInput(T defaultInput) { + this.defaultInput = defaultInput; + return this; + } + + public WorkflowBuilder restartable(boolean restartable) { + this.restartable = restartable; + return this; + } + + public WorkflowBuilder variables(Object variables) { + try { + this.state = objectMapper.convertValue(variables, Map.class); + } catch (Exception e) { + throw new IllegalArgumentException( + "Workflow Variables cannot be converted to Map. Supplied: " + + variables.getClass().getName()); + } + return this; + } + + public WorkflowBuilder output(String key, boolean value) { + output.put(key, value); + return this; + } + + public WorkflowBuilder output(String key, String value) { + output.put(key, value); + return this; + } + + public WorkflowBuilder output(String key, Number value) { + output.put(key, value); + return this; + } + + public WorkflowBuilder output(String key, Object value) { + output.put(key, value); + return this; + } + + public WorkflowBuilder output(MapBuilder mapBuilder) { + output.putAll(mapBuilder.build()); + return this; + } + + public ConductorWorkflow build() throws ValidationError { + + validate(); + + ConductorWorkflow workflow = new ConductorWorkflow<>(workflowExecutor); + if (description != null) { + workflow.setDescription(description); + } + + workflow.setName(name); + workflow.setVersion(version); + workflow.setDescription(description); + workflow.setFailureWorkflow(failureWorkflow); + workflow.setOwnerEmail(ownerEmail); + workflow.setTimeoutPolicy(timeoutPolicy); + workflow.setTimeoutSeconds(timeoutSeconds); + workflow.setRestartable(restartable); + workflow.setDefaultInput(defaultInput); + workflow.setWorkflowOutput(output); + workflow.setVariables(state); + + for (Task task : tasks) { + workflow.add(task); + } + + return workflow; + } + + /** + * Validate: 1. There are no tasks with duplicate reference names 2. Each of the task is + * consistent with its definition 3. + */ + private void validate() throws ValidationError { + + List allTasks = new ArrayList<>(); + for (Task task : tasks) { + List workflowDefTasks = task.getWorkflowDefTasks(); + for (WorkflowTask workflowDefTask : workflowDefTasks) { + allTasks.addAll(workflowDefTask.collectTasks()); + } + } + + Map taskMap = new HashMap<>(); + Set duplicateTasks = new HashSet<>(); + for (WorkflowTask task : allTasks) { + if (taskMap.containsKey(task.getTaskReferenceName())) { + duplicateTasks.add(task.getTaskReferenceName()); + } else { + taskMap.put(task.getTaskReferenceName(), task); + } + } + if (!duplicateTasks.isEmpty()) { + throw new ValidationError( + "Task Reference Names MUST be unique across all the tasks in the workkflow. " + + "Please update/change reference names to be unique for the following tasks: " + + duplicateTasks); + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java new file mode 100644 index 000000000..9ffbecc7c --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java @@ -0,0 +1,97 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +public class DoWhile extends Task { + + private final String loopCondition; + + private final List> loopTasks = new ArrayList<>(); + + /** + * Execute tasks in a loop determined by the condition set using condition parameter. The loop + * will continue till the condition is true + * + * @param taskReferenceName + * @param condition Javascript that evaluates to a boolean value + * @param tasks + */ + public DoWhile(String taskReferenceName, String condition, Task... tasks) { + super(taskReferenceName, TaskType.DO_WHILE); + Collections.addAll(this.loopTasks, tasks); + this.loopCondition = condition; + } + + /** + * Similar to a for loop, run tasks for N times + * + * @param taskReferenceName + * @param loopCount + * @param tasks + */ + public DoWhile(String taskReferenceName, int loopCount, Task... tasks) { + super(taskReferenceName, TaskType.DO_WHILE); + Collections.addAll(this.loopTasks, tasks); + this.loopCondition = getForLoopCondition(loopCount); + } + + DoWhile(WorkflowTask workflowTask) { + super(workflowTask); + this.loopCondition = workflowTask.getLoopCondition(); + for (WorkflowTask task : workflowTask.getLoopOver()) { + Task loopTask = TaskRegistry.getTask(task); + this.loopTasks.add(loopTask); + } + } + + public DoWhile loopOver(Task... tasks) { + for (Task task : tasks) { + this.loopTasks.add(task); + } + return this; + } + + private String getForLoopCondition(int loopCount) { + return "if ( $." + + getTaskReferenceName() + + "['iteration'] < " + + loopCount + + ") { true; } else { false; }"; + } + + public String getLoopCondition() { + return loopCondition; + } + + public List getLoopTasks() { + return loopTasks; + } + + @Override + public void updateWorkflowTask(WorkflowTask workflowTask) { + workflowTask.setLoopCondition(loopCondition); + + List loopWorkflowTasks = new ArrayList<>(); + for (Task task : this.loopTasks) { + loopWorkflowTasks.addAll(task.getWorkflowDefTasks()); + } + workflowTask.setLoopOver(loopWorkflowTasks); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java new file mode 100644 index 000000000..d34bfed0d --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java @@ -0,0 +1,41 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +import com.google.common.base.Strings; + +/** Wait task */ +public class Dynamic extends Task { + + public static final String TASK_NAME_INPUT_PARAM = "taskToExecute"; + + public Dynamic(String taskReferenceName, String dynamicTaskNameValue) { + super(taskReferenceName, TaskType.DYNAMIC); + if (Strings.isNullOrEmpty(dynamicTaskNameValue)) { + throw new AssertionError("Null/Empty dynamicTaskNameValue"); + } + super.input(TASK_NAME_INPUT_PARAM, dynamicTaskNameValue); + } + + Dynamic(WorkflowTask workflowTask) { + super(workflowTask); + } + + @Override + public void updateWorkflowTask(WorkflowTask task) { + task.setDynamicTaskNameParam(TASK_NAME_INPUT_PARAM); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java new file mode 100644 index 000000000..9a4aad2d4 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java @@ -0,0 +1,115 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import java.util.ArrayList; +import java.util.List; + +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +public class DynamicFork extends Task { + + public static final String FORK_TASK_PARAM = "forkedTasks"; + + public static final String FORK_TASK_INPUT_PARAM = "forkedTasksInputs"; + + private final String forkTasksParameter; + + private final String forkTasksInputsParameter; + + private Join join; + + private SimpleTask forkPrepareTask; + + /** + * Dynamic fork task that executes a set of tasks in parallel which are determined at run time. + * Use cases: Based on the input, you want to fork N number of processes in parallel to be + * executed. The number N is not pre-determined at the definition time and so a regular ForkJoin + * cannot be used. + * + * @param taskReferenceName + */ + public DynamicFork( + String taskReferenceName, String forkTasksParameter, String forkTasksInputsParameter) { + super(taskReferenceName, TaskType.FORK_JOIN_DYNAMIC); + this.join = new Join(taskReferenceName + "_join"); + this.forkTasksParameter = forkTasksParameter; + this.forkTasksInputsParameter = forkTasksInputsParameter; + super.input(FORK_TASK_PARAM, forkTasksParameter); + super.input(FORK_TASK_INPUT_PARAM, forkTasksInputsParameter); + } + + /** + * Dynamic fork task that executes a set of tasks in parallel which are determined at run time. + * Use cases: Based on the input, you want to fork N number of processes in parallel to be + * executed. The number N is not pre-determined at the definition time and so a regular ForkJoin + * cannot be used. + * + * @param taskReferenceName + * @param forkPrepareTask A Task that produces the output as {@link DynamicForkInput} to specify + * which tasks to fork. + */ + public DynamicFork(String taskReferenceName, SimpleTask forkPrepareTask) { + super(taskReferenceName, TaskType.FORK_JOIN_DYNAMIC); + this.forkPrepareTask = forkPrepareTask; + this.join = new Join(taskReferenceName + "_join"); + this.forkTasksParameter = forkPrepareTask.taskOutput.get(FORK_TASK_PARAM); + this.forkTasksInputsParameter = forkPrepareTask.taskOutput.get(FORK_TASK_INPUT_PARAM); + super.input(FORK_TASK_PARAM, forkTasksParameter); + super.input(FORK_TASK_INPUT_PARAM, forkTasksInputsParameter); + } + + DynamicFork(WorkflowTask workflowTask) { + super(workflowTask); + String nameOfParamForForkTask = workflowTask.getDynamicForkTasksParam(); + String nameOfParamForForkTaskInput = workflowTask.getDynamicForkTasksInputParamName(); + this.forkTasksParameter = + (String) workflowTask.getInputParameters().get(nameOfParamForForkTask); + this.forkTasksInputsParameter = + (String) workflowTask.getInputParameters().get(nameOfParamForForkTaskInput); + } + + public Join getJoin() { + return join; + } + + public String getForkTasksParameter() { + return forkTasksParameter; + } + + public String getForkTasksInputsParameter() { + return forkTasksInputsParameter; + } + + @Override + public void updateWorkflowTask(WorkflowTask task) { + task.setDynamicForkTasksParam("forkedTasks"); + task.setDynamicForkTasksInputParamName("forkedTasksInputs"); + } + + @Override + protected List getChildrenTasks() { + List tasks = new ArrayList<>(); + tasks.addAll(join.getWorkflowDefTasks()); + return tasks; + } + + @Override + protected List getParentTasks() { + if (forkPrepareTask != null) { + return List.of(forkPrepareTask.toWorkflowTask()); + } + return List.of(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java new file mode 100644 index 000000000..fc917b5d4 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java @@ -0,0 +1,51 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import java.util.List; +import java.util.Map; + +public class DynamicForkInput { + + /** List of tasks to execute in parallel */ + private List> tasks; + + /** + * Input to the tasks. Key is the reference name of the task and value is an Object that is sent + * as input to the task + */ + private Map inputs; + + public DynamicForkInput(List> tasks, Map inputs) { + this.tasks = tasks; + this.inputs = inputs; + } + + public DynamicForkInput() {} + + public List> getTasks() { + return tasks; + } + + public void setTasks(List> tasks) { + this.tasks = tasks; + } + + public Map getInputs() { + return inputs; + } + + public void setInputs(Map inputs) { + this.inputs = inputs; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java new file mode 100644 index 000000000..b3b8306ed --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java @@ -0,0 +1,46 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +import com.google.common.base.Strings; + +/** Task to publish Events to external queuing systems like SQS, NATS, AMQP etc. */ +public class Event extends Task { + + private static final String SINK_PARAMETER = "sink"; + + /** + * @param taskReferenceName Unique reference name within the workflow + * @param eventSink qualified name of the event sink where the message is published. Using the + * format sink_type:location e.g. sqs:sqs_queue_name, amqp_queue:queue_name, + * amqp_exchange:queue_name, nats:queue_name + */ + public Event(String taskReferenceName, String eventSink) { + super(taskReferenceName, TaskType.EVENT); + if (Strings.isNullOrEmpty(eventSink)) { + throw new AssertionError("Null/Empty eventSink"); + } + super.input(SINK_PARAMETER, eventSink); + } + + Event(WorkflowTask workflowTask) { + super(workflowTask); + } + + public String getSink() { + return (String) getInput().get(SINK_PARAMETER); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java new file mode 100644 index 000000000..328bae9a7 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java @@ -0,0 +1,130 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +/** ForkJoin task */ +public class ForkJoin extends Task { + + private Join join; + + private final Task[][] forkedTasks; + + /** + * execute task specified in the forkedTasks parameter in parallel. + * + *

forkedTask is a two-dimensional list that executes the outermost list in parallel and list + * within that is executed sequentially. + * + *

e.g. [[task1, task2],[task3, task4],[task5]] are executed as: + * + *

+     *                    ---------------
+     *                    |     fork    |
+     *                    ---------------
+     *                    |       |     |
+     *                    |       |     |
+     *                  task1  task3  task5
+     *                  task2  task4    |
+     *                    |      |      |
+     *                 ---------------------
+     *                 |       join        |
+     *                 ---------------------
+     * 
+ * + *

This method automatically adds a join that waits for all the *last* tasks in the fork + * (e.g. task2, task4 and task5 in the above example) to be completed.* + * + *

Use join method @see {@link ForkJoin#joinOn(String...)} to override this behavior (note: + * not a common scenario) + * + * @param taskReferenceName unique task reference name + * @param forkedTasks List of tasks to be executed in parallel + */ + public ForkJoin(String taskReferenceName, Task[]... forkedTasks) { + super(taskReferenceName, TaskType.FORK_JOIN); + this.forkedTasks = forkedTasks; + } + + ForkJoin(WorkflowTask workflowTask) { + super(workflowTask); + int size = workflowTask.getForkTasks().size(); + this.forkedTasks = new Task[size][]; + int i = 0; + for (List forkTasks : workflowTask.getForkTasks()) { + Task[] tasks = new Task[forkTasks.size()]; + for (int j = 0; j < forkTasks.size(); j++) { + WorkflowTask forkWorkflowTask = forkTasks.get(j); + Task task = TaskRegistry.getTask(forkWorkflowTask); + tasks[j] = task; + } + this.forkedTasks[i++] = tasks; + } + } + + public ForkJoin joinOn(String... joinOn) { + this.join = new Join(getTaskReferenceName() + "_join", joinOn); + return this; + } + + @Override + protected List getChildrenTasks() { + WorkflowTask fork = toWorkflowTask(); + + WorkflowTask joinWorkflowTask = null; + if (this.join != null) { + List joinTasks = this.join.getWorkflowDefTasks(); + joinWorkflowTask = joinTasks.get(0); + } else { + joinWorkflowTask = new WorkflowTask(); + joinWorkflowTask.setWorkflowTaskType(TaskType.JOIN); + joinWorkflowTask.setTaskReferenceName(getTaskReferenceName() + "_join"); + joinWorkflowTask.setName(joinWorkflowTask.getTaskReferenceName()); + joinWorkflowTask.setJoinOn(fork.getJoinOn()); + } + return Arrays.asList(joinWorkflowTask); + } + + @Override + public void updateWorkflowTask(WorkflowTask fork) { + List joinOnTaskRefNames = new ArrayList<>(); + List> forkTasks = new ArrayList<>(); + + for (Task[] forkedTaskList : forkedTasks) { + List forkedWorkflowTasks = new ArrayList<>(); + for (Task baseWorkflowTask : forkedTaskList) { + forkedWorkflowTasks.addAll(baseWorkflowTask.getWorkflowDefTasks()); + } + forkTasks.add(forkedWorkflowTasks); + joinOnTaskRefNames.add( + forkedWorkflowTasks.get(forkedWorkflowTasks.size() - 1).getTaskReferenceName()); + } + if (this.join != null) { + fork.setJoinOn(List.of(this.join.getJoinOn())); + } else { + fork.setJoinOn(joinOnTaskRefNames); + } + + fork.setForkTasks(forkTasks); + } + + public Task[][] getForkedTasks() { + return forkedTasks; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java new file mode 100644 index 000000000..7de82a3a4 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java @@ -0,0 +1,310 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.common.config.ObjectMapperProvider; +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** Wait task */ +public class Http extends Task { + + private static final Logger LOGGER = LoggerFactory.getLogger(Http.class); + + private static final String INPUT_PARAM = "http_request"; + + private final ObjectMapper objectMapper = new ObjectMapperProvider().getObjectMapper(); + + private Input httpRequest; + + public Http(String taskReferenceName) { + super(taskReferenceName, TaskType.HTTP); + this.httpRequest = new Input(); + this.httpRequest.method = Input.HttpMethod.GET; + super.input(INPUT_PARAM, httpRequest); + } + + Http(WorkflowTask workflowTask) { + super(workflowTask); + + Object inputRequest = workflowTask.getInputParameters().get(INPUT_PARAM); + if (inputRequest != null) { + try { + this.httpRequest = objectMapper.convertValue(inputRequest, Input.class); + } catch (Exception e) { + LOGGER.error("Error while trying to convert input request " + e.getMessage(), e); + } + } + } + + public Http input(Input httpRequest) { + this.httpRequest = httpRequest; + return this; + } + + public Http url(String url) { + this.httpRequest.setUri(url); + return this; + } + + public Http method(Input.HttpMethod method) { + this.httpRequest.setMethod(method); + return this; + } + + public Http headers(Map headers) { + this.httpRequest.setHeaders(headers); + return this; + } + + public Http body(Object body) { + this.httpRequest.setBody(body); + return this; + } + + public Http readTimeout(int readTimeout) { + this.httpRequest.setReadTimeOut(readTimeout); + return this; + } + + public Input getHttpRequest() { + return httpRequest; + } + + @Override + protected void updateWorkflowTask(WorkflowTask workflowTask) { + workflowTask.getInputParameters().put(INPUT_PARAM, httpRequest); + } + + public static class Input { + public enum HttpMethod { + PUT, + POST, + GET, + DELETE, + OPTIONS, + HEAD + } + + private HttpMethod method; // PUT, POST, GET, DELETE, OPTIONS, HEAD + private String vipAddress; + private String appName; + private Map headers = new HashMap<>(); + private String uri; + private Object body; + private String accept = "application/json"; + private String contentType = "application/json"; + private Integer connectionTimeOut; + private Integer readTimeOut; + + /** + * @return the method + */ + public HttpMethod getMethod() { + return method; + } + + /** + * @param method the method to set + */ + public void setMethod(HttpMethod method) { + this.method = method; + } + + /** + * @return the headers + */ + public Map getHeaders() { + return headers; + } + + /** + * @param headers the headers to set + */ + public void setHeaders(Map headers) { + this.headers = headers; + } + + /** + * @return the body + */ + public Object getBody() { + return body; + } + + /** + * @param body the body to set + */ + public void setBody(Object body) { + this.body = body; + } + + /** + * @return the uri + */ + public String getUri() { + return uri; + } + + /** + * @param uri the uri to set + */ + public void setUri(String uri) { + this.uri = uri; + } + + /** + * @return the vipAddress + */ + public String getVipAddress() { + return vipAddress; + } + + /** + * @param vipAddress the vipAddress to set + */ + public void setVipAddress(String vipAddress) { + this.vipAddress = vipAddress; + } + + /** + * @return the accept + */ + public String getAccept() { + return accept; + } + + /** + * @param accept the accept to set + */ + public void setAccept(String accept) { + this.accept = accept; + } + + /** + * @return the MIME content type to use for the request + */ + public String getContentType() { + return contentType; + } + + /** + * @param contentType the MIME content type to set + */ + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + /** + * @return the connectionTimeOut + */ + public Integer getConnectionTimeOut() { + return connectionTimeOut; + } + + /** + * @return the readTimeOut + */ + public Integer getReadTimeOut() { + return readTimeOut; + } + + public void setConnectionTimeOut(Integer connectionTimeOut) { + this.connectionTimeOut = connectionTimeOut; + } + + public void setReadTimeOut(Integer readTimeOut) { + this.readTimeOut = readTimeOut; + } + + @Override + public String toString() { + return "Input{" + + "method=" + + method + + ", vipAddress='" + + vipAddress + + '\'' + + ", appName='" + + appName + + '\'' + + ", headers=" + + headers + + ", uri='" + + uri + + '\'' + + ", body=" + + body + + ", accept='" + + accept + + '\'' + + ", contentType='" + + contentType + + '\'' + + ", connectionTimeOut=" + + connectionTimeOut + + ", readTimeOut=" + + readTimeOut + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Input input = (Input) o; + return method == input.method + && Objects.equals(vipAddress, input.vipAddress) + && Objects.equals(appName, input.appName) + && Objects.equals(headers, input.headers) + && Objects.equals(uri, input.uri) + && Objects.equals(body, input.body) + && Objects.equals(accept, input.accept) + && Objects.equals(contentType, input.contentType) + && Objects.equals(connectionTimeOut, input.connectionTimeOut) + && Objects.equals(readTimeOut, input.readTimeOut); + } + + @Override + public int hashCode() { + return Objects.hash( + method, + vipAddress, + appName, + headers, + uri, + body, + accept, + contentType, + connectionTimeOut, + readTimeOut); + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java new file mode 100644 index 000000000..b4cef5c6f --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java @@ -0,0 +1,43 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +import com.google.common.base.Strings; + +/** + * JQ Transformation task See https://stedolan.github.io/jq/ for how to form the queries to parse + * JSON payloads + */ +public class JQ extends Task { + + private static final String QUERY_EXPRESSION_PARAMETER = "queryExpression"; + + public JQ(String taskReferenceName, String queryExpression) { + super(taskReferenceName, TaskType.JSON_JQ_TRANSFORM); + if (Strings.isNullOrEmpty(queryExpression)) { + throw new AssertionError("Null/Empty queryExpression"); + } + super.input(QUERY_EXPRESSION_PARAMETER, queryExpression); + } + + JQ(WorkflowTask workflowTask) { + super(workflowTask); + } + + public String getQueryExpression() { + return (String) getInput().get(QUERY_EXPRESSION_PARAMETER); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java new file mode 100644 index 000000000..2a2436c6f --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java @@ -0,0 +1,148 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.sdk.workflow.def.ValidationError; + +import com.google.common.base.Strings; + +/** + * JQ Transformation task See https://stedolan.github.io/jq/ for how to form the queries to parse + * JSON payloads + */ +public class Javascript extends Task { + + private static final Logger LOGGER = LoggerFactory.getLogger(Javascript.class); + + private static final String EXPRESSION_PARAMETER = "expression"; + + private static final String EVALUATOR_TYPE_PARAMETER = "evaluatorType"; + + private static final String ENGINE = "nashorn"; + + /** + * Javascript tasks are executed on the Conductor server without having to write worker code + * + *

Use {@link Javascript#validate()} method to validate the javascript to ensure the script + * is valid. + * + * @param taskReferenceName + * @param script script to execute + */ + public Javascript(String taskReferenceName, String script) { + super(taskReferenceName, TaskType.INLINE); + if (Strings.isNullOrEmpty(script)) { + throw new AssertionError("Null/Empty script"); + } + super.input(EVALUATOR_TYPE_PARAMETER, "javascript"); + super.input(EXPRESSION_PARAMETER, script); + } + + /** + * Javascript tasks are executed on the Conductor server without having to write worker code + * + *

Use {@link Javascript#validate()} method to validate the javascript to ensure the script + * is valid. + * + * @param taskReferenceName + * @param stream stream to load the script file from + */ + public Javascript(String taskReferenceName, InputStream stream) { + super(taskReferenceName, TaskType.INLINE); + if (stream == null) { + throw new AssertionError("Stream is empty"); + } + super.input(EVALUATOR_TYPE_PARAMETER, "javascript"); + try { + String script = new String(stream.readAllBytes()); + super.input(EXPRESSION_PARAMETER, script); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + Javascript(WorkflowTask workflowTask) { + super(workflowTask); + } + + public String getExpression() { + return (String) getInput().get(EXPRESSION_PARAMETER); + } + + /** + * Validates the script. + * + * @return + */ + public Javascript validate() { + ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName(ENGINE); + if (scriptEngine == null) { + LOGGER.error("missing " + ENGINE + " engine. Ensure you are running supported JVM"); + return this; + } + + try { + + Bindings bindings = scriptEngine.createBindings(); + bindings.put("$", new HashMap<>()); + scriptEngine.eval(getExpression(), bindings); + + } catch (ScriptException e) { + String message = e.getMessage(); + throw new ValidationError(message); + } + return this; + } + + /** + * Helper method to unit test your javascript. The method is not used for creating or executing + * workflow but is meant for testing only. + * + * @param input Input that against which the script will be executed + * @return Output of the script + */ + public Object test(Map input) { + + ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName(ENGINE); + if (scriptEngine == null) { + LOGGER.error("missing " + ENGINE + " engine. Ensure you are running supported JVM"); + return this; + } + + try { + + Bindings bindings = scriptEngine.createBindings(); + bindings.put("$", input); + return scriptEngine.eval(getExpression(), bindings); + + } catch (ScriptException e) { + String message = e.getMessage(); + throw new ValidationError(message); + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java new file mode 100644 index 000000000..c9c0748ed --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java @@ -0,0 +1,46 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import java.util.Arrays; + +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +public class Join extends Task { + + private final String[] joinOn; + + /** + * @param taskReferenceName + * @param joinOn List of task reference names to join on + */ + public Join(String taskReferenceName, String... joinOn) { + super(taskReferenceName, TaskType.JOIN); + this.joinOn = joinOn; + } + + Join(WorkflowTask workflowTask) { + super(workflowTask); + this.joinOn = workflowTask.getJoinOn().toArray(new String[0]); + } + + @Override + protected void updateWorkflowTask(WorkflowTask workflowTask) { + workflowTask.setJoinOn(Arrays.asList(joinOn)); + } + + public String[] getJoinOn() { + return joinOn; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java new file mode 100644 index 000000000..dd1a33211 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.sdk.workflow.def.WorkflowBuilder; + +public class SetVariable extends Task { + /** + * Sets the value of the variable in workflow. Used for workflow state management. Workflow + * state is a Map that is initialized using @see {@link WorkflowBuilder#variables(Object)} + * + * @param taskReferenceName Use input methods to set the variable values + */ + public SetVariable(String taskReferenceName) { + super(taskReferenceName, TaskType.SET_VARIABLE); + } + + SetVariable(WorkflowTask workflowTask) { + super(workflowTask); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java new file mode 100644 index 000000000..d33aa57d3 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java @@ -0,0 +1,47 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +/** Workflow task executed by a worker */ +public class SimpleTask extends Task { + + private TaskDef taskDef; + + public SimpleTask(String taskDefName, String taskReferenceName) { + super(taskReferenceName, TaskType.SIMPLE); + super.name(taskDefName); + } + + SimpleTask(WorkflowTask workflowTask) { + super(workflowTask); + this.taskDef = workflowTask.getTaskDefinition(); + } + + public TaskDef getTaskDef() { + return taskDef; + } + + public SimpleTask setTaskDef(TaskDef taskDef) { + this.taskDef = taskDef; + return this; + } + + @Override + protected void updateWorkflowTask(WorkflowTask workflowTask) { + workflowTask.setTaskDefinition(taskDef); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java new file mode 100644 index 000000000..5e77b1e67 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java @@ -0,0 +1,90 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.SubWorkflowParams; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.sdk.workflow.def.ConductorWorkflow; + +public class SubWorkflow extends Task { + + private ConductorWorkflow conductorWorkflow; + + private String workflowName; + + private Integer workflowVersion; + + /** + * Start a workflow as a sub-workflow + * + * @param taskReferenceName + * @param workflowName + * @param workflowVersion + */ + public SubWorkflow(String taskReferenceName, String workflowName, Integer workflowVersion) { + super(taskReferenceName, TaskType.SUB_WORKFLOW); + this.workflowName = workflowName; + this.workflowVersion = workflowVersion; + } + + /** + * Start a workflow as a sub-workflow + * + * @param taskReferenceName + * @param conductorWorkflow + */ + public SubWorkflow(String taskReferenceName, ConductorWorkflow conductorWorkflow) { + super(taskReferenceName, TaskType.SUB_WORKFLOW); + this.conductorWorkflow = conductorWorkflow; + } + + SubWorkflow(WorkflowTask workflowTask) { + super(workflowTask); + SubWorkflowParams subworkflowParam = workflowTask.getSubWorkflowParam(); + this.workflowName = subworkflowParam.getName(); + this.workflowVersion = subworkflowParam.getVersion(); + if (subworkflowParam.getWorkflowDefinition() != null + && subworkflowParam.getWorkflowDefinition() instanceof WorkflowDef) { + this.conductorWorkflow = + ConductorWorkflow.fromWorkflowDef( + (WorkflowDef) subworkflowParam.getWorkflowDefinition()); + } + } + + public ConductorWorkflow getConductorWorkflow() { + return conductorWorkflow; + } + + public String getWorkflowName() { + return workflowName; + } + + public int getWorkflowVersion() { + return workflowVersion; + } + + @Override + protected void updateWorkflowTask(WorkflowTask workflowTask) { + SubWorkflowParams subWorkflowParam = new SubWorkflowParams(); + + if (conductorWorkflow != null) { + subWorkflowParam.setWorkflowDefinition(conductorWorkflow.toWorkflowDef()); + } else { + subWorkflowParam.setName(workflowName); + subWorkflowParam.setVersion(workflowVersion); + } + workflowTask.setSubWorkflowParam(subWorkflowParam); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java new file mode 100644 index 000000000..42f249292 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java @@ -0,0 +1,167 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +/** Switch Task */ +public class Switch extends Task { + + public static final String VALUE_PARAM_NAME = "value-param"; + + public static final String JAVASCRIPT_NAME = "javascript"; + + private String caseExpression; + + private boolean useJavascript; + + private List> defaultTasks = new ArrayList<>(); + + private Map>> branches = new HashMap<>(); + + /** + * Switch case (similar to if...then...else or switch in java language) + * + * @param taskReferenceName + * @param caseExpression An expression that outputs a string value to be used as case branches. + * Case expression can be a support value parameter e.g. ${workflow.input.key} or + * ${task.output.key} or a Javascript statement. + * @param useJavascript set to true if the caseExpression is a javascript statement + */ + public Switch(String taskReferenceName, String caseExpression, boolean useJavascript) { + super(taskReferenceName, TaskType.SWITCH); + this.caseExpression = caseExpression; + this.useJavascript = useJavascript; + } + + /** + * Switch case (similar to if...then...else or switch in java language) + * + * @param taskReferenceName + * @param caseExpression + */ + public Switch(String taskReferenceName, String caseExpression) { + super(taskReferenceName, TaskType.SWITCH); + this.caseExpression = caseExpression; + this.useJavascript = false; + } + + Switch(WorkflowTask workflowTask) { + super(workflowTask); + Map> decisions = workflowTask.getDecisionCases(); + + decisions.entrySet().stream() + .forEach( + branch -> { + String branchName = branch.getKey(); + List branchWorkflowTasks = branch.getValue(); + List> branchTasks = new ArrayList<>(); + for (WorkflowTask branchWorkflowTask : branchWorkflowTasks) { + branchTasks.add(TaskRegistry.getTask(branchWorkflowTask)); + } + this.branches.put(branchName, branchTasks); + }); + + List defaultCases = workflowTask.getDefaultCase(); + for (WorkflowTask defaultCase : defaultCases) { + this.defaultTasks.add(TaskRegistry.getTask(defaultCase)); + } + } + + public Switch defaultCase(Task... tasks) { + defaultTasks = Arrays.asList(tasks); + return this; + } + + public Switch defaultCase(List> defaultTasks) { + this.defaultTasks = defaultTasks; + return this; + } + + public Switch decisionCases(Map>> branches) { + this.branches = branches; + return this; + } + + public Switch defaultCase(String... workerTasks) { + for (String workerTask : workerTasks) { + this.defaultTasks.add(new SimpleTask(workerTask, workerTask)); + } + return this; + } + + public Switch switchCase(String caseValue, Task... tasks) { + branches.put(caseValue, Arrays.asList(tasks)); + return this; + } + + public Switch switchCase(String caseValue, String... workerTasks) { + List> tasks = new ArrayList<>(workerTasks.length); + int i = 0; + for (String workerTask : workerTasks) { + tasks.add(new SimpleTask(workerTask, workerTask)); + } + branches.put(caseValue, tasks); + return this; + } + + public List> getDefaultTasks() { + return defaultTasks; + } + + public Map>> getBranches() { + return branches; + } + + @Override + public void updateWorkflowTask(WorkflowTask workflowTask) { + + if (useJavascript) { + workflowTask.setEvaluatorType(JAVASCRIPT_NAME); + workflowTask.setExpression(caseExpression); + + } else { + workflowTask.setEvaluatorType(VALUE_PARAM_NAME); + workflowTask.getInputParameters().put("switchCaseValue", caseExpression); + workflowTask.setExpression("switchCaseValue"); + } + + Map> decisionCases = new HashMap<>(); + branches.entrySet() + .forEach( + entry -> { + String decisionCase = entry.getKey(); + List> decisionTasks = entry.getValue(); + List decionTaskDefs = + new ArrayList<>(decisionTasks.size()); + for (Task decisionTask : decisionTasks) { + decionTaskDefs.addAll(decisionTask.getWorkflowDefTasks()); + } + decisionCases.put(decisionCase, decionTaskDefs); + }); + + workflowTask.setDecisionCases(decisionCases); + List defaultCaseTaskDefs = new ArrayList<>(defaultTasks.size()); + for (Task defaultTask : defaultTasks) { + defaultCaseTaskDefs.addAll(defaultTask.getWorkflowDefTasks()); + } + workflowTask.setDefaultCase(defaultCaseTaskDefs); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java new file mode 100644 index 000000000..8564640a6 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java @@ -0,0 +1,241 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.netflix.conductor.common.config.ObjectMapperProvider; +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.sdk.workflow.utils.InputOutputGetter; +import com.netflix.conductor.sdk.workflow.utils.MapBuilder; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Strings; + +/** Workflow Task */ +public abstract class Task { + + private String name; + + private String description; + + private String taskReferenceName; + + private boolean optional; + + private int startDelay; + + private final TaskType type; + + private Map input = new HashMap<>(); + + protected final ObjectMapper om = new ObjectMapperProvider().getObjectMapper(); + + public final InputOutputGetter taskInput; + + public final InputOutputGetter taskOutput; + + public Task(String taskReferenceName, TaskType type) { + if (Strings.isNullOrEmpty(taskReferenceName)) { + throw new AssertionError("taskReferenceName cannot be null"); + } + if (type == null) { + throw new AssertionError("type cannot be null"); + } + + this.name = taskReferenceName; + this.taskReferenceName = taskReferenceName; + this.type = type; + this.taskInput = new InputOutputGetter(taskReferenceName, InputOutputGetter.Field.input); + this.taskOutput = new InputOutputGetter(taskReferenceName, InputOutputGetter.Field.output); + } + + Task(WorkflowTask workflowTask) { + this(workflowTask.getTaskReferenceName(), TaskType.valueOf(workflowTask.getType())); + this.input = workflowTask.getInputParameters(); + this.description = workflowTask.getDescription(); + this.name = workflowTask.getName(); + } + + public T name(String name) { + this.name = name; + return (T) this; + } + + public T description(String description) { + this.description = description; + return (T) this; + } + + public T input(String key, boolean value) { + input.put(key, value); + return (T) this; + } + + public T input(String key, Object value) { + input.put(key, value); + return (T) this; + } + + public T input(String key, char value) { + input.put(key, value); + return (T) this; + } + + public T input(String key, InputOutputGetter value) { + input.put(key, value.getParent()); + return (T) this; + } + + public T input(InputOutputGetter value) { + return input("input", value); + } + + public T input(String key, String value) { + input.put(key, value); + return (T) this; + } + + public T input(String key, Number value) { + input.put(key, value); + return (T) this; + } + + public T input(String key, Map value) { + input.put(key, value); + return (T) this; + } + + public T input(Map map) { + input.putAll(map); + return (T) this; + } + + public T input(MapBuilder builder) { + input.putAll(builder.build()); + return (T) this; + } + + public T input(Object... keyValues) { + if (keyValues.length == 1) { + Object kv = keyValues[0]; + Map objectMap = om.convertValue(kv, Map.class); + input.putAll(objectMap); + return (T) this; + } + if (keyValues.length % 2 == 1) { + throw new IllegalArgumentException("Not all keys have value specified"); + } + for (int i = 0; i < keyValues.length; ) { + String key = keyValues[i].toString(); + Object value = keyValues[i + 1]; + input.put(key, value); + i += 2; + } + return (T) this; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTaskReferenceName() { + return taskReferenceName; + } + + public void setTaskReferenceName(String taskReferenceName) { + this.taskReferenceName = taskReferenceName; + } + + public boolean isOptional() { + return optional; + } + + public void setOptional(boolean optional) { + this.optional = optional; + } + + public int getStartDelay() { + return startDelay; + } + + public void setStartDelay(int startDelay) { + this.startDelay = startDelay; + } + + public TaskType getType() { + return type; + } + + public String getDescription() { + return description; + } + + public Map getInput() { + return input; + } + + public final List getWorkflowDefTasks() { + List workflowTasks = new ArrayList<>(); + workflowTasks.addAll(getParentTasks()); + workflowTasks.add(toWorkflowTask()); + workflowTasks.addAll(getChildrenTasks()); + return workflowTasks; + } + + protected final WorkflowTask toWorkflowTask() { + WorkflowTask workflowTask = new WorkflowTask(); + workflowTask.setName(name); + workflowTask.setTaskReferenceName(taskReferenceName); + workflowTask.setWorkflowTaskType(type); + workflowTask.setDescription(description); + workflowTask.setInputParameters(input); + workflowTask.setStartDelay(startDelay); + workflowTask.setOptional(optional); + + // Let the sub-classes enrich the workflow task before returning back + updateWorkflowTask(workflowTask); + + return workflowTask; + } + + /** + * Override this method when the sub-class should update the default WorkflowTask generated + * using {@link #toWorkflowTask()} + * + * @param workflowTask + */ + protected void updateWorkflowTask(WorkflowTask workflowTask) {} + + /** + * Override this method when sub-classes will generate multiple workflow tasks. Used by tasks + * which have children tasks such as do_while, fork, etc. + * + * @return + */ + protected List getChildrenTasks() { + return List.of(); + } + + protected List getParentTasks() { + return List.of(); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java new file mode 100644 index 000000000..5975f9ded --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java @@ -0,0 +1,48 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +public class TaskRegistry { + + private static final Logger LOGGER = LoggerFactory.getLogger(TaskRegistry.class); + + private static final Map> taskTypeMap = new HashMap<>(); + + public static void register(String taskType, Class taskImplementation) { + taskTypeMap.put(taskType, taskImplementation); + } + + public static Task getTask(WorkflowTask workflowTask) { + Class clazz = taskTypeMap.get(workflowTask.getType()); + if (clazz == null) { + throw new UnsupportedOperationException( + "No support to convert " + workflowTask.getType()); + } + Task task = null; + try { + task = clazz.getDeclaredConstructor(WorkflowTask.class).newInstance(workflowTask); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + return task; + } + return task; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java new file mode 100644 index 000000000..08c8a1d89 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java @@ -0,0 +1,66 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import java.util.HashMap; + +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.common.run.Workflow; + +public class Terminate extends Task { + + private static final String TERMINATION_STATUS_PARAMETER = "terminationStatus"; + + private static final String TERMINATION_WORKFLOW_OUTPUT = "workflowOutput"; + + private static final String TERMINATION_REASON_PARAMETER = "terminationReason"; + + /** + * Terminate the workflow and mark it as FAILED + * + * @param taskReferenceName + * @param reason + */ + public Terminate(String taskReferenceName, String reason) { + this(taskReferenceName, Workflow.WorkflowStatus.FAILED, reason, new HashMap<>()); + } + + /** + * Terminate the workflow with a specific terminate status + * + * @param taskReferenceName + * @param terminationStatus + * @param reason + */ + public Terminate( + String taskReferenceName, Workflow.WorkflowStatus terminationStatus, String reason) { + this(taskReferenceName, terminationStatus, reason, new HashMap<>()); + } + + public Terminate( + String taskReferenceName, + Workflow.WorkflowStatus terminationStatus, + String reason, + Object workflowOutput) { + super(taskReferenceName, TaskType.TERMINATE); + + input(TERMINATION_STATUS_PARAMETER, terminationStatus.name()); + input(TERMINATION_WORKFLOW_OUTPUT, workflowOutput); + input(TERMINATION_REASON_PARAMETER, reason); + } + + Terminate(WorkflowTask workflowTask) { + super(workflowTask); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java new file mode 100644 index 000000000..a8a3cec51 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java @@ -0,0 +1,61 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def.tasks; + +import java.time.Duration; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +/** Wait task */ +public class Wait extends Task { + + public static final String DURATION_INPUT = "duration"; + public static final String UNTIL_INPUT = "until"; + + public static final DateTimeFormatter dateTimeFormatter = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z"); + + /** + * Wait until and external signal completes the task. The external signal can be either an API + * call (POST /api/task) to update the task status or an event coming from a supported external + * queue integration like SQS, Kafka, NATS, AMQP etc. + * + *


+ * see
+ * https://netflix.github.io/conductor/reference-docs/wait-task for more details + * + * @param taskReferenceName + */ + public Wait(String taskReferenceName) { + super(taskReferenceName, TaskType.WAIT); + } + + public Wait(String taskReferenceName, Duration waitFor) { + super(taskReferenceName, TaskType.WAIT); + long seconds = waitFor.getSeconds(); + input(DURATION_INPUT, seconds + "s"); + } + + public Wait(String taskReferenceName, ZonedDateTime waitUntil) { + super(taskReferenceName, TaskType.WAIT); + String formattedDateTime = waitUntil.format(dateTimeFormatter); + input(UNTIL_INPUT, formattedDateTime); + } + + Wait(WorkflowTask workflowTask) { + super(workflowTask); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java new file mode 100644 index 000000000..c8113d0f3 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java @@ -0,0 +1,260 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.executor; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.MetadataClient; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.http.WorkflowClient; +import com.netflix.conductor.common.config.ObjectMapperProvider; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.sdk.workflow.def.ConductorWorkflow; +import com.netflix.conductor.sdk.workflow.def.tasks.DoWhile; +import com.netflix.conductor.sdk.workflow.def.tasks.Dynamic; +import com.netflix.conductor.sdk.workflow.def.tasks.DynamicFork; +import com.netflix.conductor.sdk.workflow.def.tasks.Event; +import com.netflix.conductor.sdk.workflow.def.tasks.ForkJoin; +import com.netflix.conductor.sdk.workflow.def.tasks.Http; +import com.netflix.conductor.sdk.workflow.def.tasks.JQ; +import com.netflix.conductor.sdk.workflow.def.tasks.Javascript; +import com.netflix.conductor.sdk.workflow.def.tasks.Join; +import com.netflix.conductor.sdk.workflow.def.tasks.SetVariable; +import com.netflix.conductor.sdk.workflow.def.tasks.SimpleTask; +import com.netflix.conductor.sdk.workflow.def.tasks.SubWorkflow; +import com.netflix.conductor.sdk.workflow.def.tasks.Switch; +import com.netflix.conductor.sdk.workflow.def.tasks.TaskRegistry; +import com.netflix.conductor.sdk.workflow.def.tasks.Terminate; +import com.netflix.conductor.sdk.workflow.def.tasks.Wait; +import com.netflix.conductor.sdk.workflow.executor.task.AnnotatedWorkerExecutor; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class WorkflowExecutor { + + private final TypeReference> MAP_STRING_OBJECT = new TypeReference<>() { + }; + + private static final Logger LOGGER = LoggerFactory.getLogger(WorkflowExecutor.class); + + private final TypeReference> listOfTaskDefs = new TypeReference<>() { + }; + + private final Map> runningWorkflowFutures = + new ConcurrentHashMap<>(); + + private final ObjectMapper objectMapper = new ObjectMapperProvider().getObjectMapper(); + + private final TaskClient taskClient; + + private final WorkflowClient workflowClient; + + private final MetadataClient metadataClient; + + private final AnnotatedWorkerExecutor annotatedWorkerExecutor; + + private final ScheduledExecutorService scheduledWorkflowMonitor = Executors.newSingleThreadScheduledExecutor(r -> { + Thread thread = new Thread(r); + thread.setName("WorkflowExecutor Monitor"); + return thread; + }); + + static { + initTaskImplementations(); + } + + public static void initTaskImplementations() { + TaskRegistry.register(TaskType.DO_WHILE.name(), DoWhile.class); + TaskRegistry.register(TaskType.DYNAMIC.name(), Dynamic.class); + TaskRegistry.register(TaskType.FORK_JOIN_DYNAMIC.name(), DynamicFork.class); + TaskRegistry.register(TaskType.FORK_JOIN.name(), ForkJoin.class); + TaskRegistry.register(TaskType.HTTP.name(), Http.class); + TaskRegistry.register(TaskType.INLINE.name(), Javascript.class); + TaskRegistry.register(TaskType.JOIN.name(), Join.class); + TaskRegistry.register(TaskType.JSON_JQ_TRANSFORM.name(), JQ.class); + TaskRegistry.register(TaskType.SET_VARIABLE.name(), SetVariable.class); + TaskRegistry.register(TaskType.SIMPLE.name(), SimpleTask.class); + TaskRegistry.register(TaskType.SUB_WORKFLOW.name(), SubWorkflow.class); + TaskRegistry.register(TaskType.SWITCH.name(), Switch.class); + TaskRegistry.register(TaskType.TERMINATE.name(), Terminate.class); + TaskRegistry.register(TaskType.WAIT.name(), Wait.class); + TaskRegistry.register(TaskType.EVENT.name(), Event.class); + } + + public WorkflowExecutor(String url) { + ConductorClient client = new ConductorClient(url); + this.taskClient = new TaskClient(client); + this.workflowClient = new WorkflowClient(client); + this.metadataClient = new MetadataClient(client); + this.annotatedWorkerExecutor = new AnnotatedWorkerExecutor(this.taskClient); + initMonitor(); + } + + public WorkflowExecutor(ConductorClient client, int pollingInterval) { + this(new TaskClient(client), new WorkflowClient(client), new MetadataClient(client), pollingInterval); + } + + public WorkflowExecutor(ConductorClient client, AnnotatedWorkerExecutor annotatedWorkerExecutor) { + this(new TaskClient(client), new WorkflowClient(client), new MetadataClient(client), annotatedWorkerExecutor); + } + + public WorkflowExecutor(TaskClient taskClient, + WorkflowClient workflowClient, + MetadataClient metadataClient, + int pollingInterval) { + this.taskClient = taskClient; + this.workflowClient = workflowClient; + this.metadataClient = metadataClient; + this.annotatedWorkerExecutor = new AnnotatedWorkerExecutor(taskClient, pollingInterval); + initMonitor(); + } + + public WorkflowExecutor(TaskClient taskClient, + WorkflowClient workflowClient, + MetadataClient metadataClient, + AnnotatedWorkerExecutor annotatedWorkerExecutor) { + this.taskClient = taskClient; + this.workflowClient = workflowClient; + this.metadataClient = metadataClient; + this.annotatedWorkerExecutor = annotatedWorkerExecutor; + initMonitor(); + } + + private void initMonitor() { + scheduledWorkflowMonitor.scheduleAtFixedRate( + () -> { + for (Map.Entry> entry : runningWorkflowFutures.entrySet()) { + String workflowId = entry.getKey(); + CompletableFuture future = entry.getValue(); + Workflow workflow = workflowClient.getWorkflow(workflowId, true); + if (workflow.getStatus().isTerminal()) { + future.complete(workflow); + runningWorkflowFutures.remove(workflowId); + } + } + }, + 100, + 100, + TimeUnit.MILLISECONDS); + } + + public void initWorkers(String packagesToScan) { + annotatedWorkerExecutor.initWorkers(packagesToScan); + } + + public CompletableFuture executeWorkflow(String name, Integer version, Object input) { + CompletableFuture future = new CompletableFuture<>(); + String workflowId = startWorkflow(name, version, input); + runningWorkflowFutures.put(workflowId, future); + return future; + } + + public CompletableFuture executeWorkflow(ConductorWorkflow conductorWorkflow, Object input) { + CompletableFuture future = new CompletableFuture<>(); + String workflowId = startWorkflow(conductorWorkflow, input); + runningWorkflowFutures.put(workflowId, future); + return future; + } + + public String startWorkflow(ConductorWorkflow conductorWorkflow, Object input) { + Map inputMap = objectMapper.convertValue(input, MAP_STRING_OBJECT); + + StartWorkflowRequest request = new StartWorkflowRequest(); + request.setInput(inputMap); + request.setName(conductorWorkflow.getName()); + request.setVersion(conductorWorkflow.getVersion()); + request.setWorkflowDef(conductorWorkflow.toWorkflowDef()); + + return workflowClient.startWorkflow(request); + } + + public String startWorkflow(String name, Integer version, Object input) { + Map inputMap = objectMapper.convertValue(input, MAP_STRING_OBJECT); + + StartWorkflowRequest request = new StartWorkflowRequest(); + request.setInput(inputMap); + request.setName(name); + request.setVersion(version); + + return workflowClient.startWorkflow(request); + } + + public void loadTaskDefs(String resourcePath) throws IOException { + InputStream resource = WorkflowExecutor.class.getResourceAsStream(resourcePath); + if (resource != null) { + List taskDefs = objectMapper.readValue(resource, listOfTaskDefs); + loadMetadata(taskDefs); + } + } + + public void loadWorkflowDefs(String resourcePath) throws IOException { + InputStream resource = WorkflowExecutor.class.getResourceAsStream(resourcePath); + if (resource != null) { + WorkflowDef workflowDef = objectMapper.readValue(resource, WorkflowDef.class); + loadMetadata(workflowDef); + } + } + + public void loadMetadata(WorkflowDef workflowDef) { + metadataClient.registerWorkflowDef(workflowDef); + } + + public void loadMetadata(List taskDefs) { + metadataClient.registerTaskDefs(taskDefs); + } + + public void shutdown() { + scheduledWorkflowMonitor.shutdown(); + annotatedWorkerExecutor.shutdown(); + } + + public boolean registerWorkflow(WorkflowDef workflowDef, boolean overwrite) { + try { + if (overwrite) { + metadataClient.updateWorkflowDefs(Arrays.asList(workflowDef)); + } else { + metadataClient.registerWorkflowDef(workflowDef); + } + return true; + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + return false; + } + } + + public MetadataClient getMetadataClient() { + return metadataClient; + } + + public TaskClient getTaskClient() { + return taskClient; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java new file mode 100644 index 000000000..de4735241 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java @@ -0,0 +1,250 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.executor.task; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.config.ObjectMapperProvider; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.sdk.workflow.def.tasks.DynamicFork; +import com.netflix.conductor.sdk.workflow.def.tasks.DynamicForkInput; +import com.netflix.conductor.sdk.workflow.task.InputParam; +import com.netflix.conductor.sdk.workflow.task.OutputParam; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class AnnotatedWorker implements Worker { + + private final String name; + + private final Method workerMethod; + + private final Object obj; + + private final ObjectMapper om = new ObjectMapperProvider().getObjectMapper(); + + private int pollingInterval = 100; + + private final Set failedStatuses = + Set.of(TaskResult.Status.FAILED, TaskResult.Status.FAILED_WITH_TERMINAL_ERROR); + + public AnnotatedWorker(String name, Method workerMethod, Object obj) { + this.name = name; + this.workerMethod = workerMethod; + this.obj = obj; + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + @Override + public String getTaskDefName() { + return name; + } + + @Override + public TaskResult execute(Task task) { + TaskResult result = null; + try { + TaskContext context = TaskContext.set(task); + Object[] parameters = getInvocationParameters(task); + Object invocationResult = workerMethod.invoke(obj, parameters); + result = setValue(invocationResult, context.getTaskResult()); + if (!failedStatuses.contains(result.getStatus()) + && result.getCallbackAfterSeconds() > 0) { + result.setStatus(TaskResult.Status.IN_PROGRESS); + } + } catch (InvocationTargetException invocationTargetException) { + if (result == null) { + result = new TaskResult(task); + } + Throwable e = invocationTargetException.getCause(); + e.printStackTrace(); + if (e instanceof NonRetryableException) { + result.setStatus(TaskResult.Status.FAILED_WITH_TERMINAL_ERROR); + } else { + result.setStatus(TaskResult.Status.FAILED); + } + + result.setReasonForIncompletion(e.getMessage()); + StringBuilder stackTrace = new StringBuilder(); + for (StackTraceElement stackTraceElement : e.getStackTrace()) { + String className = stackTraceElement.getClassName(); + if (className.startsWith("jdk.") + || className.startsWith(AnnotatedWorker.class.getName())) { + break; + } + stackTrace.append(stackTraceElement); + stackTrace.append("\n"); + } + result.log(stackTrace.toString()); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return result; + } + + private Object[] getInvocationParameters(Task task) { + Class[] parameterTypes = workerMethod.getParameterTypes(); + Parameter[] parameters = workerMethod.getParameters(); + + if (parameterTypes.length == 1 && parameterTypes[0].equals(Task.class)) { + return new Object[] {task}; + } else if (parameterTypes.length == 1 && parameterTypes[0].equals(Map.class)) { + return new Object[] {task.getInputData()}; + } + + return getParameters(task, parameterTypes, parameters); + } + + private Object[] getParameters(Task task, Class[] parameterTypes, Parameter[] parameters) { + Annotation[][] parameterAnnotations = workerMethod.getParameterAnnotations(); + Object[] values = new Object[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + Annotation[] paramAnnotation = parameterAnnotations[i]; + if (paramAnnotation != null && paramAnnotation.length > 0) { + Type type = parameters[i].getParameterizedType(); + Class parameterType = parameterTypes[i]; + values[i] = getInputValue(task, parameterType, type, paramAnnotation); + } else { + values[i] = om.convertValue(task.getInputData(), parameterTypes[i]); + } + } + + return values; + } + + private Object getInputValue( + Task task, Class parameterType, Type type, Annotation[] paramAnnotation) { + InputParam ip = findInputParamAnnotation(paramAnnotation); + + if (ip == null) { + return om.convertValue(task.getInputData(), parameterType); + } + + final String name = ip.value(); + final Object value = task.getInputData().get(name); + if (value == null) { + return null; + } + + if (List.class.isAssignableFrom(parameterType)) { + List list = om.convertValue(value, List.class); + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + Class typeOfParameter = (Class) parameterizedType.getActualTypeArguments()[0]; + List parameterizedList = new ArrayList<>(); + for (Object item : list) { + parameterizedList.add(om.convertValue(item, typeOfParameter)); + } + + return parameterizedList; + } else { + return list; + } + } else { + return om.convertValue(value, parameterType); + } + } + + private static InputParam findInputParamAnnotation(Annotation[] paramAnnotation) { + return (InputParam) + Arrays.stream(paramAnnotation) + .filter(ann -> ann.annotationType().equals(InputParam.class)) + .findFirst() + .orElse(null); + } + + private TaskResult setValue(Object invocationResult, TaskResult result) { + + if (invocationResult == null) { + result.setStatus(TaskResult.Status.COMPLETED); + return result; + } + + OutputParam opAnnotation = + workerMethod.getAnnotatedReturnType().getAnnotation(OutputParam.class); + if (opAnnotation != null) { + + String name = opAnnotation.value(); + result.getOutputData().put(name, invocationResult); + result.setStatus(TaskResult.Status.COMPLETED); + return result; + + } else if (invocationResult instanceof TaskResult) { + + return (TaskResult) invocationResult; + + } else if (invocationResult instanceof Map) { + Map resultAsMap = (Map) invocationResult; + result.getOutputData().putAll(resultAsMap); + result.setStatus(TaskResult.Status.COMPLETED); + return result; + } else if (invocationResult instanceof String + || invocationResult instanceof Number + || invocationResult instanceof Boolean) { + result.getOutputData().put("result", invocationResult); + result.setStatus(TaskResult.Status.COMPLETED); + return result; + } else if (invocationResult instanceof List) { + + List resultAsList = om.convertValue(invocationResult, List.class); + result.getOutputData().put("result", resultAsList); + result.setStatus(TaskResult.Status.COMPLETED); + return result; + + } else if (invocationResult instanceof DynamicForkInput) { + DynamicForkInput forkInput = (DynamicForkInput) invocationResult; + List> tasks = forkInput.getTasks(); + List workflowTasks = new ArrayList<>(); + for (com.netflix.conductor.sdk.workflow.def.tasks.Task sdkTask : tasks) { + workflowTasks.addAll(sdkTask.getWorkflowDefTasks()); + } + result.getOutputData().put(DynamicFork.FORK_TASK_PARAM, workflowTasks); + result.getOutputData().put(DynamicFork.FORK_TASK_INPUT_PARAM, forkInput.getInputs()); + result.setStatus(TaskResult.Status.COMPLETED); + return result; + + } else { + Map resultAsMap = om.convertValue(invocationResult, Map.class); + result.getOutputData().putAll(resultAsMap); + result.setStatus(TaskResult.Status.COMPLETED); + return result; + } + } + + public void setPollingInterval(int pollingInterval) { + System.out.println( + "Setting the polling interval for " + getTaskDefName() + ", to " + pollingInterval); + this.pollingInterval = pollingInterval; + } + + @Override + public int getPollingInterval() { + System.out.println("Sending the polling interval to " + pollingInterval); + return pollingInterval; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java new file mode 100644 index 000000000..7296940db --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java @@ -0,0 +1,218 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.executor.task; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.client.automator.TaskRunnerConfigurer; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.sdk.workflow.task.WorkerTask; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; +import com.google.common.reflect.ClassPath; + + +public class AnnotatedWorkerExecutor { + + private static final Logger LOGGER = LoggerFactory.getLogger(AnnotatedWorkerExecutor.class); + + protected TaskClient taskClient; + + private TaskRunnerConfigurer taskRunner; + + protected List workers = new ArrayList<>(); + + protected Map workerToThreadCount = new HashMap<>(); + + protected Map workerToPollingInterval = new HashMap<>(); + + protected Map workerDomains = new HashMap<>(); + + private static final Set scannedPackages = new HashSet<>(); + + private final WorkerConfiguration workerConfiguration; + + public AnnotatedWorkerExecutor(TaskClient taskClient) { + this.taskClient = taskClient; + this.workerConfiguration = new WorkerConfiguration(); + } + + public AnnotatedWorkerExecutor(TaskClient taskClient, int pollingIntervalInMillis) { + this.taskClient = taskClient; + this.workerConfiguration = new WorkerConfiguration(pollingIntervalInMillis); + } + + public AnnotatedWorkerExecutor(TaskClient taskClient, WorkerConfiguration workerConfiguration) { + this.taskClient = taskClient; + this.workerConfiguration = workerConfiguration; + } + + /** + * Finds any worker implementation and starts polling for tasks + * + * @param basePackage list of packages - comma separated - to scan for annotated worker + * implementation + */ + public synchronized void initWorkers(String basePackage) { + scanWorkers(basePackage); + startPolling(); + } + + /** Shuts down the workers */ + public void shutdown() { + if (taskRunner != null) { + taskRunner.shutdown(); + } + } + + private void scanWorkers(String basePackage) { + try { + if (scannedPackages.contains(basePackage)) { + // skip + LOGGER.info("Package {} already scanned and will skip", basePackage); + return; + } + // Add here so to avoid infinite recursion where a class in the package contains the + // code to init workers + scannedPackages.add(basePackage); + List packagesToScan = new ArrayList<>(); + if (basePackage != null) { + String[] packages = basePackage.split(","); + Collections.addAll(packagesToScan, packages); + } + + LOGGER.info("packages to scan {}", packagesToScan); + + long s = System.currentTimeMillis(); + ClassPath.from(AnnotatedWorkerExecutor.class.getClassLoader()) + .getAllClasses() + .forEach( + classMeta -> { + String name = classMeta.getName(); + if (!includePackage(packagesToScan, name)) { + return; + } + try { + Class clazz = classMeta.load(); + Object obj = clazz.getConstructor().newInstance(); + addBean(obj); + } catch (Throwable t) { + // trace because many classes won't have a default no-args + // constructor and will fail + LOGGER.trace( + "Caught exception while loading and scanning class {}", + t.getMessage()); + } + }); + LOGGER.info( + "Took {} ms to scan all the classes, loading {} tasks", + (System.currentTimeMillis() - s), + workers.size()); + + } catch (Exception e) { + LOGGER.error("Error while scanning for workers: ", e); + } + } + + private boolean includePackage(List packagesToScan, String name) { + for (String scanPkg : packagesToScan) { + if (name.startsWith(scanPkg)) return true; + } + return false; + } + + public void addBean(Object bean) { + Class clazz = bean.getClass(); + for (Method method : clazz.getMethods()) { + WorkerTask annotation = method.getAnnotation(WorkerTask.class); + if (annotation == null) { + continue; + } + addMethod(annotation, method, bean); + } + } + + private void addMethod(WorkerTask annotation, Method method, Object bean) { + String name = annotation.value(); + + int threadCount = workerConfiguration.getThreadCount(name); + if (threadCount == 0) { + threadCount = annotation.threadCount(); + } + workerToThreadCount.put(name, threadCount); + + int pollingInterval = workerConfiguration.getPollingInterval(name); + if (pollingInterval == 0) { + pollingInterval = annotation.pollingInterval(); + } + workerToPollingInterval.put(name, pollingInterval); + + String domain = workerConfiguration.getDomain(name); + if (Strings.isNullOrEmpty(domain)) { + domain = annotation.domain(); + } + if (!Strings.isNullOrEmpty(domain)) { + workerDomains.put(name, domain); + } + + AnnotatedWorker executor = new AnnotatedWorker(name, method, bean); + executor.setPollingInterval(workerToPollingInterval.get(name)); + workers.add(executor); + + LOGGER.info( + "Adding worker for task {}, method {} with threadCount {} and polling interval set to {} ms", + name, + method, + threadCount, + pollingInterval); + } + + public void startPolling() { + if (workers.isEmpty()) { + return; + } + + LOGGER.info("Starting workers with threadCount {}", workerToThreadCount); + LOGGER.info("Worker domains {}", workerDomains); + + taskRunner = + new TaskRunnerConfigurer.Builder(taskClient, workers) + .withTaskThreadCount(workerToThreadCount) + .withTaskToDomain(workerDomains) + .build(); + + taskRunner.init(); + } + + @VisibleForTesting + List getWorkers() { + return workers; + } + + @VisibleForTesting + TaskRunnerConfigurer getTaskRunner() { + return taskRunner; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java new file mode 100644 index 000000000..4c8b9a898 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java @@ -0,0 +1,119 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.executor.task; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.function.Function; + +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.config.ObjectMapperProvider; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.sdk.workflow.def.tasks.DynamicFork; +import com.netflix.conductor.sdk.workflow.def.tasks.DynamicForkInput; +import com.netflix.conductor.sdk.workflow.task.InputParam; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class DynamicForkWorker implements Worker { + + private final int pollingInterval; + + private final Function workerMethod; + + private final String name; + + private final ObjectMapper objectMapper = new ObjectMapperProvider().getObjectMapper(); + + public DynamicForkWorker( + String name, Function workerMethod, int pollingInterval) { + this.name = name; + this.workerMethod = workerMethod; + this.pollingInterval = pollingInterval; + } + + @Override + public String getTaskDefName() { + return name; + } + + @Override + public TaskResult execute(Task task) { + TaskResult result = new TaskResult(task); + try { + + Object parameter = getInvocationParameters(this.workerMethod, task); + DynamicForkInput output = this.workerMethod.apply(parameter); + result.getOutputData().put(DynamicFork.FORK_TASK_PARAM, output.getTasks()); + result.getOutputData().put(DynamicFork.FORK_TASK_INPUT_PARAM, output.getInputs()); + result.setStatus(TaskResult.Status.COMPLETED); + + } catch (Exception e) { + throw new RuntimeException(e); + } + return result; + } + + @Override + public int getPollingInterval() { + return pollingInterval; + } + + private Object getInvocationParameters(Function function, Task task) { + InputParam annotation = null; + Class parameterType = null; + for (Method method : function.getClass().getDeclaredMethods()) { + if (method.getReturnType().equals(DynamicForkInput.class)) { + annotation = method.getParameters()[0].getAnnotation(InputParam.class); + parameterType = method.getParameters()[0].getType(); + } + } + + if (parameterType.equals(Task.class)) { + return task; + } else if (parameterType.equals(Map.class)) { + return task.getInputData(); + } + if (annotation != null) { + String name = annotation.value(); + Object value = task.getInputData().get(name); + return objectMapper.convertValue(value, parameterType); + } + return objectMapper.convertValue(task.getInputData(), parameterType); + } + + public static void main(String[] args) { + Function fn = + new Function() { + @Override + public DynamicForkInput apply(@InputParam("a") TaskDef s) { + return null; + } + }; + + for (Method method : fn.getClass().getDeclaredMethods()) { + if (method.getReturnType().equals(DynamicForkInput.class)) { + System.out.println( + "\n\n-->method: " + + method + + ", input: " + + method.getParameters()[0].getType()); + System.out.println("I take input as " + method.getParameters()[0].getType()); + InputParam annotation = method.getParameters()[0].getAnnotation(InputParam.class); + System.out.println("I have annotation " + annotation); + } + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java new file mode 100644 index 000000000..9e6116d4b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.executor.task; + +/** + * Runtime exception indicating the non-retriable error with the task execution. If thrown, the task + * will fail with FAILED_WITH_TERMINAL_ERROR and will not kick off retries. + */ +public class NonRetryableException extends RuntimeException { + + public NonRetryableException(String message) { + super(message); + } + + public NonRetryableException(String message, Throwable cause) { + super(message, cause); + } + + public NonRetryableException(Throwable cause) { + super(cause); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java new file mode 100644 index 000000000..69742e9b1 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java @@ -0,0 +1,80 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.executor.task; + + +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; + +/** Context for the task */ +public class TaskContext { + + public static final ThreadLocal TASK_CONTEXT_INHERITABLE_THREAD_LOCAL = + InheritableThreadLocal.withInitial(() -> null); + + public TaskContext(Task task, TaskResult taskResult) { + this.task = task; + this.taskResult = taskResult; + } + + public static TaskContext get() { + return TASK_CONTEXT_INHERITABLE_THREAD_LOCAL.get(); + } + + public static TaskContext set(Task task) { + TaskResult result = new TaskResult(task); + TaskContext context = new TaskContext(task, result); + TASK_CONTEXT_INHERITABLE_THREAD_LOCAL.set(context); + return context; + } + + private final Task task; + + private final TaskResult taskResult; + + public String getWorkflowInstanceId() { + return task.getWorkflowInstanceId(); + } + + public String getTaskId() { + return task.getTaskId(); + } + + public int getRetryCount() { + return task.getRetryCount(); + } + + public int getPollCount() { + return task.getPollCount(); + } + + public long getCallbackAfterSeconds() { + return task.getCallbackAfterSeconds(); + } + + public void addLog(String log) { + this.taskResult.log(log); + } + + public Task getTask() { + return task; + } + + public TaskResult getTaskResult() { + return taskResult; + } + + public void setCallbackAfter(int seconds) { + this.taskResult.setCallbackAfterSeconds(seconds); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java new file mode 100644 index 000000000..3a5e276e2 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.executor.task; + +public class WorkerConfiguration { + + private int defaultPollingInterval = 0; + + public WorkerConfiguration(int defaultPollingInterval) { + this.defaultPollingInterval = defaultPollingInterval; + } + + public WorkerConfiguration() {} + + public int getPollingInterval(String taskName) { + return defaultPollingInterval; + } + + public int getThreadCount(String taskName) { + return 0; + } + + public String getDomain(String taskName) { + return null; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java new file mode 100644 index 000000000..3ae32421e --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.task; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface InputParam { + String value(); + + boolean required() default false; +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java new file mode 100644 index 000000000..152c92480 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java @@ -0,0 +1,24 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.task; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE_USE) +public @interface OutputParam { + String value(); +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java new file mode 100644 index 000000000..7f481ff30 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java @@ -0,0 +1,32 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.task; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Identifies a simple worker task. */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface WorkerTask { + String value(); + + // No. of threads to use for executing the task + int threadCount() default 1; + + int pollingInterval() default 100; + + String domain() default ""; +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java new file mode 100644 index 000000000..ac7bb5fde --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java @@ -0,0 +1,105 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.utils; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +public class InputOutputGetter { + + public enum Field { + input, + output + } + + public static final class Map { + private final String parent; + + public Map(String parent) { + this.parent = parent; + } + + public String get(String key) { + return parent + "." + key + "}"; + } + + public Map map(String key) { + return new Map(parent + "." + key); + } + + public List list(String key) { + return new List(parent + "." + key); + } + + @Override + public String toString() { + return parent + "}"; + } + } + + public static final class List { + + private final String parent; + + public List(String parent) { + this.parent = parent; + } + + public List list(String key) { + return new List(parent + "." + key); + } + + public Map map(String key) { + return new Map(parent + "." + key); + } + + public String get(String key, int index) { + return parent + "." + key + "[" + index + "]}"; + } + + public String get(int index) { + return parent + "[" + index + "]}"; + } + + @Override + public String toString() { + return parent + "}"; + } + } + + private final String name; + + private final Field field; + + public InputOutputGetter(String name, Field field) { + this.name = name; + this.field = field; + } + + public String get(String key) { + return "${" + name + "." + field + "." + key + "}"; + } + + public String getParent() { + return "${" + name + "." + field + "}"; + } + + @JsonIgnore + public Map map(String key) { + return new Map("${" + name + "." + field + "." + key); + } + + @JsonIgnore + public List list(String key) { + return new List("${" + name + "." + field + "." + key); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java new file mode 100644 index 000000000..e52249f69 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java @@ -0,0 +1,39 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.utils; + +import java.util.HashMap; +import java.util.Map; + +public class MapBuilder { + private final Map map = new HashMap<>(); + + public MapBuilder add(String key, String value) { + map.put(key, value); + return this; + } + + public MapBuilder add(String key, Number value) { + map.put(key, value); + return this; + } + + public MapBuilder add(String key, MapBuilder value) { + map.put(key, value.build()); + return this; + } + + public Map build() { + return map; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java new file mode 100644 index 000000000..1e7f02d64 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java @@ -0,0 +1,548 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def; + +import java.time.Duration; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.sdk.workflow.def.tasks.DoWhile; +import com.netflix.conductor.sdk.workflow.def.tasks.Dynamic; +import com.netflix.conductor.sdk.workflow.def.tasks.DynamicFork; +import com.netflix.conductor.sdk.workflow.def.tasks.Event; +import com.netflix.conductor.sdk.workflow.def.tasks.ForkJoin; +import com.netflix.conductor.sdk.workflow.def.tasks.Http; +import com.netflix.conductor.sdk.workflow.def.tasks.JQ; +import com.netflix.conductor.sdk.workflow.def.tasks.Javascript; +import com.netflix.conductor.sdk.workflow.def.tasks.Join; +import com.netflix.conductor.sdk.workflow.def.tasks.SetVariable; +import com.netflix.conductor.sdk.workflow.def.tasks.SimpleTask; +import com.netflix.conductor.sdk.workflow.def.tasks.SubWorkflow; +import com.netflix.conductor.sdk.workflow.def.tasks.Switch; +import com.netflix.conductor.sdk.workflow.def.tasks.Task; +import com.netflix.conductor.sdk.workflow.def.tasks.TaskRegistry; +import com.netflix.conductor.sdk.workflow.def.tasks.Terminate; +import com.netflix.conductor.sdk.workflow.def.tasks.Wait; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TaskConversionsTests { + + static { + WorkflowExecutor.initTaskImplementations(); + } + + @Test + public void testSimpleTaskConversion() { + SimpleTask simpleTask = new SimpleTask("task_name", "task_ref_name"); + + Map map = new HashMap<>(); + map.put("key11", "value11"); + map.put("key12", 100); + + simpleTask.input("key1", "value"); + simpleTask.input("key2", 42); + simpleTask.input("key3", true); + simpleTask.input("key4", map); + + WorkflowTask workflowTask = simpleTask.getWorkflowDefTasks().get(0); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue(fromWorkflowTask instanceof SimpleTask); + SimpleTask simpleTaskFromWorkflowTask = (SimpleTask) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(simpleTask.getName(), fromWorkflowTask.getName()); + assertEquals(simpleTask.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(simpleTask.getTaskDef(), simpleTaskFromWorkflowTask.getTaskDef()); + assertEquals(simpleTask.getType(), simpleTaskFromWorkflowTask.getType()); + assertEquals(simpleTask.getStartDelay(), simpleTaskFromWorkflowTask.getStartDelay()); + assertEquals(simpleTask.getInput(), simpleTaskFromWorkflowTask.getInput()); + } + + @Test + public void testDynamicTaskCoversion() { + Dynamic dynamicTask = new Dynamic("task_name", "task_ref_name"); + + WorkflowTask workflowTask = dynamicTask.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getInputParameters().get(Dynamic.TASK_NAME_INPUT_PARAM)); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue(fromWorkflowTask instanceof Dynamic); + Dynamic taskFromWorkflowTask = (Dynamic) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(dynamicTask.getName(), fromWorkflowTask.getName()); + assertEquals(dynamicTask.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(dynamicTask.getType(), taskFromWorkflowTask.getType()); + assertEquals(dynamicTask.getStartDelay(), taskFromWorkflowTask.getStartDelay()); + assertEquals(dynamicTask.getInput(), taskFromWorkflowTask.getInput()); + } + + @Test + public void testForkTaskConversion() { + SimpleTask task1 = new SimpleTask("task1", "task1"); + SimpleTask task2 = new SimpleTask("task2", "task2"); + SimpleTask task3 = new SimpleTask("task3", "task3"); + + ForkJoin forkTask = + new ForkJoin("task_ref_name", new Task[] {task1}, new Task[] {task2, task3}); + + WorkflowTask workflowTask = forkTask.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getForkTasks()); + assertFalse(workflowTask.getForkTasks().isEmpty()); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue(fromWorkflowTask instanceof ForkJoin); + ForkJoin taskFromWorkflowTask = (ForkJoin) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(forkTask.getName(), fromWorkflowTask.getName()); + assertEquals(forkTask.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(forkTask.getType(), taskFromWorkflowTask.getType()); + assertEquals(forkTask.getInput(), taskFromWorkflowTask.getInput()); + + assertEquals( + forkTask.getForkedTasks().length, taskFromWorkflowTask.getForkedTasks().length); + for (int i = 0; i < forkTask.getForkedTasks().length; i++) { + assertEquals( + forkTask.getForkedTasks()[i].length, + taskFromWorkflowTask.getForkedTasks()[i].length); + for (int j = 0; j < forkTask.getForkedTasks()[i].length; j++) { + assertEquals( + forkTask.getForkedTasks()[i][j].getTaskReferenceName(), + taskFromWorkflowTask.getForkedTasks()[i][j].getTaskReferenceName()); + + assertEquals( + forkTask.getForkedTasks()[i][j].getName(), + taskFromWorkflowTask.getForkedTasks()[i][j].getName()); + + assertEquals( + forkTask.getForkedTasks()[i][j].getType(), + taskFromWorkflowTask.getForkedTasks()[i][j].getType()); + } + } + } + + @Test + public void testDynamicForkTaskConversion() { + DynamicFork dynamicTask = new DynamicFork("task_ref_name", "forkTasks", "forkTaskInputs"); + + WorkflowTask workflowTask = dynamicTask.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getInputParameters()); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue(fromWorkflowTask instanceof DynamicFork); + DynamicFork taskFromWorkflowTask = (DynamicFork) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(dynamicTask.getName(), fromWorkflowTask.getName()); + assertEquals(dynamicTask.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(dynamicTask.getType(), taskFromWorkflowTask.getType()); + assertEquals(dynamicTask.getStartDelay(), taskFromWorkflowTask.getStartDelay()); + assertEquals(dynamicTask.getInput(), taskFromWorkflowTask.getInput()); + assertEquals( + dynamicTask.getForkTasksParameter(), taskFromWorkflowTask.getForkTasksParameter()); + assertEquals( + dynamicTask.getForkTasksInputsParameter(), + taskFromWorkflowTask.getForkTasksInputsParameter()); + } + + @Test + public void testDoWhileConversion() { + SimpleTask task1 = new SimpleTask("task_name", "task_ref_name"); + SimpleTask task2 = new SimpleTask("task_name", "task_ref_name"); + + DoWhile doWhileTask = new DoWhile("task_ref_name", 2, task1, task2); + + WorkflowTask workflowTask = doWhileTask.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getInputParameters()); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue(fromWorkflowTask instanceof DoWhile); + DoWhile taskFromWorkflowTask = (DoWhile) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(doWhileTask.getName(), fromWorkflowTask.getName()); + assertEquals(doWhileTask.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(doWhileTask.getType(), taskFromWorkflowTask.getType()); + assertEquals(doWhileTask.getStartDelay(), taskFromWorkflowTask.getStartDelay()); + assertEquals(doWhileTask.getInput(), taskFromWorkflowTask.getInput()); + + assertEquals(doWhileTask.getLoopCondition(), taskFromWorkflowTask.getLoopCondition()); + assertEquals( + doWhileTask.getLoopTasks().stream() + .map(Task::getTaskReferenceName) + .sorted() + .collect(Collectors.toSet()), + taskFromWorkflowTask.getLoopTasks().stream() + .map(Task::getTaskReferenceName) + .sorted() + .collect(Collectors.toSet())); + } + + @Test + public void testJoin() { + + Join joinTask = new Join("task_ref_name", "task1", "task2"); + + WorkflowTask workflowTask = joinTask.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getInputParameters()); + assertNotNull(workflowTask.getJoinOn()); + assertTrue(!workflowTask.getJoinOn().isEmpty()); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue( + fromWorkflowTask instanceof Join, + "task is not of type Join, but of type " + fromWorkflowTask.getClass().getName()); + Join taskFromWorkflowTask = (Join) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(joinTask.getName(), fromWorkflowTask.getName()); + assertEquals(joinTask.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(joinTask.getType(), taskFromWorkflowTask.getType()); + assertEquals(joinTask.getStartDelay(), taskFromWorkflowTask.getStartDelay()); + assertEquals(joinTask.getInput(), taskFromWorkflowTask.getInput()); + + assertEquals(joinTask.getJoinOn().length, taskFromWorkflowTask.getJoinOn().length); + assertEquals( + Arrays.asList(joinTask.getJoinOn()).stream().sorted().collect(Collectors.toSet()), + Arrays.asList(taskFromWorkflowTask.getJoinOn()).stream() + .sorted() + .collect(Collectors.toSet())); + } + + @Test + public void testEvent() { + + Event eventTask = new Event("task_ref_name", "sqs:queue11"); + + WorkflowTask workflowTask = eventTask.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getInputParameters()); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue( + fromWorkflowTask instanceof Event, + "task is not of type Event, but of type " + fromWorkflowTask.getClass().getName()); + Event taskFromWorkflowTask = (Event) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(eventTask.getName(), fromWorkflowTask.getName()); + assertEquals(eventTask.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(eventTask.getType(), taskFromWorkflowTask.getType()); + assertEquals(eventTask.getStartDelay(), taskFromWorkflowTask.getStartDelay()); + assertEquals(eventTask.getInput(), taskFromWorkflowTask.getInput()); + assertEquals(eventTask.getSink(), taskFromWorkflowTask.getSink()); + } + + @Test + public void testSetVariableConversion() { + + SetVariable setVariableTask = new SetVariable("task_ref_name"); + + WorkflowTask workflowTask = setVariableTask.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getInputParameters()); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue( + fromWorkflowTask instanceof SetVariable, + "task is not of type SetVariable, but of type " + + fromWorkflowTask.getClass().getName()); + SetVariable taskFromWorkflowTask = (SetVariable) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(setVariableTask.getName(), fromWorkflowTask.getName()); + assertEquals( + setVariableTask.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(setVariableTask.getType(), taskFromWorkflowTask.getType()); + assertEquals(setVariableTask.getStartDelay(), taskFromWorkflowTask.getStartDelay()); + assertEquals(setVariableTask.getInput(), taskFromWorkflowTask.getInput()); + } + + @Test + public void testSubWorkflowConversion() { + + SubWorkflow subWorkflowTask = new SubWorkflow("task_ref_name", "sub_flow", 2); + + WorkflowTask workflowTask = subWorkflowTask.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getInputParameters()); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue( + fromWorkflowTask instanceof SubWorkflow, + "task is not of type SubWorkflow, but of type " + + fromWorkflowTask.getClass().getName()); + SubWorkflow taskFromWorkflowTask = (SubWorkflow) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(subWorkflowTask.getName(), fromWorkflowTask.getName()); + assertEquals( + subWorkflowTask.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(subWorkflowTask.getType(), taskFromWorkflowTask.getType()); + assertEquals(subWorkflowTask.getStartDelay(), taskFromWorkflowTask.getStartDelay()); + assertEquals(subWorkflowTask.getInput(), taskFromWorkflowTask.getInput()); + assertEquals(subWorkflowTask.getWorkflowName(), taskFromWorkflowTask.getWorkflowName()); + assertEquals( + subWorkflowTask.getWorkflowVersion(), taskFromWorkflowTask.getWorkflowVersion()); + } + + @Test + public void testSwitchConversion() { + + SimpleTask task1 = new SimpleTask("task_name", "task_ref_name1"); + SimpleTask task2 = new SimpleTask("task_name", "task_ref_name2"); + SimpleTask task3 = new SimpleTask("task_name", "task_ref_name3"); + + Switch decision = new Switch("switch", "${workflow.input.zip"); + decision.switchCase("caseA", task1); + decision.switchCase("caseB", task2, task3); + + decision.defaultCase( + new Terminate("terminate", Workflow.WorkflowStatus.FAILED, "", new HashMap<>())); + + WorkflowTask workflowTask = decision.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getInputParameters()); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue( + fromWorkflowTask instanceof Switch, + "task is not of type Switch, but of type " + fromWorkflowTask.getClass().getName()); + Switch taskFromWorkflowTask = (Switch) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(decision.getName(), fromWorkflowTask.getName()); + assertEquals(decision.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(decision.getType(), taskFromWorkflowTask.getType()); + assertEquals(decision.getStartDelay(), taskFromWorkflowTask.getStartDelay()); + assertEquals(decision.getInput(), taskFromWorkflowTask.getInput()); + // TODO: ADD CASES FOR DEFAULT CASE + assertEquals(decision.getBranches().keySet(), taskFromWorkflowTask.getBranches().keySet()); + assertEquals( + decision.getBranches().values().stream() + .map( + tasks -> + tasks.stream() + .map(Task::getTaskReferenceName) + .collect(Collectors.toSet())) + .collect(Collectors.toSet()), + taskFromWorkflowTask.getBranches().values().stream() + .map( + tasks -> + tasks.stream() + .map(Task::getTaskReferenceName) + .collect(Collectors.toSet())) + .collect(Collectors.toSet())); + assertEquals(decision.getBranches().size(), taskFromWorkflowTask.getBranches().size()); + } + + @Test + public void testTerminateConversion() { + + Terminate terminateTask = + new Terminate("terminate", Workflow.WorkflowStatus.FAILED, "", new HashMap<>()); + + WorkflowTask workflowTask = terminateTask.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getInputParameters()); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue( + fromWorkflowTask instanceof Terminate, + "task is not of type Terminate, but of type " + + fromWorkflowTask.getClass().getName()); + Terminate taskFromWorkflowTask = (Terminate) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(terminateTask.getName(), fromWorkflowTask.getName()); + assertEquals(terminateTask.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(terminateTask.getType(), taskFromWorkflowTask.getType()); + assertEquals(terminateTask.getStartDelay(), taskFromWorkflowTask.getStartDelay()); + assertEquals(terminateTask.getInput(), taskFromWorkflowTask.getInput()); + } + + @Test + public void testWaitConversion() { + + Wait waitTask = new Wait("terminate"); + + WorkflowTask workflowTask = waitTask.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getInputParameters()); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue( + fromWorkflowTask instanceof Wait, + "task is not of type Wait, but of type " + fromWorkflowTask.getClass().getName()); + Wait taskFromWorkflowTask = (Wait) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(waitTask.getName(), fromWorkflowTask.getName()); + assertEquals(waitTask.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(waitTask.getType(), taskFromWorkflowTask.getType()); + assertEquals(waitTask.getStartDelay(), taskFromWorkflowTask.getStartDelay()); + assertEquals(waitTask.getInput(), taskFromWorkflowTask.getInput()); + + // Wait for 10 seconds + waitTask = new Wait("wait_for_10_seconds", Duration.of(10, ChronoUnit.SECONDS)); + workflowTask = waitTask.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getInputParameters()); + assertEquals("10s", workflowTask.getInputParameters().get(Wait.DURATION_INPUT)); + + // Wait for 10 minutes + waitTask = new Wait("wait_for_10_seconds", Duration.of(10, ChronoUnit.MINUTES)); + workflowTask = waitTask.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getInputParameters()); + assertEquals("600s", workflowTask.getInputParameters().get(Wait.DURATION_INPUT)); + + // Wait till next week some time + ZonedDateTime nextWeek = ZonedDateTime.now().plusDays(7); + String formattedDateTime = Wait.dateTimeFormatter.format(nextWeek); + waitTask = new Wait("wait_till_next_week", nextWeek); + workflowTask = waitTask.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getInputParameters()); + assertEquals(formattedDateTime, workflowTask.getInputParameters().get(Wait.UNTIL_INPUT)); + } + + @Test + public void testHttpConverter() { + + Http httpTask = new Http("terminate"); + Http.Input input = new Http.Input(); + input.setUri("http://example.com"); + input.setMethod(Http.Input.HttpMethod.POST); + input.setBody("Hello World"); + input.setReadTimeOut(100); + Map headers = new HashMap<>(); + headers.put("X-AUTHORIZATION", "my_api_key"); + input.setHeaders(headers); + + httpTask.input(input); + + WorkflowTask workflowTask = httpTask.getWorkflowDefTasks().get(0); + assertNotNull(workflowTask.getInputParameters()); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue( + fromWorkflowTask instanceof Http, + "task is not of type Http, but of type " + fromWorkflowTask.getClass().getName()); + Http taskFromWorkflowTask = (Http) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(httpTask.getName(), fromWorkflowTask.getName()); + assertEquals(httpTask.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(httpTask.getType(), taskFromWorkflowTask.getType()); + assertEquals(httpTask.getStartDelay(), taskFromWorkflowTask.getStartDelay()); + assertEquals(httpTask.getInput(), taskFromWorkflowTask.getInput()); + assertEquals(httpTask.getHttpRequest(), taskFromWorkflowTask.getHttpRequest()); + + System.out.println(httpTask.getInput()); + System.out.println(taskFromWorkflowTask.getInput()); + } + + @Test + public void testJQTaskConversion() { + JQ jqTask = new JQ("task_name", "{ key3: (.key1.value1 + .key2.value2) }"); + + Map map = new HashMap<>(); + map.put("key11", "value11"); + map.put("key12", 100); + + jqTask.input("key1", "value"); + jqTask.input("key2", 42); + jqTask.input("key3", true); + jqTask.input("key4", map); + + WorkflowTask workflowTask = jqTask.getWorkflowDefTasks().get(0); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue(fromWorkflowTask instanceof JQ, "Found the instance " + fromWorkflowTask); + JQ taskFromWorkflowTask = (JQ) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(jqTask.getName(), fromWorkflowTask.getName()); + assertEquals(jqTask.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(jqTask.getQueryExpression(), taskFromWorkflowTask.getQueryExpression()); + assertEquals(jqTask.getType(), taskFromWorkflowTask.getType()); + assertEquals(jqTask.getInput(), taskFromWorkflowTask.getInput()); + } + + @Test + public void testInlineTaskConversion() { + + Javascript inlineTask = + new Javascript( + "task_name", + "function e() { if ($.value == 1){return {\"result\": true}} else { return {\"result\": false}}} e();"); + inlineTask.validate(); + + Map map = new HashMap<>(); + map.put("key11", "value11"); + map.put("key12", 100); + + inlineTask.input("key1", "value"); + inlineTask.input("key2", 42); + inlineTask.input("key3", true); + inlineTask.input("key4", map); + + WorkflowTask workflowTask = inlineTask.getWorkflowDefTasks().get(0); + + Task fromWorkflowTask = TaskRegistry.getTask(workflowTask); + assertTrue( + fromWorkflowTask instanceof Javascript, "Found the instance " + fromWorkflowTask); + Javascript taskFromWorkflowTask = (Javascript) fromWorkflowTask; + + assertNotNull(fromWorkflowTask); + assertEquals(inlineTask.getName(), fromWorkflowTask.getName()); + assertEquals(inlineTask.getTaskReferenceName(), fromWorkflowTask.getTaskReferenceName()); + assertEquals(inlineTask.getExpression(), taskFromWorkflowTask.getExpression()); + assertEquals(inlineTask.getType(), taskFromWorkflowTask.getType()); + assertEquals(inlineTask.getInput(), taskFromWorkflowTask.getInput()); + } + + @Test + public void testJavascriptValidation() { + //Validation is using Nashorn engine which was removed in Java 15: https://openjdk.org/jeps/372 + // Skipping it if version is greater than 11 + var version = Runtime.version(); + if (version.feature() > 11) { + //FIXME + System.err.println("WARNING: skipping testJavascriptValidation because Nashorn is not supported"); + return; + } + + // This script has syntax errors "==>" + Javascript inlineTask = new Javascript( + "task_name", + "function e() { if ($.value ==> 1){return {\"result\": true}} else { return {\"result\": false}}} e();"); + assertThrows(ValidationError.class, inlineTask::validate); + + // This script does NOT have errors + inlineTask = new Javascript( + "task_name", + "function e() { if ($.value == 1){return {\"result\": true}} else { return {\"result\": false}}} e();"); + var result = inlineTask.validate(); + assertNotNull(result); + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java new file mode 100644 index 000000000..74922c831 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java @@ -0,0 +1,225 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.conductor.common.metadata.tasks.TaskType; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.sdk.testing.WorkflowTestRunner; +import com.netflix.conductor.sdk.workflow.def.tasks.DynamicFork; +import com.netflix.conductor.sdk.workflow.def.tasks.DynamicForkInput; +import com.netflix.conductor.sdk.workflow.def.tasks.ForkJoin; +import com.netflix.conductor.sdk.workflow.def.tasks.Javascript; +import com.netflix.conductor.sdk.workflow.def.tasks.SimpleTask; +import com.netflix.conductor.sdk.workflow.def.tasks.Switch; +import com.netflix.conductor.sdk.workflow.def.tasks.Task; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; +import com.netflix.conductor.sdk.workflow.task.InputParam; +import com.netflix.conductor.sdk.workflow.task.OutputParam; +import com.netflix.conductor.sdk.workflow.task.WorkerTask; +import com.netflix.conductor.sdk.workflow.testing.TestWorkflowInput; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +@Disabled +public class WorkflowCreationTests { + + private static final Logger LOGGER = LoggerFactory.getLogger(WorkflowCreationTests.class); + + private static WorkflowExecutor executor; + + private static WorkflowTestRunner runner; + + @BeforeAll + public static void init() throws IOException { + runner = new WorkflowTestRunner(8080, "3.7.3"); + runner.init("com.netflix.conductor.sdk"); + executor = runner.getWorkflowExecutor(); + } + + @AfterAll + public static void cleanUp() { + runner.shutdown(); + } + + @WorkerTask("get_user_info") + public @OutputParam("zipCode") String getZipCode(@InputParam("name") String userName) { + return "95014"; + } + + @WorkerTask("task2") + public @OutputParam("greetings") String task2() { + return "Hello World"; + } + + @WorkerTask("task3") + public @OutputParam("greetings") String task3() { + return "Hello World-3"; + } + + @WorkerTask("fork_gen") + public DynamicForkInput generateDynamicFork() { + DynamicForkInput forks = new DynamicForkInput(); + Map inputs = new HashMap<>(); + forks.setInputs(inputs); + List> tasks = new ArrayList<>(); + forks.setTasks(tasks); + + for (int i = 0; i < 3; i++) { + SimpleTask task = new SimpleTask("task2", "fork_task_" + i); + tasks.add(task); + HashMap taskInput = new HashMap<>(); + taskInput.put("key", "value"); + taskInput.put("key2", 101); + inputs.put(task.getTaskReferenceName(), taskInput); + } + return forks; + } + + private ConductorWorkflow registerTestWorkflow() + throws InterruptedException { + InputStream script = getClass().getResourceAsStream("/script.js"); + SimpleTask getUserInfo = new SimpleTask("get_user_info", "get_user_info"); + getUserInfo.input("name", ConductorWorkflow.input.get("name")); + + SimpleTask sendToCupertino = new SimpleTask("task2", "cupertino"); + SimpleTask sendToNYC = new SimpleTask("task2", "nyc"); + + int len = 4; + Task[][] parallelTasks = new Task[len][1]; + for (int i = 0; i < len; i++) { + parallelTasks[i][0] = new SimpleTask("task2", "task_parallel_" + i); + } + + WorkflowBuilder builder = new WorkflowBuilder<>(executor); + TestWorkflowInput defaultInput = new TestWorkflowInput(); + defaultInput.setName("defaultName"); + + builder.name("sdk_workflow_example") + .version(1) + .ownerEmail("hello@example.com") + .description("Example Workflow") + .restartable(true) + .variables(new WorkflowState()) + .timeoutPolicy(WorkflowDef.TimeoutPolicy.TIME_OUT_WF, 100) + .defaultInput(defaultInput) + .add(new Javascript("js", script)) + .add(new ForkJoin("parallel", parallelTasks)) + .add(getUserInfo) + .add( + new Switch("decide2", "${workflow.input.zipCode}") + .switchCase("95014", sendToCupertino) + .switchCase("10121", sendToNYC)) + // .add(new SubWorkflow("subflow", "sub_workflow_example", 5)) + .add(new SimpleTask("task2", "task222")) + .add(new DynamicFork("dynamic_fork", new SimpleTask("fork_gen", "fork_gen"))); + + ConductorWorkflow workflow = builder.build(); + boolean registered = workflow.registerWorkflow(true, true); + assertTrue(registered); + + return workflow; + } + + @Test + public void verifyCreatedWorkflow() throws Exception { + ConductorWorkflow conductorWorkflow = registerTestWorkflow(); + WorkflowDef def = conductorWorkflow.toWorkflowDef(); + assertNotNull(def); + assertTrue( + def.getTasks() + .get(def.getTasks().size() - 2) + .getType() + .equals(TaskType.TASK_TYPE_FORK_JOIN_DYNAMIC)); + assertTrue( + def.getTasks() + .get(def.getTasks().size() - 1) + .getType() + .equals(TaskType.TASK_TYPE_JOIN)); + } + + @Test + public void verifyInlineWorkflowExecution() throws ValidationError { + TestWorkflowInput workflowInput = new TestWorkflowInput("username", "10121", "US"); + try { + Workflow run = registerTestWorkflow().execute(workflowInput).get(10, TimeUnit.SECONDS); + assertEquals( + Workflow.WorkflowStatus.COMPLETED, + run.getStatus(), + run.getReasonForIncompletion()); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + @Test + public void testWorkflowExecutionByName() throws ExecutionException, InterruptedException { + + // Register the workflow first + registerTestWorkflow(); + + TestWorkflowInput input = new TestWorkflowInput("username", "10121", "US"); + + ConductorWorkflow conductorWorkflow = + new ConductorWorkflow(executor) + .from("sdk_workflow_example", null); + + CompletableFuture execution = conductorWorkflow.execute(input); + try { + execution.get(10, TimeUnit.SECONDS); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + @Test + public void verifyWorkflowExecutionFailsIfNotExists() + throws ExecutionException, InterruptedException { + + // Register the workflow first + registerTestWorkflow(); + + TestWorkflowInput input = new TestWorkflowInput("username", "10121", "US"); + + try { + ConductorWorkflow conductorWorkflow = + new ConductorWorkflow(executor) + .from("non_existent_workflow", null); + conductorWorkflow.execute(input); + fail("execution should have failed"); + } catch (Exception e) { + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java new file mode 100644 index 000000000..fcd168563 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java @@ -0,0 +1,55 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def; + +import org.junit.jupiter.api.Test; + +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.sdk.workflow.def.tasks.SimpleTask; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class WorkflowDefTaskTests { + + static { + WorkflowExecutor.initTaskImplementations(); + } + + @Test + public void testWorkflowDefTaskWithStartDelay() { + SimpleTask simpleTask = new SimpleTask("task_name", "task_ref_name"); + int startDelay = 5; + + simpleTask.setStartDelay(startDelay); + + WorkflowTask workflowTask = simpleTask.getWorkflowDefTasks().get(0); + + assertEquals(simpleTask.getStartDelay(), workflowTask.getStartDelay()); + assertEquals(startDelay, simpleTask.getStartDelay()); + assertEquals(startDelay, workflowTask.getStartDelay()); + } + + @Test + public void testWorkflowDefTaskWithOptionalEnabled() { + SimpleTask simpleTask = new SimpleTask("task_name", "task_ref_name"); + + simpleTask.setOptional(true); + + WorkflowTask workflowTask = simpleTask.getWorkflowDefTasks().get(0); + + assertEquals(simpleTask.getStartDelay(), workflowTask.getStartDelay()); + assertEquals(true, simpleTask.isOptional()); + assertEquals(true, workflowTask.isOptional()); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java new file mode 100644 index 000000000..d30b01e8e --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.def; + +public class WorkflowState { + + private boolean paymentCompleted; + + private int timeTaken; + + public boolean isPaymentCompleted() { + return paymentCompleted; + } + + public void setPaymentCompleted(boolean paymentCompleted) { + this.paymentCompleted = paymentCompleted; + } + + public int getTimeTaken() { + return timeTaken; + } + + public void setTimeTaken(int timeTaken) { + this.timeTaken = timeTaken; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java new file mode 100644 index 000000000..9e328d0a6 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java @@ -0,0 +1,344 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.executor.task; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.netflix.conductor.client.automator.TaskRunnerConfigurer; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.sdk.workflow.task.InputParam; +import com.netflix.conductor.sdk.workflow.task.OutputParam; +import com.netflix.conductor.sdk.workflow.task.WorkerTask; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +public class AnnotatedWorkerTests { + + static class Car { + String brand; + + String getBrand() { + return brand; + } + + void setBrand(String brand) { + this.brand = brand; + } + } + + static class Bike { + String brand; + + String getBrand() { + return brand; + } + + void setBrand(String brand) { + this.brand = brand; + } + } + + static class CarWorker { + @WorkerTask("test_1") + public @OutputParam("result") List doWork(@InputParam("input") List input) { + return input; + } + } + + @Test + @DisplayName("it should handle null values when InputParam is a List") + void nullListAsInputParam() throws NoSuchMethodException { + var worker = new CarWorker(); + var annotatedWorker = + new AnnotatedWorker( + "test_1", worker.getClass().getMethod("doWork", List.class), worker); + + var task = new Task(); + task.setStatus(Task.Status.IN_PROGRESS); + + var result0 = annotatedWorker.execute(task); + var outputData = result0.getOutputData(); + assertNull(outputData.get("result")); + } + + @Test + @DisplayName("it should handle an empty List as InputParam") + void emptyListAsInputParam() throws NoSuchMethodException { + var worker = new CarWorker(); + var annotatedWorker = + new AnnotatedWorker( + "test_1", worker.getClass().getMethod("doWork", List.class), worker); + + var task = new Task(); + task.setStatus(Task.Status.IN_PROGRESS); + task.setInputData(Map.of("input", List.of())); + + var result0 = annotatedWorker.execute(task); + var outputData = result0.getOutputData(); + + @SuppressWarnings("unchecked") + List result = (List) outputData.get("result"); + assertTrue(result.isEmpty()); + } + + @Test + @DisplayName("it should handle a non empty List as InputParam") + void nonEmptyListAsInputParam() throws NoSuchMethodException { + var worker = new CarWorker(); + var annotatedWorker = + new AnnotatedWorker( + "test_1", worker.getClass().getMethod("doWork", List.class), worker); + + var task = new Task(); + task.setStatus(Task.Status.IN_PROGRESS); + task.setInputData(Map.of("input", List.of(Map.of("brand", "BMW")))); + + var result0 = annotatedWorker.execute(task); + var outputData = result0.getOutputData(); + + @SuppressWarnings("unchecked") + List result = (List) outputData.get("result"); + assertEquals(1, result.size()); + + Car car = result.get(0); + assertEquals("BMW", car.getBrand()); + } + + @SuppressWarnings("rawtypes") + static class RawListInput { + @WorkerTask("test_1") + public @OutputParam("result") List doWork(@InputParam("input") List input) { + return input; + } + } + + @Test + @DisplayName("it should handle a Raw List Type as InputParam") + void rawListAsInputParam() throws NoSuchMethodException { + var worker = new RawListInput(); + var annotatedWorker = + new AnnotatedWorker( + "test_1", worker.getClass().getMethod("doWork", List.class), worker); + + var task = new Task(); + task.setStatus(Task.Status.IN_PROGRESS); + task.setInputData(Map.of("input", List.of(Map.of("brand", "BMW")))); + + var result0 = annotatedWorker.execute(task); + var outputData = result0.getOutputData(); + assertEquals(task.getInputData().get("input"), outputData.get("result")); + } + + static class MapInput { + @WorkerTask("test_1") + public @OutputParam("result") Map doWork(Map input) { + return input; + } + } + + @Test + @DisplayName("it should accept a not annotated Map as input") + void mapAsInputParam() throws NoSuchMethodException { + var worker = new MapInput(); + var annotatedWorker = + new AnnotatedWorker( + "test_1", worker.getClass().getMethod("doWork", Map.class), worker); + + var task = new Task(); + task.setStatus(Task.Status.IN_PROGRESS); + task.setInputData(Map.of("input", List.of(Map.of("brand", "BMW")))); + + var result0 = annotatedWorker.execute(task); + var outputData = result0.getOutputData(); + assertEquals(task.getInputData(), outputData.get("result")); + } + + static class TaskInput { + @WorkerTask("test_1") + public @OutputParam("result") Task doWork(Task input) { + return input; + } + } + + @Test + @DisplayName("it should accept a Task as input") + void taskAsInputParam() throws NoSuchMethodException { + var task = new Task(); + task.setStatus(Task.Status.IN_PROGRESS); + task.setTaskId(UUID.randomUUID().toString()); + task.setInputData(Map.of("input", List.of(Map.of("brand", "BMW")))); + + var worker = new TaskInput(); + var annotatedWorker = + new AnnotatedWorker( + "test_1", worker.getClass().getMethod("doWork", Task.class), worker); + + var result0 = annotatedWorker.execute(task); + var outputData = result0.getOutputData(); + var result = (Task) outputData.get("result"); + assertEquals(result.getTaskId(), task.getTaskId()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.PARAMETER) + public @interface AnotherAnnotation {} + + static class AnotherAnnotationInput { + @WorkerTask("test_2") + public @OutputParam("result") Bike doWork(@AnotherAnnotation Bike input) { + return input; + } + } + + @Test + @DisplayName( + "it should convert to the correct type even if there's no @InputParam and parameters are annotated with other annotations") + void annotatedWithAnotherAnnotation() throws NoSuchMethodException { + var worker = new AnotherAnnotationInput(); + var annotatedWorker = + new AnnotatedWorker( + "test_1", worker.getClass().getMethod("doWork", Bike.class), worker); + + var task = new Task(); + task.setStatus(Task.Status.IN_PROGRESS); + task.setTaskId(UUID.randomUUID().toString()); + task.setInputData(Map.of("brand", "Trek")); + + var result0 = annotatedWorker.execute(task); + var outputData = result0.getOutputData(); + var bike = (Bike) outputData.get("result"); + assertEquals("Trek", bike.getBrand()); + } + + static class MultipleInputParams { + @WorkerTask(value = "test_1", threadCount = 3, pollingInterval = 333) + public Map doWork( + @InputParam("bike") Bike bike, @InputParam("car") Car car) { + return Map.of("bike", bike, "car", car); + } + } + + @Test + @DisplayName("it should handle multiple input params") + void multipleInputParams() throws NoSuchMethodException { + var worker = new MultipleInputParams(); + var annotatedWorker = + new AnnotatedWorker( + "test_1", + worker.getClass().getMethod("doWork", Bike.class, Car.class), + worker); + + var task = new Task(); + task.setStatus(Task.Status.IN_PROGRESS); + task.setTaskId(UUID.randomUUID().toString()); + task.setInputData(Map.of("bike", Map.of("brand", "Trek"), "car", Map.of("brand", "BMW"))); + + var result0 = annotatedWorker.execute(task); + var outputData = result0.getOutputData(); + + var bike = (Bike) outputData.get("bike"); + assertEquals("Trek", bike.getBrand()); + + var car = (Car) outputData.get("car"); + assertEquals("BMW", car.getBrand()); + } + + @Test + @DisplayName("it should honor the polling interval from annotations and config") + void pollingIntervalTest() { + var config = new TestWorkerConfig(); + + var worker = new MultipleInputParams(); + + AnnotatedWorkerExecutor annotatedWorkerExecutor = new AnnotatedWorkerExecutor(mock(TaskClient.class)); + annotatedWorkerExecutor.addBean(worker); + annotatedWorkerExecutor.startPolling(); + List workers = annotatedWorkerExecutor.getWorkers(); + assertNotNull(workers); + assertEquals(1, workers.size()); + Worker taskWorker = workers.get(0); + assertEquals(333, taskWorker.getPollingInterval()); + + var worker2 = new AnotherAnnotationInput(); + annotatedWorkerExecutor = new AnnotatedWorkerExecutor(mock(TaskClient.class)); + annotatedWorkerExecutor.addBean(worker2); + annotatedWorkerExecutor.startPolling(); + workers = annotatedWorkerExecutor.getWorkers(); + assertNotNull(workers); + assertEquals(1, workers.size()); + taskWorker = workers.get(0); + assertEquals(100, taskWorker.getPollingInterval()); + + config.setPollingInterval("test_2", 123); + annotatedWorkerExecutor = new AnnotatedWorkerExecutor(mock(TaskClient.class), config); + annotatedWorkerExecutor.addBean(worker2); + annotatedWorkerExecutor.startPolling(); + workers = annotatedWorkerExecutor.getWorkers(); + assertNotNull(workers); + assertEquals(1, workers.size()); + taskWorker = workers.get(0); + assertEquals(123, taskWorker.getPollingInterval()); + } + + @Test + @DisplayName("it should return the correct thread test count") + void threadCountTest() { + var config = new TestWorkerConfig(); + + var worker = new MultipleInputParams(); + var worker2 = new AnotherAnnotationInput(); + + AnnotatedWorkerExecutor annotatedWorkerExecutor = new AnnotatedWorkerExecutor(mock(TaskClient.class), config); + annotatedWorkerExecutor.addBean(worker); + annotatedWorkerExecutor.addBean(worker2); + + annotatedWorkerExecutor.startPolling(); + TaskRunnerConfigurer runner = annotatedWorkerExecutor.getTaskRunner(); + assertNotNull(runner); + Map taskThreadCount = runner.getTaskThreadCount(); + + assertNotNull(taskThreadCount); + assertEquals(3, taskThreadCount.get("test_1")); + assertEquals(1, taskThreadCount.get("test_2")); + + annotatedWorkerExecutor.shutdown(); + config.setThreadCount("test_2", 2); + annotatedWorkerExecutor = new AnnotatedWorkerExecutor(mock(TaskClient.class), config); + annotatedWorkerExecutor.addBean(worker); + annotatedWorkerExecutor.addBean(worker2); + + annotatedWorkerExecutor.startPolling(); + runner = annotatedWorkerExecutor.getTaskRunner(); + + taskThreadCount = runner.getTaskThreadCount(); + + assertNotNull(taskThreadCount); + assertEquals(3, taskThreadCount.get("test_1")); + assertEquals(2, taskThreadCount.get("test_2")); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java new file mode 100644 index 000000000..165ef1b71 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java @@ -0,0 +1,41 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.executor.task; + +import java.util.HashMap; +import java.util.Map; + +public class TestWorkerConfig extends WorkerConfiguration { + + private Map pollingIntervals = new HashMap<>(); + + private Map threadCounts = new HashMap<>(); + + @Override + public int getPollingInterval(String taskName) { + return pollingIntervals.getOrDefault(taskName, 0); + } + + public void setPollingInterval(String taskName, int interval) { + pollingIntervals.put(taskName, interval); + } + + public void setThreadCount(String taskName, int threadCount) { + threadCounts.put(taskName, threadCount); + } + + @Override + public int getThreadCount(String taskName) { + return threadCounts.getOrDefault(taskName, 0); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java new file mode 100644 index 000000000..3d081b4c6 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java @@ -0,0 +1,41 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.testing; + +public class Task1Input { + + private int mod; + + private int oddEven; + + public int getMod() { + return mod; + } + + public void setMod(int mod) { + this.mod = mod; + } + + public int getOddEven() { + return oddEven; + } + + public void setOddEven(int oddEven) { + this.oddEven = oddEven; + } + + @Override + public String toString() { + return "Task1Input{" + "mod=" + mod + ", oddEven=" + oddEven + '}'; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java new file mode 100644 index 000000000..58aba51d1 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java @@ -0,0 +1,54 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.testing; + +public class TestWorkflowInput { + + private String name; + + private String zipCode; + + private String countryCode; + + public TestWorkflowInput(String name, String zipCode, String countryCode) { + this.name = name; + this.zipCode = zipCode; + this.countryCode = countryCode; + } + + public TestWorkflowInput() {} + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getZipCode() { + return zipCode; + } + + public void setZipCode(String zipCode) { + this.zipCode = zipCode; + } + + public String getCountryCode() { + return countryCode; + } + + public void setCountryCode(String countryCode) { + this.countryCode = countryCode; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java new file mode 100644 index 000000000..33c96f75e --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java @@ -0,0 +1,208 @@ +/* + * Copyright 2021 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.sdk.workflow.testing; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.sdk.testing.WorkflowTestRunner; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; +import com.netflix.conductor.sdk.workflow.task.InputParam; +import com.netflix.conductor.sdk.workflow.task.OutputParam; +import com.netflix.conductor.sdk.workflow.task.WorkerTask; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class WorkflowTestFrameworkTests { + + private static WorkflowTestRunner testRunner; + + private static WorkflowExecutor executor; + + @BeforeAll + public static void init() throws IOException { + testRunner = new WorkflowTestRunner(8080, "3.7.3"); + testRunner.init("com.netflix.conductor.sdk.workflow.testing"); + + executor = testRunner.getWorkflowExecutor(); + executor.loadTaskDefs("/tasks.json"); + executor.loadWorkflowDefs("/simple_workflow.json"); + } + + @AfterAll + public static void cleanUp() { + testRunner.shutdown(); + } + + @Test + public void testDynamicTaskExecuted() throws Exception { + + Map input = new HashMap<>(); + input.put("task2Name", "task_2"); + input.put("mod", "1"); + input.put("oddEven", "12"); + input.put("number", 0); + + // Start the workflow and wait for it to complete + Workflow workflow = executor.executeWorkflow("Decision_TaskExample", 1, input).get(); + + assertNotNull(workflow); + assertEquals(Workflow.WorkflowStatus.COMPLETED, workflow.getStatus()); + assertNotNull(workflow.getOutput()); + assertNotNull(workflow.getTasks()); + assertFalse(workflow.getTasks().isEmpty()); + assertTrue( + workflow.getTasks().stream() + .anyMatch(task -> task.getTaskDefName().equals("task_6"))); + + // task_2's implementation fails at the first try, so we should have to instances of task_2 + // execution + // 2 executions of task_2 should be present + assertEquals( + 2, + workflow.getTasks().stream() + .filter(task -> task.getTaskDefName().equals("task_2")) + .count()); + List task2Executions = + workflow.getTasks().stream() + .filter(task -> task.getTaskDefName().equals("task_2")) + .collect(Collectors.toList()); + assertNotNull(task2Executions); + assertEquals(2, task2Executions.size()); + + // First instance would have failed and second succeeded. + assertEquals(Task.Status.FAILED, task2Executions.get(0).getStatus()); + assertEquals(Task.Status.COMPLETED, task2Executions.get(1).getStatus()); + + // task10's output + assertEquals(100, workflow.getOutput().get("c")); + } + + @Test + public void testWorkflowFailure() throws Exception { + + Map input = new HashMap<>(); + // task2Name is missing which will cause workflow to fail + input.put("mod", "1"); + input.put("oddEven", "12"); + input.put("number", 0); + + // we are missing task2Name parameter which is required to wire up dynamictask + // The workflow should fail as we are not passing it as input + Workflow workflow = executor.executeWorkflow("Decision_TaskExample", 1, input).get(); + assertNotNull(workflow); + assertEquals(Workflow.WorkflowStatus.FAILED, workflow.getStatus()); + assertNotNull(workflow.getReasonForIncompletion()); + } + + @WorkerTask("task_1") + public Map task1(Task1Input input) { + Map result = new HashMap<>(); + result.put("input", input); + return result; + } + + @WorkerTask("task_2") + public TaskResult task2(Task task) { + if (task.getRetryCount() < 1) { + task.setStatus(Task.Status.FAILED); + task.setReasonForIncompletion("try again"); + return new TaskResult(task); + } + + task.setStatus(Task.Status.COMPLETED); + return new TaskResult(task); + } + + @WorkerTask("task_6") + public TaskResult task6(Task task) { + task.setStatus(Task.Status.COMPLETED); + return new TaskResult(task); + } + + @WorkerTask("task_10") + public TaskResult task10(Task task) { + task.setStatus(Task.Status.COMPLETED); + task.getOutputData().put("a", "b"); + task.getOutputData().put("c", 100); + task.getOutputData().put("x", false); + return new TaskResult(task); + } + + @WorkerTask("task_8") + public TaskResult task8(Task task) { + task.setStatus(Task.Status.COMPLETED); + return new TaskResult(task); + } + + @WorkerTask("task_5") + public TaskResult task5(Task task) { + task.setStatus(Task.Status.COMPLETED); + return new TaskResult(task); + } + + @WorkerTask("task_3") + public @OutputParam("z1") String task3(@InputParam("taskToExecute") String p1) { + return "output of task3, p1=" + p1; + } + + @WorkerTask("task_30") + public Map task30(Task task) { + Map output = new HashMap<>(); + output.put("v1", "b"); + output.put("v2", Arrays.asList("one", "two", 3)); + output.put("v3", 5); + return output; + } + + @WorkerTask("task_31") + public Map task31(Task task) { + Map output = new HashMap<>(); + output.put("a1", "b"); + output.put("a2", Arrays.asList("one", "two", 3)); + output.put("a3", 5); + return output; + } + + @WorkerTask("HTTP") + public Map http(Task task) { + Map output = new HashMap<>(); + output.put("a1", "b"); + output.put("a2", Arrays.asList("one", "two", 3)); + output.put("a3", 5); + return output; + } + + @WorkerTask("EVENT") + public Map event(Task task) { + Map output = new HashMap<>(); + output.put("a1", "b"); + output.put("a2", Arrays.asList("one", "two", 3)); + output.put("a3", 5); + return output; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/application-integrationtest.properties b/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/application-integrationtest.properties new file mode 100644 index 000000000..efb46697d --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/application-integrationtest.properties @@ -0,0 +1,55 @@ +# +# /* +# * Copyright 2023 Conductor authors +# *

+# * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with +# * the License. You may obtain a copy of the License at +# *

+# * http://www.apache.org/licenses/LICENSE-2.0 +# *

+# * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +# * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +# * specific language governing permissions and limitations under the License. +# */ +# + +conductor.db.type=memory +# disable trying to connect to redis and use in-memory +conductor.queue.type=xxx +conductor.workflow-execution-lock.type=local_only +conductor.external-payload-storage.type=mock +conductor.indexing.enabled=false + +conductor.app.stack=test +conductor.app.appId=conductor + +conductor.app.workflow-offset-timeout=30s + +conductor.system-task-workers.enabled=false +conductor.app.system-task-worker-callback-duration=0 + +conductor.app.event-message-indexing-enabled=true +conductor.app.event-execution-indexing-enabled=true + +conductor.workflow-reconciler.enabled=true +conductor.workflow-repair-service.enabled=false + +conductor.app.workflow-execution-lock-enabled=false + +conductor.app.workflow-input-payload-size-threshold=10KB +conductor.app.max-workflow-input-payload-size-threshold=10240KB +conductor.app.workflow-output-payload-size-threshold=10KB +conductor.app.max-workflow-output-payload-size-threshold=10240KB +conductor.app.task-input-payload-size-threshold=10KB +conductor.app.max-task-input-payload-size-threshold=10240KB +conductor.app.task-output-payload-size-threshold=10KB +conductor.app.max-task-output-payload-size-threshold=10240KB +conductor.app.max-workflow-variables-payload-size-threshold=2KB + +conductor.redis.availability-zone=us-east-1c +conductor.redis.data-center-region=us-east-1 +conductor.redis.workflow-namespace-prefix=integration-test +conductor.redis.queue-namespace-prefix=integtest + +conductor.elasticsearch.index-prefix=conductor +conductor.elasticsearch.cluster-health-color=yellow diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/log4j2.xml b/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/log4j2.xml new file mode 100644 index 000000000..a35b65824 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/log4j2.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/script.js b/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/script.js new file mode 100644 index 000000000..af6d42c43 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/script.js @@ -0,0 +1,11 @@ +function e() { + if ($.value > 1){ + return { + "key": "value", + "key2": 42 + }; + } else { + return {}; + } +} +e(); \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/simple_workflow.json b/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/simple_workflow.json new file mode 100644 index 000000000..cc8a78051 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/simple_workflow.json @@ -0,0 +1,151 @@ +{ + "createTime": 1635491472393, + "updateTime": 1635356450472, + "name": "Decision_TaskExample", + "description": "Decision_TaskExample", + "version": 1, + "tasks": [ + { + "name": "decision_task", + "taskReferenceName": "decision_task", + "inputParameters": { + "case_value_param": "${workflow.input.number}" + }, + "type": "DECISION", + "caseValueParam": "case_value_param", + "decisionCases": { + "0": [ + { + "name": "task_5", + "taskReferenceName": "task_5", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "dyntask", + "taskReferenceName": "task_2", + "inputParameters": { + "taskToExecute":"${workflow.input.task2Name}" + }, + "type": "DYNAMIC", + "dynamicTaskNameParam":"taskToExecute", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "task_6", + "taskReferenceName": "task_6", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "1": [ + { + "name": "task_8", + "taskReferenceName": "task_8", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "task_10", + "taskReferenceName": "task_10", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ] + }, + "defaultCase": [ + { + "name": "task_8", + "taskReferenceName": "task_8_default", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "task_10", + "taskReferenceName": "task_10_last", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [], + "outputParameters": {}, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": true, + "ownerEmail": "abc@example.com", + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "variables": {}, + "inputTemplate": {} +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/tasks.json b/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/tasks.json new file mode 100644 index 000000000..b60881d08 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/tasks.json @@ -0,0 +1,1252 @@ +[ + { + "createTime": 1635656118884, + "createdBy": "", + "name": "task_38", + "description": "task_38", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 1, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635638846956, + "createdBy": "", + "name": "encode", + "retryCount": 3, + "timeoutSeconds": 1200, + "inputKeys": [ + "fileLocation" + ], + "outputKeys": [ + "encodeLocation" + ], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 1, + "responseTimeoutSeconds": 1200, + "concurrentExecLimit": 100, + "inputTemplate": {}, + "rateLimitPerFrequency": 50, + "rateLimitFrequencyInSeconds": 60, + "ownerEmail": "encode_admin@test.com", + "pollTimeoutSeconds": 1200 + }, + { + "createTime": 1635656118436, + "createdBy": "", + "name": "task_8", + "description": "task_8", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 1, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118873, + "createdBy": "", + "name": "task_37", + "description": "task_37", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 1, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118460, + "createdBy": "", + "name": "task_9", + "description": "task_9", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 1, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118390, + "createdBy": "", + "name": "task_6", + "description": "task_6", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 1, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118861, + "createdBy": "", + "name": "task_36", + "description": "task_36", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635638847017, + "createdBy": "", + "name": "collect_payment_task", + "description": "collect_payment_task", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 1200, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118847, + "createdBy": "", + "name": "task_35", + "description": "task_35", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118422, + "createdBy": "", + "name": "task_7", + "description": "task_7", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118835, + "createdBy": "", + "name": "task_34", + "description": "task_34", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118349, + "createdBy": "", + "name": "task_4", + "description": "task_4", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118819, + "createdBy": "", + "name": "task_33", + "description": "task_33", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118371, + "createdBy": "", + "name": "task_5", + "description": "task_5", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118808, + "createdBy": "", + "name": "task_32", + "description": "task_32", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118302, + "createdBy": "", + "name": "task_2", + "description": "task_2", + "retryCount": 3, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 1, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118797, + "createdBy": "", + "name": "task_31", + "description": "task_31", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118323, + "createdBy": "", + "name": "task_3", + "description": "task_3", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118228, + "createdBy": "", + "name": "task_0", + "description": "task_0", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118775, + "createdBy": "", + "name": "task_30", + "description": "task_30", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118267, + "createdBy": "", + "name": "task_1", + "description": "task_1", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635638847161, + "createdBy": "", + "name": "BookHotels", + "retryCount": 3, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 1200, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "ui@example.com" + }, + { + "createTime": 1635638847170, + "createdBy": "", + "name": "deploy", + "retryCount": 3, + "timeoutSeconds": 1200, + "inputKeys": [ + "fileLocation" + ], + "outputKeys": [ + "deployLocation" + ], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 600, + "responseTimeoutSeconds": 1200, + "concurrentExecLimit": 100, + "inputTemplate": {}, + "rateLimitPerFrequency": 50, + "rateLimitFrequencyInSeconds": 60, + "ownerEmail": "encode_admin@test.com", + "pollTimeoutSeconds": 1200 + }, + { + "createTime": 1635763310960, + "createdBy": "", + "name": "ship_via_dhl", + "retryCount": 3, + "timeoutSeconds": 600, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 300, + "responseTimeoutSeconds": 300, + "concurrentExecLimit": 100, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 2, + "ownerEmail": "abc@example.com", + "pollTimeoutSeconds": 1200 + }, + { + "createTime": 1635638847180, + "createdBy": "", + "name": "StartBooking", + "retryCount": 3, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 1200, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "ui@example.com" + }, + { + "createTime": 1635434088645, + "createdBy": "", + "name": "Read_Name", + "retryCount": 1, + "timeoutSeconds": 600, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 300, + "responseTimeoutSeconds": 300, + "concurrentExecLimit": 100, + "inputTemplate": {}, + "rateLimitPerFrequency": 1, + "rateLimitFrequencyInSeconds": 60, + "ownerEmail": "abc@example.com", + "pollTimeoutSeconds": 1200 + }, + { + "createTime": 1635638847189, + "createdBy": "", + "name": "book_flight_task", + "description": "book_flight_task", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 1200, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635638847198, + "createdBy": "", + "name": "book_car_task", + "description": "book_car_task", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 1200, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118758, + "createdBy": "", + "name": "task_29", + "description": "task_29", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118747, + "createdBy": "", + "name": "task_28", + "description": "task_28", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635278952348, + "createdBy": "", + "name": "ship_via_ups", + "retryCount": 3, + "timeoutSeconds": 600, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 300, + "responseTimeoutSeconds": 300, + "concurrentExecLimit": 100, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 2, + "ownerEmail": "abc@example.com", + "pollTimeoutSeconds": 1200 + }, + { + "createTime": 1635638847226, + "createdBy": "", + "name": "image_convert_resize", + "retryCount": 3, + "timeoutSeconds": 1200, + "inputKeys": [ + "fileLocation", + "outputFormat", + "outputWidth", + "outputHeight", + "maintainAspectRatio" + ], + "outputKeys": [ + "fileLocation" + ], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 600, + "responseTimeoutSeconds": 1200, + "concurrentExecLimit": 100, + "inputTemplate": {}, + "rateLimitPerFrequency": 50, + "rateLimitFrequencyInSeconds": 60, + "ownerEmail": "test@example.com", + "pollTimeoutSeconds": 3600 + }, + { + "createTime": 1635638847238, + "createdBy": "", + "name": "deposit_money", + "description": "deposit_money", + "retryCount": 5, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 10, + "responseTimeoutSeconds": 1200, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118912, + "createdBy": "", + "name": "search_elasticsearch", + "description": "search_elasticsearch", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118900, + "createdBy": "", + "name": "task_39", + "description": "task_39", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118586, + "createdBy": "", + "name": "task_16", + "description": "task_16", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635278952330, + "createdBy": "", + "name": "shipping_info", + "retryCount": 1, + "timeoutSeconds": 600, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 300, + "responseTimeoutSeconds": 300, + "concurrentExecLimit": 100, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 2, + "ownerEmail": "abc@example.com", + "pollTimeoutSeconds": 1200 + }, + { + "createTime": 1635656118567, + "createdBy": "", + "name": "task_15", + "description": "task_15", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118545, + "createdBy": "", + "name": "task_14", + "description": "task_14", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118528, + "createdBy": "", + "name": "task_13", + "description": "task_13", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118513, + "createdBy": "", + "name": "task_12", + "description": "task_12", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635638847312, + "createdBy": "", + "name": "withdraw_money", + "description": "withdraw_money", + "retryCount": 5, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 10, + "responseTimeoutSeconds": 1200, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118495, + "createdBy": "", + "name": "task_11", + "description": "task_11", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118480, + "createdBy": "", + "name": "task_10", + "description": "task_10", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "updateTime": 1636574526469, + "createdBy": "user", + "updatedBy": "", + "name": "sample_task_name_1", + "description": "This is a sample task for demo", + "retryCount": 3, + "timeoutSeconds": 30, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 5, + "responseTimeoutSeconds": 10, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1636051623273, + "createdBy": "", + "name": "order_details", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 60, + "ownerEmail": "abc@example.com" + }, + { + "createTime": 1635638847343, + "createdBy": "", + "name": "CompleteFlightBooking", + "retryCount": 3, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 1200, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "ui@example.com" + }, + { + "createTime": 1635278952339, + "createdBy": "", + "name": "ship_via_fedex", + "retryCount": 3, + "timeoutSeconds": 600, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 300, + "responseTimeoutSeconds": 300, + "concurrentExecLimit": 100, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 2, + "ownerEmail": "abc@example.com", + "pollTimeoutSeconds": 1200 + }, + { + "createTime": 1635638847353, + "createdBy": "", + "name": "map_state_codes", + "retryCount": 3, + "timeoutSeconds": 300, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 10, + "responseTimeoutSeconds": 180, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@gmail.com" + }, + { + "createTime": 1635638847362, + "createdBy": "", + "name": "compute_median_top_states", + "retryCount": 3, + "timeoutSeconds": 300, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 10, + "responseTimeoutSeconds": 180, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@gmail.com" + }, + { + "createTime": 1635638847374, + "createdBy": "", + "name": "scaleS3Image", + "retryCount": 3, + "timeoutSeconds": 300, + "inputKeys": [ + "inputBucketName", + "inputKeyName", + "scalingFactor", + "outputBucketName", + "outputKeyName" + ], + "outputKeys": [ + "response" + ], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 10, + "responseTimeoutSeconds": 180, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "conductor@example.com" + }, + { + "createTime": 1635656118736, + "createdBy": "", + "name": "task_27", + "description": "task_27", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118725, + "createdBy": "", + "name": "task_26", + "description": "task_26", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118713, + "createdBy": "", + "name": "task_25", + "description": "task_25", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118700, + "createdBy": "", + "name": "task_24", + "description": "task_24", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635878631466, + "createdBy": "", + "name": "task_23", + "retryCount": 1, + "timeoutSeconds": 600, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 600, + "responseTimeoutSeconds": 300, + "concurrentExecLimit": 100, + "inputTemplate": {}, + "rateLimitPerFrequency": 50, + "rateLimitFrequencyInSeconds": 60, + "pollTimeoutSeconds": 600, + "ownerEmail": "test@example.com" + }, + { + "createTime": 1635878631456, + "createdBy": "", + "name": "task_22", + "retryCount": 1, + "timeoutSeconds": 600, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 600, + "responseTimeoutSeconds": 300, + "concurrentExecLimit": 100, + "inputTemplate": {}, + "rateLimitPerFrequency": 50, + "rateLimitFrequencyInSeconds": 60, + "pollTimeoutSeconds": 600, + "ownerEmail": "test@example.com" + }, + { + "createTime": 1635638847436, + "createdBy": "", + "name": "send_email_task", + "description": "send_email_task", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 1200, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118658, + "createdBy": "", + "name": "task_21", + "description": "task_21", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635638847459, + "createdBy": "", + "name": "image_multiple_convert_resize", + "retryCount": 3, + "timeoutSeconds": 1200, + "inputKeys": [ + "fileLocation", + "outputFormats", + "outputSizes", + "maintainAspectRatio" + ], + "outputKeys": [ + "dynamicTasks", + "dynamicTasksInput" + ], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 600, + "responseTimeoutSeconds": 1200, + "concurrentExecLimit": 100, + "inputTemplate": {}, + "rateLimitPerFrequency": 50, + "rateLimitFrequencyInSeconds": 60, + "ownerEmail": "exampl@example.com", + "pollTimeoutSeconds": 3600 + }, + { + "createTime": 1635656118644, + "createdBy": "", + "name": "task_20", + "description": "task_20", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635638847477, + "createdBy": "", + "name": "simple_worker", + "retryCount": 3, + "timeoutSeconds": 300, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 10, + "responseTimeoutSeconds": 180, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@gmail.com" + }, + { + "createTime": 1635638847486, + "createdBy": "", + "name": "book_hotel_task", + "description": "book_hotel_task", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 1200, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635638847495, + "createdBy": "", + "name": "watermarkS3Image", + "retryCount": 3, + "timeoutSeconds": 300, + "inputKeys": [ + "inputBucketName", + "inputKeyName", + "watermarkBucketName", + "watermarkKeyName", + "outputBucketName", + "outputKeyName" + ], + "outputKeys": [ + "response" + ], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 10, + "responseTimeoutSeconds": 180, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "conductor@example.com" + }, + { + "createTime": 1635656118631, + "createdBy": "", + "name": "task_19", + "description": "task_19", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118616, + "createdBy": "", + "name": "task_18", + "description": "task_18", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + }, + { + "createTime": 1635656118601, + "createdBy": "", + "name": "task_17", + "description": "task_17", + "retryCount": 1, + "timeoutSeconds": 0, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 3600, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "example@email.com" + } +] \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/test-server.properties b/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/test-server.properties new file mode 100644 index 000000000..900b554d3 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/resources/test-server.properties @@ -0,0 +1,5 @@ +conductor.db.type=memory +conductor.indexing.enabled=false +conductor.workflow-repair-service.enabled=false +loadSample=true +conductor.system-task-workers.enabled=false \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/sdk/testing_framework.md b/conductor-clients/java/conductor-java-sdk/sdk/testing_framework.md new file mode 100644 index 000000000..601ee2d84 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/testing_framework.md @@ -0,0 +1,74 @@ +# Unit Testing Framework for Workflows + +The framework allows you to test the workflow definitions against a specific version of Conductor server. + +The unit tests allow the following: +1. **Input/Output Wiring**: Ensure the tasks are wired up correctly. +2. **Parameter check**: Workflow behavior with missing mandatory parameters is expected (fail if required). +3. **Task Failure behavior**: Ensure the task definitions have the right number of retries etc. + For example, if the task is not idempotent, it does not get retried. +4. **Branch Testing**: Given a specific input, ensure the workflow executes a specific branch of the fork/decision. + +The local test server is self-contained with no additional dependencies required and stores all the data +in memory. Once the test completes, the server is terminated and all the data is wiped out. + +## Unit Testing Frameworks +The unit testing framework is agnostic to the framework you use for testing and can be easily integrated into +JUnit, Spock and other testing frameworks being used. + +## Setting Up Local Server for Testing​ + +```java +//Setup method code - should be called once per the test lifecycle +//e.g. @BeforeClass in JUnit + +//Download the published conductor server version 3.5.2 +//Start the local server at port 8096 +testRunner = new WorkflowTestRunner(8096, "3.5.2"); + +//Scan the packages for task workers +testRunner.init("com.netflix.conductor.testing.workflows"); + +//Get the executor instance used for loading workflows +executor = testRunner.getWorkflowExecutor(); +``` + +Clean up method: +```java +//Clean up method code -- place in a clean up method e.g. @AfterClass in Junit + +//Shutdown local workers and servers and clean up any local resources in use. +testRunner.shutdown(); +``` + +Loading workflows from JSON files for testing: +```java +executor.loadTaskDefs("/tasks.json"); +executor.loadWorkflowDefs("/simple_workflow.json"); +``` + +## Sample test code that starts a workflow and verifies its execution + +```java +GetInsuranceQuote getQuote = new GetInsuranceQuote(); +getQuote.setName("personA"); +getQuote.setAmount(1000000.0); +getQuote.setZipCode("10121"); + +// Start the workflow and wait for it to complete +CompletableFuture workflowFuture = executor.executeWorkflow("InsuranceQuoteWorkflow", 1, getQuote); + +//Wait for the workflow execution to complete +Workflow workflow = workflowFuture.get(); + +//Assertions +assertNotNull(workflow); +assertEquals(Workflow.WorkflowStatus.COMPLETED, workflow.getStatus()); +assertNotNull(workflow.getOutput()); +assertNotNull(workflow.getTasks()); +assertFalse(workflow.getTasks().isEmpty()); +assertTrue(workflow.getTasks().stream().anyMatch(task -> task.getTaskDefName().equals("task_6"))); +``` + + + diff --git a/conductor-clients/java/conductor-java-sdk/sdk/worker_sdk.md b/conductor-clients/java/conductor-java-sdk/sdk/worker_sdk.md new file mode 100644 index 000000000..36a498568 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/worker_sdk.md @@ -0,0 +1,117 @@ +# Worker SDK +Worker SDK makes it easy to write Conductor workers which are strongly typed with specific inputs and outputs. + +Annotations for the worker methods: + +* `@WorkerTask` - When annotated, convert a method to a Conductor worker. +* `@InputParam` - Name of the input parameter to bind to from the task's input. +* `@OutputParam` - Name of the output key of the task's output. + +Please note inputs and outputs to a task in Conductor are JSON documents. + + +**Examples** + +Create a worker named `task1` that gets Task as input and produces TaskResult as output. +```java +@WorkerTask("task1") + public TaskResult task1(Task task) { + task.setStatus(Task.Status.COMPLETED); + return new TaskResult(task); + } +``` + +Create a worker named `task2` that takes the `name` as a String input and produces an output `return "Hello, " + name` + +```java +@WorkerTask("task2") +public @OutputParam("greetings") String task2(@InputParam("name") String name) { + return "Hello, " + name; +} +``` +Example Task Input/Output + +Input: +```json +{ + "name": "conductor" +} +``` + +Output: +```json +{ + "greetings": "Hello, conductor" +} +``` +A worker that takes complex java type as input and produces the complex output: +```java +@WorkerTask("get_insurance_quote") + public InsuranceQuote getInsuranceQuote(GetInsuranceQuote quoteInput) { + InsuranceQuote quote = new InsuranceQuote(); + //Implementation + return quote; + } +``` + +Example Task Input/Output + +Input: +```json +{ + "name": "personA", + "zipCode": "10121", + "amount": 1000000 +} +``` + +Output: +```json +{ + "name": "personA", + "quotedPremium": 123.50, + "quotedAmount": 1000000 +} +``` + +## Managing Task Workers +Annotated Workers are managed by [WorkflowExecutor](https://github.com/conductor-oss/conductor/blob/main/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java) + +### Start Workers +```java +WorkflowExecutor executor = new WorkflowExecutor("http://server/api/"); +//List of packages (comma separated) to scan for annotated workers. +// Please note, the worker method MUST be public and the class in which they are defined +//MUST have a no-args constructor +executor.initWorkers("com.company.package1,com.company.package2"); +``` + +### Stop Workers +The code fragment to stop workers at shutdown of the application. +```java +executor.shutdown(); +``` + +### Unit Testing Workers +Workers implemented with the annotations are regular Java methods that can be unit tested with any testing framework. + +#### Mock Workers for Workflow Testing​ +Create a mock worker in a different package (e.g., test) and scan for these packages when loading up the workers for integration testing. + +See [Unit Testing Framework](testing_framework.md) for more details on testing. + +## Best Practices +In a typical production environment, you will have multiple workers across different machines/VMs/pods polling for the same task. +As with all Conductor workers, the following best practices apply: + +1. Workers should be stateless and should not maintain any state on the process they are running. +2. Ideally, workers should be idempotent. +3. The worker should follow the Single Responsibility Principle and do exactly one thing they are responsible for. +4. The worker should not embed any workflow logic - i.e., scheduling another worker, sending a message, etc. The Conductor has features to do this, making it possible to decouple your workflow logic from worker implementation. + + + + + + + diff --git a/conductor-clients/java/conductor-java-sdk/sdk/workflow_sdk.md b/conductor-clients/java/conductor-java-sdk/sdk/workflow_sdk.md new file mode 100644 index 000000000..60dc77a9e --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/sdk/workflow_sdk.md @@ -0,0 +1,122 @@ +# Workflow SDK +Workflow SDK provides fluent API to create workflows with strongly typed interfaces. + +## APIs +### ConductorWorkflow +[ConductorWorkflow](https://github.com/conductor-oss/conductor/blob/main/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java) is the SDK representation of a Conductor workflow. + +#### Create a `ConductorWorkflow` Instance +```java +ConductorWorkflow conductorWorkflow = new WorkflowBuilder(executor) + .name("sdk_workflow_example") + .version(1) + .ownerEmail("hello@example.com") + .description("Example Workflow") + .timeoutPolicy(WorkflowDef.TimeoutPolicy.TIME_OUT_WF, 100) + .add(new SimpleTask("calculate_insurance_premium", "calculate_insurance_premium")) + .add(new SimpleTask("send_email", "send_email")) + .build(); +``` +### Working with Simple Worker Tasks +Use [SimpleTask](https://github.com/conductor-oss/conductor/blob/main/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java) to add a simple task to a workflow. + +Example: +```java +... +builder.add(new SimpleTask("send_email", "send_email")) +... +``` +### Wiring Inputs to Task +Use `input` methods to configure the inputs to the task. + +See our doc on [task inputs](https://conductor.netflix.com/how-tos/Tasks/task-inputs.html) for more details. + +Example +```java +builder.add( + new SimpleTask("send_email", "send_email") + .input("email", "${workflow.input.email}") + .input("subject", "Your insurance quote for the amount ${generate_quote.output.amount}") +); +``` + +### Working with Operators +Each operator has its own class that can be added to the workflow builder. + +* [ForkJoin](https://github.com/conductor-oss/conductor/blob/main/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java) +* [Wait](https://github.com/conductor-oss/conductor/blob/main/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java) +* [Switch](https://github.com/conductor-oss/conductor/blob/main/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java) +* [DynamicFork](https://github.com/conductor-oss/conductor/blob/main/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java) +* [DoWhile](https://github.com/conductor-oss/conductor/blob/main/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java) +* [Join](https://github.com/conductor-oss/conductor/blob/main/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java) +* [Dynamic](https://github.com/conductor-oss/conductor/blob/main/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java) +* [Terminate](https://github.com/conductor-oss/conductor/blob/main/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java) +* [SubWorkflow](https://github.com/conductor-oss/conductor/blob/main/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java) +* [SetVariable](https://github.com/conductor-oss/conductor/blob/main/java-sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java) + + +#### Register Workflow with Conductor Server +```java +//Returns true if the workflow is successfully created +//Reasons why this method will return false +//1. Network connectivity issue +//2. Workflow already exists with the specified name and version +//3. There are missing task definitions +boolean registered = workflow.registerWorkflow(); +``` +#### Overwrite Existing Workflow Definition​ +```java +boolean registered = workflow.registerWorkflow(true); +``` + +#### Overwrite existing workflow definitions & registering any missing task definitions +```java +boolean registered = workflow.registerWorkflow(true, true); +``` + +#### Create `ConductorWorkflow` based on the definition registered on the server + +```java +ConductorWorkflow conductorWorkflow = + new ConductorWorkflow(executor) + .from("sdk_workflow_example", 1); +``` + +#### Start Workflow Execution +Start the execution of the workflow based on the definition registered on the server. Use the register method to register a workflow on the server before executing. + +```java + +//Returns a completable future +CompletableFuture execution = conductorWorkflow.execute(input); + +//Wait for the workflow to complete -- useful if workflow completes within a reasonable amount of time +Workflow workflowRun = execution.get(); + +//Get the workflowId +String workflowId = workflowRun.getWorkflowId(); + +//Get the status of workflow execution +WorkflowStatus status = workflowRun.getStatus(); +``` +See [Workflow](https://github.com/conductor-oss/conductor/blob/main/common/src/main/java/com/netflix/conductor/common/run/Workflow.java) for more details on the Workflow object. + +#### Start Dynamic Workflow Execution +Dynamic workflows are executed by specifying the workflow definition along with the execution and do not require registering the workflow on the server before executing. + +##### Use cases for dynamic workflows +1. Each workflow run has a unique workflow definition +2. Workflows are defined based on the user data and cannot be modeled ahead of time statically + +```java +//1. Use WorkflowBuilder to create ConductorWorkflow. +//2. Execute using the definition created by SDK. +CompletableFuture execution = conductorWorkflow.executeDynamic(input); + +``` + + + + + + diff --git a/conductor-clients/java/conductor-java-sdk/settings.gradle b/conductor-clients/java/conductor-java-sdk/settings.gradle new file mode 100644 index 000000000..6860b50bd --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/settings.gradle @@ -0,0 +1,9 @@ +rootProject.name = 'conductor-java-sdk' +include 'conductor-client' +include 'orkes-client' +include 'sdk' +include 'examples' +include 'tests' +include 'orkes-spring' +include 'conductor-client-spring' + diff --git a/conductor-clients/java/conductor-java-sdk/tests/build.gradle b/conductor-clients/java/conductor-java-sdk/tests/build.gradle new file mode 100644 index 000000000..0788d5c9f --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/build.gradle @@ -0,0 +1,40 @@ +// The main reason why this project exists on it's own is that the client tests use the SDK +// Ideally we should split them. But, we might keep this project for tests that use 2 or more modules. + +plugins { + id 'jacoco' +} + +dependencies { + testImplementation project(':conductor-client') + testImplementation project(':sdk') + testImplementation project(':orkes-client') + // test dependencies + testAnnotationProcessor "org.projectlombok:lombok:${versions.lombok}" + testCompileOnly "org.projectlombok:lombok:${versions.lombok}" + + testImplementation "org.junit.jupiter:junit-jupiter-api:${versions.junit}" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${versions.junit}" + testImplementation "ch.qos.logback:logback-classic:1.5.6" + testImplementation "org.mockito:mockito-core:${versions.mockito}" + testImplementation "org.testcontainers:localstack:${versions.testContainers}" + testImplementation "org.testcontainers:testcontainers:${versions.testContainers}" +} + +test { + useJUnitPlatform() + finalizedBy jacocoTestReport // report is always generated after tests run + testLogging { + events = ["SKIPPED", "FAILED"] + exceptionFormat = "full" + showStandardStreams = true + } +} + +tasks.withType(Test) { + maxParallelForks = 1 +} + +jacocoTestReport { + dependsOn test // tests are required to run before generating the report +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LoadTestWorker.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LoadTestWorker.java new file mode 100644 index 000000000..56702471b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LoadTestWorker.java @@ -0,0 +1,78 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client; + +import java.security.SecureRandom; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; + +import com.google.common.util.concurrent.Uninterruptibles; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class LoadTestWorker implements Worker { + + private final String name; + private final SecureRandom secureRandom = new SecureRandom(); + + public LoadTestWorker(String name) { + this.name = name; + } + + @Override + public String getTaskDefName() { + return name; + } + + @Override + public TaskResult execute(Task task) { + log.info("Executing {} - {}", task.getTaskType(), task.getTaskId()); + TaskResult result = new TaskResult(task); + + Uninterruptibles.sleepUninterruptibly(10_000, TimeUnit.MILLISECONDS); + + result.setStatus(TaskResult.Status.COMPLETED); + int keyCount = 50; + int resultCount = Math.max(20, secureRandom.nextInt(keyCount)); + + result.getOutputData().put("fixed", "hello"); + result.getOutputData().put("oddEven", "odd" + secureRandom.nextInt(2)); + result.getOutputData().put("thirds", "thirds" + secureRandom.nextInt(3)); + result.getOutputData().put("fourths", "fourths" + secureRandom.nextInt(4)); + result.getOutputData().put("fifths", "fifths" + secureRandom.nextInt(5)); + result.getOutputData().put("tenths", "tenths" + secureRandom.nextInt(10)); + + result.getOutputData().put("randomNumber", resultCount); + result.getOutputData().put("uuid1", UUID.randomUUID().toString()); + result.getOutputData().put("uuid2", UUID.randomUUID().toString()); + result.getOutputData().put("float", secureRandom.nextDouble()); + + result.addOutputData("scheduledTime", task.getScheduledTime()); + result.addOutputData("startTime", task.getStartTime()); + log.info("Done executing task {} @the worker", task.getTaskId()); + return result; + } + + public void onErrorUpdate(Task task) { + log.info("I just can't update the task {}", task.getTaskId()); + } + + @Override + public int getPollingInterval() { + return 10; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LocalWorkerTest.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LocalWorkerTest.java new file mode 100644 index 000000000..6c21bc338 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LocalWorkerTest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.netflix.conductor.client.automator.TaskRunnerConfigurer; +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.worker.Worker; + +public class LocalWorkerTest { + + public static void main(String[] args) { + ConductorClient client = new ConductorClient("http://localhost:8080/api"); + TaskClient taskClient = new TaskClient(client); + + List workers = new ArrayList<>(); + Map taskThreadCount = new HashMap<>(); + + workers.add(new LoadTestWorker("x_test_worker_4")); + taskThreadCount.put("x_test_worker_4", 1000); + + for (int i = 0; i < 4; i++) { + workers.add(new LoadTestWorker("x_test_worker_" + i)); + taskThreadCount.put("x_test_worker_" + i, 100); + } + + TaskRunnerConfigurer configurer = + new TaskRunnerConfigurer.Builder(taskClient, workers) + .withSleepWhenRetry(10) + .withTaskThreadCount(taskThreadCount) + .withTaskPollTimeout(10) + .withTaskPollCount(5) + .build(); + configurer.init(); + + System.out.println("Ready..."); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/WorkflowRetryTest.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/WorkflowRetryTest.java new file mode 100644 index 000000000..c17d9ef2d --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/WorkflowRetryTest.java @@ -0,0 +1,128 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client; + +import java.util.*; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; +import com.netflix.conductor.common.run.Workflow; + +import io.orkes.conductor.client.http.OrkesMetadataClient; +import io.orkes.conductor.client.http.OrkesTaskClient; +import io.orkes.conductor.client.http.OrkesWorkflowClient; +import io.orkes.conductor.client.util.ClientTestUtil; +import io.orkes.conductor.client.util.Commons; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class WorkflowRetryTest { + private final OrkesMetadataClient metadataClient; + private final OrkesWorkflowClient workflowClient; + private final OrkesTaskClient taskClient; + private static final String taskName = "test_retry_operation"; + private static final String workflowName = "Sample_Retry"; + + public WorkflowRetryTest() { + OrkesClients orkesClients = ClientTestUtil.getOrkesClients(); + metadataClient = orkesClients.getMetadataClient(); + workflowClient = orkesClients.getWorkflowClient(); + taskClient = orkesClients.getTaskClient(); + } + + @Test + @DisplayName("test retry operation") + public void testRetry() { + registerTask(); + registerWorkflow(); + StartWorkflowRequest startWorkflowRequest = new StartWorkflowRequest(); + startWorkflowRequest.setName(workflowName); + startWorkflowRequest.setVersion(1); + startWorkflowRequest.setInput(new HashMap<>()); + String workflowId = workflowClient.startWorkflow(startWorkflowRequest); + Workflow workflow = workflowClient.getWorkflow(workflowId, true); + + String taskId = workflow.getTasks().get(0).getTaskId(); + // Fail the task + failTask(workflow.getWorkflowId(), taskId); + workflow = workflowClient.getWorkflow(workflowId, true); + + // Upload all the workflows to s3 + workflowClient.uploadCompletedWorkflows(); + + workflowClient.terminateWorkflow(workflowId, "testing out some stuff"); + // Retry the workflow + + log.debug("Going to retry " + workflowId); + workflowClient.retryLastFailedTask(workflowId); + taskId = workflowClient.getWorkflow(workflowId, true).getTasks().get(0).getTaskId(); + completeTask(workflow.getWorkflowId(), taskId); + Assertions.assertEquals(Workflow.WorkflowStatus.RUNNING, workflowClient.getWorkflow(workflowId, false).getStatus()); + workflowClient.terminateWorkflow(workflowId, "test completed"); + Assertions.assertEquals(Workflow.WorkflowStatus.TERMINATED, workflowClient.getWorkflow(workflowId, false).getStatus()); + } + + private void completeTask(String workflowId, String taskId) { + TaskResult taskResult = new TaskResult(); + taskResult.setStatus(TaskResult.Status.COMPLETED); + taskResult.setTaskId(taskId); + taskResult.setWorkflowInstanceId(workflowId); + taskClient.updateTask(taskResult); + } + + private void failTask(String workflowId, String taskId) { + TaskResult taskResult = new TaskResult(); + taskResult.setStatus(TaskResult.Status.FAILED); + taskResult.setReasonForIncompletion("No reason"); + taskResult.setTaskId(taskId); + taskResult.setWorkflowInstanceId(workflowId); + taskClient.updateTask(taskResult); + } + + void registerTask() { + TaskDef taskDef = new TaskDef(); + taskDef.setName(taskName); + taskDef.setOwnerEmail("test@orkes.com"); + List taskDefs = new ArrayList<>(); + taskDefs.add(taskDef); + metadataClient.registerTaskDefs(taskDefs); + } + + void registerWorkflow() { + WorkflowDef workflowDef = getWorkflowDef(); + metadataClient.registerWorkflowDef(workflowDef); + } + + static WorkflowDef getWorkflowDef() { + WorkflowDef workflowDef = new WorkflowDef(); + workflowDef.setName(workflowName); + workflowDef.setVersion(1); + workflowDef.setOwnerEmail(Commons.OWNER_EMAIL); + workflowDef.setTimeoutSeconds(600); + workflowDef.setTimeoutPolicy(WorkflowDef.TimeoutPolicy.TIME_OUT_WF); + WorkflowTask workflowTask = new WorkflowTask(); + workflowTask.setName(taskName); + workflowTask.setTaskReferenceName(taskName); + workflowDef.setTasks(List.of(workflowTask)); + return workflowDef; + } + +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/AuthorizationClientTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/AuthorizationClientTests.java new file mode 100644 index 000000000..e3fb160a9 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/AuthorizationClientTests.java @@ -0,0 +1,343 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.netflix.conductor.client.exception.ConductorClientException; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +import io.orkes.conductor.client.AuthorizationClient; +import io.orkes.conductor.client.OrkesClients; +import io.orkes.conductor.client.model.AccessKeyResponse; +import io.orkes.conductor.client.model.AuthorizationRequest; +import io.orkes.conductor.client.model.ConductorApplication; +import io.orkes.conductor.client.model.ConductorUser; +import io.orkes.conductor.client.model.CreateAccessKeyResponse; +import io.orkes.conductor.client.model.CreateOrUpdateApplicationRequest; +import io.orkes.conductor.client.model.Group; +import io.orkes.conductor.client.model.SubjectRef; +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.TargetRef; +import io.orkes.conductor.client.model.TargetRef.TypeEnum; +import io.orkes.conductor.client.model.UpsertGroupRequest; +import io.orkes.conductor.client.model.UpsertGroupRequest.RolesEnum; +import io.orkes.conductor.client.model.UpsertUserRequest; +import io.orkes.conductor.client.util.ClientTestUtil; +import io.orkes.conductor.client.util.Commons; + +public class AuthorizationClientTests { + private static AuthorizationClient authorizationClient; + private static String applicationId; + private static OrkesMetadataClient metadataClient; + + @BeforeAll + public static void setup() { + OrkesClients orkesClients = ClientTestUtil.getOrkesClients(); + authorizationClient = orkesClients.getAuthorizationClient(); + metadataClient = orkesClients.getMetadataClient(); + CreateOrUpdateApplicationRequest request = new CreateOrUpdateApplicationRequest(); + request.setName("test-" + UUID.randomUUID()); + ConductorApplication app = authorizationClient.createApplication(request); + applicationId = app.getId(); + } + + @AfterAll + public static void cleanup() { + if(applicationId != null) { + authorizationClient.deleteApplication(applicationId); + } + } + + @Test + @DisplayName("auto assign group permission on workflow creation by any group member") + public void autoAssignWorkflowPermissions() { + giveApplicationPermissions(applicationId); + Group group = authorizationClient.upsertGroup(getUpsertGroupRequest(), "sdk-test-group"); + validateGroupPermissions(group.getId()); + } + + @Test + void testUser() { + ConductorUser user = + authorizationClient.upsertUser(getUpserUserRequest(), Commons.USER_EMAIL); + ConductorUser receivedUser = authorizationClient.getUser(Commons.USER_EMAIL); + Assertions.assertEquals(user.getName(), receivedUser.getName()); + Assertions.assertEquals(user.getGroups().get(0).getId(), receivedUser.getGroups().get(0).getId()); + Assertions.assertEquals(user.getRoles().get(0).getName(), receivedUser.getRoles().get(0).getName()); + authorizationClient.sendInviteEmail(user.getId()); + Group group = authorizationClient.upsertGroup(getUpsertGroupRequest(), Commons.GROUP_ID); + Assertions.assertNotNull(group); + authorizationClient.removeUserFromGroup(Commons.GROUP_ID, user.getId()); + authorizationClient.removePermissions(getAuthorizationRequest()); + } + + @Test + void testGroup() { + UpsertGroupRequest request = new UpsertGroupRequest(); + + // Default Access for the group. When specified, any new workflow or task + // created by the + // members of this group + // get this default permission inside the group. + Map> defaultAccess = new HashMap<>(); + + // Grant READ access to the members of the group for any new workflow created by + // a member of + // this group + defaultAccess.put(TypeEnum.WORKFLOW_DEF.getValue(), List.of("READ")); + + // Grant EXECUTE access to the members of the group for any new task created by + // a member of + // this group + defaultAccess.put(TypeEnum.TASK_DEF.getValue(), List.of("EXECUTE")); + request.setDefaultAccess(defaultAccess); + + request.setDescription("Example group created for testing"); + request.setRoles(Arrays.asList(UpsertGroupRequest.RolesEnum.USER)); + + Group group = authorizationClient.upsertGroup(request, Commons.GROUP_ID); + Assertions.assertNotNull(group); + Group found = authorizationClient.getGroup(Commons.GROUP_ID); + Assertions.assertNotNull(found); + Assertions.assertEquals(group.getId(), found.getId()); + Assertions.assertEquals(group.getDefaultAccess().keySet(), found.getDefaultAccess().keySet()); + } + + @Test + void testApplication() { + CreateOrUpdateApplicationRequest request = new CreateOrUpdateApplicationRequest(); + request.setName("Test Application for the testing"); + + // WARNING: Application Name is not a UNIQUE value and if called multiple times, + // it will + // create a new application + ConductorApplication application = authorizationClient.createApplication(request); + Assertions.assertNotNull(application); + Assertions.assertNotNull(application.getId()); + + // Get the list of applications + List apps = authorizationClient.listApplications(); + Assertions.assertNotNull(apps); + long found = + apps.stream() + .map(ConductorApplication::getId) + .filter(id -> id.equals(application.getId())) + .count(); + Assertions.assertEquals(1, found); + + // Create new access key + CreateAccessKeyResponse accessKey = + authorizationClient.createAccessKey(application.getId()); + List accessKeyResponses = + authorizationClient.getAccessKeys(application.getId()); + Assertions.assertEquals(1, accessKeyResponses.size()); + authorizationClient.toggleAccessKeyStatus(application.getId(), accessKey.getId()); + authorizationClient.deleteAccessKey(application.getId(), accessKey.getId()); + authorizationClient.setApplicationTags(getTagObject(), application.getId()); + Assertions.assertEquals(getTagObject(), authorizationClient.getApplicationTags(application.getId())); + authorizationClient.deleteApplicationTags(getTagObject(), application.getId()); + Assertions.assertEquals(0, authorizationClient.getApplicationTags(application.getId()).size()); + accessKeyResponses = authorizationClient.getAccessKeys(application.getId()); + Assertions.assertEquals(0, accessKeyResponses.size()); + + authorizationClient.removeRoleFromApplicationUser( + application.getId(), RolesEnum.ADMIN.getValue()); + + String newName = "ansdjansdjna"; + authorizationClient.updateApplication( + new CreateOrUpdateApplicationRequest().name(newName), application.getId()); + Assertions.assertEquals(newName, authorizationClient.getApplication(application.getId()).getName()); + + authorizationClient.deleteApplication(application.getId()); + } + + @Test + void testGrantPermissionsToGroup() { + AuthorizationRequest request = new AuthorizationRequest(); + request.access(Arrays.asList(AuthorizationRequest.AccessEnum.READ)); + SubjectRef subject = new SubjectRef(); + subject.setId("worker-test-group31dfe7a4-bd85-4ccc-9571-7c0e018ebc32"); + subject.setType(SubjectRef.TypeEnum.GROUP); + request.setSubject(subject); + TargetRef target = new TargetRef(); + target.setId("Test_032"); + target.setType(TargetRef.TypeEnum.WORKFLOW_DEF); + request.setTarget(target); + authorizationClient.grantPermissions(request); + } + + @Test + void testGrantPermissionsToDomain() { + AuthorizationRequest request = new AuthorizationRequest(); + request.access(Arrays.asList(AuthorizationRequest.AccessEnum.EXECUTE)); + SubjectRef subject = new SubjectRef(); + subject.setId("conductoruser1@gmail.com"); + subject.setType(SubjectRef.TypeEnum.USER); + request.setSubject(subject); + TargetRef target = new TargetRef(); + target.setId("my-domain"); + target.setType(TargetRef.TypeEnum.DOMAIN); + request.setTarget(target); + authorizationClient.grantPermissions(request); + } + + @Test + @DisplayName("tag a workflows and task") + public void tagWorkflowsAndTasks() { + registerWorkflow(); + TagObject tagObject = new TagObject(); + tagObject.setType(TagObject.TypeEnum.METADATA); + tagObject.setKey("a"); + tagObject.setValue("b"); + metadataClient.addTaskTag(tagObject, Commons.TASK_NAME); + metadataClient.addWorkflowTag(tagObject, Commons.WORKFLOW_NAME); + } + + public void registerWorkflow() { + WorkflowDef workflowDef = new WorkflowDef(); + workflowDef.setName(Commons.WORKFLOW_NAME); + workflowDef.setVersion(Commons.WORKFLOW_VERSION); + workflowDef.setOwnerEmail(Commons.OWNER_EMAIL); + WorkflowTask workflowTask = new WorkflowTask(); + workflowTask.setName(Commons.TASK_NAME); + workflowTask.setTaskReferenceName(Commons.TASK_NAME); + workflowDef.setTasks(List.of(workflowTask)); + metadataClient.updateWorkflowDefs(Arrays.asList(workflowDef)); + } + + @Test + void testGrantPermissionsToTag() { + authorizationClient.grantPermissions(getAuthorizationRequest()); + } + + @Test + void testMethods() { + try { + authorizationClient.deleteUser(Commons.USER_EMAIL); + } catch (ConductorClientException e) { + if (e.getStatus() != 404) { + throw e; + } + } + authorizationClient.upsertUser(getUpserUserRequest(), Commons.USER_EMAIL); + List users = authorizationClient.listUsers(false); + Assertions.assertFalse(users.isEmpty()); + users = authorizationClient.listUsers(true); + Assertions.assertFalse(users.isEmpty()); + try { + authorizationClient.deleteGroup(Commons.GROUP_ID); + } catch (ConductorClientException e) { + if (e.getStatus() != 404) { + throw e; + } + } + authorizationClient.upsertGroup(getUpsertGroupRequest(), Commons.GROUP_ID); + List groups = authorizationClient.listGroups(); + Assertions.assertFalse(groups.isEmpty()); + authorizationClient.addUserToGroup(Commons.GROUP_ID, Commons.USER_EMAIL); + boolean found = false; + for (ConductorUser user : authorizationClient.getUsersInGroup(Commons.GROUP_ID)) { + if (user.getName().equals(Commons.USER_NAME)) { + found = true; + } + } + Assertions.assertTrue(found); + authorizationClient.getPermissions("APPLICATION", applicationId); + Assertions.assertEquals(authorizationClient.getApplication(applicationId).getId(), applicationId); + Assertions.assertTrue( + authorizationClient + .getGrantedPermissionsForGroup(Commons.GROUP_ID) + .getGrantedAccess() + .isEmpty()); + // The user is added just now so it should not have any access. + Assertions.assertTrue( + authorizationClient + .getGrantedPermissionsForUser(Commons.USER_EMAIL) + .getGrantedAccess() + .isEmpty()); + } + + void giveApplicationPermissions(String applicationId) { + authorizationClient.addRoleToApplicationUser(applicationId, RolesEnum.ADMIN.getValue()); + } + + void validateGroupPermissions(String id) { + Group group = authorizationClient.getGroup(id); + for (Map.Entry> entry : group.getDefaultAccess().entrySet()) { + List expectedList = new ArrayList<>(getAccessListAll()); + List actualList = new ArrayList<>(entry.getValue()); + Collections.sort(expectedList); + Collections.sort(actualList); + Assertions.assertEquals(expectedList, actualList); + } + } + + UpsertGroupRequest getUpsertGroupRequest() { + return new UpsertGroupRequest() + .defaultAccess( + Map.of( + TypeEnum.WORKFLOW_DEF.getValue(), getAccessListAll(), + TypeEnum.TASK_DEF.getValue(), getAccessListAll())) + .description("Group used for SDK testing") + .roles(List.of(RolesEnum.ADMIN)); + } + + UpsertUserRequest getUpserUserRequest() { + UpsertUserRequest request = new UpsertUserRequest(); + request.setName(Commons.USER_NAME); + request.setGroups(List.of(Commons.GROUP_ID)); + request.setRoles(List.of(UpsertUserRequest.RolesEnum.USER)); + return request; + } + + + + List getAccessListAll() { + return List.of("CREATE", "READ", "UPDATE", "EXECUTE", "DELETE"); + } + + AuthorizationRequest getAuthorizationRequest() { + AuthorizationRequest request = new AuthorizationRequest(); + request.access(Arrays.asList(AuthorizationRequest.AccessEnum.READ)); + SubjectRef subject = new SubjectRef(); + subject.setId("worker-test-group31dfe7a4-bd85-4ccc-9571-7c0e018ebc32"); + subject.setType(SubjectRef.TypeEnum.GROUP); + request.setSubject(subject); + TargetRef target = new TargetRef(); + target.setId("org:accounting"); + target.setType(TargetRef.TypeEnum.TAG); + request.setTarget(target); + return request; + } + + private List getTagObject() { + TagObject tagObject = new TagObject(); + tagObject.setKey("department"); + tagObject.setValue("accounts"); + return List.of(tagObject); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/EventClientTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/EventClientTests.java new file mode 100644 index 000000000..a576c00ba --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/EventClientTests.java @@ -0,0 +1,77 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.netflix.conductor.client.exception.ConductorClientException; +import com.netflix.conductor.common.metadata.events.EventHandler; +import com.netflix.conductor.common.metadata.events.EventHandler.Action; +import com.netflix.conductor.common.metadata.events.EventHandler.StartWorkflow; + +import io.orkes.conductor.client.util.ClientTestUtil; +import io.orkes.conductor.client.util.Commons; + +public class EventClientTests { + private static final String EVENT_NAME = "test_sdk_java_event_name"; + private static final String EVENT = "test_sdk_java_event"; + private final OrkesEventClient eventClient = ClientTestUtil.getOrkesClients().getEventClient(); + + @Test + void testEventHandler() { + try { + eventClient.unregisterEventHandler(EVENT_NAME); + } catch (ConductorClientException e) { + if (e.getStatus() != 404) { + throw e; + } + } + EventHandler eventHandler = getEventHandler(); + eventClient.registerEventHandler(eventHandler); + eventClient.updateEventHandler(eventHandler); + List events = eventClient.getEventHandlers(EVENT, false); + Assertions.assertEquals(1, events.size()); + events.forEach( + event -> { + Assertions.assertEquals(eventHandler.getName(), event.getName()); + Assertions.assertEquals(eventHandler.getEvent(), event.getEvent()); + }); + eventClient.unregisterEventHandler(EVENT_NAME); + Assertions.assertIterableEquals(List.of(), eventClient.getEventHandlers(EVENT, false)); + } + + EventHandler getEventHandler() { + EventHandler eventHandler = new EventHandler(); + eventHandler.setName(EVENT_NAME); + eventHandler.setEvent(EVENT); + eventHandler.setActions(List.of(getEventHandlerAction())); + return eventHandler; + } + + Action getEventHandlerAction() { + Action action = new Action(); + action.setAction(Action.Type.start_workflow); + action.setStart_workflow(getStartWorkflowAction()); + return action; + } + + StartWorkflow getStartWorkflowAction() { + StartWorkflow startWorkflow = new StartWorkflow(); + startWorkflow.setName(Commons.WORKFLOW_NAME); + startWorkflow.setVersion(Commons.WORKFLOW_VERSION); + return startWorkflow; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/MetadataClientTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/MetadataClientTests.java new file mode 100644 index 000000000..90916e112 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/MetadataClientTests.java @@ -0,0 +1,115 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.netflix.conductor.client.exception.ConductorClientException; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; + +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.util.ClientTestUtil; +import io.orkes.conductor.client.util.Commons; +import io.orkes.conductor.client.util.TestUtil; +import io.orkes.conductor.client.util.WorkflowUtil; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SuppressWarnings("unchecked") +public class MetadataClientTests { + private final OrkesMetadataClient metadataClient = ClientTestUtil.getOrkesClients().getMetadataClient(); + + @Test + void taskDefinition() { + try { + metadataClient.unregisterTaskDef(Commons.TASK_NAME); + } catch (ConductorClientException e) { + if (e.getStatus() != 404) { + throw e; + } + } + TaskDef taskDef = Commons.getTaskDef(); + metadataClient.registerTaskDefs(List.of(taskDef)); + metadataClient.updateTaskDef(taskDef); + TaskDef receivedTaskDef = metadataClient.getTaskDef(Commons.TASK_NAME); + Assertions.assertEquals(taskDef.getName(), receivedTaskDef.getName()); + } + + @Test + void workflow() { + try { + metadataClient.unregisterWorkflowDef(Commons.WORKFLOW_NAME, Commons.WORKFLOW_VERSION); + } catch (ConductorClientException e) { + if (e.getStatus() != 404) { + throw e; + } + } + metadataClient.registerTaskDefs(List.of(Commons.getTaskDef())); + WorkflowDef workflowDef = WorkflowUtil.getWorkflowDef(); + metadataClient.registerWorkflowDef(workflowDef); + metadataClient.updateWorkflowDefs(List.of(workflowDef)); + metadataClient.updateWorkflowDefs(List.of(workflowDef), true); + metadataClient.registerWorkflowDef(workflowDef, true); + ((OrkesMetadataClient) metadataClient) + .getWorkflowDefWithMetadata(Commons.WORKFLOW_NAME, Commons.WORKFLOW_VERSION); + WorkflowDef receivedWorkflowDef = metadataClient.getWorkflowDef(Commons.WORKFLOW_NAME, + Commons.WORKFLOW_VERSION); + assertEquals(receivedWorkflowDef.getName(), Commons.WORKFLOW_NAME); + assertEquals(receivedWorkflowDef.getVersion(), Commons.WORKFLOW_VERSION); + } + + @Test + void tagTask() throws Exception { + metadataClient.registerTaskDefs(List.of(Commons.getTaskDef())); + try { + metadataClient.deleteTaskTag(Commons.getTagString(), Commons.TASK_NAME); + } catch (ConductorClientException e) { + if (e.getStatus() != 404) { + throw e; + } + } + TagObject tagObject = Commons.getTagObject(); + metadataClient.addTaskTag(tagObject, Commons.TASK_NAME); + metadataClient.setTaskTags(List.of(tagObject), Commons.TASK_NAME); + Assertions.assertNotNull( + TestUtil.retryMethodCall( + metadataClient::getTags)); + List tags = (List) TestUtil.retryMethodCall( + () -> metadataClient.getTaskTags(Commons.TASK_NAME)); + Assertions.assertIterableEquals(List.of(tagObject), tags); + metadataClient.deleteTaskTag(Commons.getTagString(), Commons.TASK_NAME); + tags = (List) TestUtil.retryMethodCall( + () -> metadataClient.getTaskTags(Commons.TASK_NAME)); + Assertions.assertIterableEquals(List.of(), tags); + } + + @Test + void tagWorkflow() { + TagObject tagObject = Commons.getTagObject(); + try { + metadataClient.deleteWorkflowTag(Commons.getTagObject(), Commons.WORKFLOW_NAME); + } catch (ConductorClientException e) { + if (e.getStatus() != 404) { + throw e; + } + } + metadataClient.addWorkflowTag(tagObject, Commons.WORKFLOW_NAME); + metadataClient.setWorkflowTags(List.of(tagObject), Commons.WORKFLOW_NAME); + List tags = metadataClient.getWorkflowTags(Commons.WORKFLOW_NAME); + Assertions.assertIterableEquals(List.of(tagObject), tags); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/PathSuffixTest.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/PathSuffixTest.java new file mode 100644 index 000000000..ee727d9a4 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/PathSuffixTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.netflix.conductor.client.http.ConductorClient; + +import static org.junit.jupiter.api.Assertions.assertEquals; + + +public class PathSuffixTest { + + @Test + @DisplayName("Trailing / should be removed by constructor") + public void baseConstructor() { + var client = new ConductorClient("https://play.orkes.io/api"); + var client2 = new ConductorClient("https://play.orkes.io/api/"); + + assertEquals("https://play.orkes.io/api", client.getBasePath()); + assertEquals(client2.getBasePath(), client.getBasePath()); + } + + @Test + @DisplayName("Trailing / should be removed by builder") + public void test() { + ConductorClient client = ConductorClient.builder().basePath("https://play.orkes.io/api/") + .build(); + assertEquals("https://play.orkes.io/api", client.getBasePath()); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SchedulerClientTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SchedulerClientTests.java new file mode 100644 index 000000000..6b3adb11a --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SchedulerClientTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.orkes.conductor.client.SchedulerClient; +import io.orkes.conductor.client.model.SaveScheduleRequest; +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.WorkflowSchedule; +import io.orkes.conductor.client.util.ClientTestUtil; +import io.orkes.conductor.client.util.Commons; + +public class SchedulerClientTests { + private final String NAME = "test_sdk_java_scheduler_name"; + private final String CRON_EXPRESSION = "0 * * * * *"; + + private final SchedulerClient schedulerClient = ClientTestUtil.getOrkesClients().getSchedulerClient(); + + @Test + void testMethods() { + schedulerClient.deleteSchedule(NAME); + Assertions.assertTrue(schedulerClient.getNextFewSchedules(CRON_EXPRESSION, 0L, 0L, 0).isEmpty()); + schedulerClient.saveSchedule(getSaveScheduleRequest()); + Assertions.assertTrue(schedulerClient.getAllSchedules(Commons.WORKFLOW_NAME).size() > 0); + WorkflowSchedule workflowSchedule = schedulerClient.getSchedule(NAME); + Assertions.assertEquals(NAME, workflowSchedule.getName()); + Assertions.assertEquals(CRON_EXPRESSION, workflowSchedule.getCronExpression()); + Assertions.assertFalse(schedulerClient.search(0, 10, "ASC", "*", "").getResults().isEmpty()); + schedulerClient.setSchedulerTags(getTagObject(), NAME); + Assertions.assertEquals(getTagObject(), schedulerClient.getSchedulerTags(NAME)); + schedulerClient.deleteSchedulerTags(getTagObject(), NAME); + Assertions.assertEquals(0, schedulerClient.getSchedulerTags(NAME).size()); + schedulerClient.pauseSchedule(NAME); + workflowSchedule = schedulerClient.getSchedule(NAME); + Assertions.assertTrue(workflowSchedule.isPaused()); + schedulerClient.resumeSchedule(NAME); + workflowSchedule = schedulerClient.getSchedule(NAME); + Assertions.assertFalse(workflowSchedule.isPaused()); + schedulerClient.deleteSchedule(NAME); + } + + @Test + void testDebugMethods() { + schedulerClient.pauseAllSchedules(); + schedulerClient.resumeAllSchedules(); + schedulerClient.requeueAllExecutionRecords(); + } + + SaveScheduleRequest getSaveScheduleRequest() { + return new SaveScheduleRequest() + .name(NAME) + .cronExpression(CRON_EXPRESSION) + .startWorkflowRequest(Commons.getStartWorkflowRequest()); + } + + private List getTagObject() { + TagObject tagObject = new TagObject(); + tagObject.setKey("department"); + tagObject.setValue("accounts"); + return List.of(tagObject); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SecretClientTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SecretClientTests.java new file mode 100644 index 000000000..de2cd096b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SecretClientTests.java @@ -0,0 +1,62 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.netflix.conductor.client.exception.ConductorClientException; + +import io.orkes.conductor.client.SecretClient; +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.util.ClientTestUtil; + + +public class SecretClientTests { + private final String SECRET_NAME = "test-sdk-java-secret_name"; + private final String SECRET_KEY = "test-sdk-java-secret_key"; + + private final SecretClient secretClient = ClientTestUtil.getOrkesClients().getSecretClient(); + + @Test + void testMethods() { + try { + secretClient.deleteSecret(SECRET_KEY); + } catch (ConductorClientException e) { + if (e.getStatus() != 500) { + throw e; + } + } + secretClient.putSecret(SECRET_NAME, SECRET_KEY); + secretClient.setSecretTags(List.of(getTagObject()), SECRET_KEY); + List tags = secretClient.getSecretTags(SECRET_KEY); + Assertions.assertEquals(tags.size(), 1); + Assertions.assertEquals(tags.get(0), getTagObject()); + secretClient.deleteSecretTags(List.of(getTagObject()), SECRET_KEY); + Assertions.assertEquals(secretClient.getSecretTags(SECRET_KEY).size(), 0); + Assertions.assertTrue(secretClient.listSecretsThatUserCanGrantAccessTo().contains(SECRET_KEY)); + Assertions.assertTrue(secretClient.listAllSecretNames().contains(SECRET_KEY)); + Assertions.assertEquals(SECRET_NAME, secretClient.getSecret(SECRET_KEY)); + Assertions.assertTrue(secretClient.secretExists(SECRET_KEY)); + secretClient.deleteSecret(SECRET_KEY); + } + + private TagObject getTagObject() { + TagObject tagObject = new TagObject(); + tagObject.setKey("department"); + tagObject.setValue("accounts"); + return tagObject; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/TaskClientTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/TaskClientTests.java new file mode 100644 index 000000000..3b2b19d99 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/TaskClientTests.java @@ -0,0 +1,228 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.testcontainers.shaded.com.google.common.util.concurrent.Uninterruptibles; + +import com.netflix.conductor.client.exception.ConductorClientException; +import com.netflix.conductor.common.config.ObjectMapperProvider; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.tasks.TaskExecLog; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.sdk.workflow.def.ConductorWorkflow; +import com.netflix.conductor.sdk.workflow.def.tasks.SimpleTask; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; + +import io.orkes.conductor.client.util.ClientTestUtil; +import io.orkes.conductor.client.util.TestUtil; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class TaskClientTests { + + private static OrkesTaskClient taskClient; + private static OrkesWorkflowClient workflowClient; + private static OrkesMetadataClient metadataClient; + private static WorkflowExecutor workflowExecutor; + + private static String workflowName = ""; + + @BeforeAll + public static void setup() throws IOException { + taskClient = ClientTestUtil.getOrkesClients().getTaskClient(); + metadataClient = ClientTestUtil.getOrkesClients().getMetadataClient(); + workflowClient = ClientTestUtil.getOrkesClients().getWorkflowClient(); + InputStream is = TaskClientTests.class.getResourceAsStream("/sdk_test.json"); + ObjectMapper om = new ObjectMapperProvider().getObjectMapper(); + WorkflowDef workflowDef = om.readValue(new InputStreamReader(is), WorkflowDef.class); + metadataClient.registerWorkflowDef(workflowDef, true); + workflowName = workflowDef.getName(); + workflowExecutor = new WorkflowExecutor(ClientTestUtil.getClient(), 10); + } + + @Test + public void testUpdateByRefName() { + StartWorkflowRequest request = new StartWorkflowRequest(); + request.setName(workflowName); + request.setVersion(1); + request.setInput(new HashMap<>()); + String workflowId = workflowClient.startWorkflow(request); + System.out.println(workflowId); + Workflow workflow = workflowClient.getWorkflow(workflowId, true); + Assertions.assertNotNull(workflow); + + System.out.println("Running test for workflow: " + workflowId); + + int maxLoop = 10; + int count = 0; + while (!workflow.getStatus().isTerminal() && count < maxLoop) { + workflow.getTasks().stream().filter(t -> !t.getStatus().isTerminal() && t.getWorkflowTask().getType().equals("SIMPLE")).forEach(running -> { + String referenceName = running.getReferenceTaskName(); + System.out.println("Updating " + referenceName + ", and its status is " + running.getStatus()); + taskClient.updateTaskSync(workflowId, referenceName, TaskResult.Status.COMPLETED, Map.of("k", "value")); + }); + count++; + Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); + workflow = workflowClient.getWorkflow(workflowId, true); + } + Assertions.assertTrue(count <= maxLoop, "count " + count + " is not less than maxLoop " + maxLoop); + workflow = workflowClient.getWorkflow(workflowId, true); + Assertions.assertEquals(Workflow.WorkflowStatus.COMPLETED, workflow.getStatus()); + } + + @Test + public void testUpdateByRefNameSync() { + StartWorkflowRequest request = new StartWorkflowRequest(); + request.setName(workflowName); + request.setVersion(1); + request.setInput(new HashMap<>()); + String workflowId = workflowClient.startWorkflow(request); + System.out.println(workflowId); + Workflow workflow = workflowClient.getWorkflow(workflowId, true); + Assertions.assertNotNull(workflow); + + int maxLoop = 10; + int count = 0; + while (!workflow.getStatus().isTerminal() && count < maxLoop) { + workflow = workflowClient.getWorkflow(workflowId, true); + List runningTasks = workflow.getTasks().stream() + .filter(task -> !task.getStatus().isTerminal() && task.getTaskType().equals("there_is_no_worker")) + .map(Task::getReferenceTaskName) + .collect(Collectors.toList()); + System.out.println("Running tasks: " + runningTasks); + if (runningTasks.isEmpty()) { + Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); + count++; + continue; + } + for (String referenceName : runningTasks) { + System.out.println("Updating " + referenceName); + try { + workflow = taskClient.updateTaskSync(workflowId, referenceName, TaskResult.Status.COMPLETED, new TaskOutput()); + System.out.println("Workflow: " + workflow); + } catch (ConductorClientException ConductorClientException) { + // 404 == task was updated already and there are no pending tasks + if (ConductorClientException.getStatus() != 404) { + Assertions.fail(ConductorClientException); + } + } + } + count++; + } + Assertions.assertTrue(count < maxLoop); + workflow = workflowClient.getWorkflow(workflowId, true); + Assertions.assertEquals(Workflow.WorkflowStatus.COMPLETED, workflow.getStatus()); + } + + @Test + public void testTaskLog() throws Exception { + var workflowName = "random_workflow_name_1hqiuwhjasdsadqqwe"; + var taskName1 = "random_task_name_1najsbdha"; + var taskName2 = "random_task_name_1bhasvdgasvd12y378t"; + + var taskDef1 = new TaskDef(taskName1); + taskDef1.setRetryCount(0); + taskDef1.setOwnerEmail("test@orkes.io"); + var taskDef2 = new TaskDef(taskName2); + taskDef2.setRetryCount(0); + taskDef2.setOwnerEmail("test@orkes.io"); + + TestUtil.retryMethodCall( + () -> metadataClient.registerTaskDefs(List.of(taskDef1, taskDef2))); + + var wf = new ConductorWorkflow<>(workflowExecutor); + wf.setName(workflowName); + wf.setVersion(1); + wf.add(new SimpleTask(taskName1, taskName1)); + wf.add(new SimpleTask(taskName2, taskName2)); + TestUtil.retryMethodCall( + () -> wf.registerWorkflow(true)); + + StartWorkflowRequest startWorkflowRequest = new StartWorkflowRequest(); + startWorkflowRequest.setName(workflowName); + startWorkflowRequest.setVersion(1); + startWorkflowRequest.setInput(new HashMap<>()); + var workflowId = (String) TestUtil.retryMethodCall( + () -> workflowClient.startWorkflow(startWorkflowRequest)); + System.out.println("Started workflow with id: " + workflowId); + + var task = (Task) TestUtil.retryMethodCall( + () -> taskClient.pollTask(taskName1, "random worker", null)); + Assertions.assertNotNull(task); + var taskId = task.getTaskId(); + + TestUtil.retryMethodCall( + () -> taskClient.logMessageForTask(taskId, "random message")); + var logs = (List) TestUtil.retryMethodCall( + () -> taskClient.getTaskLogs(taskId)); + Assertions.assertNotNull(logs); + var details = (Task) TestUtil.retryMethodCall( + () -> taskClient.getTaskDetails(taskId)); + Assertions.assertNotNull(details); + TestUtil.retryMethodCall( + () -> taskClient.requeuePendingTasksByTaskType(taskName2)); + TestUtil.retryMethodCall( + () -> taskClient.getQueueSizeForTask(taskName1)); + TestUtil.retryMethodCall( + () -> taskClient.getQueueSizeForTask(taskName1, null, null, null)); + TestUtil.retryMethodCall( + () -> taskClient.batchPollTasksByTaskType(taskName2, "random worker id", 5, 3000)); + } + + @Test + public void testUnsupportedMethods() { + // Not supported by Orkes Conductor Server + var ex = Assertions.assertThrows(ConductorClientException.class, + () -> taskClient.searchV2(4, 20, "sort", "freeText", "query")); + Assertions.assertEquals(404, ex.getStatus()); + } + + private static class TaskOutput { + private String name = "hello"; + + private BigDecimal value = BigDecimal.TEN; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public BigDecimal getValue() { + return value; + } + + public void setValue(BigDecimal value) { + this.value = value; + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowClientTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowClientTests.java new file mode 100644 index 000000000..430c7de7e --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowClientTests.java @@ -0,0 +1,194 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.sdk.workflow.def.ConductorWorkflow; +import com.netflix.conductor.sdk.workflow.def.tasks.Http; +import com.netflix.conductor.sdk.workflow.def.tasks.SimpleTask; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; + +import io.orkes.conductor.client.util.ClientTestUtil; +import io.orkes.conductor.client.util.Commons; +import io.orkes.conductor.client.util.TestUtil; + +import com.google.common.util.concurrent.Uninterruptibles; + +public class WorkflowClientTests { + private static OrkesWorkflowClient workflowClient; + private static OrkesMetadataClient metadataClient; + private static WorkflowExecutor workflowExecutor; + + @BeforeAll + public static void setup() { + workflowClient = ClientTestUtil.getOrkesClients().getWorkflowClient(); + metadataClient = ClientTestUtil.getOrkesClients().getMetadataClient(); + workflowExecutor = new WorkflowExecutor(ClientTestUtil.getClient(), 10); + } + + @Test + public void startWorkflow() { + String workflowId = workflowClient.startWorkflow(getStartWorkflowRequest()); + Workflow workflow = workflowClient.getWorkflow(workflowId, false); + Assertions.assertEquals(workflow.getWorkflowName(), Commons.WORKFLOW_NAME); + } + + @Test + public void testSearchByCorrelationIds() { + List correlationIds = new ArrayList<>(); + Set workflowNames = new HashSet<>(); + Map> correlationIdToWorkflows = new HashMap<>(); + for (int i = 0; i < 3; i++) { + String correlationId = UUID.randomUUID().toString(); + correlationIds.add(correlationId); + for (int j = 0; j < 5; j++) { + ConductorWorkflow workflow = new ConductorWorkflow<>(workflowExecutor); + workflow.add(new Http("http").url("https://orkes-api-tester.orkesconductor.com/get")); + workflow.setName("workflow_" + j); + workflowNames.add(workflow.getName()); + StartWorkflowRequest request = new StartWorkflowRequest(); + request.setName(workflow.getName()); + request.setWorkflowDef(workflow.toWorkflowDef()); + request.setCorrelationId(correlationId); + String id = workflowClient.startWorkflow(request); + System.out.println("started " + id); + Set ids = correlationIdToWorkflows.getOrDefault(correlationId, new HashSet<>()); + ids.add(id); + correlationIdToWorkflows.put(correlationId, ids); + } + } + // Let's give couple of seconds for indexing to complete + Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS); + Map> result = workflowClient.getWorkflowsByNamesAndCorrelationIds(correlationIds, new ArrayList<>(workflowNames), true, false); + Assertions.assertNotNull(result); + Assertions.assertEquals(correlationIds.size(), result.size()); + for (String correlationId : correlationIds) { + Assertions.assertEquals(5, result.get(correlationId).size()); + Set ids = result.get(correlationId).stream().map(Workflow::getWorkflowId) + .collect(Collectors.toSet()); + Assertions.assertEquals(correlationIdToWorkflows.get(correlationId), ids); + } + } + + @Test + public void testWorkflowTerminate() { + String workflowId = workflowClient.startWorkflow(getStartWorkflowRequest()); + workflowClient.terminateWorkflowWithFailure( + workflowId, "testing out some stuff", true); + var workflow = workflowClient.getWorkflow(workflowId, false); + Assertions.assertEquals(Workflow.WorkflowStatus.TERMINATED, workflow.getStatus()); + } + + @Test + public void testSkipTaskFromWorkflow() throws Exception { + var workflowName = "random_workflow_name_1hqiuwheiquwhe"; + var taskName1 = "random_task_name_1hqiuwheiquwheajnsdsand"; + var taskName2 = "random_task_name_1hqiuwheiquwheajnsdsandjsadh"; + + var taskDef1 = new TaskDef(taskName1); + taskDef1.setRetryCount(0); + taskDef1.setOwnerEmail("test@orkes.io"); + var taskDef2 = new TaskDef(taskName2); + taskDef2.setRetryCount(0); + taskDef2.setOwnerEmail("test@orkes.io"); + + TestUtil.retryMethodCall( + () -> metadataClient.registerTaskDefs(List.of(taskDef1, taskDef2))); + + var wf = new ConductorWorkflow<>(workflowExecutor); + wf.setName(workflowName); + wf.setVersion(1); + wf.add(new SimpleTask(taskName1, taskName1)); + wf.add(new SimpleTask(taskName2, taskName2)); + TestUtil.retryMethodCall( + () -> wf.registerWorkflow(true)); + + StartWorkflowRequest startWorkflowRequest = new StartWorkflowRequest(); + startWorkflowRequest.setName(workflowName); + startWorkflowRequest.setVersion(1); + startWorkflowRequest.setInput(new HashMap<>()); + var workflowId = (String) TestUtil.retryMethodCall( + () -> workflowClient.startWorkflow(startWorkflowRequest)); + System.out.println("workflowId: " + workflowId); + + TestUtil.retryMethodCall( + () -> workflowClient.skipTaskFromWorkflow(workflowId, taskName2)); + TestUtil.retryMethodCall( + () -> workflowClient.terminateWorkflowsWithFailure(List.of(workflowId), null, false)); + } + + @Test + public void testUpdateVariables() { + ConductorWorkflow workflow = new ConductorWorkflow<>(workflowExecutor); + workflow.add(new SimpleTask("simple_task", "simple_task_ref")); + workflow.setTimeoutPolicy(WorkflowDef.TimeoutPolicy.TIME_OUT_WF); + workflow.setTimeoutSeconds(60); + workflow.setName("update_variable_test"); + workflow.setVersion(1); + workflow.registerWorkflow(true, true); + + StartWorkflowRequest request = new StartWorkflowRequest(); + request.setName(workflow.getName()); + request.setVersion(workflow.getVersion()); + request.setInput(Map.of()); + String workflowId = workflowClient.startWorkflow(request); + Assertions.assertNotNull(workflowId); + + Workflow execution = workflowClient.getWorkflow(workflowId, false); + Assertions.assertNotNull(execution); + Assertions.assertTrue(execution.getVariables().isEmpty()); + + Map variables = Map.of("k1", "v1", "k2", 42, "k3", Arrays.asList(3, 4, 5)); + execution = workflowClient.updateVariables(workflowId, variables); + Assertions.assertNotNull(execution); + Assertions.assertFalse(execution.getVariables().isEmpty()); + Assertions.assertEquals(variables.get("k1"), execution.getVariables().get("k1")); + Assertions.assertEquals(variables.get("k2").toString(), execution.getVariables().get("k2").toString()); + Assertions.assertEquals(variables.get("k3").toString(), execution.getVariables().get("k3").toString()); + + Map map = new HashMap<>(); + map.put("k1", null); + map.put("v1", "xyz"); + execution = workflowClient.updateVariables(workflowId, map); + Assertions.assertNotNull(execution); + Assertions.assertFalse(execution.getVariables().isEmpty()); + Assertions.assertNull(execution.getVariables().get("k1")); + Assertions.assertEquals(variables.get("k2").toString(), execution.getVariables().get("k2").toString()); + Assertions.assertEquals(variables.get("k3").toString(), execution.getVariables().get("k3").toString()); + Assertions.assertEquals("xyz", execution.getVariables().get("v1").toString()); + } + + @Test + void testExecuteWorkflow() { + // TODO + } + + StartWorkflowRequest getStartWorkflowRequest() { + StartWorkflowRequest startWorkflowRequest = new StartWorkflowRequest(); + startWorkflowRequest.setName(Commons.WORKFLOW_NAME); + startWorkflowRequest.setVersion(1); + startWorkflowRequest.setInput(new HashMap<>()); + return startWorkflowRequest; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowStateUpdateTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowStateUpdateTests.java new file mode 100644 index 000000000..2bb078149 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowStateUpdateTests.java @@ -0,0 +1,128 @@ +/* + * Copyright 2024 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.http; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.netflix.conductor.client.exception.ConductorClientException; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; +import com.netflix.conductor.common.metadata.workflow.IdempotencyStrategy; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; +import com.netflix.conductor.common.run.Workflow; + +import io.orkes.conductor.client.model.WorkflowRun; +import io.orkes.conductor.client.model.WorkflowStateUpdate; +import io.orkes.conductor.client.util.ClientTestUtil; + +import lombok.SneakyThrows; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +public class WorkflowStateUpdateTests { + + private static OrkesWorkflowClient workflowClient; + + @BeforeAll + public static void init() { + workflowClient = ClientTestUtil.getOrkesClients().getWorkflowClient(); + } + + @SneakyThrows + public String startWorkflow() { + StartWorkflowRequest startWorkflowRequest = new StartWorkflowRequest(); + startWorkflowRequest.setName("sync_task_variable_updates"); + startWorkflowRequest.setVersion(1); + var run = workflowClient.executeWorkflow(startWorkflowRequest, "wait_task_ref"); + return run.get(10, TimeUnit.SECONDS) + .getWorkflowId(); + } + + @Test + public void test() { + String workflowId = startWorkflow(); + System.out.println(workflowId); + + TaskResult taskResult = new TaskResult(); + taskResult.setOutputData(Map.of("a", "b")); + + WorkflowStateUpdate request = new WorkflowStateUpdate(); + request.setTaskReferenceName("wait_task_ref"); + request.setTaskResult(taskResult); + + request.setVariables(Map.of("case", "case1")); + + WorkflowRun workflowRun = workflowClient.updateWorkflow(workflowId, List.of("wait_task_ref_1", "wait_task_ref_2"), 10, request); + + System.out.println(workflowRun); + System.out.println(workflowRun.getStatus()); + System.out.println(workflowRun.getTasks() + .stream() + .map(task -> task.getReferenceTaskName() + ":" + task.getStatus()) + .collect(Collectors.toList())); + + request = new WorkflowStateUpdate(); + request.setTaskReferenceName("wait_task_ref_2"); + request.setTaskResult(taskResult); + workflowRun = workflowClient.updateWorkflow(workflowId, List.of(), 10, request); + + assertEquals(Workflow.WorkflowStatus.COMPLETED, workflowRun.getStatus()); + Set allTaskStatus = workflowRun.getTasks() + .stream() + .map(t -> t.getStatus()) + .collect(Collectors.toSet()); + assertEquals(1, allTaskStatus.size()); + assertEquals(Task.Status.COMPLETED, allTaskStatus.iterator().next()); + + System.out.println(workflowRun.getStatus()); + System.out.println(workflowRun.getTasks() + .stream() + .map(task -> task.getReferenceTaskName() + ":" + task.getStatus()) + .collect(Collectors.toList())); + + } + + @Test + public void testIdempotency() { + StartWorkflowRequest startWorkflowRequest = new StartWorkflowRequest(); + startWorkflowRequest.setName("sync_task_variable_updates"); + startWorkflowRequest.setVersion(1); + String idempotencyKey = UUID.randomUUID().toString(); + startWorkflowRequest.setIdempotencyKey(idempotencyKey); + startWorkflowRequest.setIdempotencyStrategy(IdempotencyStrategy.FAIL); + String workflowId = workflowClient.startWorkflow(startWorkflowRequest); + + startWorkflowRequest.setIdempotencyStrategy(IdempotencyStrategy.RETURN_EXISTING); + String workflowId2 = workflowClient.startWorkflow(startWorkflowRequest); + assertEquals(workflowId, workflowId2); + + startWorkflowRequest.setIdempotencyStrategy(IdempotencyStrategy.FAIL); + boolean conflict = false; + try { + workflowClient.startWorkflow(startWorkflowRequest); + } catch (ConductorClientException ce) { + conflict = ce.getStatusCode() == 409; + } + assertTrue(conflict); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/ClientTestUtil.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/ClientTestUtil.java new file mode 100644 index 000000000..9476ec83b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/ClientTestUtil.java @@ -0,0 +1,56 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.util; + +import org.junit.jupiter.api.Assertions; + +import com.netflix.conductor.client.http.ConductorClient; + +import io.orkes.conductor.client.OrkesClients; +import io.orkes.conductor.client.http.OrkesAuthentication; + +public class ClientTestUtil { + private static final String ENV_ROOT_URI = "CONDUCTOR_SERVER_URL"; + private static final String ENV_KEY_ID = "CONDUCTOR_SERVER_AUTH_KEY"; + private static final String ENV_SECRET = "CONDUCTOR_SERVER_AUTH_SECRET"; + private static final ConductorClient CLIENT = getClient(); + + public static OrkesClients getOrkesClients() { + return new OrkesClients(CLIENT); + } + + public static ConductorClient getClient() { + if (CLIENT != null) { + return CLIENT; + } + + String basePath = getEnv(ENV_ROOT_URI); + Assertions.assertNotNull(basePath, ENV_ROOT_URI + " env not set"); + String keyId = getEnv(ENV_KEY_ID); + Assertions.assertNotNull(keyId, ENV_KEY_ID + " env not set"); + String keySecret = getEnv(ENV_SECRET); + Assertions.assertNotNull(keySecret, ENV_SECRET + " env not set"); + + return ConductorClient.builder() + .basePath(basePath) + .addHeaderSupplier(new OrkesAuthentication(keyId, keySecret)) + .readTimeout(30_000) + .connectTimeout(30_000) + .writeTimeout(30_000) + .build(); + } + + private static String getEnv(String key) { + return System.getenv(key); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/Commons.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/Commons.java new file mode 100644 index 000000000..4c92caf34 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/Commons.java @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.util; + +import com.netflix.conductor.common.metadata.tasks.TaskDef; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; + +import io.orkes.conductor.client.model.TagObject; +import io.orkes.conductor.client.model.TagString; + +public class Commons { + public static String WORKFLOW_NAME = "test-sdk-java-workflow"; + public static String TASK_NAME = "test-sdk-java-task"; + public static String OWNER_EMAIL = "example@orkes.io"; + public static int WORKFLOW_VERSION = 1; + public static String GROUP_ID = "sdk-test-group"; + public static String USER_NAME = "Orkes User"; + public static String USER_EMAIL = "user@orkes.io"; + public static String APPLICATION_ID = "46f0bf10-b59d-4fbd-a053-935307c8cb86"; + public static final String SECRET_MANAGER_KEY_PATH = "path/to/key"; + public static final String SECRET_MANAGER_SECRET_PATH = "path/to/secret"; + + public static TagObject getTagObject() { + TagObject tagObject = new TagObject(); + tagObject.setType(null); + tagObject.setKey("a"); + tagObject.setValue("b"); + return tagObject; + } + + public static TagString getTagString() { + TagString tagString = new TagString(); + tagString.setType(null); + tagString.setKey("a"); + tagString.setValue("b"); + return tagString; + } + + public static TaskDef getTaskDef() { + TaskDef taskDef = new TaskDef(); + taskDef.setName(Commons.TASK_NAME); + return taskDef; + } + + public static StartWorkflowRequest getStartWorkflowRequest() { + return new StartWorkflowRequest().withName(WORKFLOW_NAME).withVersion(WORKFLOW_VERSION); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/SimpleWorker.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/SimpleWorker.java new file mode 100644 index 000000000..4dd5811bc --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/SimpleWorker.java @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.util; + + +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; + +public class SimpleWorker implements Worker { + @Override + public String getTaskDefName() { + return Commons.TASK_NAME; + } + + @Override + public TaskResult execute(Task task) { + task.setStatus(Task.Status.COMPLETED); + task.getOutputData().put("key", "value"); + task.getOutputData().put("key2", 42); + return new TaskResult(task); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/TestUtil.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/TestUtil.java new file mode 100644 index 000000000..22104e770 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/TestUtil.java @@ -0,0 +1,66 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.util; + +public class TestUtil { + private static int RETRY_ATTEMPT_LIMIT = 4; + + public static void retryMethodCall(VoidRunnableWithException function) + throws Exception { + Exception lastException = null; + for (int retryCounter = 0; retryCounter < RETRY_ATTEMPT_LIMIT; retryCounter += 1) { + try { + function.run(); + return; + } catch (Exception e) { + lastException = e; + System.out.println("Attempt " + (retryCounter + 1) + " failed: " + e.getMessage()); + try { + Thread.sleep(1000 * (1 << retryCounter)); // Sleep for 2^retryCounter second(s) before retrying + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + } + throw lastException; + } + + public static Object retryMethodCall(RunnableWithException function) + throws Exception { + Exception lastException = null; + for (int retryCounter = 0; retryCounter < RETRY_ATTEMPT_LIMIT; retryCounter += 1) { + try { + return function.run(); + } catch (Exception e) { + lastException = e; + System.out.println("Attempt " + (retryCounter + 1) + " failed: " + e.getMessage()); + try { + Thread.sleep(1000 * (1 << retryCounter)); // Sleep for 2^retryCounter second(s) before retrying + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + } + throw lastException; + } + + @FunctionalInterface + public interface RunnableWithException { + Object run() throws Exception; + } + + @FunctionalInterface + public interface VoidRunnableWithException { + void run() throws Exception; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/WorkflowUtil.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/WorkflowUtil.java new file mode 100644 index 000000000..bf283cf1c --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/WorkflowUtil.java @@ -0,0 +1,34 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.util; + +import java.util.List; + +import com.netflix.conductor.common.metadata.workflow.WorkflowDef; +import com.netflix.conductor.common.metadata.workflow.WorkflowTask; + +public class WorkflowUtil { + public static WorkflowDef getWorkflowDef() { + WorkflowDef workflowDef = new WorkflowDef(); + workflowDef.setName(Commons.WORKFLOW_NAME); + workflowDef.setVersion(Commons.WORKFLOW_VERSION); + workflowDef.setOwnerEmail(Commons.OWNER_EMAIL); + workflowDef.setTimeoutSeconds(600); + workflowDef.setTimeoutPolicy(WorkflowDef.TimeoutPolicy.TIME_OUT_WF); + WorkflowTask workflowTask = new WorkflowTask(); + workflowTask.setName(Commons.TASK_NAME); + workflowTask.setTaskReferenceName(Commons.TASK_NAME); + workflowDef.setTasks(List.of(workflowTask)); + return workflowDef; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/LocalServerWorkflowExecutionTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/LocalServerWorkflowExecutionTests.java new file mode 100644 index 000000000..cc9ae3386 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/LocalServerWorkflowExecutionTests.java @@ -0,0 +1,70 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.worker; + +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import com.netflix.conductor.client.automator.TaskRunnerConfigurer; +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.metadata.tasks.TaskResult; + + +public class LocalServerWorkflowExecutionTests { + public static void main(String[] args) { + ConductorClient client = new ConductorClient("http://localhost:8080/api"); + TaskClient taskClient = new TaskClient(client); + Iterable workers = Arrays.asList(new MyWorker()); + Map taskToDomain = new HashMap<>(); + taskToDomain.put("simple_task_0", "viren"); + TaskRunnerConfigurer configurer = + new TaskRunnerConfigurer.Builder(taskClient, workers) + .withSleepWhenRetry(100) + .withThreadCount(10) + .withWorkerNamePrefix("Hello") + .withTaskToDomain(taskToDomain) + .build(); + configurer.init(); + } + + private static class MyWorker implements Worker { + @Override + public String getTaskDefName() { + return "simple_task_0"; + } + + @Override + public TaskResult execute(Task task) { + System.out.println( + "Executing " + + task.getTaskId() + + ":" + + task.getPollCount() + + "::" + + new Date()); + TaskResult result = new TaskResult(task); + result.getOutputData().put("a", "b"); + if (task.getPollCount() < 2) { + result.setCallbackAfterSeconds(5); + } else { + result.setStatus(TaskResult.Status.COMPLETED); + } + return result; + } + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/WorkflowExecutionTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/WorkflowExecutionTests.java new file mode 100644 index 000000000..2b1e7c54b --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/WorkflowExecutionTests.java @@ -0,0 +1,90 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.worker; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.netflix.conductor.client.automator.TaskRunnerConfigurer; +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.worker.Worker; +import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest; + +import io.orkes.conductor.client.OrkesClients; +import io.orkes.conductor.client.http.OrkesWorkflowClient; +import io.orkes.conductor.client.model.WorkflowStatus; +import io.orkes.conductor.client.util.ClientTestUtil; +import io.orkes.conductor.client.util.Commons; +import io.orkes.conductor.client.util.SimpleWorker; + +import com.google.common.util.concurrent.Uninterruptibles; + + +public class WorkflowExecutionTests { + private OrkesWorkflowClient workflowClient; + private TaskRunnerConfigurer taskRunnerConfigurer; + + @BeforeEach + public void init() { + ConductorClient client = ClientTestUtil.getClient(); + workflowClient = new OrkesClients(client).getWorkflowClient(); + Worker worker = new SimpleWorker(); + this.taskRunnerConfigurer = + new TaskRunnerConfigurer.Builder(new TaskClient(client), Collections.singletonList(worker)) + .withTaskThreadCount(Map.of(Commons.TASK_NAME, 10)) + .build(); + } + + @Test + @DisplayName("Test workflow completion") + public void workflow() throws Exception { + List workflowIds = startWorkflows(2, Commons.WORKFLOW_NAME); + workflowIds.add(startWorkflow(Commons.WORKFLOW_NAME)); + this.taskRunnerConfigurer.init(); + Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS); + workflowIds.forEach(this::validateCompletedWorkflow); + this.taskRunnerConfigurer.shutdown(); + } + + String startWorkflow(String workflowName) { + StartWorkflowRequest request = new StartWorkflowRequest(); + request.setName(workflowName); + return workflowClient.startWorkflow(request); + } + + List startWorkflows(int quantity, String workflowName) { + StartWorkflowRequest startWorkflowRequest = new StartWorkflowRequest(); + startWorkflowRequest.setName(workflowName); + List workflowIds = new ArrayList<>(); + for (int i = 0; i < quantity; i += 1) { + String workflowId = workflowClient.startWorkflow(startWorkflowRequest); + workflowIds.add(workflowId); + } + return workflowIds; + } + + void validateCompletedWorkflow(String workflowId) { + WorkflowStatus workflowStatus = + workflowClient.getWorkflowStatusSummary(workflowId, false, false); + Assertions.assertEquals(WorkflowStatus.StatusEnum.COMPLETED, workflowStatus.getStatus()); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/sdk/WorkflowSDKTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/sdk/WorkflowSDKTests.java new file mode 100644 index 000000000..2688237be --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/sdk/WorkflowSDKTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2023 Orkes, Inc. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.sdk; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.netflix.conductor.client.http.ConductorClient; +import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.common.run.Workflow; +import com.netflix.conductor.sdk.workflow.def.ConductorWorkflow; +import com.netflix.conductor.sdk.workflow.def.tasks.SimpleTask; +import com.netflix.conductor.sdk.workflow.executor.WorkflowExecutor; +import com.netflix.conductor.sdk.workflow.executor.task.AnnotatedWorkerExecutor; +import com.netflix.conductor.sdk.workflow.executor.task.WorkerConfiguration; +import com.netflix.conductor.sdk.workflow.task.InputParam; +import com.netflix.conductor.sdk.workflow.task.WorkerTask; + +import io.orkes.conductor.client.util.ClientTestUtil; + + +public class WorkflowSDKTests { + + @Test + public void testCreateWorkflow() { + ConductorClient client = ClientTestUtil.getClient(); + + AnnotatedWorkerExecutor workerExecutor = new AnnotatedWorkerExecutor(new TaskClient(client), new WorkerConfiguration()); + workerExecutor.initWorkers("io.orkes.conductor.sdk"); + workerExecutor.startPolling(); + + WorkflowExecutor executor = new WorkflowExecutor(client, workerExecutor); + + ConductorWorkflow> workflow = new ConductorWorkflow<>(executor); + workflow.setName("sdk_workflow"); + workflow.setVersion(1); + workflow.add(new SimpleTask("sdk_task", "sdk_task")); + + workflow.registerWorkflow(true, true); + + CompletableFuture result = workflow.execute(Map.of("name", "orkes")); + Assertions.assertNotNull(result); + try { + Workflow executedWorkflow = result.get(3, TimeUnit.SECONDS); + Assertions.assertNotNull(executedWorkflow); + Assertions.assertEquals(Workflow.WorkflowStatus.COMPLETED, executedWorkflow.getStatus()); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + Assertions.fail(e.getMessage()); + } + } + + @WorkerTask(value = "sdk_task", pollingInterval = 10) + public String sdkTask(@InputParam("name") String name) { + return "Hello " + name; + } +} diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/application.properties b/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/application.properties new file mode 100644 index 000000000..93c260106 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/application.properties @@ -0,0 +1,7 @@ +conductor.worker.all.domain=domainx +conductor.worker.all.pollingInterval=3 +#conductor.worker.all.threadCount=7 + +conductor.worker.hello.domain=helo_domain +conductor.worker.hello.pollingInterval=1 +conductor.worker.hello.threadCount=13 \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/logback-test.xml b/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/logback-test.xml new file mode 100644 index 000000000..7a8e8980c --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/logback-test.xml @@ -0,0 +1,14 @@ + + + + + + + %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{}): %msg%n%throwable + + + + + + + diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/metadata/sub_workflow_tests.json b/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/metadata/sub_workflow_tests.json new file mode 100644 index 000000000..938cad35f --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/metadata/sub_workflow_tests.json @@ -0,0 +1,131 @@ +[{ + "name": "sub_workflow", + "description": "sub_workflow", + "version": 1, + "tasks": [ + { + "name": "simple_task_in_sub_wf", + "taskReferenceName": "t1", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [], + "outputParameters": {}, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "ownerEmail": "test@harness.com" +},{ + "name": "integration_test_wf", + "description": "integration_test_wf", + "version": 1, + "tasks": [ + { + "name": "integration_task_1", + "taskReferenceName": "t1", + "inputParameters": { + "p1": "${workflow.input.param1}", + "p2": "${workflow.input.param2}", + "p3": "${CPEWF_TASK_ID}", + "someNullKey": null + }, + "type": "SIMPLE" + }, + { + "name": "integration_task_2", + "taskReferenceName": "t2", + "inputParameters": { + "tp1": "${workflow.input.param1}", + "tp2": "${t1.output.op}", + "tp3": "${CPEWF_TASK_ID}" + }, + "type": "SIMPLE" + } + ], + "inputParameters": [ + "param1", + "param2" + ], + "outputParameters": { + "o1": "${workflow.input.param1}", + "o2": "${t2.output.uuid}", + "o3": "${t1.output.op}" + }, + "failureWorkflow": "$workflow.input.failureWfName", + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "ownerEmail": "test@harness.com" +},{ + "name": "integration_test_wf_with_sub_wf", + "description": "integration_test_wf_with_sub_wf", + "version": 1, + "tasks": [ + { + "name": "integration_task_1", + "taskReferenceName": "t1", + "inputParameters": { + "p1": "${workflow.input.param1}", + "p2": "${workflow.input.param2}", + "someNullKey": null + }, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "sub_workflow_task", + "taskReferenceName": "t2", + "inputParameters": { + "param1": "${workflow.input.param1}", + "param2": "${workflow.input.param2}", + "subwf": "${workflow.input.nextSubwf}" + }, + "type": "SUB_WORKFLOW", + "subWorkflowParam": { + "name": "${workflow.input.subwf}", + "version": 1 + }, + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "retryCount": 0 + } + ], + "inputParameters": [ + "param1", + "param2" + ], + "failureWorkflow": "$workflow.input.failureWfName", + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "timeoutPolicy": "TIME_OUT_WF", + "timeoutSeconds": 5, + "ownerEmail": "test@harness.com" +} +] \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/metadata/workflows.json b/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/metadata/workflows.json new file mode 100644 index 000000000..43f655458 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/metadata/workflows.json @@ -0,0 +1,594 @@ +[{ + "createTime": 1670136330055, + "updateTime": 1670176591044, + "name": "sub_workflow_test", + "version": 1, + "tasks": [ + { + "name": "x_test_worker_0", + "taskReferenceName": "simple_task_0", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "jq", + "taskReferenceName": "jq", + "inputParameters": { + "key1": { + "value1": [ + "a", + "b" + ] + }, + "queryExpression": "{ key3: (.key1.value1 + .key2.value2) }", + "value2": [ + "d", + "e" + ] + }, + "type": "JSON_JQ_TRANSFORM", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "wait", + "taskReferenceName": "wait", + "inputParameters": { + "duration": "1 s" + }, + "type": "WAIT", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "set_state", + "taskReferenceName": "set_state", + "inputParameters": { + "call_made": true, + "number": "${simple_task_0.output.number}" + }, + "type": "SET_VARIABLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "sub_flow", + "taskReferenceName": "sub_flow", + "inputParameters": {}, + "type": "SUB_WORKFLOW", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "subWorkflowParam": { + "name": "PopulationMinMax2a27fdfb-295d-4c70-b813-7e3a44e2cb58" + }, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "sub_flow_v1", + "taskReferenceName": "sub_flow_v1", + "inputParameters": {}, + "type": "SUB_WORKFLOW", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "subWorkflowParam": { + "name": "PopulationMinMax2a27fdfb-295d-4c70-b813-7e3a44e2cb58", + "version": 1 + }, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "dynamic_fork", + "taskReferenceName": "dynamic_fork", + "inputParameters": { + "forkTaskName": "x_test_worker_0", + "forkTaskInputs": [ + 1, + 2, + 3 + ] + }, + "type": "FORK_JOIN_DYNAMIC", + "decisionCases": {}, + "dynamicForkTasksParam": "forkedTasks", + "dynamicForkTasksInputParamName": "forkedTasksInputs", + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "dynamic_fork_join", + "taskReferenceName": "dynamic_fork_join", + "inputParameters": {}, + "type": "JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "fork", + "taskReferenceName": "fork", + "inputParameters": {}, + "type": "FORK_JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [ + [ + { + "name": "loop_until_success", + "taskReferenceName": "loop_until_success", + "inputParameters": { + "loop_count": 2 + }, + "type": "DO_WHILE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": true, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopCondition": "if ( $.loop_count['iteration'] < $.loop_until_success ) { true; } else { false; }", + "loopOver": [ + { + "name": "fact_length", + "taskReferenceName": "fact_length", + "description": "Fail if the fact is too short", + "inputParameters": { + "number": "${get_data.output.number}" + }, + "type": "SWITCH", + "decisionCases": { + "LONG": [ + { + "name": "x_test_worker_1", + "taskReferenceName": "simple_task_1", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "SHORT": [ + { + "name": "too_short", + "taskReferenceName": "too_short", + "inputParameters": { + "terminationReason": "value too short", + "terminationStatus": "FAILED" + }, + "type": "TERMINATE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ] + }, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "evaluatorType": "javascript", + "expression": "$.number < 15 ? 'LONG':'LONG'" + } + ] + }, + { + "name": "sub_flow_inline", + "taskReferenceName": "sub_flow_inline", + "inputParameters": {}, + "type": "SUB_WORKFLOW", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "subWorkflowParam": { + "name": "inline_sub", + "version": 1, + "workflowDefinition": { + "name": "inline_sub", + "version": 1, + "tasks": [ + { + "name": "x_test_worker_2", + "taskReferenceName": "simple_task_0", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "fact_length2", + "taskReferenceName": "fact_length2", + "description": "Fail if the fact is too short", + "inputParameters": { + "number": "${get_data.output.number}" + }, + "type": "SWITCH", + "decisionCases": { + "LONG": [ + { + "name": "x_test_worker_1", + "taskReferenceName": "simple_task_1", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "SHORT": [ + { + "name": "too_short", + "taskReferenceName": "too_short", + "inputParameters": { + "terminationReason": "value too short", + "terminationStatus": "FAILED" + }, + "type": "TERMINATE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ] + }, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "evaluatorType": "javascript", + "expression": "$.number < 15 ? 'LONG':'LONG'" + }, + { + "name": "sub_flow_inline_lvl2", + "taskReferenceName": "sub_flow_inline_lvl2", + "inputParameters": {}, + "type": "SUB_WORKFLOW", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "subWorkflowParam": { + "name": "inline_sub", + "version": 1, + "workflowDefinition": { + "name": "inline_sub", + "version": 1, + "tasks": [ + { + "name": "x_test_worker_2", + "taskReferenceName": "simple_task_0", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [], + "outputParameters": {}, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "variables": {}, + "inputTemplate": {} + } + }, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "taskDefinition": { + "name": "sub_flow_inline", + "description": "sub_flow_inline", + "retryCount": 0, + "timeoutSeconds": 3000, + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 20, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "pollTimeoutSeconds": 3600, + "backoffScaleFactor": 1 + } + } + ], + "inputParameters": [], + "outputParameters": {}, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "variables": {}, + "inputTemplate": {} + } + }, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "taskDefinition": { + "name": "sub_flow_inline", + "description": "sub_flow_inline", + "retryCount": 0, + "timeoutSeconds": 3000, + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 60, + "responseTimeoutSeconds": 20, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "pollTimeoutSeconds": 3600, + "backoffScaleFactor": 1 + } + } + ], + [ + { + "name": "x_test_worker_2", + "taskReferenceName": "simple_task_2", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "x_test_worker_1", + "taskReferenceName": "simple_task_5", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ] + ], + "startDelay": 0, + "joinOn": ["sub_flow_inline","simple_task_5"], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "fork_join", + "taskReferenceName": "fork_join", + "inputParameters": {}, + "type": "JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": ["simple_task_5","sub_flow_inline"], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "sub_flow_v0", + "taskReferenceName": "sub_flow_v0", + "inputParameters": {}, + "type": "SUB_WORKFLOW", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "subWorkflowParam": { + "name": "PopulationMinMax2a27fdfb-295d-4c70-b813-7e3a44e2cb58", + "version": 0 + }, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [], + "outputParameters": {}, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "ownerEmail": "viren@orkes.io", + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "variables": {}, + "inputTemplate": {} +}, + { + "createTime": 1670136356629, + "updateTime": 1670136356636, + "name": "PopulationMinMax2a27fdfb-295d-4c70-b813-7e3a44e2cb58", + "description": "PopulationMinMax v3", + "version": 3, + "tasks": [ + { + "name": "x_test_worker_4", + "taskReferenceName": "x_test_worker_4", + "inputParameters": { + "name": "Orkes" + }, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [], + "outputParameters": { + "data": "${get_random_fact.output.response.body.fact}" + }, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "ownerEmail": "viren@orkes.io", + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "variables": {}, + "inputTemplate": {} + }, + { + "createTime": 1670136356629, + "updateTime": 1670136356636, + "name": "PopulationMinMax2a27fdfb-295d-4c70-b813-7e3a44e2cb58", + "description": "PopulationMinMax v1", + "version": 1, + "tasks": [ + { + "name": "x_test_worker_1", + "taskReferenceName": "x_test_worker_1", + "inputParameters": { + "name": "Orkes" + }, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + "inputParameters": [], + "outputParameters": { + "data": "${get_random_fact.output.response.body.fact}" + }, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "ownerEmail": "viren@orkes.io", + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "variables": {}, + "inputTemplate": {} + }] \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/sample_tasks.json b/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/sample_tasks.json new file mode 100644 index 000000000..46c54bb39 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/sample_tasks.json @@ -0,0 +1,59 @@ +[ + { + "createTime": 1651856131126, + "createdBy": "", + "name": "image_compression", + "description": "Edit or extend this sample task. Set the task name to get started", + "retryCount": 3, + "timeoutSeconds": 600, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "ALERT_ONLY", + "retryLogic": "FIXED", + "retryDelaySeconds": 6, + "responseTimeoutSeconds": 10, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "test@orkes.io", + "backoffScaleFactor": 1 + }, + { + "createTime": 1651853134126, + "createdBy": "", + "name": "download_file_from_ec2", + "description": "Edit or extend this sample task. Set the task name to get started", + "retryCount": 1, + "timeoutSeconds": 300, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 3, + "responseTimeoutSeconds": 50, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "test@orkes.io", + "backoffScaleFactor": 1 + }, + { + "createTime": 1652856134126, + "createdBy": "", + "name": "update_database", + "description": "Edit or extend this sample task. Set the task name to get started", + "retryCount": 3, + "timeoutSeconds": 900, + "inputKeys": [], + "outputKeys": [], + "timeoutPolicy": "TIME_OUT_WF", + "retryLogic": "FIXED", + "retryDelaySeconds": 10, + "responseTimeoutSeconds": 60, + "inputTemplate": {}, + "rateLimitPerFrequency": 0, + "rateLimitFrequencyInSeconds": 1, + "ownerEmail": "test@orkes.io", + "backoffScaleFactor": 1 + } +] \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/sample_workflow.json b/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/sample_workflow.json new file mode 100644 index 000000000..e6723a3ed --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/sample_workflow.json @@ -0,0 +1,117 @@ +{ + "name": "Do_While_Workflow", + "description": "Do_While_Workflow", + "version": 1, + "tasks": [ + { + "name": "loopTask", + "taskReferenceName": "loopTask", + "inputParameters": { + "value": "${workflow.input.loop}" + }, + "type": "DO_WHILE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopCondition": "if ($.loopTask['iteration'] < $.value) { true; } else { false;} ", + "loopOver": [ + { + "name": "integration_task_0", + "taskReferenceName": "integration_task_0", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "fork", + "taskReferenceName": "fork", + "inputParameters": {}, + "type": "FORK_JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [ + [ + { + "name": "integration_task_1", + "taskReferenceName": "integration_task_1", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ], + [ + { + "name": "integration_task_2", + "taskReferenceName": "integration_task_2", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ] + ], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + }, + { + "name": "join", + "taskReferenceName": "join", + "inputParameters": {}, + "type": "JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [ + "integration_task_1", + "integration_task_2" + ], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [] + } + ] + } + ], + "inputParameters": [], + "outputParameters": {}, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "timeoutPolicy": "ALERT_ONLY", + "timeoutSeconds": 0, + "ownerEmail": "test@harness.com" +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/sdk_test.json b/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/sdk_test.json new file mode 100644 index 000000000..416869376 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/resources/sdk_test.json @@ -0,0 +1,252 @@ +{ + "createTime": 1685128933480, + "updateTime": 1685128981013, + "name": "sdk_test_workflow", + "version": 1, + "tasks": [ + { + "name": "x_test_worker_0", + "taskReferenceName": "simple_task_0", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + }, + { + "name": "jq", + "taskReferenceName": "jq", + "inputParameters": { + "key1": { + "value1": [ + "a", + "b" + ] + }, + "queryExpression": "{ key3: (.key1.value1 + .key2.value2) }", + "value2": [ + "d", + "e" + ] + }, + "type": "JSON_JQ_TRANSFORM", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + }, + { + "name": "set_state", + "taskReferenceName": "set_state", + "inputParameters": { + "call_made": true, + "number": "${simple_task_0.output.number}" + }, + "type": "SET_VARIABLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + }, + { + "name": "fork_task_t7nhng", + "taskReferenceName": "fork_task_t7nhng_ref", + "inputParameters": {}, + "type": "FORK_JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [ + [ + { + "name": "there_is_no_worker", + "taskReferenceName": "no_worker_3", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + } + ], + [ + { + "name": "there_is_no_worker", + "taskReferenceName": "no_worker_2", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + } + ], + [ + { + "name": "there_is_no_worker", + "taskReferenceName": "no_worker_1", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + } + ] + ], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + }, + { + "name": "join_task_y6nux", + "taskReferenceName": "join_task_y6nux_ref", + "inputParameters": {}, + "type": "JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [ + "no_worker_1", + "no_worker_2", + "no_worker_3" + ], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + }, + { + "name": "fork", + "taskReferenceName": "fork", + "inputParameters": {}, + "type": "FORK_JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [ + [ + { + "name": "there_is_no_worker", + "taskReferenceName": "simple_task_5", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + } + ], + [ + { + "name": "there_is_no_worker", + "taskReferenceName": "simple_task_2", + "inputParameters": {}, + "type": "SIMPLE", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + } + ] + ], + "startDelay": 0, + "joinOn": [ + "sub_flow_inline", + "simple_task_5" + ], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + }, + { + "name": "fork_join", + "taskReferenceName": "fork_join", + "inputParameters": {}, + "type": "JOIN", + "decisionCases": {}, + "defaultCase": [], + "forkTasks": [], + "startDelay": 0, + "joinOn": [ + "simple_task_2", + "simple_task_5" + ], + "optional": false, + "defaultExclusiveJoinTask": [], + "asyncComplete": false, + "loopOver": [], + "onStateChange": {} + } + ], + "inputParameters": [], + "outputParameters": { + "task1": "${simple_task_0.output}", + "jq": "${jq.output}", + "inner_task": "${x_test_worker_1.output}" + }, + "schemaVersion": 2, + "restartable": true, + "workflowStatusListenerEnabled": false, + "ownerEmail": "viren@orkes.io", + "timeoutPolicy": "TIME_OUT_WF", + "timeoutSeconds": 120, + "variables": {}, + "inputTemplate": {}, + "onStateChange": {} +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/versions.gradle b/conductor-clients/java/conductor-java-sdk/versions.gradle new file mode 100644 index 000000000..b783eb711 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/versions.gradle @@ -0,0 +1,17 @@ +ext { + versions = [ + okHttp : '4.12.0', + guava : '32.1.2-jre', + jackson : '2.17.1', + archaius : '0.7.12', + slf4j : '1.7.36', + log4j : '2.23.1', + lombok : '1.18.30', + commonsLang : '3.12.0', + junit : '5.10.3', + awaitility : '4.2.0', + wiremock : '2.33.2', + mockito : '5.12.0', + testContainers: '1.19.8', + ] +} \ No newline at end of file From 73e3a4506cd3ce2046c41125c512857d93567b3a Mon Sep 17 00:00:00 2001 From: Miguel Prieto Date: Wed, 11 Sep 2024 12:51:57 -0400 Subject: [PATCH 2/7] Fix License Header - It should be Copyright $YEAR Conductor Authors. --- .../com/netflix/conductor/client/spring/ClientProperties.java | 2 +- .../client/spring/ConductorClientAutoConfiguration.java | 2 +- .../client/spring/ConductorWorkerAutoConfiguration.java | 2 +- .../conductor/client/spring/SpringWorkerConfiguration.java | 2 +- .../java/com/netflix/conductor/client/automator/TaskRunner.java | 2 +- .../conductor/client/automator/TaskRunnerConfigurer.java | 2 +- .../conductor/client/automator/events/PollCompleted.java | 2 +- .../netflix/conductor/client/automator/events/PollFailure.java | 2 +- .../netflix/conductor/client/automator/events/PollStarted.java | 2 +- .../client/automator/events/TaskExecutionCompleted.java | 2 +- .../conductor/client/automator/events/TaskExecutionFailure.java | 2 +- .../conductor/client/automator/events/TaskExecutionStarted.java | 2 +- .../conductor/client/automator/events/TaskRunnerEvent.java | 2 +- .../netflix/conductor/client/automator/filters/PollFilter.java | 2 +- .../conductor/client/config/ConductorClientConfiguration.java | 2 +- .../com/netflix/conductor/client/config/PropertyFactory.java | 2 +- .../conductor/client/exception/ConductorClientException.java | 2 +- .../java/com/netflix/conductor/client/http/ConductorClient.java | 2 +- .../netflix/conductor/client/http/ConductorClientRequest.java | 2 +- .../netflix/conductor/client/http/ConductorClientResponse.java | 2 +- .../com/netflix/conductor/client/http/ConnectionPoolConfig.java | 2 +- .../java/com/netflix/conductor/client/http/EventClient.java | 2 +- .../java/com/netflix/conductor/client/http/HeaderSupplier.java | 2 +- .../java/com/netflix/conductor/client/http/MetadataClient.java | 2 +- .../src/main/java/com/netflix/conductor/client/http/Param.java | 2 +- .../main/java/com/netflix/conductor/client/http/TaskClient.java | 2 +- .../java/com/netflix/conductor/client/http/WorkflowClient.java | 2 +- .../main/java/com/netflix/conductor/client/worker/Worker.java | 2 +- .../netflix/conductor/common/config/ObjectMapperProvider.java | 2 +- .../java/com/netflix/conductor/common/metadata/Auditable.java | 2 +- .../java/com/netflix/conductor/common/metadata/SchemaDef.java | 2 +- .../conductor/common/metadata/events/EventExecution.java | 2 +- .../netflix/conductor/common/metadata/events/EventHandler.java | 2 +- .../com/netflix/conductor/common/metadata/tasks/PollData.java | 2 +- .../java/com/netflix/conductor/common/metadata/tasks/Task.java | 2 +- .../com/netflix/conductor/common/metadata/tasks/TaskDef.java | 2 +- .../netflix/conductor/common/metadata/tasks/TaskExecLog.java | 2 +- .../com/netflix/conductor/common/metadata/tasks/TaskResult.java | 2 +- .../com/netflix/conductor/common/metadata/tasks/TaskType.java | 2 +- .../conductor/common/metadata/workflow/DynamicForkJoinTask.java | 2 +- .../common/metadata/workflow/DynamicForkJoinTaskList.java | 2 +- .../conductor/common/metadata/workflow/IdempotencyStrategy.java | 2 +- .../conductor/common/metadata/workflow/RateLimitConfig.java | 2 +- .../common/metadata/workflow/RerunWorkflowRequest.java | 2 +- .../conductor/common/metadata/workflow/SkipTaskRequest.java | 2 +- .../common/metadata/workflow/StartWorkflowRequest.java | 2 +- .../conductor/common/metadata/workflow/StateChangeEvent.java | 2 +- .../conductor/common/metadata/workflow/SubWorkflowParams.java | 2 +- .../common/metadata/workflow/UpgradeWorkflowRequest.java | 2 +- .../netflix/conductor/common/metadata/workflow/WorkflowDef.java | 2 +- .../conductor/common/metadata/workflow/WorkflowDefSummary.java | 2 +- .../conductor/common/metadata/workflow/WorkflowTask.java | 2 +- .../java/com/netflix/conductor/common/model/BulkResponse.java | 2 +- .../netflix/conductor/common/run/ExternalStorageLocation.java | 2 +- .../java/com/netflix/conductor/common/run/SearchResult.java | 2 +- .../main/java/com/netflix/conductor/common/run/TaskSummary.java | 2 +- .../main/java/com/netflix/conductor/common/run/Workflow.java | 2 +- .../java/com/netflix/conductor/common/run/WorkflowSummary.java | 2 +- .../com/netflix/conductor/common/run/WorkflowTestRequest.java | 2 +- .../com/netflix/conductor/common/utils/ConstraintParamUtil.java | 2 +- .../main/java/com/netflix/conductor/common/utils/EnvUtils.java | 2 +- .../netflix/conductor/common/utils/ExternalPayloadStorage.java | 2 +- .../java/com/netflix/conductor/common/utils/SummaryUtil.java | 2 +- .../main/java/com/netflix/conductor/common/utils/TaskUtils.java | 2 +- .../com/netflix/conductor/common/validation/ErrorResponse.java | 2 +- .../netflix/conductor/common/validation/ValidationError.java | 2 +- .../conductor/client/automator/PollingSemaphoreTest.java | 2 +- .../conductor/client/automator/TaskPollExecutorTest.java | 2 +- .../conductor/client/automator/TaskRunnerConfigurerTest.java | 2 +- .../netflix/conductor/client/config/TestPropertyFactory.java | 2 +- .../src/test/java/com/netflix/conductor/client/sample/Main.java | 2 +- .../java/com/netflix/conductor/client/sample/SampleWorker.java | 2 +- .../netflix/conductor/client/testing/AbstractWorkflowTests.java | 2 +- .../com/netflix/conductor/client/testing/LoanWorkflowInput.java | 2 +- .../com/netflix/conductor/client/testing/LoanWorkflowTest.java | 2 +- .../com/netflix/conductor/client/testing/RegressionTest.java | 2 +- .../com/netflix/conductor/client/testing/SubWorkflowTest.java | 2 +- .../com/netflix/conductor/client/worker/TestWorkflowTask.java | 2 +- .../com/netflix/conductor/sdk/examples/TaskRegistration.java | 2 +- .../java/com/netflix/conductor/sdk/examples/TaskRunner.java | 2 +- .../conductor/sdk/examples/events/EventListenerExample.java | 2 +- .../com/netflix/conductor/sdk/examples/helloworld/Main.java | 2 +- .../conductor/sdk/examples/helloworld/workers/Workers.java | 2 +- .../sdk/examples/helloworld/workflowdef/GreetingsWorkflow.java | 2 +- .../java/com/netflix/conductor/sdk/examples/sendemail/Main.java | 2 +- .../conductor/sdk/examples/sendemail/workers/Workers.java | 2 +- .../sdk/examples/sendemail/workflowdef/SendEmailWorkflow.java | 2 +- .../java/com/netflix/conductor/sdk/examples/shipment/Main.java | 2 +- .../java/com/netflix/conductor/sdk/examples/shipment/Order.java | 2 +- .../com/netflix/conductor/sdk/examples/shipment/Shipment.java | 2 +- .../netflix/conductor/sdk/examples/shipment/ShipmentState.java | 2 +- .../conductor/sdk/examples/shipment/ShipmentWorkers.java | 2 +- .../conductor/sdk/examples/shipment/ShipmentWorkflow.java | 2 +- .../java/com/netflix/conductor/sdk/examples/shipment/User.java | 2 +- .../com/netflix/conductor/sdk/examples/taskdomains/Main.java | 2 +- .../com/netflix/conductor/sdk/examples/taskdomains/Workers.java | 2 +- .../orkes/conductor/sdk/examples/AuthorizationManagement.java | 2 +- .../io/orkes/conductor/sdk/examples/MetadataManagement.java | 2 +- .../io/orkes/conductor/sdk/examples/SchedulerManagement.java | 2 +- .../io/orkes/conductor/sdk/examples/WorkflowManagement.java | 2 +- .../io/orkes/conductor/sdk/examples/WorkflowManagement2.java | 2 +- .../java/io/orkes/conductor/sdk/examples/util/ClientUtil.java | 2 +- .../java/io/orkes/conductor/sdk/examples/workflowops/Main.java | 2 +- .../sdk/examples/workflowops/workflowdef/GreetingsWorkflow.java | 2 +- conductor-clients/java/conductor-java-sdk/licenseheader.txt | 2 +- .../src/main/java/io/orkes/conductor/client/ApiClient.java | 2 +- .../java/io/orkes/conductor/client/AuthorizationClient.java | 2 +- .../main/java/io/orkes/conductor/client/IntegrationClient.java | 2 +- .../src/main/java/io/orkes/conductor/client/OrkesClients.java | 2 +- .../src/main/java/io/orkes/conductor/client/PromptClient.java | 2 +- .../main/java/io/orkes/conductor/client/SchedulerClient.java | 2 +- .../src/main/java/io/orkes/conductor/client/SecretClient.java | 2 +- .../main/java/io/orkes/conductor/client/http/ApiCallback.java | 2 +- .../main/java/io/orkes/conductor/client/http/ApiException.java | 2 +- .../main/java/io/orkes/conductor/client/http/ApiResponse.java | 2 +- .../io/orkes/conductor/client/http/ApplicationResource.java | 2 +- .../io/orkes/conductor/client/http/AuthorizationResource.java | 2 +- .../main/java/io/orkes/conductor/client/http/EventResource.java | 2 +- .../main/java/io/orkes/conductor/client/http/GroupResource.java | 2 +- .../io/orkes/conductor/client/http/IntegrationResource.java | 2 +- .../java/io/orkes/conductor/client/http/MetadataResource.java | 2 +- .../io/orkes/conductor/client/http/OrkesAuthentication.java | 2 +- .../orkes/conductor/client/http/OrkesAuthorizationClient.java | 2 +- .../java/io/orkes/conductor/client/http/OrkesEventClient.java | 2 +- .../io/orkes/conductor/client/http/OrkesIntegrationClient.java | 2 +- .../io/orkes/conductor/client/http/OrkesMetadataClient.java | 2 +- .../java/io/orkes/conductor/client/http/OrkesPromptClient.java | 2 +- .../io/orkes/conductor/client/http/OrkesSchedulerClient.java | 2 +- .../java/io/orkes/conductor/client/http/OrkesSecretClient.java | 2 +- .../java/io/orkes/conductor/client/http/OrkesTaskClient.java | 2 +- .../io/orkes/conductor/client/http/OrkesWorkflowClient.java | 2 +- .../src/main/java/io/orkes/conductor/client/http/Pair.java | 2 +- .../java/io/orkes/conductor/client/http/SchedulerResource.java | 2 +- .../java/io/orkes/conductor/client/http/SecretResource.java | 2 +- .../main/java/io/orkes/conductor/client/http/TagsResource.java | 2 +- .../main/java/io/orkes/conductor/client/http/TokenResource.java | 2 +- .../main/java/io/orkes/conductor/client/http/UserResource.java | 2 +- .../io/orkes/conductor/client/http/WorkflowBulkResource.java | 2 +- .../java/io/orkes/conductor/client/http/WorkflowResource.java | 2 +- .../java/io/orkes/conductor/client/model/AccessKeyResponse.java | 2 +- .../java/io/orkes/conductor/client/model/AccessKeyStatus.java | 2 +- .../io/orkes/conductor/client/model/AuthorizationRequest.java | 2 +- .../io/orkes/conductor/client/model/ConductorApplication.java | 2 +- .../java/io/orkes/conductor/client/model/ConductorUser.java | 2 +- .../conductor/client/model/CorrelationIdsSearchRequest.java | 2 +- .../orkes/conductor/client/model/CreateAccessKeyResponse.java | 2 +- .../client/model/CreateOrUpdateApplicationRequest.java | 2 +- .../orkes/conductor/client/model/ExternalStorageLocation.java | 2 +- .../io/orkes/conductor/client/model/GenerateTokenRequest.java | 2 +- .../java/io/orkes/conductor/client/model/GrantedAccess.java | 2 +- .../io/orkes/conductor/client/model/GrantedAccessResponse.java | 2 +- .../src/main/java/io/orkes/conductor/client/model/Group.java | 2 +- .../main/java/io/orkes/conductor/client/model/Permission.java | 2 +- .../src/main/java/io/orkes/conductor/client/model/Role.java | 2 +- .../io/orkes/conductor/client/model/SaveScheduleRequest.java | 2 +- .../client/model/SearchResultWorkflowScheduleExecution.java | 2 +- .../model/SearchResultWorkflowScheduleExecutionModel.java | 2 +- .../conductor/client/model/SearchResultWorkflowSummary.java | 2 +- .../src/main/java/io/orkes/conductor/client/model/Subject.java | 2 +- .../main/java/io/orkes/conductor/client/model/SubjectRef.java | 2 +- .../main/java/io/orkes/conductor/client/model/TagObject.java | 2 +- .../main/java/io/orkes/conductor/client/model/TagString.java | 2 +- .../main/java/io/orkes/conductor/client/model/TargetRef.java | 2 +- .../main/java/io/orkes/conductor/client/model/TaskDetails.java | 2 +- .../java/io/orkes/conductor/client/model/TerminateWorkflow.java | 2 +- .../java/io/orkes/conductor/client/model/TokenResponse.java | 2 +- .../io/orkes/conductor/client/model/UpsertGroupRequest.java | 2 +- .../java/io/orkes/conductor/client/model/UpsertUserRequest.java | 2 +- .../main/java/io/orkes/conductor/client/model/WorkflowRun.java | 2 +- .../java/io/orkes/conductor/client/model/WorkflowSchedule.java | 2 +- .../conductor/client/model/WorkflowScheduleExecutionModel.java | 2 +- .../io/orkes/conductor/client/model/WorkflowStateUpdate.java | 2 +- .../java/io/orkes/conductor/client/model/WorkflowStatus.java | 2 +- .../orkes/conductor/client/model/event/QueueConfiguration.java | 2 +- .../conductor/client/model/event/QueueWorkerConfiguration.java | 2 +- .../io/orkes/conductor/client/model/integration/Category.java | 2 +- .../orkes/conductor/client/model/integration/Integration.java | 2 +- .../conductor/client/model/integration/IntegrationApi.java | 2 +- .../client/model/integration/IntegrationApiUpdate.java | 2 +- .../conductor/client/model/integration/IntegrationDef.java | 2 +- .../conductor/client/model/integration/IntegrationUpdate.java | 2 +- .../client/model/integration/PromptTemplateTestRequest.java | 2 +- .../conductor/client/model/integration/ai/ChatCompletion.java | 2 +- .../conductor/client/model/integration/ai/ChatMessage.java | 2 +- .../conductor/client/model/integration/ai/EmbeddingRequest.java | 2 +- .../conductor/client/model/integration/ai/IndexDocInput.java | 2 +- .../orkes/conductor/client/model/integration/ai/IndexedDoc.java | 2 +- .../conductor/client/model/integration/ai/LLMResponse.java | 2 +- .../conductor/client/model/integration/ai/LLMWorkerInput.java | 2 +- .../conductor/client/model/integration/ai/PromptTemplate.java | 2 +- .../client/model/integration/ai/PromptTemplateTestRequest.java | 2 +- .../client/model/integration/ai/StoreEmbeddingsInput.java | 2 +- .../conductor/client/model/integration/ai/TextCompletion.java | 2 +- .../conductor/client/model/integration/ai/VectorDBInput.java | 2 +- .../main/java/io/orkes/conductor/client/worker/WorkerFn.java | 2 +- .../src/main/java/io/orkes/conductor/client/worker/Workers.java | 2 +- .../client/spring/OrkesConductorClientAutoConfiguration.java | 2 +- .../netflix/conductor/sdk/healthcheck/HealthCheckClient.java | 2 +- .../com/netflix/conductor/sdk/testing/LocalServerRunner.java | 2 +- .../com/netflix/conductor/sdk/testing/WorkflowTestRunner.java | 2 +- .../netflix/conductor/sdk/workflow/def/ConductorWorkflow.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/ValidationError.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java | 2 +- .../netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java | 2 +- .../conductor/sdk/workflow/def/tasks/DynamicForkInput.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/tasks/Event.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java | 2 +- .../java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java | 2 +- .../java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java | 2 +- .../netflix/conductor/sdk/workflow/def/tasks/Javascript.java | 2 +- .../java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java | 2 +- .../netflix/conductor/sdk/workflow/def/tasks/SetVariable.java | 2 +- .../netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java | 2 +- .../netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/tasks/Switch.java | 2 +- .../java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java | 2 +- .../netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java | 2 +- .../java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java | 2 +- .../conductor/sdk/workflow/executor/WorkflowExecutor.java | 2 +- .../conductor/sdk/workflow/executor/task/AnnotatedWorker.java | 2 +- .../sdk/workflow/executor/task/AnnotatedWorkerExecutor.java | 2 +- .../conductor/sdk/workflow/executor/task/DynamicForkWorker.java | 2 +- .../sdk/workflow/executor/task/NonRetryableException.java | 2 +- .../conductor/sdk/workflow/executor/task/TaskContext.java | 2 +- .../sdk/workflow/executor/task/WorkerConfiguration.java | 2 +- .../com/netflix/conductor/sdk/workflow/task/InputParam.java | 2 +- .../com/netflix/conductor/sdk/workflow/task/OutputParam.java | 2 +- .../com/netflix/conductor/sdk/workflow/task/WorkerTask.java | 2 +- .../netflix/conductor/sdk/workflow/utils/InputOutputGetter.java | 2 +- .../com/netflix/conductor/sdk/workflow/utils/MapBuilder.java | 2 +- .../conductor/sdk/workflow/def/TaskConversionsTests.java | 2 +- .../conductor/sdk/workflow/def/WorkflowCreationTests.java | 2 +- .../conductor/sdk/workflow/def/WorkflowDefTaskTests.java | 2 +- .../com/netflix/conductor/sdk/workflow/def/WorkflowState.java | 2 +- .../sdk/workflow/executor/task/AnnotatedWorkerTests.java | 2 +- .../conductor/sdk/workflow/executor/task/TestWorkerConfig.java | 2 +- .../com/netflix/conductor/sdk/workflow/testing/Task1Input.java | 2 +- .../conductor/sdk/workflow/testing/TestWorkflowInput.java | 2 +- .../sdk/workflow/testing/WorkflowTestFrameworkTests.java | 2 +- .../src/test/java/io/orkes/conductor/client/LoadTestWorker.java | 2 +- .../test/java/io/orkes/conductor/client/LocalWorkerTest.java | 2 +- .../test/java/io/orkes/conductor/client/WorkflowRetryTest.java | 2 +- .../orkes/conductor/client/http/AuthorizationClientTests.java | 2 +- .../java/io/orkes/conductor/client/http/EventClientTests.java | 2 +- .../io/orkes/conductor/client/http/MetadataClientTests.java | 2 +- .../java/io/orkes/conductor/client/http/PathSuffixTest.java | 2 +- .../io/orkes/conductor/client/http/SchedulerClientTests.java | 2 +- .../java/io/orkes/conductor/client/http/SecretClientTests.java | 2 +- .../java/io/orkes/conductor/client/http/TaskClientTests.java | 2 +- .../io/orkes/conductor/client/http/WorkflowClientTests.java | 2 +- .../orkes/conductor/client/http/WorkflowStateUpdateTests.java | 2 +- .../java/io/orkes/conductor/client/util/ClientTestUtil.java | 2 +- .../src/test/java/io/orkes/conductor/client/util/Commons.java | 2 +- .../test/java/io/orkes/conductor/client/util/SimpleWorker.java | 2 +- .../src/test/java/io/orkes/conductor/client/util/TestUtil.java | 2 +- .../test/java/io/orkes/conductor/client/util/WorkflowUtil.java | 2 +- .../client/worker/LocalServerWorkflowExecutionTests.java | 2 +- .../orkes/conductor/client/worker/WorkflowExecutionTests.java | 2 +- .../src/test/java/io/orkes/conductor/sdk/WorkflowSDKTests.java | 2 +- 262 files changed, 262 insertions(+), 262 deletions(-) diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java index c90272bbc..f459c1cd7 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ClientProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java index f157d5377..194ad6cf6 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java index db0432ebf..cdc212c5e 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorWorkerAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java index 0dec407d3..8dce540e0 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/SpringWorkerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java index 4facd4d71..a388faeef 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java index ea0949d78..8fbf3ba09 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollCompleted.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollCompleted.java index 3ef4f8cdf..15274c1b9 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollCompleted.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollCompleted.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollFailure.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollFailure.java index 8981da4e6..3a5d375dd 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollFailure.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollFailure.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollStarted.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollStarted.java index 61ea80e6e..62068bfb0 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollStarted.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/PollStarted.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionCompleted.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionCompleted.java index 5880938de..2ca962d1c 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionCompleted.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionCompleted.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionFailure.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionFailure.java index c4991857a..3ec43c7b2 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionFailure.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionFailure.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionStarted.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionStarted.java index 8dae66ebe..6ab388653 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionStarted.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskExecutionStarted.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskRunnerEvent.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskRunnerEvent.java index fd4123abe..c4838cfc4 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskRunnerEvent.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskRunnerEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/filters/PollFilter.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/filters/PollFilter.java index 8de09542e..d01275e04 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/filters/PollFilter.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/filters/PollFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java index 0aaad6fb8..bb5a28926 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/ConductorClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Orkes, Inc. + * Copyright 2018 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java index 690671816..5d194794b 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/config/PropertyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java index 67e392b2b..8be9bbf69 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/exception/ConductorClientException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClient.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClient.java index e977236d6..6a0af40f2 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClient.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientRequest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientRequest.java index da98d5090..a1e0ffa12 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientRequest.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientResponse.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientResponse.java index 72eb92911..537909eb4 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientResponse.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClientResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConnectionPoolConfig.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConnectionPoolConfig.java index 896642a91..5909a4197 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConnectionPoolConfig.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/ConnectionPoolConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/EventClient.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/EventClient.java index 85b539fdb..ea536dc34 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/EventClient.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/EventClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/HeaderSupplier.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/HeaderSupplier.java index b6f862800..e6b4cb377 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/HeaderSupplier.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/HeaderSupplier.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java index 9d0f7314f..c9e88c978 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/MetadataClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/Param.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/Param.java index d96cb6d60..6bc8ace51 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/Param.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/Param.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/TaskClient.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/TaskClient.java index d192bf3ee..279028a12 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/TaskClient.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/TaskClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java index ad53e19a4..8330440d7 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/http/WorkflowClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/worker/Worker.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/worker/Worker.java index 5ea4ed5e2..0ea08be7e 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/worker/Worker.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/worker/Worker.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java index 2a1101c77..04b36ecf2 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/config/ObjectMapperProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/Auditable.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/Auditable.java index 59fecb6bc..bef2e1792 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/Auditable.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/Auditable.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/SchemaDef.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/SchemaDef.java index 2e4e6d0c7..e61cd40d8 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/SchemaDef.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/SchemaDef.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java index 53cf3ad53..211c98913 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventExecution.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java index a016b0c84..4887993bc 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/events/EventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java index 350ff5f9a..5801922aa 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/PollData.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java index ef421189d..d36239227 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java index 947231e87..d4bfc0fc1 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java index 9c5153b0c..5c0fa47ee 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskExecLog.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java index b5489c708..11b0df281 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java index 4fd0fa004..a9322e89b 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskType.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java index 8a441a75a..eb488eb49 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java index b1fa27ac0..786161ff6 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/DynamicForkJoinTaskList.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/IdempotencyStrategy.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/IdempotencyStrategy.java index 2449dd320..f0d1ef5fa 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/IdempotencyStrategy.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/IdempotencyStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RateLimitConfig.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RateLimitConfig.java index a27d91752..b6d087a9b 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RateLimitConfig.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RateLimitConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java index cf762983b..65c47d1e9 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/RerunWorkflowRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java index abfc841ec..807bea8f1 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SkipTaskRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java index 5ca2ce001..28eab2172 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StartWorkflowRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StateChangeEvent.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StateChangeEvent.java index 16f1a4fb8..471caa322 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StateChangeEvent.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/StateChangeEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java index 73c800afa..89a459bbf 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/SubWorkflowParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/UpgradeWorkflowRequest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/UpgradeWorkflowRequest.java index 4ab9ba928..906bf21a6 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/UpgradeWorkflowRequest.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/UpgradeWorkflowRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java index b4afd450b..27fc20470 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDef.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java index 8f4720095..ef46077eb 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowDefSummary.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java index 338e6e302..5b8eecc4d 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/metadata/workflow/WorkflowTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/model/BulkResponse.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/model/BulkResponse.java index bdba8c471..99b3f48f9 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/model/BulkResponse.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/model/BulkResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java index 50a29b760..4a7c5e818 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/ExternalStorageLocation.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/SearchResult.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/SearchResult.java index 921df2559..f0feaf5f4 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/SearchResult.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/SearchResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/TaskSummary.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/TaskSummary.java index 51bb783d0..f7dc3fff5 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/TaskSummary.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/TaskSummary.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/Workflow.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/Workflow.java index fca0c8e39..652dd34f7 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/Workflow.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/Workflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java index 8b35d467c..ab246baab 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowSummary.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java index a82e5ee66..896e71626 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/run/WorkflowTestRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java index c4db75a63..706681d8a 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ConstraintParamUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/EnvUtils.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/EnvUtils.java index dbbc4190a..c8d04b6ef 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/EnvUtils.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/EnvUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java index ae57ec0b6..c093a986b 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/ExternalPayloadStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java index fc986c662..8070351a0 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/SummaryUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java index 1d2864f2e..7a790a152 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/utils/TaskUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java index fa27105b6..e7fd44db7 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ErrorResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ValidationError.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ValidationError.java index 24569854a..9dbe055ba 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ValidationError.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/common/validation/ValidationError.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java index 54bf161fc..9750f8f77 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java index ac9350016..6a41d476e 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java index d6d269a71..cbd9df8a9 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskRunnerConfigurerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java index 732e3dc54..398738c59 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/config/TestPropertyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/Main.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/Main.java index 00e64a3ea..7a474b146 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/Main.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/Main.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java index f05bd6629..6073c3f90 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/sample/SampleWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java index de6cdc592..b99f519ff 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java index 5aa857ad4..cd73b1af4 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java index 1d8b66de3..84f876a1a 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java index 207c26e10..e67f83658 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/RegressionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java index 937b750ab..6e8320810 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/testing/SubWorkflowTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java index 72ffb7f36..32f7bcb76 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/worker/TestWorkflowTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRegistration.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRegistration.java index b7ccb6cb7..d1539881e 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRegistration.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRegistration.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRunner.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRunner.java index 7ae856624..fba9e8d58 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRunner.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/TaskRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/events/EventListenerExample.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/events/EventListenerExample.java index 916d1260b..8d437ebb0 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/events/EventListenerExample.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/events/EventListenerExample.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/Main.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/Main.java index 1342c6e88..5ca54cf45 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/Main.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/Main.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workers/Workers.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workers/Workers.java index 4c16f0609..6b37fb1a7 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workers/Workers.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workers/Workers.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workflowdef/GreetingsWorkflow.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workflowdef/GreetingsWorkflow.java index 0052baa50..417923f21 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workflowdef/GreetingsWorkflow.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/helloworld/workflowdef/GreetingsWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/Main.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/Main.java index 45c0c6b09..4c43bd6bc 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/Main.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/Main.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workers/Workers.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workers/Workers.java index 144049e41..927e56fa9 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workers/Workers.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workers/Workers.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workflowdef/SendEmailWorkflow.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workflowdef/SendEmailWorkflow.java index 602f18c78..2ce711e0f 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workflowdef/SendEmailWorkflow.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/sendemail/workflowdef/SendEmailWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Main.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Main.java index 2675ac0bd..b7436071c 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Main.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Main.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Order.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Order.java index 89d53d0b0..be7cd01a1 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Order.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Order.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Shipment.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Shipment.java index f297613ce..e69ba863a 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Shipment.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/Shipment.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentState.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentState.java index c5fd59a16..4c73cfd5f 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentState.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentState.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkers.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkers.java index b39639251..eca566e86 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkers.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkers.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkflow.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkflow.java index 4a0321c0b..1a48c9291 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkflow.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/ShipmentWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/User.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/User.java index 24c3f93e8..2d75d9437 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/User.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/shipment/User.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Main.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Main.java index 94e34ff9d..cff2d53dd 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Main.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Main.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Workers.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Workers.java index fe17af0c7..876c74816 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Workers.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/com/netflix/conductor/sdk/examples/taskdomains/Workers.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/AuthorizationManagement.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/AuthorizationManagement.java index a23aff7a1..8a94d496b 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/AuthorizationManagement.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/AuthorizationManagement.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/MetadataManagement.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/MetadataManagement.java index b74960f45..d4ef13f6b 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/MetadataManagement.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/MetadataManagement.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/SchedulerManagement.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/SchedulerManagement.java index c108c03c6..1d61c53c4 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/SchedulerManagement.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/SchedulerManagement.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement.java index 18c6c5f58..3ea6fb956 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement2.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement2.java index 20988816c..3de4c17c6 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement2.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/WorkflowManagement2.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/util/ClientUtil.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/util/ClientUtil.java index 35315adf4..23285f431 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/util/ClientUtil.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/util/ClientUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/Main.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/Main.java index f05e3899b..e811da045 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/Main.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/Main.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/workflowdef/GreetingsWorkflow.java b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/workflowdef/GreetingsWorkflow.java index e66ce916f..22b766fba 100644 --- a/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/workflowdef/GreetingsWorkflow.java +++ b/conductor-clients/java/conductor-java-sdk/examples/src/main/java/io/orkes/conductor/sdk/examples/workflowops/workflowdef/GreetingsWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/licenseheader.txt b/conductor-clients/java/conductor-java-sdk/licenseheader.txt index 8145074ab..03879c60c 100644 --- a/conductor-clients/java/conductor-java-sdk/licenseheader.txt +++ b/conductor-clients/java/conductor-java-sdk/licenseheader.txt @@ -1,5 +1,5 @@ /* - * Copyright $YEAR Orkes, Inc. + * Copyright $YEAR Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/ApiClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/ApiClient.java index 76b7127c0..af79ad146 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/ApiClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/ApiClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/AuthorizationClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/AuthorizationClient.java index 2c6f94b02..3fd132152 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/AuthorizationClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/AuthorizationClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/IntegrationClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/IntegrationClient.java index bc8eb2ec8..638f30e38 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/IntegrationClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/IntegrationClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/OrkesClients.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/OrkesClients.java index 2d38038e5..4efaf7517 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/OrkesClients.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/OrkesClients.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/PromptClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/PromptClient.java index cbee63f7c..6b14a675f 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/PromptClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/PromptClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SchedulerClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SchedulerClient.java index c206fc9cd..1542bcd61 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SchedulerClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SchedulerClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SecretClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SecretClient.java index 69314d106..597a3fc81 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SecretClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/SecretClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiCallback.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiCallback.java index 7c9608e97..1c02618e9 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiCallback.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiException.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiException.java index c1e5cef1e..8b9023982 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiException.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiException.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiResponse.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiResponse.java index 0a746c942..058887dd6 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiResponse.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApiResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApplicationResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApplicationResource.java index 639c1f4f3..3f04e3039 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApplicationResource.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/ApplicationResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/AuthorizationResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/AuthorizationResource.java index c9a4b3dbc..421cefe5f 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/AuthorizationResource.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/AuthorizationResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/EventResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/EventResource.java index f71000731..ade570af5 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/EventResource.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/EventResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/GroupResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/GroupResource.java index 534eeb023..3be465d9b 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/GroupResource.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/GroupResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/IntegrationResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/IntegrationResource.java index 89d7e9d76..8e338ce38 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/IntegrationResource.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/IntegrationResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/MetadataResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/MetadataResource.java index 9d02e176d..c5d54eabb 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/MetadataResource.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/MetadataResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthentication.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthentication.java index 63fa54f8a..ec47e9683 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthentication.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthentication.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthorizationClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthorizationClient.java index 4782560f3..556e74bd5 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthorizationClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthorizationClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesEventClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesEventClient.java index 2092e6b5f..5ae74700b 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesEventClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesEventClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesIntegrationClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesIntegrationClient.java index c059da7cc..1137b067b 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesIntegrationClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesIntegrationClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesMetadataClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesMetadataClient.java index e7203c1dc..d948b8e25 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesMetadataClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesMetadataClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesPromptClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesPromptClient.java index 76d5aebd0..254f995af 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesPromptClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesPromptClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSchedulerClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSchedulerClient.java index 1b0599a01..1c408f1f7 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSchedulerClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSchedulerClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSecretClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSecretClient.java index 400a6cb43..0b66c75dc 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSecretClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesSecretClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesTaskClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesTaskClient.java index 62bbeb26a..73d9ebe68 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesTaskClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesTaskClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesWorkflowClient.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesWorkflowClient.java index 4c2e844fa..c12498dca 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesWorkflowClient.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/OrkesWorkflowClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/Pair.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/Pair.java index 031e68c64..adf174daa 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/Pair.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/Pair.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SchedulerResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SchedulerResource.java index 30ad749ae..2506c0df1 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SchedulerResource.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SchedulerResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SecretResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SecretResource.java index 0a78fd849..da099ab83 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SecretResource.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/SecretResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TagsResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TagsResource.java index 4daff987b..ad922383d 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TagsResource.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TagsResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TokenResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TokenResource.java index 17c8a5e40..62fe1f3c9 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TokenResource.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/TokenResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/UserResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/UserResource.java index 671aba75d..bf73ed87b 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/UserResource.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/UserResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowBulkResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowBulkResource.java index 856382a31..31db8c05a 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowBulkResource.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowBulkResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowResource.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowResource.java index f6869c926..323179f95 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowResource.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/http/WorkflowResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyResponse.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyResponse.java index 775ac9d5a..c5e8b6c67 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyResponse.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyStatus.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyStatus.java index 55578c5af..47eadd6ea 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyStatus.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AccessKeyStatus.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AuthorizationRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AuthorizationRequest.java index 3db2b1579..11847a642 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AuthorizationRequest.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/AuthorizationRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorApplication.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorApplication.java index 064093213..2ef97312a 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorApplication.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorUser.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorUser.java index b3a67e8e1..ed1859b70 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorUser.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ConductorUser.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CorrelationIdsSearchRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CorrelationIdsSearchRequest.java index 3da10251f..fb8b57e7d 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CorrelationIdsSearchRequest.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CorrelationIdsSearchRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateAccessKeyResponse.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateAccessKeyResponse.java index ff610b8bb..db2b74172 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateAccessKeyResponse.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateAccessKeyResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateOrUpdateApplicationRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateOrUpdateApplicationRequest.java index de3fe84d3..1fcaa2f87 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateOrUpdateApplicationRequest.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/CreateOrUpdateApplicationRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ExternalStorageLocation.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ExternalStorageLocation.java index e410835e6..d0d36cf36 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ExternalStorageLocation.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/ExternalStorageLocation.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GenerateTokenRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GenerateTokenRequest.java index ebfb21ea3..9e5f7315e 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GenerateTokenRequest.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GenerateTokenRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccess.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccess.java index 0d4b936db..71aa22367 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccess.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccess.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccessResponse.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccessResponse.java index 26003584c..f1ad6268e 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccessResponse.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/GrantedAccessResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Group.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Group.java index 0ccc406e9..223e959fd 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Group.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Group.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Permission.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Permission.java index 87a8a2b91..368f4c33b 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Permission.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Permission.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Role.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Role.java index 450bf8861..cede3a2fb 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Role.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Role.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SaveScheduleRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SaveScheduleRequest.java index bf6f669f4..da8640c90 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SaveScheduleRequest.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SaveScheduleRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecution.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecution.java index ca2fd1b76..60331621a 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecution.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecution.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecutionModel.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecutionModel.java index 795dbd7a2..345d5d0e6 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecutionModel.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowScheduleExecutionModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowSummary.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowSummary.java index 94c366e17..62dd948f4 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowSummary.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SearchResultWorkflowSummary.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Subject.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Subject.java index 0b05dd85c..4db744c70 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Subject.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/Subject.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SubjectRef.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SubjectRef.java index 3f3fe4208..74dc3423b 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SubjectRef.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/SubjectRef.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagObject.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagObject.java index 7971b86e8..fac9eb9b0 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagObject.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagObject.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagString.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagString.java index d82da42a3..35b7d0c56 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagString.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TagString.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TargetRef.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TargetRef.java index 4a08983ad..fc42bb5dc 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TargetRef.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TargetRef.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TaskDetails.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TaskDetails.java index fab49397d..0c5dff2a7 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TaskDetails.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TaskDetails.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TerminateWorkflow.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TerminateWorkflow.java index 2c0464427..2e12ac9e1 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TerminateWorkflow.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TerminateWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TokenResponse.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TokenResponse.java index cdaafefc2..d0bfd89d7 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TokenResponse.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/TokenResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertGroupRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertGroupRequest.java index f4aa291ec..2e2164063 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertGroupRequest.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertGroupRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertUserRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertUserRequest.java index 53c8e6c1a..6ab1f735b 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertUserRequest.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/UpsertUserRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowRun.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowRun.java index 5bf424799..299484d95 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowRun.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowRun.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowSchedule.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowSchedule.java index 616e25169..bf0ae9610 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowSchedule.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowSchedule.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowScheduleExecutionModel.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowScheduleExecutionModel.java index 582289d0b..4b14ec8fd 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowScheduleExecutionModel.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowScheduleExecutionModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStateUpdate.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStateUpdate.java index f374a35f5..4f413518a 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStateUpdate.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStateUpdate.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStatus.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStatus.java index 3f89866b6..d9e1fd320 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStatus.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/WorkflowStatus.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueConfiguration.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueConfiguration.java index 1b4e9ea1c..4200fbd82 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueConfiguration.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueWorkerConfiguration.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueWorkerConfiguration.java index 277ec42ed..7ef317c96 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueWorkerConfiguration.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/event/QueueWorkerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Category.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Category.java index c970e1403..017e0718b 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Category.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Category.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Integration.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Integration.java index f8f33bb6e..c91b694ed 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Integration.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/Integration.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApi.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApi.java index 7f50d1df6..457179580 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApi.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApi.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApiUpdate.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApiUpdate.java index 375c358c5..77fd22e31 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApiUpdate.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationApiUpdate.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationDef.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationDef.java index 75f61dbfa..5cb0d3a08 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationDef.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationDef.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationUpdate.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationUpdate.java index 1c65850ea..e634fc290 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationUpdate.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/IntegrationUpdate.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/PromptTemplateTestRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/PromptTemplateTestRequest.java index 3acf1c56b..bfabaca0b 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/PromptTemplateTestRequest.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/PromptTemplateTestRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatCompletion.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatCompletion.java index 74896dba0..3beb3b384 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatCompletion.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatCompletion.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatMessage.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatMessage.java index 3e5fc2855..22cd0ebf0 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatMessage.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/ChatMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/EmbeddingRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/EmbeddingRequest.java index bb717b7b2..ce679ee92 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/EmbeddingRequest.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/EmbeddingRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexDocInput.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexDocInput.java index a31ad317f..f0b5f69c6 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexDocInput.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexDocInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexedDoc.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexedDoc.java index 6ac627544..b2309cdbc 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexedDoc.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/IndexedDoc.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMResponse.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMResponse.java index 334557613..76f068493 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMResponse.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMWorkerInput.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMWorkerInput.java index c4746fbdf..1edf54bf1 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMWorkerInput.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/LLMWorkerInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplate.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplate.java index 6d0bd88dd..89c07f922 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplate.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplateTestRequest.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplateTestRequest.java index 69a56590f..a75046ab9 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplateTestRequest.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/PromptTemplateTestRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/StoreEmbeddingsInput.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/StoreEmbeddingsInput.java index feaadc9f0..f9e07288f 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/StoreEmbeddingsInput.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/StoreEmbeddingsInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/TextCompletion.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/TextCompletion.java index 46ee461f1..cb8561f7d 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/TextCompletion.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/TextCompletion.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/VectorDBInput.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/VectorDBInput.java index 847695a30..9c7aea0f5 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/VectorDBInput.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/model/integration/ai/VectorDBInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/WorkerFn.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/WorkerFn.java index 19c3d3c7a..6722ef7ab 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/WorkerFn.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/WorkerFn.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/Workers.java b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/Workers.java index c920be762..588336f14 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/Workers.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/src/main/java/io/orkes/conductor/client/worker/Workers.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/orkes-spring/src/main/java/io/orkes/conductor/client/spring/OrkesConductorClientAutoConfiguration.java b/conductor-clients/java/conductor-java-sdk/orkes-spring/src/main/java/io/orkes/conductor/client/spring/OrkesConductorClientAutoConfiguration.java index 2149c93cc..005096e46 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-spring/src/main/java/io/orkes/conductor/client/spring/OrkesConductorClientAutoConfiguration.java +++ b/conductor-clients/java/conductor-java-sdk/orkes-spring/src/main/java/io/orkes/conductor/client/spring/OrkesConductorClientAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Orkes, Inc. + * Copyright 2020 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java index b1fa171f0..014ab1d80 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/healthcheck/HealthCheckClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java index 941494e1c..0f3044cbc 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/LocalServerRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java index 28bd41ad3..d1646418a 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/testing/WorkflowTestRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java index 81295a469..0edc0c8be 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ConductorWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java index 23e064d33..f795ab5f9 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/ValidationError.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java index 5ecb604d6..3f6af12bf 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/WorkflowBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java index 9ffbecc7c..498aafd71 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DoWhile.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java index d34bfed0d..9ad9b3593 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Dynamic.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java index 9a4aad2d4..af78a8c29 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicFork.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java index fc917b5d4..d715f82f9 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/DynamicForkInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java index b3b8306ed..4911a635d 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Event.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java index 328bae9a7..f75240f56 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/ForkJoin.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java index 7de82a3a4..37468ff1c 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Http.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java index b4cef5c6f..9121d539d 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/JQ.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java index 2a2436c6f..0c4a43c47 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Javascript.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java index c9c0748ed..1e9ae90aa 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Join.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java index dd1a33211..c4260154a 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SetVariable.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java index d33aa57d3..7cea277c3 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SimpleTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java index 5e77b1e67..a1a9d98a2 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/SubWorkflow.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java index 42f249292..14c9d42a1 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Switch.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java index 8564640a6..c5a81e096 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Task.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java index 5975f9ded..65893ab64 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/TaskRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java index 08c8a1d89..fb7b3d0fd 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Terminate.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java index a8a3cec51..308c17096 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/def/tasks/Wait.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java index c8113d0f3..bc674c470 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/WorkflowExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java index de4735241..68cb2635f 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java index 7296940db..f1b34aa13 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java index 4c8b9a898..89a882e52 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/DynamicForkWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java index 9e6116d4b..61b70fdc7 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/NonRetryableException.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java index 69742e9b1..10786a941 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/TaskContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java index 3a5e276e2..45a615773 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/WorkerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java index 3ae32421e..2afa8bc42 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/InputParam.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java index 152c92480..3c1251317 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/OutputParam.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java index 7f481ff30..f60783124 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/task/WorkerTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java index ac7bb5fde..4e8e34832 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/InputOutputGetter.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java index e52249f69..e1334ce05 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/main/java/com/netflix/conductor/sdk/workflow/utils/MapBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java index 1e7f02d64..e0d044729 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/TaskConversionsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java index 74922c831..8f2dce3e7 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowCreationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java index fcd168563..80629a783 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowDefTaskTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java index d30b01e8e..05dce8080 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/def/WorkflowState.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java index 9e328d0a6..6793ec179 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorkerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java index 165ef1b71..93ebd6989 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/executor/task/TestWorkerConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java index 3d081b4c6..0f7996e00 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/Task1Input.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java index 58aba51d1..62716e161 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/TestWorkflowInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java index 33c96f75e..94cea90c1 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java +++ b/conductor-clients/java/conductor-java-sdk/sdk/src/test/java/com/netflix/conductor/sdk/workflow/testing/WorkflowTestFrameworkTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Orkes, Inc. + * Copyright 2021 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LoadTestWorker.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LoadTestWorker.java index 56702471b..463c93e76 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LoadTestWorker.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LoadTestWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LocalWorkerTest.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LocalWorkerTest.java index 6c21bc338..efe6aef10 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LocalWorkerTest.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/LocalWorkerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/WorkflowRetryTest.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/WorkflowRetryTest.java index c17d9ef2d..8f68fd617 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/WorkflowRetryTest.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/WorkflowRetryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/AuthorizationClientTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/AuthorizationClientTests.java index e3fb160a9..9cb807a09 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/AuthorizationClientTests.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/AuthorizationClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/EventClientTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/EventClientTests.java index a576c00ba..c1e21d90b 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/EventClientTests.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/EventClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/MetadataClientTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/MetadataClientTests.java index 90916e112..f7af78e21 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/MetadataClientTests.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/MetadataClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/PathSuffixTest.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/PathSuffixTest.java index ee727d9a4..e56f15655 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/PathSuffixTest.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/PathSuffixTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SchedulerClientTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SchedulerClientTests.java index 6b3adb11a..1fba3bd1e 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SchedulerClientTests.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SchedulerClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SecretClientTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SecretClientTests.java index de2cd096b..1a8daecf8 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SecretClientTests.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/SecretClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/TaskClientTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/TaskClientTests.java index 3b2b19d99..81cc0b911 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/TaskClientTests.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/TaskClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowClientTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowClientTests.java index 430c7de7e..465ec03b8 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowClientTests.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowStateUpdateTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowStateUpdateTests.java index 2bb078149..5b7cad1d0 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowStateUpdateTests.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/http/WorkflowStateUpdateTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Orkes, Inc. + * Copyright 2024 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/ClientTestUtil.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/ClientTestUtil.java index 9476ec83b..bcd735932 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/ClientTestUtil.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/ClientTestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/Commons.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/Commons.java index 4c92caf34..a37826e5c 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/Commons.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/Commons.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/SimpleWorker.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/SimpleWorker.java index 4dd5811bc..bd97c30a7 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/SimpleWorker.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/SimpleWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/TestUtil.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/TestUtil.java index 22104e770..892a17de5 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/TestUtil.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/TestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/WorkflowUtil.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/WorkflowUtil.java index bf283cf1c..e66b5c1a6 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/WorkflowUtil.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/util/WorkflowUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/LocalServerWorkflowExecutionTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/LocalServerWorkflowExecutionTests.java index cc9ae3386..8045608ac 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/LocalServerWorkflowExecutionTests.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/LocalServerWorkflowExecutionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/WorkflowExecutionTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/WorkflowExecutionTests.java index 2b1e7c54b..d31aaefa0 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/WorkflowExecutionTests.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/client/worker/WorkflowExecutionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Orkes, Inc. + * Copyright 2022 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at diff --git a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/sdk/WorkflowSDKTests.java b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/sdk/WorkflowSDKTests.java index 2688237be..452da6872 100644 --- a/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/sdk/WorkflowSDKTests.java +++ b/conductor-clients/java/conductor-java-sdk/tests/src/test/java/io/orkes/conductor/sdk/WorkflowSDKTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Orkes, Inc. + * Copyright 2023 Conductor Authors. *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at From fcebed0ce10a382f41ba81c081704af11609aa8f Mon Sep 17 00:00:00 2001 From: Miguel Prieto Date: Mon, 16 Sep 2024 18:11:36 -0400 Subject: [PATCH 3/7] Removed tests which were relevant for TaskPoller and PollingSemaphore (those classes have been removed). Might need to add similar test for TaskRunner --- .../automator/PollingSemaphoreTest.java | 73 -- .../automator/TaskPollExecutorTest.java | 647 ------------------ 2 files changed, 720 deletions(-) delete mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java delete mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java deleted file mode 100644 index 9750f8f77..000000000 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/PollingSemaphoreTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2020 Conductor Authors. - *

- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ -package com.netflix.conductor.client.automator; - -//FIXME do we need these tests? -public class PollingSemaphoreTest { - -// @Test -// public void testBlockAfterAvailablePermitsExhausted() throws Exception { -// int threads = 5; -// ExecutorService executorService = Executors.newFixedThreadPool(threads); -// PollingSemaphore pollingSemaphore = new PollingSemaphore(threads); -// -// List> futuresList = new ArrayList<>(); -// IntStream.range(0, threads) -// .forEach( -// t -> -// futuresList.add( -// CompletableFuture.runAsync( -// () -> pollingSemaphore.acquireSlots(1), -// executorService))); -// -// CompletableFuture allFutures = -// CompletableFuture.allOf( -// futuresList.toArray(new CompletableFuture[futuresList.size()])); -// -// allFutures.get(); -// -// assertEquals(0, pollingSemaphore.availableSlots()); -// assertFalse(pollingSemaphore.acquireSlots(1)); -// -// executorService.shutdown(); -// } -// -// @Test -// public void testAllowsPollingWhenPermitBecomesAvailable() throws Exception { -// int threads = 5; -// ExecutorService executorService = Executors.newFixedThreadPool(threads); -// PollingSemaphore pollingSemaphore = new PollingSemaphore(threads); -// -// List> futuresList = new ArrayList<>(); -// IntStream.range(0, threads) -// .forEach( -// t -> -// futuresList.add( -// CompletableFuture.runAsync( -// () -> pollingSemaphore.acquireSlots(1), -// executorService))); -// -// CompletableFuture allFutures = -// CompletableFuture.allOf( -// futuresList.toArray(new CompletableFuture[futuresList.size()])); -// allFutures.get(); -// -// assertEquals(0, pollingSemaphore.availableSlots()); -// pollingSemaphore.complete(1); -// -// assertTrue(pollingSemaphore.availableSlots() > 0); -// assertTrue(pollingSemaphore.acquireSlots(1)); -// -// executorService.shutdown(); -// } -} diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java deleted file mode 100644 index 6a41d476e..000000000 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/test/java/com/netflix/conductor/client/automator/TaskPollExecutorTest.java +++ /dev/null @@ -1,647 +0,0 @@ -/* - * Copyright 2022 Conductor Authors. - *

- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ -package com.netflix.conductor.client.automator; - -import static org.mockito.Mockito.*; - -//FIXME Fix these tests -public class TaskPollExecutorTest { -/* - private static final String TEST_TASK_DEF_NAME = "test"; - - private static final Map TASK_THREAD_MAP = - Collections.singletonMap(TEST_TASK_DEF_NAME, 1); - - @Test - public void testTaskExecutionException() throws InterruptedException { - Worker worker = - Worker.create( - TEST_TASK_DEF_NAME, - task -> { - throw new NoSuchMethodError(); - }); - TaskClient taskClient = Mockito.mock(TaskClient.class); - TaskPollExecutor taskPollExecutor = - new TaskPollExecutor( - null, taskClient, 1, new HashMap<>(), "test-worker-%d", TASK_THREAD_MAP); - - when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) - .thenReturn(Arrays.asList(testTask())); - when(taskClient.ack(any(), any())).thenReturn(true); - CountDownLatch latch = new CountDownLatch(1); - doAnswer( - invocation -> { - assertEquals("test-worker-1", Thread.currentThread().getName()); - Object[] args = invocation.getArguments(); - TaskResult result = (TaskResult) args[0]; - assertEquals(TaskResult.Status.FAILED, result.getStatus()); - latch.countDown(); - return null; - }) - .when(taskClient) - .updateTask(any()); - - Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate( - () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); - - latch.await(); - verify(taskClient).updateTask(any()); - } - - @SuppressWarnings("rawtypes") - @Test - public void testMultipleTasksExecution() throws InterruptedException { - String outputKey = "KEY"; - Task task = testTask(); - Worker worker = mock(Worker.class); - when(worker.getPollingInterval()).thenReturn(3000); - when(worker.getTaskDefName()).thenReturn(TEST_TASK_DEF_NAME); - when(worker.execute(any())) - .thenAnswer( - new Answer() { - private int count = 0; - Map outputMap = new HashMap<>(); - - public TaskResult answer(InvocationOnMock invocation) - throws InterruptedException { - // Sleep for 2 seconds to simulate task execution - Thread.sleep(2000L); - TaskResult taskResult = new TaskResult(task); - outputMap.put(outputKey, count++); - taskResult.setOutputData(outputMap); - return taskResult; - } - }); - - TaskClient taskClient = Mockito.mock(TaskClient.class); - TaskPollExecutor taskPollExecutor = - new TaskPollExecutor( - null, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); - when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) - .thenReturn(Arrays.asList(task)); - when(taskClient.ack(any(), any())).thenReturn(true); - CountDownLatch latch = new CountDownLatch(3); - doAnswer( - new Answer() { - private int count = 0; - - public TaskResult answer(InvocationOnMock invocation) { - Object[] args = invocation.getArguments(); - TaskResult result = (TaskResult) args[0]; - assertEquals(IN_PROGRESS, result.getStatus()); - assertEquals(count, result.getOutputData().get(outputKey)); - count++; - latch.countDown(); - return null; - } - }) - .when(taskClient) - .updateTask(any()); - - Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate( - () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); - latch.await(); - - // execute() is called 3 times on the worker (once for each task) - verify(worker, times(3)).execute(any()); - verify(taskClient, times(3)).updateTask(any()); - } - - @SuppressWarnings("unchecked") - @Test - public void testLargePayloadCanFailUpdateWithRetry() throws InterruptedException { - Task task = testTask(); - - Worker worker = mock(Worker.class); - when(worker.getPollingInterval()).thenReturn(3000); - when(worker.getTaskDefName()).thenReturn(TEST_TASK_DEF_NAME); - when(worker.execute(any())).thenReturn(new TaskResult(task)); - - TaskClient taskClient = Mockito.mock(TaskClient.class); - when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) - .thenReturn(Arrays.asList(task)); - when(taskClient.ack(any(), any())).thenReturn(true); - - doAnswer( - invocation -> { - Object[] args = invocation.getArguments(); - TaskResult result = (TaskResult) args[0]; - assertNull(result.getReasonForIncompletion()); - result.setReasonForIncompletion("some_reason_1"); - throw new ConductorClientException(); - }) - .when(taskClient) - .evaluateAndUploadLargePayload(any(Map.class), any()); - - TaskPollExecutor taskPollExecutor = - new TaskPollExecutor( - null, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); - CountDownLatch latch = new CountDownLatch(1); - doAnswer( - invocation -> { - latch.countDown(); - return null; - }) - .when(worker) - .onErrorUpdate(any()); - - Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate( - () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); - latch.await(); - - // When evaluateAndUploadLargePayload fails indefinitely, task update shouldn't be called. - verify(taskClient, times(0)).updateTask(any()); - } - - @Test - public void testLargePayloadLocationUpdate() throws InterruptedException { - Task task = testTask(); - String largePayloadLocation = "large_payload_location"; - - Worker worker = mock(Worker.class); - when(worker.getPollingInterval()).thenReturn(3000); - when(worker.getTaskDefName()).thenReturn(TEST_TASK_DEF_NAME); - when(worker.execute(any())).thenReturn(new TaskResult(task)); - - TaskClient taskClient = Mockito.mock(TaskClient.class); - when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) - .thenReturn(Arrays.asList(task)); - when(taskClient.ack(any(), any())).thenReturn(true); - //noinspection unchecked - when(taskClient.evaluateAndUploadLargePayload(any(Map.class), any())) - .thenReturn(Optional.of(largePayloadLocation)); - - TaskPollExecutor taskPollExecutor = - new TaskPollExecutor( - null, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); - CountDownLatch latch = new CountDownLatch(1); - - doAnswer( - invocation -> { - Object[] args = invocation.getArguments(); - TaskResult result = (TaskResult) args[0]; - assertNull(result.getOutputData()); - assertEquals( - largePayloadLocation, - result.getExternalOutputPayloadStoragePath()); - latch.countDown(); - return null; - }) - .when(taskClient) - .updateTask(any()); - - Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate( - () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); - latch.await(); - - verify(taskClient, times(1)).updateTask(any()); - } - - @Test - public void testTaskPollException() throws InterruptedException { - Task task = testTask(); - - Worker worker = mock(Worker.class); - when(worker.getPollingInterval()).thenReturn(3000); - when(worker.getTaskDefName()).thenReturn(TEST_TASK_DEF_NAME); - when(worker.execute(any())).thenReturn(new TaskResult(task)); - - TaskClient taskClient = Mockito.mock(TaskClient.class); - when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) - .thenThrow(ConductorClientException.class) - .thenReturn(Arrays.asList(task)); - - TaskPollExecutor taskPollExecutor = - new TaskPollExecutor( - null, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); - CountDownLatch latch = new CountDownLatch(1); - doAnswer( - invocation -> { - Object[] args = invocation.getArguments(); - TaskResult result = (TaskResult) args[0]; - assertEquals(IN_PROGRESS, result.getStatus()); - assertEquals(task.getTaskId(), result.getTaskId()); - latch.countDown(); - return null; - }) - .when(taskClient) - .updateTask(any()); - - Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate( - () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); - - latch.await(); - verify(taskClient).updateTask(any()); - } - - @Test - public void testTaskPoll() throws InterruptedException { - Task task = testTask(); - - Worker worker = mock(Worker.class); - when(worker.getPollingInterval()).thenReturn(3000); - when(worker.getTaskDefName()).thenReturn("test"); - when(worker.execute(any())).thenReturn(new TaskResult(task)); - - TaskClient taskClient = Mockito.mock(TaskClient.class); - when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) - .thenReturn(Arrays.asList(task)); - - TaskPollExecutor taskPollExecutor = - new TaskPollExecutor( - null, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); - CountDownLatch latch = new CountDownLatch(1); - doAnswer( - invocation -> { - Object[] args = invocation.getArguments(); - TaskResult result = (TaskResult) args[0]; - assertEquals(IN_PROGRESS, result.getStatus()); - assertEquals(task.getTaskId(), result.getTaskId()); - latch.countDown(); - return null; - }) - .when(taskClient) - .updateTask(any()); - - Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate( - () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); - - latch.await(); - verify(taskClient).updateTask(any()); - } - - @Test - public void testTaskPollDomain() throws InterruptedException { - TaskClient taskClient = Mockito.mock(TaskClient.class); - String testDomain = "foo"; - Map taskToDomain = new HashMap<>(); - taskToDomain.put(TEST_TASK_DEF_NAME, testDomain); - TaskPollExecutor taskPollExecutor = - new TaskPollExecutor( - null, taskClient, 1, taskToDomain, "test-worker-", TASK_THREAD_MAP); - - String workerName = "test-worker"; - Worker worker = mock(Worker.class); - when(worker.getTaskDefName()).thenReturn(TEST_TASK_DEF_NAME); - when(worker.getIdentity()).thenReturn(workerName); - - CountDownLatch latch = new CountDownLatch(1); - doAnswer( - invocation -> { - latch.countDown(); - return null; - }) - .when(taskClient) - .batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt()); - - Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate( - () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); - - latch.await(); - verify(taskClient).batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt()); - } - - @Test - public void testPollOutOfDiscoveryForTask() throws InterruptedException { - Task task = testTask(); - - EurekaClient client = mock(EurekaClient.class); - when(client.getInstanceRemoteStatus()).thenReturn(InstanceInfo.InstanceStatus.UNKNOWN); - - Worker worker = mock(Worker.class); - when(worker.getPollingInterval()).thenReturn(3000); - when(worker.getTaskDefName()).thenReturn("task_run_always"); - when(worker.execute(any())).thenReturn(new TaskResult(task)); - - TaskClient taskClient = Mockito.mock(TaskClient.class); - when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) - .thenReturn(Arrays.asList(new Task())) - .thenReturn(Arrays.asList(task)); - - TaskPollExecutor taskPollExecutor = - new TaskPollExecutor( - client, - taskClient, - 1, - new HashMap<>(), - "test-worker-", - Collections.singletonMap("task_run_always", 1)); - CountDownLatch latch = new CountDownLatch(1); - doAnswer( - invocation -> { - Object[] args = invocation.getArguments(); - TaskResult result = (TaskResult) args[0]; - assertEquals(IN_PROGRESS, result.getStatus()); - assertEquals(task.getTaskId(), result.getTaskId()); - latch.countDown(); - return null; - }) - .when(taskClient) - .updateTask(any()); - - Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate( - () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); - - latch.await(); - verify(taskClient).updateTask(any()); - } - - @Test - public void testPollOutOfDiscoveryAsDefaultFalseForTask() - throws ExecutionException, InterruptedException { - Task task = testTask(); - - EurekaClient client = mock(EurekaClient.class); - when(client.getInstanceRemoteStatus()).thenReturn(InstanceInfo.InstanceStatus.UNKNOWN); - - Worker worker = mock(Worker.class); - when(worker.getPollingInterval()).thenReturn(3000); - when(worker.getTaskDefName()).thenReturn("task_do_not_run_always"); - when(worker.execute(any())).thenReturn(new TaskResult(task)); - - TaskClient taskClient = Mockito.mock(TaskClient.class); - when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) - .thenReturn(Arrays.asList(task)); - - TaskPollExecutor taskPollExecutor = - new TaskPollExecutor( - client, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); - CountDownLatch latch = new CountDownLatch(1); - doAnswer( - invocation -> { - Object[] args = invocation.getArguments(); - TaskResult result = (TaskResult) args[0]; - assertEquals(IN_PROGRESS, result.getStatus()); - assertEquals(task.getTaskId(), result.getTaskId()); - latch.countDown(); - return null; - }) - .when(taskClient) - .updateTask(any()); - - ScheduledFuture f = - Executors.newSingleThreadScheduledExecutor() - .schedule( - () -> taskPollExecutor.pollAndExecute(worker), 0, TimeUnit.SECONDS); - - f.get(); - verify(taskClient, times(0)).updateTask(any()); - } - - @Test - public void testPollOutOfDiscoveryAsExplicitFalseForTask() - throws ExecutionException, InterruptedException { - Task task = testTask(); - - EurekaClient client = mock(EurekaClient.class); - when(client.getInstanceRemoteStatus()).thenReturn(InstanceInfo.InstanceStatus.UNKNOWN); - - Worker worker = mock(Worker.class); - when(worker.getPollingInterval()).thenReturn(3000); - when(worker.getTaskDefName()).thenReturn("task_explicit_do_not_run_always"); - when(worker.execute(any())).thenReturn(new TaskResult(task)); - - TaskClient taskClient = Mockito.mock(TaskClient.class); - when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) - .thenReturn(Arrays.asList(task)); - - TaskPollExecutor taskPollExecutor = - new TaskPollExecutor( - client, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); - CountDownLatch latch = new CountDownLatch(1); - doAnswer( - invocation -> { - Object[] args = invocation.getArguments(); - TaskResult result = (TaskResult) args[0]; - assertEquals(IN_PROGRESS, result.getStatus()); - assertEquals(task.getTaskId(), result.getTaskId()); - latch.countDown(); - return null; - }) - .when(taskClient) - .updateTask(any()); - - ScheduledFuture f = - Executors.newSingleThreadScheduledExecutor() - .schedule( - () -> taskPollExecutor.pollAndExecute(worker), 0, TimeUnit.SECONDS); - - f.get(); - verify(taskClient, times(0)).updateTask(any()); - } - - @Test - public void testPollOutOfDiscoveryIsIgnoredWhenDiscoveryIsUp() throws InterruptedException { - Task task = testTask(); - - EurekaClient client = mock(EurekaClient.class); - when(client.getInstanceRemoteStatus()).thenReturn(InstanceInfo.InstanceStatus.UP); - - Worker worker = mock(Worker.class); - when(worker.getPollingInterval()).thenReturn(3000); - when(worker.getTaskDefName()).thenReturn("task_ignore_override"); - when(worker.execute(any())).thenReturn(new TaskResult(task)); - - TaskClient taskClient = Mockito.mock(TaskClient.class); - when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) - .thenReturn(Arrays.asList(task)); - - TaskPollExecutor taskPollExecutor = - new TaskPollExecutor( - client, - taskClient, - 1, - new HashMap<>(), - "test-worker-", - Collections.singletonMap("task_ignore_override", 1)); - CountDownLatch latch = new CountDownLatch(1); - doAnswer( - invocation -> { - Object[] args = invocation.getArguments(); - TaskResult result = (TaskResult) args[0]; - assertEquals(IN_PROGRESS, result.getStatus()); - assertEquals(task.getTaskId(), result.getTaskId()); - latch.countDown(); - return null; - }) - .when(taskClient) - .updateTask(any()); - - Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate( - () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); - - latch.await(); - verify(taskClient).updateTask(any()); - } - - @Test - public void testTaskThreadCount() throws InterruptedException { - TaskClient taskClient = Mockito.mock(TaskClient.class); - - Map taskThreadCount = new HashMap<>(); - taskThreadCount.put(TEST_TASK_DEF_NAME, 1); - - TaskPollExecutor taskPollExecutor = - new TaskPollExecutor( - null, taskClient, -1, new HashMap<>(), "test-worker-", taskThreadCount); - - String workerName = "test-worker"; - Worker worker = mock(Worker.class); - when(worker.getTaskDefName()).thenReturn(TEST_TASK_DEF_NAME); - when(worker.getIdentity()).thenReturn(workerName); - - CountDownLatch latch = new CountDownLatch(1); - doAnswer( - invocation -> { - latch.countDown(); - return null; - }) - .when(taskClient) - .batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt()); - - Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate( - () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); - - latch.await(); - verify(taskClient).batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt()); - } - - @Test - public void testTaskLeaseExtend() throws InterruptedException { - Task task = testTask(); - task.setResponseTimeoutSeconds(1); - - Worker worker = mock(Worker.class); - when(worker.getPollingInterval()).thenReturn(3000); - when(worker.getTaskDefName()).thenReturn("test"); - when(worker.execute(any())).thenReturn(new TaskResult(task)); - when(worker.leaseExtendEnabled()).thenReturn(true); - - TaskClient taskClient = Mockito.mock(TaskClient.class); - when(taskClient.batchPollTasksInDomain(any(), any(), any(), anyInt(), anyInt())) - .thenReturn(Arrays.asList(task)); - - TaskResult result = new TaskResult(task); - result.getLogs().add(new TaskExecLog("lease extend")); - result.setExtendLease(true); - - TaskPollExecutor taskPollExecutor = - new TaskPollExecutor( - null, taskClient, 1, new HashMap<>(), "test-worker-", TASK_THREAD_MAP); - CountDownLatch latch = new CountDownLatch(1); - doAnswer( - invocation -> { - assertTrue( - taskPollExecutor.leaseExtendMap.containsKey(task.getTaskId())); - latch.countDown(); - return null; - }) - .when(taskClient) - .updateTask(any()); - - Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate( - () -> taskPollExecutor.pollAndExecute(worker), 0, 5, TimeUnit.SECONDS); - - latch.await(); - } - - @Test - public void testBatchTasksExecution() throws InterruptedException { - int threadCount = 10; - TaskClient taskClient = Mockito.mock(TaskClient.class); - Map taskThreadCount = new HashMap<>(); - taskThreadCount.put(TEST_TASK_DEF_NAME, threadCount); - - String workerName = "test-worker"; - Worker worker = mock(Worker.class); - when(worker.getPollingInterval()).thenReturn(3000); - when(worker.getBatchPollTimeoutInMS()).thenReturn(1000); - when(worker.getTaskDefName()).thenReturn(TEST_TASK_DEF_NAME); - when(worker.getIdentity()).thenReturn(workerName); - - List tasks = new ArrayList<>(); - for (int i = 0; i < threadCount; i++) { - Task task = testTask(); - tasks.add(task); - - when(worker.execute(task)) - .thenAnswer( - new Answer() { - Map outputMap = new HashMap<>(); - - public TaskResult answer(InvocationOnMock invocation) - throws InterruptedException { - // Sleep for 1 seconds to simulate task execution - Thread.sleep(1000L); - TaskResult taskResult = new TaskResult(task); - outputMap.put("key", "value"); - taskResult.setOutputData(outputMap); - taskResult.setStatus(COMPLETED); - return taskResult; - } - }); - } - when(taskClient.batchPollTasksInDomain( - TEST_TASK_DEF_NAME, null, workerName, threadCount, 1000)) - .thenReturn(tasks); - when(taskClient.ack(any(), any())).thenReturn(true); - - TaskPollExecutor taskPollExecutor = - new TaskPollExecutor( - null, taskClient, 1, new HashMap<>(), "test-worker-", taskThreadCount); - - CountDownLatch latch = new CountDownLatch(threadCount); - doAnswer( - new Answer() { - public TaskResult answer(InvocationOnMock invocation) { - Object[] args = invocation.getArguments(); - TaskResult result = (TaskResult) args[0]; - assertEquals(COMPLETED, result.getStatus()); - assertEquals("value", result.getOutputData().get("key")); - latch.countDown(); - return null; - } - }) - .when(taskClient) - .updateTask(any()); - - Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate( - () -> taskPollExecutor.pollAndExecute(worker), 0, 1, TimeUnit.SECONDS); - latch.await(); - - // execute() is called 10 times on the worker (once for each task) - verify(worker, times(threadCount)).execute(any()); - verify(taskClient, times(threadCount)).updateTask(any()); - } - - private Task testTask() { - Task task = new Task(); - task.setTaskId(UUID.randomUUID().toString()); - task.setStatus(Task.Status.IN_PROGRESS); - task.setTaskDefName(TEST_TASK_DEF_NAME); - return task; - } - */ -} From caa8bc2745cdd1844d53e19ee24d79d0bb27916b Mon Sep 17 00:00:00 2001 From: Miguel Prieto Date: Mon, 16 Sep 2024 18:12:49 -0400 Subject: [PATCH 4/7] Allow promiscuous listeners. They listen for all type of Events. --- .../client/automator/TaskRunner.java | 30 +++++++++++++++++-- .../automator/events/TaskRunnerEvent.java | 2 +- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java index a388faeef..f23a8e539 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java @@ -15,6 +15,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -26,6 +27,8 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.slf4j.Logger; @@ -369,18 +372,41 @@ private R retryOperation(Function operation, int count, T input, St } private void publish(TaskRunnerEvent event) { - if (listeners.get(event.getClass()) == null) { + if (noListeners(event)) { return; } CompletableFuture.runAsync(() -> { - List> eventListeners = listeners.get(event.getClass()); + List> eventListeners = getEventListeners(event); for (Consumer listener : eventListeners) { ((Consumer) listener).accept(event); } }); } + private boolean noListeners(TaskRunnerEvent event) { + List> specificEventListeners = this.listeners.get(event.getClass()); + List> promiscuousListeners = this.listeners.get(TaskRunnerEvent.class); + + return (specificEventListeners == null || specificEventListeners.isEmpty()) + && (promiscuousListeners == null || promiscuousListeners.isEmpty()); + } + + private List> getEventListeners(TaskRunnerEvent event) { + List> specificEventListeners = this.listeners.get(event.getClass()); + List> promiscuousListeners = this.listeners.get(TaskRunnerEvent.class); + if (promiscuousListeners == null || promiscuousListeners.isEmpty()) { + return specificEventListeners; + } + + if (specificEventListeners == null || specificEventListeners.isEmpty()) { + return promiscuousListeners; + } + + return Stream.concat(specificEventListeners.stream(), promiscuousListeners.stream()) + .collect(Collectors.toList()); + } + private void handleException(Throwable t, TaskResult result, Worker worker, Task task) { LOGGER.error(String.format("Error while executing task %s", task.toString()), t); result.setStatus(TaskResult.Status.FAILED); diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskRunnerEvent.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskRunnerEvent.java index c4838cfc4..48474f134 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskRunnerEvent.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/events/TaskRunnerEvent.java @@ -21,7 +21,7 @@ @AllArgsConstructor @Getter @ToString -public class TaskRunnerEvent { +public abstract class TaskRunnerEvent { private final Instant time = Instant.now(); private final String taskType; } From 027d8952fe91c80ec1525e50d53edc20e373a55e Mon Sep 17 00:00:00 2001 From: Miguel Prieto Date: Mon, 16 Sep 2024 18:18:00 -0400 Subject: [PATCH 5/7] Fixed repo info in Maven Publish puglin --- .../conductor-client-spring/build.gradle | 8 ++++---- .../java/conductor-java-sdk/conductor-client/build.gradle | 8 ++++---- .../java/conductor-java-sdk/orkes-client/build.gradle | 8 ++++---- .../java/conductor-java-sdk/orkes-spring/build.gradle | 8 ++++---- .../java/conductor-java-sdk/sdk/build.gradle | 8 ++++---- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/build.gradle b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/build.gradle index 637d103b8..3966fa469 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client-spring/build.gradle +++ b/conductor-clients/java/conductor-java-sdk/conductor-client-spring/build.gradle @@ -28,11 +28,11 @@ publishing { pom { name = 'Conductor OSS Client Spring' description = 'Spring autoconfig for Conductor Client and SDK' - url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + url = 'https://github.com/conductor-oss/conductor.git' scm { - connection = 'scm:git:git://github.com/conductor-oss/conductor-java-sdk.git' - developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor-java-sdk.git' - url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + connection = 'scm:git:git://github.com/conductor-oss/conductor.git' + developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor.git' + url = 'https://github.com/conductor-oss/conductor.git' } licenses { license { diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/build.gradle b/conductor-clients/java/conductor-java-sdk/conductor-client/build.gradle index 6d66d5745..9839d0327 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/build.gradle +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/build.gradle @@ -34,11 +34,11 @@ publishing { pom { name = 'Conductor Client' description = 'Conductor OSS client (http)' - url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + url = 'https://github.com/conductor-oss/conductor.git' scm { - connection = 'scm:git:git://github.com/conductor-oss/conductor-java-sdk.git' - developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor-java-sdk.git' - url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + connection = 'scm:git:git://github.com/conductor-oss/conductor.git' + developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor.git' + url = 'https://github.com/conductor-oss/conductor.git' } licenses { license { diff --git a/conductor-clients/java/conductor-java-sdk/orkes-client/build.gradle b/conductor-clients/java/conductor-java-sdk/orkes-client/build.gradle index d8d212e4a..50fff4eb5 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-client/build.gradle +++ b/conductor-clients/java/conductor-java-sdk/orkes-client/build.gradle @@ -35,11 +35,11 @@ publishing { pom { name = 'Orkes Conductor Client' description = 'OSS & Orkes Conductor client (http)' - url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + url = 'https://github.com/conductor-oss/conductor.git' scm { - connection = 'scm:git:git://github.com/conductor-oss/conductor-java-sdk.git' - developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor-java-sdk.git' - url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + connection = 'scm:git:git://github.com/conductor-oss/conductor.git' + developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor.git' + url = 'https://github.com/conductor-oss/conductor.git' } licenses { license { diff --git a/conductor-clients/java/conductor-java-sdk/orkes-spring/build.gradle b/conductor-clients/java/conductor-java-sdk/orkes-spring/build.gradle index b4025341f..8067c8172 100644 --- a/conductor-clients/java/conductor-java-sdk/orkes-spring/build.gradle +++ b/conductor-clients/java/conductor-java-sdk/orkes-spring/build.gradle @@ -30,11 +30,11 @@ publishing { pom { name = 'Orkes Conductor Client/SDK Spring' description = 'Spring autoconfig for Orkes Conductor Client and SDK' - url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + url = 'https://github.com/conductor-oss/conductor.git' scm { - connection = 'scm:git:git://github.com/conductor-oss/conductor-java-sdk.git' - developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor-java-sdk.git' - url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + connection = 'scm:git:git://github.com/conductor-oss/conductor.git' + developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor.git' + url = 'https://github.com/conductor-oss/conductor.git' } licenses { license { diff --git a/conductor-clients/java/conductor-java-sdk/sdk/build.gradle b/conductor-clients/java/conductor-java-sdk/sdk/build.gradle index 9ce4cdf3e..875f2addf 100644 --- a/conductor-clients/java/conductor-java-sdk/sdk/build.gradle +++ b/conductor-clients/java/conductor-java-sdk/sdk/build.gradle @@ -24,11 +24,11 @@ publishing { pom { name = 'Orkes Conductor SDK' description = 'OSS & Orkes Conductor SDK' - url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + url = 'https://github.com/conductor-oss/conductor.git' scm { - connection = 'scm:git:git://github.com/conductor-oss/conductor-java-sdk.git' - developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor-java-sdk.git' - url = 'https://github.com/conductor-oss/conductor-java-sdk.git' + connection = 'scm:git:git://github.com/conductor-oss/conductor.git' + developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor.git' + url = 'https://github.com/conductor-oss/conductor.git' } licenses { license { From a2818fd9a4e5c118d0686683375e06ef95d17fd5 Mon Sep 17 00:00:00 2001 From: Miguel Prieto Date: Mon, 16 Sep 2024 18:53:52 -0400 Subject: [PATCH 6/7] - Added MetricsCollector interface - Added module with minimal Prometheus Collector --- .../client/automator/TaskRunner.java | 1 - .../automator/TaskRunnerConfigurer.java | 29 ++++++ .../client/metrics/MetricsCollector.java | 35 ++++++++ .../java/conductor-java-sdk/gradle.properties | 2 +- .../conductor-java-sdk/metrics/build.gradle | 90 +++++++++++++++++++ .../PrometheusMetricsCollector.java | 86 ++++++++++++++++++ .../java/conductor-java-sdk/settings.gradle | 2 +- 7 files changed, 242 insertions(+), 3 deletions(-) create mode 100644 conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/metrics/MetricsCollector.java create mode 100644 conductor-clients/java/conductor-java-sdk/metrics/build.gradle create mode 100644 conductor-clients/java/conductor-java-sdk/metrics/src/main/java/com/netflix/conductor/client/metrics/prometheus/PrometheusMetricsCollector.java diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java index f23a8e539..3c3d9bfbd 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java @@ -15,7 +15,6 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.Collections; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java index 8fbf3ba09..a04acd622 100644 --- a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java @@ -24,10 +24,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.netflix.conductor.client.automator.events.PollCompleted; +import com.netflix.conductor.client.automator.events.PollFailure; +import com.netflix.conductor.client.automator.events.PollStarted; +import com.netflix.conductor.client.automator.events.TaskExecutionCompleted; +import com.netflix.conductor.client.automator.events.TaskExecutionFailure; +import com.netflix.conductor.client.automator.events.TaskExecutionStarted; import com.netflix.conductor.client.automator.events.TaskRunnerEvent; import com.netflix.conductor.client.automator.filters.PollFilter; import com.netflix.conductor.client.config.ConductorClientConfiguration; import com.netflix.conductor.client.http.TaskClient; +import com.netflix.conductor.client.metrics.MetricsCollector; import com.netflix.conductor.client.worker.Worker; import com.google.common.base.Preconditions; @@ -321,5 +328,27 @@ public Builder withListener(Class eventType, Cons listeners.computeIfAbsent(eventType, k -> new LinkedList<>()).add(listener); return this; } + + public Builder withMetricsCollector(MetricsCollector metricsCollector) { + listeners.computeIfAbsent(PollFailure.class, k -> new LinkedList<>()) + .add((Consumer) metricsCollector::consume); + + listeners.computeIfAbsent(PollCompleted.class, k -> new LinkedList<>()) + .add((Consumer) metricsCollector::consume); + + listeners.computeIfAbsent(PollStarted.class, k -> new LinkedList<>()) + .add((Consumer) metricsCollector::consume); + + listeners.computeIfAbsent(TaskExecutionStarted.class, k -> new LinkedList<>()) + .add((Consumer) metricsCollector::consume); + + listeners.computeIfAbsent(TaskExecutionCompleted.class, k -> new LinkedList<>()) + .add((Consumer) metricsCollector::consume); + + listeners.computeIfAbsent(TaskExecutionFailure.class, k -> new LinkedList<>()) + .add((Consumer) metricsCollector::consume); + + return this; + } } } diff --git a/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/metrics/MetricsCollector.java b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/metrics/MetricsCollector.java new file mode 100644 index 000000000..48275d8f7 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/conductor-client/src/main/java/com/netflix/conductor/client/metrics/MetricsCollector.java @@ -0,0 +1,35 @@ +/* + * Copyright 2024 Conductor Authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.metrics; + +import com.netflix.conductor.client.automator.events.PollCompleted; +import com.netflix.conductor.client.automator.events.PollFailure; +import com.netflix.conductor.client.automator.events.PollStarted; +import com.netflix.conductor.client.automator.events.TaskExecutionCompleted; +import com.netflix.conductor.client.automator.events.TaskExecutionFailure; +import com.netflix.conductor.client.automator.events.TaskExecutionStarted; + +public interface MetricsCollector { + + void consume(PollFailure e); + + void consume(PollCompleted e); + + void consume(PollStarted e); + + void consume(TaskExecutionStarted e); + + void consume(TaskExecutionCompleted e); + + void consume(TaskExecutionFailure e); +} diff --git a/conductor-clients/java/conductor-java-sdk/gradle.properties b/conductor-clients/java/conductor-java-sdk/gradle.properties index ffca90162..d8cbc6bb0 100644 --- a/conductor-clients/java/conductor-java-sdk/gradle.properties +++ b/conductor-clients/java/conductor-java-sdk/gradle.properties @@ -1 +1 @@ -version=3.0.0-alpha15-SNAPSHOT +version=3.0.0-alpha16-SNAPSHOT diff --git a/conductor-clients/java/conductor-java-sdk/metrics/build.gradle b/conductor-clients/java/conductor-java-sdk/metrics/build.gradle new file mode 100644 index 000000000..62c67e7e8 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/metrics/build.gradle @@ -0,0 +1,90 @@ +plugins { + id 'java-library' + id 'idea' + id 'maven-publish' + id 'signing' +} + +dependencies { + implementation 'io.micrometer:micrometer-registry-prometheus:1.10.5' + implementation project(":conductor-client") + + testImplementation 'org.mockito:mockito-core:5.4.0' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' +} + +java { + withSourcesJar() + withJavadocJar() +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + pom { + name = 'Orkes Conductor Metrics module' + description = 'OSS & Orkes Conductor Metrics' + url = 'https://github.com/conductor-oss/conductor.git' + scm { + connection = 'scm:git:git://github.com/conductor-oss/conductor.git' + developerConnection = 'scm:git:ssh://github.com/conductor-oss/conductor.git' + url = 'https://github.com/conductor-oss/conductor.git' + } + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + organization = 'Orkes' + organizationUrl = 'https://orkes.io' + name = 'Orkes Development Team' + email = 'developers@orkes.io' + } + } + } + } + } + + repositories { + maven { + if (project.hasProperty("mavenCentral")) { + println "Publishing to Sonatype Repository" + url = "https://s01.oss.sonatype.org/${project.version.endsWith('-SNAPSHOT') ? "content/repositories/snapshots/" : "service/local/staging/deploy/maven2/"}" + credentials { + username project.properties.username + password project.properties.password + } + } else { + url = "s3://orkes-artifacts-repo/${project.version.endsWith('-SNAPSHOT') ? "snapshots" : "releases"}" + authentication { + awsIm(AwsImAuthentication) + } + } + } + } +} + +signing { + def signingKeyId = findProperty('signingKeyId') + if (signingKeyId) { + println 'Signing the artifact with keys' + signing { + def signingKey = findProperty('signingKey') + def signingPassword = findProperty('signingPassword') + if (signingKeyId && signingKey && signingPassword) { + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + } + + sign publishing.publications + } + } +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/conductor-clients/java/conductor-java-sdk/metrics/src/main/java/com/netflix/conductor/client/metrics/prometheus/PrometheusMetricsCollector.java b/conductor-clients/java/conductor-java-sdk/metrics/src/main/java/com/netflix/conductor/client/metrics/prometheus/PrometheusMetricsCollector.java new file mode 100644 index 000000000..307da9109 --- /dev/null +++ b/conductor-clients/java/conductor-java-sdk/metrics/src/main/java/com/netflix/conductor/client/metrics/prometheus/PrometheusMetricsCollector.java @@ -0,0 +1,86 @@ +/* + * Copyright 2024 Conductor Authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.netflix.conductor.client.metrics.prometheus; + +import java.io.IOException; +import java.net.InetSocketAddress; + +import com.netflix.conductor.client.automator.events.PollCompleted; +import com.netflix.conductor.client.automator.events.PollFailure; +import com.netflix.conductor.client.automator.events.PollStarted; +import com.netflix.conductor.client.automator.events.TaskExecutionCompleted; +import com.netflix.conductor.client.automator.events.TaskExecutionFailure; +import com.netflix.conductor.client.automator.events.TaskExecutionStarted; +import com.netflix.conductor.client.metrics.MetricsCollector; + +import com.sun.net.httpserver.HttpServer; +import io.micrometer.prometheus.PrometheusConfig; +import io.micrometer.prometheus.PrometheusMeterRegistry; + +public class PrometheusMetricsCollector implements MetricsCollector { + + private static final PrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + + public void startServer() throws IOException { + startServer(9991, "/metrics"); + } + + public void startServer(int port, String endpoint) throws IOException { + var server = HttpServer.create(new InetSocketAddress(port), 0); + server.createContext(endpoint, (exchange -> { + var body = prometheusRegistry.scrape(); + exchange.getResponseHeaders().set("Content-Type", "text/plain"); + exchange.sendResponseHeaders(200, body.getBytes().length); + try (var os = exchange.getResponseBody()) { + os.write(body.getBytes()); + } + })); + server.start(); + } + + @Override + public void consume(PollFailure e) { + var timer = prometheusRegistry.timer("poll_failure", "type", e.getTaskType()); + timer.record(e.getDuration()); + } + + @Override + public void consume(PollCompleted e) { + var timer = prometheusRegistry.timer("poll_success", "type", e.getTaskType()); + timer.record(e.getDuration()); + } + + @Override + public void consume(PollStarted e) { + var counter = prometheusRegistry.counter("poll_started", "type", e.getTaskType()); + counter.increment(); + } + + @Override + public void consume(TaskExecutionStarted e) { + var counter = prometheusRegistry.counter("task_execution_started", "type", e.getTaskType()); + counter.increment(); + } + + @Override + public void consume(TaskExecutionCompleted e) { + var timer = prometheusRegistry.timer("task_execution_completed", "type", e.getTaskType()); + timer.record(e.getDuration()); + } + + @Override + public void consume(TaskExecutionFailure e) { + var timer = prometheusRegistry.timer("task_execution_failure", "type", e.getTaskType()); + timer.record(e.getDuration()); + } +} diff --git a/conductor-clients/java/conductor-java-sdk/settings.gradle b/conductor-clients/java/conductor-java-sdk/settings.gradle index 6860b50bd..8e008bf99 100644 --- a/conductor-clients/java/conductor-java-sdk/settings.gradle +++ b/conductor-clients/java/conductor-java-sdk/settings.gradle @@ -6,4 +6,4 @@ include 'examples' include 'tests' include 'orkes-spring' include 'conductor-client-spring' - +include 'metrics' From 96ed7efba3e92784165c4d7e30406e03fd356ffa Mon Sep 17 00:00:00 2001 From: Miguel Prieto Date: Tue, 17 Sep 2024 11:23:28 -0400 Subject: [PATCH 7/7] Moved metrics module to conductor-client-metrics --- .../{metrics => conductor-client-metrics}/build.gradle | 0 .../metrics/prometheus/PrometheusMetricsCollector.java | 0 conductor-clients/java/conductor-java-sdk/settings.gradle | 6 +++--- 3 files changed, 3 insertions(+), 3 deletions(-) rename conductor-clients/java/conductor-java-sdk/{metrics => conductor-client-metrics}/build.gradle (100%) rename conductor-clients/java/conductor-java-sdk/{metrics => conductor-client-metrics}/src/main/java/com/netflix/conductor/client/metrics/prometheus/PrometheusMetricsCollector.java (100%) diff --git a/conductor-clients/java/conductor-java-sdk/metrics/build.gradle b/conductor-clients/java/conductor-java-sdk/conductor-client-metrics/build.gradle similarity index 100% rename from conductor-clients/java/conductor-java-sdk/metrics/build.gradle rename to conductor-clients/java/conductor-java-sdk/conductor-client-metrics/build.gradle diff --git a/conductor-clients/java/conductor-java-sdk/metrics/src/main/java/com/netflix/conductor/client/metrics/prometheus/PrometheusMetricsCollector.java b/conductor-clients/java/conductor-java-sdk/conductor-client-metrics/src/main/java/com/netflix/conductor/client/metrics/prometheus/PrometheusMetricsCollector.java similarity index 100% rename from conductor-clients/java/conductor-java-sdk/metrics/src/main/java/com/netflix/conductor/client/metrics/prometheus/PrometheusMetricsCollector.java rename to conductor-clients/java/conductor-java-sdk/conductor-client-metrics/src/main/java/com/netflix/conductor/client/metrics/prometheus/PrometheusMetricsCollector.java diff --git a/conductor-clients/java/conductor-java-sdk/settings.gradle b/conductor-clients/java/conductor-java-sdk/settings.gradle index 8e008bf99..591f86d40 100644 --- a/conductor-clients/java/conductor-java-sdk/settings.gradle +++ b/conductor-clients/java/conductor-java-sdk/settings.gradle @@ -1,9 +1,9 @@ rootProject.name = 'conductor-java-sdk' include 'conductor-client' -include 'orkes-client' +include 'conductor-client-metrics' +include 'conductor-client-spring' include 'sdk' include 'examples' include 'tests' +include 'orkes-client' include 'orkes-spring' -include 'conductor-client-spring' -include 'metrics'