From 851b6d05b459f2a47075907c6d64a704c576f5be Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 19:52:18 +0000 Subject: [PATCH] deploy: 6d295e13a586d904e9b9b2de786fc4bb2eb8a0c5 --- 404.html | 2 +- assets/js/62796615.08ee0677.js | 1 + assets/js/62796615.ec8840ea.js | 1 - assets/js/a06c0eb2.13eb8aee.js | 1 - assets/js/a06c0eb2.47a7d66e.js | 1 + assets/js/e33a6d67.34ee8db2.js | 1 - assets/js/e33a6d67.82468289.js | 1 + ...n.3f9a0c72.js => runtime~main.93137d14.js} | 2 +- blog/archive/index.html | 2 +- blog/authors/coltmcnealy/index.html | 2 +- blog/authors/index.html | 2 +- blog/authors/lh-council/index.html | 2 +- blog/authors/mitchellh/index.html | 2 +- blog/basics-of-workflow/index.html | 2 +- blog/challenge-of-microservices/index.html | 2 +- blog/index.html | 2 +- blog/littlehorse-0.10-release/index.html | 2 +- blog/littlehorse-0.11-release/index.html | 2 +- blog/littlehorse-0.2.0-release/index.html | 2 +- blog/littlehorse-0.5.0-release/index.html | 2 +- blog/littlehorse-0.7-release/index.html | 2 +- blog/littlehorse-0.8-release/index.html | 2 +- blog/littlehorse-0.9-release/index.html | 2 +- blog/microservices-and-workflow/index.html | 2 +- blog/promise-of-microservices/index.html | 2 +- blog/saga-pattern/index.html | 2 +- blog/tags/analysis/index.html | 2 +- blog/tags/index.html | 2 +- blog/tags/integration-patterns/index.html | 2 +- blog/tags/littlehorse/index.html | 2 +- .../tags/microservice-and-workflow/index.html | 2 +- blog/tags/release/index.html | 2 +- blog/transactional-outbox/index.html | 2 +- docs/api/index.html | 2 +- docs/architecture-and-guarantees/index.html | 2 +- docs/concepts/advanced/index.html | 2 +- .../advanced/wfspec-versioning/index.html | 2 +- docs/concepts/external-events/index.html | 2 +- docs/concepts/index.html | 2 +- .../principals-and-tenants/index.html | 2 +- docs/concepts/tasks/index.html | 2 +- docs/concepts/user-tasks/index.html | 2 +- docs/concepts/workflows/index.html | 2 +- .../client-configuration/index.html | 2 +- docs/developer-guide/grpc/basics/index.html | 2 +- docs/developer-guide/grpc/index.html | 2 +- .../grpc/managing-metadata/index.html | 2 +- .../grpc/posting-external-events/index.html | 2 +- .../grpc/running-workflows/index.html | 8 +++++-- .../grpc/user-tasks/index.html | 2 +- docs/developer-guide/index.html | 2 +- docs/developer-guide/install/index.html | 2 +- docs/developer-guide/lhctl/index.html | 22 +++++++++++++++---- .../task-worker-development/index.html | 2 +- .../wfspec-development/basics/index.html | 12 ++++++++-- .../child-threads/index.html | 2 +- .../conditionals/index.html | 2 +- .../exception-handling/index.html | 2 +- .../external-events/index.html | 2 +- .../wfspec-development/index.html | 2 +- .../wfspec-development/interrupts/index.html | 2 +- .../mutating-variables/index.html | 2 +- .../wfspec-development/user-tasks/index.html | 2 +- docs/faq/index.html | 2 +- .../client-configuration/index.html | 2 +- .../dashboard-configuration/index.html | 2 +- .../docker-compose/basic/index.html | 2 +- .../docker-compose/confluent-cloud/index.html | 2 +- docs/operations/docker-compose/index.html | 2 +- .../docker-compose/three-servers/index.html | 2 +- docs/operations/index.html | 2 +- docs/operations/overview/index.html | 2 +- .../server-configuration/index.html | 2 +- docs/overview/index.html | 2 +- index.html | 2 +- markdown-page/index.html | 2 +- search/index.html | 2 +- 77 files changed, 105 insertions(+), 79 deletions(-) create mode 100644 assets/js/62796615.08ee0677.js delete mode 100644 assets/js/62796615.ec8840ea.js delete mode 100644 assets/js/a06c0eb2.13eb8aee.js create mode 100644 assets/js/a06c0eb2.47a7d66e.js delete mode 100644 assets/js/e33a6d67.34ee8db2.js create mode 100644 assets/js/e33a6d67.82468289.js rename assets/js/{runtime~main.3f9a0c72.js => runtime~main.93137d14.js} (97%) diff --git a/404.html b/404.html index abfd8771e..56b640788 100644 --- a/404.html +++ b/404.html @@ -18,7 +18,7 @@ - +
diff --git a/assets/js/62796615.08ee0677.js b/assets/js/62796615.08ee0677.js new file mode 100644 index 000000000..ca5ca21d8 --- /dev/null +++ b/assets/js/62796615.08ee0677.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunklh_site=self.webpackChunklh_site||[]).push([[9445],{1296:(e,n,r)=>{r.r(n),r.d(n,{assets:()=>u,contentTitle:()=>o,default:()=>f,frontMatter:()=>i,metadata:()=>c,toc:()=>d});var t=r(4848),s=r(8453),a=r(1470),l=r(9365);const i={},o="Running a Workflow",c={id:"developer-guide/grpc/running-workflows",title:"Running a Workflow",description:"You can run a WfSpec, thus creating a WfRun, in two ways:",source:"@site/docs/05-developer-guide/09-grpc/10-running-workflows.md",sourceDirName:"05-developer-guide/09-grpc",slug:"/developer-guide/grpc/running-workflows",permalink:"/docs/developer-guide/grpc/running-workflows",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:10,frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Managing Metadata",permalink:"/docs/developer-guide/grpc/managing-metadata"},next:{title:"Posting ExternalEvents",permalink:"/docs/developer-guide/grpc/posting-external-events"}},u={},d=[{value:"Simple",id:"simple",level:2},{value:"Pinning to a Major Version",id:"pinning-to-a-major-version",level:2},{value:"Pinning the Revision",id:"pinning-the-revision",level:2},{value:"Passing the ID",id:"passing-the-id",level:2},{value:"Parameters",id:"parameters",level:2},{value:"Scheduling",id:"scheduling",level:2}];function h(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.header,{children:(0,t.jsx)(n.h1,{id:"running-a-workflow",children:"Running a Workflow"})}),"\n",(0,t.jsxs)(n.p,{children:["You can run a ",(0,t.jsx)(n.code,{children:"WfSpec"}),", thus creating a ",(0,t.jsx)(n.code,{children:"WfRun"}),", in two ways:"]}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsx)(n.li,{children:"Using a grpc client in an SDK of your choice."}),"\n",(0,t.jsxs)(n.li,{children:["Using ",(0,t.jsx)(n.code,{children:"lhctl"}),"."]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["For a tutorial on running a ",(0,t.jsx)(n.code,{children:"WfSpec"})," to create a ",(0,t.jsx)(n.code,{children:"WfRun"})," using ",(0,t.jsx)(n.code,{children:"lhctl"}),", see the ",(0,t.jsxs)(n.a,{href:"/docs/developer-guide/lhctl",children:[(0,t.jsx)(n.code,{children:"lhctl"})," docs"]}),"."]}),"\n",(0,t.jsx)(n.h2,{id:"simple",children:"Simple"}),"\n",(0,t.jsxs)(n.p,{children:["The most basic way to run a ",(0,t.jsx)(n.code,{children:"WfRun"})," is as follows. This will run the latest version of the ",(0,t.jsx)(n.code,{children:"WfSpec"})," with the provided name."]}),"\n",(0,t.jsxs)(a.A,{children:[(0,t.jsx)(l.A,{value:"java",label:"Java",default:!0,children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-java",children:'LittleHorseBlockingStub client = ...;\n\nWfRun result = client.runWf(RunWfRequest.newBuilder().setWfSpecName("some-wf-spec").build());\n'})})}),(0,t.jsx)(l.A,{value:"go",label:"Go",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-go",children:'config := littlehorse.NewConfigFromEnv()\nclient, _ := config.GetGrpcClient()\n\nresult, err := (*client).RunWf(context.Background(), &lhproto.RunWfRequest{\n WfSpecName: "some-wf-spec",\n})\n'})})}),(0,t.jsx)(l.A,{value:"python",label:"Python",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-python",children:'config = LHConfig()\nstub = config.stub()\nstub.RunWf(RunWfRequest(wf_spec_name="some-wf-spec"))\n'})})})]}),"\n",(0,t.jsx)(n.h2,{id:"pinning-to-a-major-version",children:"Pinning to a Major Version"}),"\n",(0,t.jsxs)(n.p,{children:["As a consumer of a ",(0,t.jsx)(n.code,{children:"WfSpec"}),", an application may wish to pin to a specific ",(0,t.jsx)(n.code,{children:"majorVersion"})," in order to allow the owner of the ",(0,t.jsx)(n.code,{children:"WfSpec"})," to evolve the ",(0,t.jsx)(n.code,{children:"WfSpec"})," in a compatible manner (i.e. without changing the exposed input variables or searchable variables). You can do this by providing the ",(0,t.jsx)(n.code,{children:"majorVersion"})," flag. When you run a ",(0,t.jsx)(n.code,{children:"WfSpec"})," and specify the ",(0,t.jsx)(n.code,{children:"majorVersion"}),", the resulting ",(0,t.jsx)(n.code,{children:"WfRun"})," will be a member of the ",(0,t.jsx)(n.code,{children:"WfSpec"})," with the provided name and ",(0,t.jsx)(n.code,{children:"majorVersion"})," and the ",(0,t.jsx)(n.strong,{children:"latest"})," ",(0,t.jsx)(n.code,{children:"revision"})," that is still compatible with that ",(0,t.jsx)(n.code,{children:"majorVersion"}),"."]}),"\n",(0,t.jsxs)(a.A,{children:[(0,t.jsx)(l.A,{value:"java",label:"Java",default:!0,children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-java",children:'LittleHorseBlockingStub client = ...;\n\nWfRun result = client.runWf(RunWfRequest.newBuilder()\n .setWfSpecName("some-wf-spec")\n .setMajorVersion(2)\n .build());\n\n'})})}),(0,t.jsx)(l.A,{value:"go",label:"Go",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-go",children:'config := littlehorse.NewConfigFromEnv()\nclient, _ := config.GetGrpcClient()\n\nvar version int32\nversion = 2\n\nresult, err := (*client).RunWf(context.Background(), &lhproto.RunWfRequest{\n WfSpecName: "some-wf-spec",\n MajorVersion: &version,\n})\n'})})}),(0,t.jsx)(l.A,{value:"python",label:"Python",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-python",children:'config = LHConfig()\nstub = config.stub()\nstub.RunWf(RunWfRequest(wf_spec_name="some-wf-spec", majorVersion=2))\n'})})})]}),"\n",(0,t.jsx)(n.h2,{id:"pinning-the-revision",children:"Pinning the Revision"}),"\n",(0,t.jsxs)(n.p,{children:["You can also pin the exact ",(0,t.jsx)(n.code,{children:"revision"})," as follows, thus guaranteeing the exact ",(0,t.jsx)(n.code,{children:"WfSpec"})," that is run:"]}),"\n",(0,t.jsxs)(a.A,{children:[(0,t.jsx)(l.A,{value:"java",label:"Java",default:!0,children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-java",children:'LittleHorseBlockingStub client = ...;\n\nWfRun result = client.runWf(RunWfRequest.newBuilder()\n .setWfSpecName("some-wf-spec")\n .setMajorVersion(2)\n .setRevision(1)\n .build());\n\n'})})}),(0,t.jsx)(l.A,{value:"go",label:"Go",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-go",children:'config := littlehorse.NewConfigFromEnv()\nclient, _ := config.GetGrpcClient()\n\nvar version int32\nversion = 2\nvar revision int32\nrevision = 1\n\nresult, err := (*client).RunWf(context.Background(), &lhproto.RunWfRequest{\n WfSpecName: "some-wf-spec",\n MajorVersion: &version,\n Revision: &revision,\n})\n'})})}),(0,t.jsx)(l.A,{value:"python",label:"Python",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-python",children:'config = LHConfig()\nstub = config.stub()\nstub.RunWf(RunWfRequest(wf_spec_name="some-wf-spec", majorVersion=2, revision=1))\n'})})})]}),"\n",(0,t.jsx)(n.h2,{id:"passing-the-id",children:"Passing the ID"}),"\n",(0,t.jsxs)(n.p,{children:["You can pass the ",(0,t.jsx)(n.code,{children:"wfRunId"})," in the ",(0,t.jsx)(n.code,{children:"RunWfRequest"})," to pre-specify the ID of a ",(0,t.jsx)(n.code,{children:"WfRun"}),". ",(0,t.jsx)(n.strong,{children:"We recommend you always do this as a best practice"})," because:"]}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["This makes your requests idempotent and safe to retry, because LittleHorse guarantees that only one ",(0,t.jsx)(n.code,{children:"WfRun"})," can exist with a given ID."]}),"\n",(0,t.jsxs)(n.li,{children:["Specifying an ID makes it easier to correlate ",(0,t.jsx)(n.code,{children:"ExternalEvent"}),"s or records in a database."]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["To do so, just set the ",(0,t.jsx)(n.code,{children:"Id"})," field in the ",(0,t.jsx)(n.code,{children:"RunWfRequest"})," proto."]}),"\n",(0,t.jsxs)(a.A,{children:[(0,t.jsx)(l.A,{value:"java",label:"Java",default:!0,children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-java",children:'LittleHorseBlockingStub client = ...;\n\nWfRun result = client.runWf(RunWfRequest.newBuilder()\n .setWfSpecName("some-wf-spec")\n .setId("my-wfrun-id")\n .build());\n\n'})})}),(0,t.jsx)(l.A,{value:"go",label:"Go",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-go",children:'config := littlehorse.NewConfigFromEnv()\nclient, _ := config.GetGrpcClient()\n\nvar wfRunId string\nwfRunId = "my-wfrun-id"\n\nresult, err := (*client).RunWf(context.Background(), &lhproto.RunWfRequest{\n WfSpecName: "some-wf-spec",\n Id: &wfRunId,\n})\n'})})}),(0,t.jsx)(l.A,{value:"python",label:"Python",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-python",children:'config = LHConfig()\nstub = config.stub()\nstub.RunWf(RunWfRequest(wf_spec_name="some-wf-spec", id="my-wfrun-id"))\n'})})})]}),"\n",(0,t.jsx)(n.h2,{id:"parameters",children:"Parameters"}),"\n",(0,t.jsxs)(n.p,{children:["You can pass variables into a ",(0,t.jsx)(n.code,{children:"WfRun"}),". To do so, set the ",(0,t.jsx)(n.code,{children:"variables"})," field on the ",(0,t.jsx)(n.code,{children:"RunWfRequest"}),"."]}),"\n",(0,t.jsxs)(a.A,{children:[(0,t.jsxs)(l.A,{value:"java",label:"Java",default:!0,children:[(0,t.jsxs)(n.p,{children:["In Java, the ",(0,t.jsx)(n.code,{children:"LHLibUtil#objToVarVal"})," method is a useful convenience function to convert any object into a ",(0,t.jsx)(n.code,{children:"VariableValue"}),"."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-java",children:'LittleHorseBlockingStub client = ...;\n\nWfRun result = client.runWf(RunWfRequest.newBuilder()\n .setWfSpecName("some-wf-spec")\n .putVariables("my-int-var", LHLibUtil.objToVarVal(1234))\n .putVariables("my-str-var", LHLibUtil.objToVarVal("asdf"))\n .build());\n'})})]}),(0,t.jsxs)(l.A,{value:"go",label:"Go",children:[(0,t.jsxs)(n.p,{children:["The Go SDK has a useful ",(0,t.jsx)(n.code,{children:"littlehorse.InterfaceToVarVal()"})," function which converts an arbitrary Go interface into a ",(0,t.jsx)(n.code,{children:"VariableValue"}),"."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-go",children:'config := littlehorse.NewConfigFromEnv()\nclient, _ := config.GetGrpcClient()\n\nvar wfRunId string\nwfRunId = "my-wfrun-id"\n\nstringVar, err := littlehorse.InterfaceToVarVal("some-string")\nintVar, err := littlehorse.InterfaceToVarVal(1234)\n\nresult, err := (*client).RunWf(context.Background(), &lhproto.RunWfRequest{\n\tWfSpecName: "some-wf-spec",\n\tVariables: map[string]*lhproto.VariableValue{\n\t\t"my-string-var": stringVar,\n\t\t"my-int-var": intVar,\n\t},\n})\n'})})]}),(0,t.jsxs)(l.A,{value:"python",label:"Python",children:[(0,t.jsxs)(n.p,{children:["In python you can use ",(0,t.jsx)(n.code,{children:"littlehorse.to_variable_value()"})," utility."]}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-python",children:'config = LHConfig()\nstub = config.stub()\nrequest = RunWfRequest(\n wf_spec_name="some-wf-spec",\n id="my-wfrun-id",\n variables={\n "my-string-var": littlehorse.to_variable_value("ABCD"),\n "my-int-var": littlehorse.to_variable_value(1234),\n },\n)\nstub.RunWf(request)\n'})})]})]}),"\n",(0,t.jsx)(n.h2,{id:"scheduling",children:"Scheduling"}),"\n",(0,t.jsxs)(n.p,{children:["You can schedule ",(0,t.jsx)(n.code,{children:"WfRun"}),"s using Cron expressions."]}),"\n",(0,t.jsxs)(a.A,{children:[(0,t.jsx)(l.A,{value:"java",label:"Java",default:!0,children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-java",children:'LittleHorseBlockingStub client = ...;\n\nScheduledWfRun result = client.scheduleWf(ScheduleWfRequest.newBuilder()\n .setWfSpecName("some-wf-spec")\n .setMajorVersion(0)\n .setRevision(2)\n .setCronExpression("5 4 * * *")\n .putVariables("my-int-var", LHLibUtil.objToVarVal(1234))\n .build());\n'})})}),(0,t.jsx)(l.A,{value:"go",label:"Go",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-go",children:'config := littlehorse.NewConfigFromEnv()\nclient, _ := config.GetGrpcClient()\n\nvar wfRunId string\nwfRunId = "my-wfrun-id"\n\nstringVar, err := littlehorse.InterfaceToVarVal("some-string")\nintVar, err := littlehorse.InterfaceToVarVal(1234)\n\nresult, err := (*client).ScheduleWf(context.Background(), &lhproto.ScheduleWfRequest{\n\tWfSpecName: "some-wf-spec",\n\tCronExpression: "5 4 * * *",\n\tVariables: map[string]*lhproto.VariableValue{\n\t\t"my-string-var": stringVar,\n\t},\n})\n'})})}),(0,t.jsx)(l.A,{value:"python",label:"Python",children:(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-python",children:'config = LHConfig()\nstub = config.stub()\nrequest = ScheduleWfRequest(\n wf_spec_name="some-wf-spec",\n cron_expression="5 4 * * *",\n variables={\n "name": littlehorse.to_variable_value("bob"),\n },\n)\nstub.RunWf(request)\n'})})})]}),"\n",(0,t.jsxs)(n.p,{children:["Note that you are not required to pick a ",(0,t.jsx)(n.code,{children:"WfSpec"})," version; if you don't, the most current version will be used for every run."]})]})}function f(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(h,{...e})}):h(e)}},9365:(e,n,r)=>{r.d(n,{A:()=>l});r(6540);var t=r(8215);const s={tabItem:"tabItem_Ymn6"};var a=r(4848);function l(e){let{children:n,hidden:r,className:l}=e;return(0,a.jsx)("div",{role:"tabpanel",className:(0,t.A)(s.tabItem,l),hidden:r,children:n})}},1470:(e,n,r)=>{r.d(n,{A:()=>R});var t=r(6540),s=r(8215),a=r(3104),l=r(6347),i=r(205),o=r(7485),c=r(1682),u=r(679);function d(e){return t.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,t.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: BadWfSpec
",id:"wfspec",level:3},{value:"WfRun
",id:"wfrun",level:3},{value:"Specific WfSpec
Version",id:"specific-wfspec-version",level:4},{value:"By wfSpecName
and status
",id:"by-wfspecname-and-status",level:4},{value:"By wfSpecName
",id:"by-wfspecname",level:4},{value:"By Time",id:"by-time",level:4},{value:"NodeRun
",id:"noderun",level:3},{value:"TaskDef
and ExternalEventDef
",id:"taskdef-and-externaleventdef",level:3},{value:"TaskRun
",id:"taskrun",level:3},{value:"UserTaskRun
",id:"usertaskrun",level:3},{value:"UserTaskDef
",id:"usertaskdef",level:3},{value:"ExternalEvent
",id:"externalevent",level:3},{value:"Variable
",id:"variable",level:3},{value:"Metrics",id:"metrics",level:3},{value:"Manage WfRun
s",id:"manage-wfruns",level:2},{value:"Run a WfRun
",id:"run-a-wfrun",level:3},{value:"Stop and Resume a WfRun
",id:"stop-and-resume-a-wfrun",level:3},{value:"Post an ExternalEvent
",id:"post-an-externalevent",level:3}];function a(e){const n={code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",p:"p",pre:"pre",ul:"ul",...(0,d.R)(),...e.components};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(n.header,{children:(0,c.jsx)(n.h1,{id:"littlehorse-cli",children:"LittleHorse CLI"})}),"\n",(0,c.jsxs)(n.p,{children:[(0,c.jsx)(n.code,{children:"lhctl"})," is the LittleHorse CLI. It allows you to manage metadata in your system, observe and analyze your ",(0,c.jsx)(n.code,{children:"WfRun"}),"s, and also perform rudimentary actions such as running a ",(0,c.jsx)(n.code,{children:"WfRun"}),"."]}),"\n",(0,c.jsx)(n.h2,{id:"create-metadata",children:"Create Metadata"}),"\n",(0,c.jsxs)(n.p,{children:["The ",(0,c.jsx)(n.code,{children:"lhctl deploy"})," command allows you to create metadata such as ",(0,c.jsx)(n.code,{children:"WfSpec"}),", ",(0,c.jsx)(n.code,{children:"TaskDef"}),", ",(0,c.jsx)(n.code,{children:"UserTaskDef"}),", and ",(0,c.jsx)(n.code,{children:"ExternalEventDef"}),"."]}),"\n",(0,c.jsx)(n.p,{children:"The syntax of the command is as follows:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl deploy {wfSpec,taskDef,externalEventDef} WfSpec
",id:"wfspec",level:3},{value:"WfRun
",id:"wfrun",level:3},{value:"Specific WfSpec
Version",id:"specific-wfspec-version",level:4},{value:"By wfSpecName
and status
",id:"by-wfspecname-and-status",level:4},{value:"By wfSpecName
",id:"by-wfspecname",level:4},{value:"By Time",id:"by-time",level:4},{value:"NodeRun
",id:"noderun",level:3},{value:"TaskDef
and ExternalEventDef
",id:"taskdef-and-externaleventdef",level:3},{value:"TaskRun
",id:"taskrun",level:3},{value:"UserTaskRun
",id:"usertaskrun",level:3},{value:"UserTaskDef
",id:"usertaskdef",level:3},{value:"ExternalEvent
",id:"externalevent",level:3},{value:"Variable
",id:"variable",level:3},{value:"Metrics",id:"metrics",level:3},{value:"Manage WfRun
s",id:"manage-wfruns",level:2},{value:"Run a WfRun
",id:"run-a-wfrun",level:3},{value:"Schedule a WfRun
s",id:"schedule-a-wfruns",level:3},{value:"Stop and Resume a WfRun
",id:"stop-and-resume-a-wfrun",level:3},{value:"Post an ExternalEvent
",id:"post-an-externalevent",level:3}];function a(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",p:"p",pre:"pre",ul:"ul",...(0,d.R)(),...e.components};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(n.header,{children:(0,c.jsx)(n.h1,{id:"littlehorse-cli",children:"LittleHorse CLI"})}),"\n",(0,c.jsxs)(n.p,{children:[(0,c.jsx)(n.code,{children:"lhctl"})," is the LittleHorse CLI. It allows you to manage metadata in your system, observe and analyze your ",(0,c.jsx)(n.code,{children:"WfRun"}),"s, and also perform rudimentary actions such as running a ",(0,c.jsx)(n.code,{children:"WfRun"}),"."]}),"\n",(0,c.jsx)(n.h2,{id:"create-metadata",children:"Create Metadata"}),"\n",(0,c.jsxs)(n.p,{children:["The ",(0,c.jsx)(n.code,{children:"lhctl deploy"})," command allows you to create metadata such as ",(0,c.jsx)(n.code,{children:"WfSpec"}),", ",(0,c.jsx)(n.code,{children:"TaskDef"}),", ",(0,c.jsx)(n.code,{children:"UserTaskDef"}),", and ",(0,c.jsx)(n.code,{children:"ExternalEventDef"}),"."]}),"\n",(0,c.jsx)(n.p,{children:"The syntax of the command is as follows:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl deploy {wfSpec,taskDef,externalEventDef} WfRunVariable
",id:"defining-a-wfrunvariable",level:2},{value:"Searchable and Required Variables",id:"searchable-and-required-variables",level:3},{value:"Defining Variables",id:"defining-variables",level:3},{value:"Executing a TASK
Node",id:"executing-a-task-node",level:2},{value:"Task Input Variables",id:"task-input-variables",level:3},{value:"Setting Retention Hours",id:"setting-retention-hours",level:3}];function u(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,t.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"basics",children:"Basics"})}),"\n","\n",(0,r.jsxs)(n.p,{children:["To develop a ",(0,r.jsx)(n.code,{children:"WfSpec"})," in LittleHorse, you can use the ",(0,r.jsx)(n.code,{children:"Workflow"})," struct or object in our SDK's. Generally, the ",(0,r.jsx)(n.code,{children:"Workflow"})," entity constructor requires two arguments:"]}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["The name of the ",(0,r.jsx)(n.code,{children:"WfSpec"})," to create."]}),"\n",(0,r.jsxs)(n.li,{children:["A ",(0,r.jsx)(n.code,{children:"ThreadFunc"}),", which is function pointer, lambda function, or interface of some sort which contains the logic for the Entrypoint ",(0,r.jsx)(n.code,{children:"ThreadSpec"}),"."]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.code,{children:"Workflow"})," object translates your ",(0,r.jsx)(n.code,{children:"ThreadFunc"})," into a ",(0,r.jsx)(n.code,{children:"WfSpec"}),". As per the ",(0,r.jsx)(n.a,{href:"/docs/developer-guide/grpc/managing-metadata",children:"Metadata Management Documentation"}),", you can easily deploy a ",(0,r.jsx)(n.code,{children:"WfSpec"})," once you've gotten the ",(0,r.jsx)(n.code,{children:"Workflow"})," object."]}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.code,{children:"ThreadFunc"})," takes in one argument: a ",(0,r.jsx)(n.code,{children:"WorkflowThread"}),". Everything you do goes through the ",(0,r.jsx)(n.code,{children:"ThreadFunc"}),". The ",(0,r.jsx)(n.code,{children:"ThreadFunc"})," defines a ",(0,r.jsx)(n.code,{children:"ThreadSpec"}),", and the ",(0,r.jsx)(n.code,{children:"ThreadFunc"})," passed into the ",(0,r.jsx)(n.code,{children:"Workflow"})," object or struct is used to build the Entrypoint Thread."]}),"\n",(0,r.jsx)(n.h2,{id:"quickstart",children:"Quickstart"}),"\n",(0,r.jsxs)(n.p,{children:["Below you can find executable files that define a ",(0,r.jsx)(n.code,{children:"WfSpec"})," with a single step: execute the ",(0,r.jsx)(n.code,{children:"greet"})," TaskDef with the supplied ",(0,r.jsx)(n.code,{children:"first-name"})," variable which comes as input. As a prerequisite, you need to have the ",(0,r.jsx)(n.code,{children:"greet"})," ",(0,r.jsx)(n.code,{children:"TaskDef"})," already registered in your LittleHorse Cluster."]}),"\n",(0,r.jsx)(n.admonition,{type:"tip",children:(0,r.jsxs)(n.p,{children:["If you haven't yet created a ",(0,r.jsx)(n.code,{children:"TaskDef"})," named ",(0,r.jsx)(n.code,{children:"greet"}),", you can do it by following our ",(0,r.jsx)(n.a,{href:"/docs/developer-guide/task-worker-development#quickstart",children:"Task Worker Development Quickstart"}),"."]})}),"\n",(0,r.jsxs)(l.A,{children:[(0,r.jsx)(o.A,{value:"java",label:"Java",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'package io.littlehorse.quickstart;\n\nimport java.io.IOException;\nimport io.littlehorse.sdk.common.config.LHConfig;\nimport io.littlehorse.sdk.wfsdk.WfRunVariable;\nimport io.littlehorse.sdk.wfsdk.Workflow;\nimport io.littlehorse.sdk.wfsdk.WorkflowThread;\nimport io.littlehorse.sdk.common.proto.VariableType;\n\n\npublic class Main {\n public static void main(String[] args) throws IOException, InterruptedException {\n LHConfig config = new LHConfig();\n\n // The `Workflow` object uses the DSL to compile the WfSpec\n Workflow workflowGenerator = Workflow.newWorkflow("my-wf-spec", Main::wfLogic);\n\n // Convenience method to register the `WfSpec` automatically.\n workflowGenerator.registerWfSpec(config.getBlockingStub());\n }\n\n // NOTE: this can be static or non-static.\n static void wfLogic(WorkflowThread wf) {\n // Required input variable.\n WfRunVariable firstName = wf.addVariable("first-name", VariableType.STR).required();\n\n // Execute the `greet` Task and pass in `first-name` as an argument.\n wf.execute("greet", firstName);\n }\n}\n'})})}),(0,r.jsx)(o.A,{value:"go",label:"Go",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'package main\n\nimport (\n\t"context"\n\t"log"\n\n\t"github.com/littlehorse-enterprises/littlehorse/sdk-go/littlehorse"\n\t"github.com/littlehorse-enterprises/littlehorse/sdk-go/lhproto"\n)\n\nfunc wfLogic(wf *littlehorse.WorkflowThread) {\n\tfirstName := wf.AddVariable("first-name", lhproto.VariableType_STR).Required()\n\twf.Execute("greet", firstName)\n}\n\nfunc main() {\n\t// Get a client\n\tconfig := littlehorse.NewConfigFromEnv()\n\tclient, _ := config.GetGrpcClient()\n\n\tworkflowGenerator := littlehorse.NewWorkflow(wfLogic, "my-wfspec")\n\n\trequest, err := workflowGenerator.Compile()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t(*client).PutWfSpec(context.Background(), request)\n}\n'})})}),(0,r.jsx)(o.A,{value:"python",label:"Python",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'from littlehorse.workflow import WorkflowThread, WfRunVariable, Workflow\nfrom littlehorse.config import LHConfig\nfrom littlehorse.model import VariableType, PutWfSpecRequest\n\n\ndef workflow_logic(wf: WorkflowThread) -> None:\n first_name: WfRunVariable = wf.add_variable("first-name", VariableType.STR).required()\n wf.execute("greet", first_name)\n\nif __name__ == \'__main__\':\n config = LHConfig()\n client = config.stub()\n\n workflow_generator = Workflow("my-wfspec", workflow_logic)\n request: PutWfSpecRequest = workflow_generator.compile()\n\n client.PutWfSpec(request)\n'})})})]}),"\n",(0,r.jsxs)(n.p,{children:["At this point, whether you used python, go, or Java for the WfSpec, you should be able to run the ",(0,r.jsx)(n.code,{children:"WfSpec"})," via the following command:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:"lhctl run my-wfspec first-name Obi-Wan\n"})}),"\n",(0,r.jsxs)(n.h2,{id:"defining-a-wfrunvariable",children:["Defining a ",(0,r.jsx)(n.code,{children:"WfRunVariable"})]}),"\n",(0,r.jsxs)(n.p,{children:["A ",(0,r.jsx)(n.code,{children:"ThreadSpec"})," can have ",(0,r.jsx)(n.code,{children:"VariableDef"}),"s, which is similar to declaring a variable in programming. When declaring a ",(0,r.jsx)(n.code,{children:"Variable"})," in LittleHorse, you need to:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Provide the ",(0,r.jsx)(n.code,{children:"name"})," of the ",(0,r.jsx)(n.code,{children:"Variable"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["Specify the ",(0,r.jsx)(n.code,{children:"VariableType"})," or provide a default value from which the type is inferred."]}),"\n"]}),"\n",(0,r.jsx)(n.admonition,{type:"note",children:(0,r.jsxs)(n.p,{children:["A ",(0,r.jsx)(n.code,{children:"Variable"}),"'s name must be a valid hostname, meaning lowercase alphanumeric characters separated by a ",(0,r.jsx)(n.code,{children:"-"}),"."]})}),"\n",(0,r.jsx)(n.p,{children:"Recall the valid types of Variables:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"STR"})}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"INT"})," (64-bit integer, represented as a ",(0,r.jsx)(n.code,{children:"long"})," in Java and ",(0,r.jsx)(n.code,{children:"int64"})," in Go)"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"DOUBLE"})," (64-bit floating point, ",(0,r.jsx)(n.code,{children:"double"})," in Java and ",(0,r.jsx)(n.code,{children:"float64"})," in Go)"]}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"BOOL"})}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"JSON_OBJ"})," (a dumped JSON String)"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"JSON_ARR"})," (a dumped JSON String)"]}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"BYTES"})}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"searchable-and-required-variables",children:"Searchable and Required Variables"}),"\n",(0,r.jsxs)(n.p,{children:["It is often desirable to be able to search for a ",(0,r.jsx)(n.code,{children:"WfRun"})," based on the value of the ",(0,r.jsx)(n.code,{children:"Variable"}),"s inside it. For example, how can I find the ",(0,r.jsx)(n.code,{children:"WfRun"})," that has ",(0,r.jsx)(n.code,{children:"email=foo@bar.com"}),"? You can do that via the ",(0,r.jsx)(n.code,{children:"rpc SearchVariable"})]}),"\n",(0,r.jsxs)(n.p,{children:["In order to do that, however, you must first put an index on your ",(0,r.jsx)(n.code,{children:"Variable"})," by using the ",(0,r.jsx)(n.code,{children:".searchable()"})," method."]}),"\n",(0,r.jsxs)(n.p,{children:["Additionally, you can use the ",(0,r.jsx)(n.code,{children:".required()"})," method to make a ",(0,r.jsx)(n.code,{children:"Variable"})," required as input to the ",(0,r.jsx)(n.code,{children:"ThreadRun"}),". If you do this on your Entrypoint ",(0,r.jsx)(n.code,{children:"ThreadRun"}),", then the ",(0,r.jsx)(n.code,{children:"RunWfRequest"})," must specify a value for that ",(0,r.jsx)(n.code,{children:"Variable"}),"."]}),"\n",(0,r.jsx)(n.admonition,{type:"note",children:(0,r.jsxs)(n.p,{children:["Putting an Index on a ",(0,r.jsx)(n.code,{children:"Variable"})," or making the ",(0,r.jsx)(n.code,{children:"Variable"}),' "Required" means that the ',(0,r.jsx)(n.code,{children:"Variable"})," becomes part of the public API of the ",(0,r.jsx)(n.code,{children:"WfSpec"}),'. That means you will increment a "major version" upon adding or removing an Index on a ',(0,r.jsx)(n.code,{children:"Variable"}),". For more info, check out our docs on ",(0,r.jsx)(n.a,{href:"/docs/concepts/advanced/wfspec-versioning",children:"WfSpec Versioning"}),"."]})}),"\n",(0,r.jsx)(n.h3,{id:"defining-variables",children:"Defining Variables"}),"\n",(0,r.jsxs)(l.A,{children:[(0,r.jsxs)(o.A,{value:"java",label:"Java",default:!0,children:[(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'import io.littlehorse.sdk.wfsdk.WorkflowThread;\nimport io.littlehorse.sdk.wfsdk.WfRunVariable;\n\npublic void threadFunction(WorkflowThread thread) {\n WfRunVariable myVar = thread.addVariable("my-variable", VariableTypePb.STR);\n}\n'})}),(0,r.jsxs)(n.p,{children:["The first argument is the name of the variable; the second is the type. Alternatively, you can pass in a default value to the ",(0,r.jsx)(n.code,{children:"Variable"}),". The following initializes ",(0,r.jsx)(n.code,{children:"myVar"})," to ",(0,r.jsx)(n.code,{children:'"Hello, there!"'}),"."]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'public void threadFunction(WorkflowThread thread) {\n WfRunVariable myVar = thread.addVariable("my-variable", "Hello, there!");\n}\n'})}),(0,r.jsx)(n.p,{children:"You can set an index on the variable as follows:"}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'public void threadFunction(WorkflowThread thread) {\n WfRunVariable myVar = thread.addVariable("my-variable", "Hello, there!").searchable();\n}\n'})}),(0,r.jsxs)(n.p,{children:["And you can mark the ",(0,r.jsx)(n.code,{children:"Variable"})," as Required as follows:"]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'public void threadFunction(WorkflowThread thread) {\n WfRunVariable myVar = thread.addVariable("my-variable", "Hello, there!").required();\n}\n'})})]}),(0,r.jsxs)(o.A,{value:"go",label:"Go",children:[(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'func myThreadFunc(thread *littlehorse.WorkflowThread) {\n myVar := thread.AddVariable("my-variable", lhproto.VariableTypePb_STR)\n}\n'})}),(0,r.jsxs)(n.p,{children:["You can add do the same and set a default value for the ",(0,r.jsx)(n.code,{children:"Variable"})," as follows:"]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'func myThreadFunc(thread *littlehorse.WorkflowThread) {\n nameVar := thread.AddVariableWithDefault("my-variable", lhproto.VariableType_STR, "Ahsoka Tano")\n}\n'})}),(0,r.jsxs)(n.p,{children:["You can add an index on a ",(0,r.jsx)(n.code,{children:"WfRunVariable"})," to make the variable searchable."]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'func myThreadFunc(thread *littlehorse.WorkflowThread) {\n nameVar := thread.AddVariableWithDefault("my-variable", lhproto.VariableType_STR, "Ahsoka Tano").Searchable()\n}\n'})})]}),(0,r.jsxs)(o.A,{value:"python",label:"Python",default:!0,children:[(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'def thread_function(thread: WorkflowThread) -> None:\n the_name = thread.add_variable("input-name", VariableType.STR)\n'})}),(0,r.jsxs)(n.p,{children:["The first argument is the name of the variable; the second is the type. Alternatively, you can pass in a default value to the ",(0,r.jsx)(n.code,{children:"Variable"}),"."]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'def thread_function(thread: WorkflowThread) -> None:\n the_name = thread.add_variable("input-name", VariableType.STR, "The Mandalorian")\n'})}),(0,r.jsx)(n.p,{children:"You can set an index on the variable as follows:"}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'def thread_function(thread: WorkflowThread) -> None:\n the_name = thread.add_variable("input-name", VariableType.STR).searchable()\n\n # optionally make the variable a Required variable\n the_name.required()\n'})})]})]}),"\n",(0,r.jsxs)(n.h2,{id:"executing-a-task-node",children:["Executing a ",(0,r.jsx)(n.code,{children:"TASK"})," Node"]}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.code,{children:"WorkflowThread::execute()"})," method can be used to execute a Task. It is required that the ",(0,r.jsx)(n.code,{children:"TaskDef"})," is already registered with the LH Server, and that you have a Task Worker that is polling for those tasks."]}),"\n",(0,r.jsx)(n.admonition,{type:"info",children:(0,r.jsxs)(n.p,{children:["It is perfectly acceptable for a ",(0,r.jsx)(n.code,{children:"WfSpec"})," written in one language to execute tasks that are defined and run in other languages."]})}),"\n",(0,r.jsxs)(n.p,{children:["To execute the ",(0,r.jsx)(n.code,{children:"foo"})," task, you simply do the following:"]}),"\n",(0,r.jsxs)(l.A,{children:[(0,r.jsx)(o.A,{value:"java",label:"Java",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'public void myWf(WorkflowThread thread) {\n NodeOutput output = thread.execute("foo");\n}\n'})})}),(0,r.jsx)(o.A,{value:"go",label:"Go",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'func myThreadFunc(thread *littlehorse.WorkflowThread) {\n taskOutput := thread.Execute("foo")\n}\n'})})}),(0,r.jsx)(o.A,{value:"python",label:"Python",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'def thread_function(thread: WorkflowThread) -> None:\n thread.execute("foo")\n'})})})]}),"\n",(0,r.jsx)(n.h3,{id:"task-input-variables",children:"Task Input Variables"}),"\n",(0,r.jsx)(n.p,{children:"You can pass input variables to a Task. Let's say, for example, I have a Python Task Function as follows:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'async def my_task(some_str: str, some_int: int) -> str:\n return f"Inputs were {some_str} and {some_int}"\n'})}),"\n",(0,r.jsxs)(n.p,{children:["The resulting ",(0,r.jsx)(n.code,{children:"TaskDef"})," has two input variables, one of type ",(0,r.jsx)(n.code,{children:"STR"})," and another of type ",(0,r.jsx)(n.code,{children:"INT"}),"."]}),"\n",(0,r.jsxs)(n.p,{children:["You can hard-code the input variables in a call to that ",(0,r.jsx)(n.code,{children:"TaskDef"})," as follows:"]}),"\n",(0,r.jsxs)(l.A,{children:[(0,r.jsx)(o.A,{value:"java",label:"Java",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'String inputStrVal = "input string value!";\nint inputIntVal = 54321;\nthread.execute("foo", inputStrVal, inputIntVal);\n'})})}),(0,r.jsx)(o.A,{value:"go",label:"Go",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'inputStrVal := "input string value!"\ninputIntVal := 54321\nthread.Execute("foo", inputStrVal, inputIntVal)\n'})})}),(0,r.jsx)(o.A,{value:"python",label:"Python",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'str_val = "input string value!"\nint_val = 54321\nthread.execute("foo", str_val, int_val)\n'})})})]}),"\n",(0,r.jsxs)(n.p,{children:["Alternatively, if you have a ",(0,r.jsx)(n.code,{children:"WfRunVariable"}),", you can use it as input:"]}),"\n",(0,r.jsxs)(l.A,{children:[(0,r.jsx)(o.A,{value:"java",label:"Java",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'public void threadFunction(WorkflowThread thread) {\n WfRunVariable myStr = thread.addVariable("my-str", VariableType.STR);\n WfRunVariable myInt = thread.addVariable("my-int", VariableType.INT);\n\n thread.execute("foo", myStr, myInt);\n}\n'})})}),(0,r.jsx)(o.A,{value:"go",label:"Go",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'func threadFunction(thread *littlehorse.WorkflowThread) {\n myStr := thread.AddVariable("my-str", lhproto.VariableType_STR)\n myInt := thread.AddVariable("my-int", lhproto.VariableType_INT)\n\n thread.Execute("foo", myStr, myInt)\n}\n'})})}),(0,r.jsx)(o.A,{value:"python",label:"Python",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'def thread_function(thread: WorkflowThread) -> None:\n my_str = thread.add_variable("my-str", VariableType.STR)\n my_int = thread.add_variable("my-int", VariableType.INT)\n thread.execute("foo", my_str, my_int)\n'})})})]}),"\n",(0,r.jsx)(n.h3,{id:"setting-retention-hours",children:"Setting Retention Hours"}),"\n",(0,r.jsxs)(n.p,{children:["You can use the ",(0,r.jsx)(n.code,{children:"Workflow::withRetentionHours()"})," method to set how long a ",(0,r.jsx)(n.code,{children:"WfRun"})," should stay on the system. Remember that our default system hosts ",(0,r.jsx)(n.code,{children:"WfRun"}),"s for 168 hours (7 days). For example, if the ",(0,r.jsx)(n.code,{children:"WfSpec"})," has a retention period of 2 hours, a ",(0,r.jsx)(n.code,{children:"WfRun"})," will be deleted 2 hours after it is ",(0,r.jsx)(n.code,{children:"COMPLETED"})," or ",(0,r.jsx)(n.code,{children:"ERROR"}),":"]}),"\n",(0,r.jsxs)(l.A,{children:[(0,r.jsx)(o.A,{value:"java",label:"Java",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:"Workflow wf = new WorkflowImpl(...)\nwf.withRetentionHours(23);\nwf.register(...);\n"})})}),(0,r.jsx)(o.A,{value:"go",label:"Go",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'client := ...;\nwf := littlehorse.NewWorkflow(basic.MyWorkflow, "my-workflow")\nputWf, _ := wf.Compile()\n\nhours := int32(23)\nputWf.WithRetentionHours(&hours)\nresp, err := client.PutWfSpec(putWf)\n'})})}),(0,r.jsx)(o.A,{value:"python",label:"Python",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'wf = Workflow("my-wf", thread_function)\nwf.retention_hours = 23\nlittlehorse.create_workflow_spec(wf, config)\n'})})})]})]})}function f(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(u,{...e})}):u(e)}},9365:(e,n,a)=>{a.d(n,{A:()=>o});a(6540);var r=a(8215);const t={tabItem:"tabItem_Ymn6"};var l=a(4848);function o(e){let{children:n,hidden:a,className:o}=e;return(0,l.jsx)("div",{role:"tabpanel",className:(0,r.A)(t.tabItem,o),hidden:a,children:n})}},1470:(e,n,a)=>{a.d(n,{A:()=>w});var r=a(6540),t=a(8215),l=a(3104),o=a(6347),i=a(205),s=a(7485),d=a(1682),c=a(679);function h(e){return r.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,r.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad WfRunVariable
",id:"defining-a-wfrunvariable",level:2},{value:"Searchable and Required Variables",id:"searchable-and-required-variables",level:3},{value:"Defining Variables",id:"defining-variables",level:3},{value:"Masked Variables",id:"masked-variables",level:3},{value:"Executing a TASK
Node",id:"executing-a-task-node",level:2},{value:"Task Input Variables",id:"task-input-variables",level:3},{value:"Setting Retention Hours",id:"setting-retention-hours",level:3}];function u(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,t.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"basics",children:"Basics"})}),"\n","\n",(0,r.jsxs)(n.p,{children:["To develop a ",(0,r.jsx)(n.code,{children:"WfSpec"})," in LittleHorse, you can use the ",(0,r.jsx)(n.code,{children:"Workflow"})," struct or object in our SDK's. Generally, the ",(0,r.jsx)(n.code,{children:"Workflow"})," entity constructor requires two arguments:"]}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["The name of the ",(0,r.jsx)(n.code,{children:"WfSpec"})," to create."]}),"\n",(0,r.jsxs)(n.li,{children:["A ",(0,r.jsx)(n.code,{children:"ThreadFunc"}),", which is function pointer, lambda function, or interface of some sort which contains the logic for the Entrypoint ",(0,r.jsx)(n.code,{children:"ThreadSpec"}),"."]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.code,{children:"Workflow"})," object translates your ",(0,r.jsx)(n.code,{children:"ThreadFunc"})," into a ",(0,r.jsx)(n.code,{children:"WfSpec"}),". As per the ",(0,r.jsx)(n.a,{href:"/docs/developer-guide/grpc/managing-metadata",children:"Metadata Management Documentation"}),", you can easily deploy a ",(0,r.jsx)(n.code,{children:"WfSpec"})," once you've gotten the ",(0,r.jsx)(n.code,{children:"Workflow"})," object."]}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.code,{children:"ThreadFunc"})," takes in one argument: a ",(0,r.jsx)(n.code,{children:"WorkflowThread"}),". Everything you do goes through the ",(0,r.jsx)(n.code,{children:"ThreadFunc"}),". The ",(0,r.jsx)(n.code,{children:"ThreadFunc"})," defines a ",(0,r.jsx)(n.code,{children:"ThreadSpec"}),", and the ",(0,r.jsx)(n.code,{children:"ThreadFunc"})," passed into the ",(0,r.jsx)(n.code,{children:"Workflow"})," object or struct is used to build the Entrypoint Thread."]}),"\n",(0,r.jsx)(n.h2,{id:"quickstart",children:"Quickstart"}),"\n",(0,r.jsxs)(n.p,{children:["Below you can find executable files that define a ",(0,r.jsx)(n.code,{children:"WfSpec"})," with a single step: execute the ",(0,r.jsx)(n.code,{children:"greet"})," TaskDef with the supplied ",(0,r.jsx)(n.code,{children:"first-name"})," variable which comes as input. As a prerequisite, you need to have the ",(0,r.jsx)(n.code,{children:"greet"})," ",(0,r.jsx)(n.code,{children:"TaskDef"})," already registered in your LittleHorse Cluster."]}),"\n",(0,r.jsx)(n.admonition,{type:"tip",children:(0,r.jsxs)(n.p,{children:["If you haven't yet created a ",(0,r.jsx)(n.code,{children:"TaskDef"})," named ",(0,r.jsx)(n.code,{children:"greet"}),", you can do it by following our ",(0,r.jsx)(n.a,{href:"/docs/developer-guide/task-worker-development#quickstart",children:"Task Worker Development Quickstart"}),"."]})}),"\n",(0,r.jsxs)(l.A,{children:[(0,r.jsx)(i.A,{value:"java",label:"Java",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'package io.littlehorse.quickstart;\n\nimport java.io.IOException;\nimport io.littlehorse.sdk.common.config.LHConfig;\nimport io.littlehorse.sdk.wfsdk.WfRunVariable;\nimport io.littlehorse.sdk.wfsdk.Workflow;\nimport io.littlehorse.sdk.wfsdk.WorkflowThread;\nimport io.littlehorse.sdk.common.proto.VariableType;\n\n\npublic class Main {\n public static void main(String[] args) throws IOException, InterruptedException {\n LHConfig config = new LHConfig();\n\n // The `Workflow` object uses the DSL to compile the WfSpec\n Workflow workflowGenerator = Workflow.newWorkflow("my-wf-spec", Main::wfLogic);\n\n // Convenience method to register the `WfSpec` automatically.\n workflowGenerator.registerWfSpec(config.getBlockingStub());\n }\n\n // NOTE: this can be static or non-static.\n static void wfLogic(WorkflowThread wf) {\n // Required input variable.\n WfRunVariable firstName = wf.addVariable("first-name", VariableType.STR).required();\n\n // Execute the `greet` Task and pass in `first-name` as an argument.\n wf.execute("greet", firstName);\n }\n}\n'})})}),(0,r.jsx)(i.A,{value:"go",label:"Go",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'package main\n\nimport (\n\t"context"\n\t"log"\n\n\t"github.com/littlehorse-enterprises/littlehorse/sdk-go/littlehorse"\n\t"github.com/littlehorse-enterprises/littlehorse/sdk-go/lhproto"\n)\n\nfunc wfLogic(wf *littlehorse.WorkflowThread) {\n\tfirstName := wf.AddVariable("first-name", lhproto.VariableType_STR).Required()\n\twf.Execute("greet", firstName)\n}\n\nfunc main() {\n\t// Get a client\n\tconfig := littlehorse.NewConfigFromEnv()\n\tclient, _ := config.GetGrpcClient()\n\n\tworkflowGenerator := littlehorse.NewWorkflow(wfLogic, "my-wfspec")\n\n\trequest, err := workflowGenerator.Compile()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t(*client).PutWfSpec(context.Background(), request)\n}\n'})})}),(0,r.jsx)(i.A,{value:"python",label:"Python",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'from littlehorse.workflow import WorkflowThread, WfRunVariable, Workflow\nfrom littlehorse.config import LHConfig\nfrom littlehorse.model import VariableType, PutWfSpecRequest\n\n\ndef workflow_logic(wf: WorkflowThread) -> None:\n first_name: WfRunVariable = wf.add_variable("first-name", VariableType.STR).required()\n wf.execute("greet", first_name)\n\nif __name__ == \'__main__\':\n config = LHConfig()\n client = config.stub()\n\n workflow_generator = Workflow("my-wfspec", workflow_logic)\n request: PutWfSpecRequest = workflow_generator.compile()\n\n client.PutWfSpec(request)\n'})})})]}),"\n",(0,r.jsxs)(n.p,{children:["At this point, whether you used python, go, or Java for the WfSpec, you should be able to run the ",(0,r.jsx)(n.code,{children:"WfSpec"})," via the following command:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:"lhctl run my-wfspec first-name Obi-Wan\n"})}),"\n",(0,r.jsxs)(n.h2,{id:"defining-a-wfrunvariable",children:["Defining a ",(0,r.jsx)(n.code,{children:"WfRunVariable"})]}),"\n",(0,r.jsxs)(n.p,{children:["A ",(0,r.jsx)(n.code,{children:"ThreadSpec"})," can have ",(0,r.jsx)(n.code,{children:"VariableDef"}),"s, which is similar to declaring a variable in programming. When declaring a ",(0,r.jsx)(n.code,{children:"Variable"})," in LittleHorse, you need to:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Provide the ",(0,r.jsx)(n.code,{children:"name"})," of the ",(0,r.jsx)(n.code,{children:"Variable"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["Specify the ",(0,r.jsx)(n.code,{children:"VariableType"})," or provide a default value from which the type is inferred."]}),"\n"]}),"\n",(0,r.jsx)(n.admonition,{type:"note",children:(0,r.jsxs)(n.p,{children:["A ",(0,r.jsx)(n.code,{children:"Variable"}),"'s name must be a valid hostname, meaning lowercase alphanumeric characters separated by a ",(0,r.jsx)(n.code,{children:"-"}),"."]})}),"\n",(0,r.jsx)(n.p,{children:"Recall the valid types of Variables:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"STR"})}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"INT"})," (64-bit integer, represented as a ",(0,r.jsx)(n.code,{children:"long"})," in Java and ",(0,r.jsx)(n.code,{children:"int64"})," in Go)"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"DOUBLE"})," (64-bit floating point, ",(0,r.jsx)(n.code,{children:"double"})," in Java and ",(0,r.jsx)(n.code,{children:"float64"})," in Go)"]}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"BOOL"})}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"JSON_OBJ"})," (a dumped JSON String)"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"JSON_ARR"})," (a dumped JSON String)"]}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"BYTES"})}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"searchable-and-required-variables",children:"Searchable and Required Variables"}),"\n",(0,r.jsxs)(n.p,{children:["It is often desirable to be able to search for a ",(0,r.jsx)(n.code,{children:"WfRun"})," based on the value of the ",(0,r.jsx)(n.code,{children:"Variable"}),"s inside it. For example, how can I find the ",(0,r.jsx)(n.code,{children:"WfRun"})," that has ",(0,r.jsx)(n.code,{children:"email=foo@bar.com"}),"? You can do that via the ",(0,r.jsx)(n.code,{children:"rpc SearchVariable"})]}),"\n",(0,r.jsxs)(n.p,{children:["In order to do that, however, you must first put an index on your ",(0,r.jsx)(n.code,{children:"Variable"})," by using the ",(0,r.jsx)(n.code,{children:".searchable()"})," method."]}),"\n",(0,r.jsxs)(n.p,{children:["Additionally, you can use the ",(0,r.jsx)(n.code,{children:".required()"})," method to make a ",(0,r.jsx)(n.code,{children:"Variable"})," required as input to the ",(0,r.jsx)(n.code,{children:"ThreadRun"}),". If you do this on your Entrypoint ",(0,r.jsx)(n.code,{children:"ThreadRun"}),", then the ",(0,r.jsx)(n.code,{children:"RunWfRequest"})," must specify a value for that ",(0,r.jsx)(n.code,{children:"Variable"}),"."]}),"\n",(0,r.jsx)(n.admonition,{type:"note",children:(0,r.jsxs)(n.p,{children:["Putting an Index on a ",(0,r.jsx)(n.code,{children:"Variable"})," or making the ",(0,r.jsx)(n.code,{children:"Variable"}),' "Required" means that the ',(0,r.jsx)(n.code,{children:"Variable"})," becomes part of the public API of the ",(0,r.jsx)(n.code,{children:"WfSpec"}),'. That means you will increment a "major version" upon adding or removing an Index on a ',(0,r.jsx)(n.code,{children:"Variable"}),". For more info, check out our docs on ",(0,r.jsx)(n.a,{href:"/docs/concepts/advanced/wfspec-versioning",children:"WfSpec Versioning"}),"."]})}),"\n",(0,r.jsx)(n.h3,{id:"defining-variables",children:"Defining Variables"}),"\n",(0,r.jsxs)(l.A,{children:[(0,r.jsxs)(i.A,{value:"java",label:"Java",default:!0,children:[(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'import io.littlehorse.sdk.wfsdk.WorkflowThread;\nimport io.littlehorse.sdk.wfsdk.WfRunVariable;\n\npublic void threadFunction(WorkflowThread thread) {\n WfRunVariable myVar = thread.addVariable("my-variable", VariableTypePb.STR);\n}\n'})}),(0,r.jsxs)(n.p,{children:["The first argument is the name of the variable; the second is the type. Alternatively, you can pass in a default value to the ",(0,r.jsx)(n.code,{children:"Variable"}),". The following initializes ",(0,r.jsx)(n.code,{children:"myVar"})," to ",(0,r.jsx)(n.code,{children:'"Hello, there!"'}),"."]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'public void threadFunction(WorkflowThread thread) {\n WfRunVariable myVar = thread.addVariable("my-variable", "Hello, there!");\n}\n'})}),(0,r.jsx)(n.p,{children:"You can set an index on the variable as follows:"}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'public void threadFunction(WorkflowThread thread) {\n WfRunVariable myVar = thread.addVariable("my-variable", "Hello, there!").searchable();\n}\n'})}),(0,r.jsxs)(n.p,{children:["And you can mark the ",(0,r.jsx)(n.code,{children:"Variable"})," as Required as follows:"]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'public void threadFunction(WorkflowThread thread) {\n WfRunVariable myVar = thread.addVariable("my-variable", "Hello, there!").required();\n}\n'})})]}),(0,r.jsxs)(i.A,{value:"go",label:"Go",children:[(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'func myThreadFunc(thread *littlehorse.WorkflowThread) {\n myVar := thread.AddVariable("my-variable", lhproto.VariableTypePb_STR)\n}\n'})}),(0,r.jsxs)(n.p,{children:["You can add do the same and set a default value for the ",(0,r.jsx)(n.code,{children:"Variable"})," as follows:"]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'func myThreadFunc(thread *littlehorse.WorkflowThread) {\n nameVar := thread.AddVariableWithDefault("my-variable", lhproto.VariableType_STR, "Ahsoka Tano")\n}\n'})}),(0,r.jsxs)(n.p,{children:["You can add an index on a ",(0,r.jsx)(n.code,{children:"WfRunVariable"})," to make the variable searchable."]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'func myThreadFunc(thread *littlehorse.WorkflowThread) {\n nameVar := thread.AddVariableWithDefault("my-variable", lhproto.VariableType_STR, "Ahsoka Tano").Searchable()\n}\n'})})]}),(0,r.jsxs)(i.A,{value:"python",label:"Python",default:!0,children:[(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'def thread_function(thread: WorkflowThread) -> None:\n the_name = thread.add_variable("input-name", VariableType.STR)\n'})}),(0,r.jsxs)(n.p,{children:["The first argument is the name of the variable; the second is the type. Alternatively, you can pass in a default value to the ",(0,r.jsx)(n.code,{children:"Variable"}),"."]}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'def thread_function(thread: WorkflowThread) -> None:\n the_name = thread.add_variable("input-name", VariableType.STR, "The Mandalorian")\n'})}),(0,r.jsx)(n.p,{children:"You can set an index on the variable as follows:"}),(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'def thread_function(thread: WorkflowThread) -> None:\n the_name = thread.add_variable("input-name", VariableType.STR).searchable()\n\n # optionally make the variable a Required variable\n the_name.required()\n'})})]})]}),"\n",(0,r.jsx)(n.h3,{id:"masked-variables",children:"Masked Variables"}),"\n",(0,r.jsxs)(n.p,{children:["In certain situations, you may need to mask the contents of a particular variable, ensuring that it remains hidden from users.\nYou can achieve this by using masked variables, which will only allow access to the variable's content within your ",(0,r.jsx)(n.code,{children:"WfRun"}),"."]}),"\n",(0,r.jsxs)(l.A,{children:[(0,r.jsx)(i.A,{value:"java",label:"Java",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'public void threadFunction(WorkflowThread thread) {\n WfRunVariable myVar = thread.addVariable("my-masked-variable", VariableTypePb.STR).masked();\n}\n'})})}),(0,r.jsx)(i.A,{value:"go",label:"Go",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'func myThreadFunc(thread *littlehorse.WorkflowThread) {\n myVar := thread.AddVariable("my-masked-variable", lhproto.VariableTypePb_STR)\n myVar.Masked()\n}\n'})})}),(0,r.jsx)(i.A,{value:"python",label:"Python",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'def thread_function(thread: WorkflowThread) -> None:\n the_name = thread.add_variable("input-name", VariableType.STR).masked()\n'})})})]}),"\n",(0,r.jsxs)(n.h2,{id:"executing-a-task-node",children:["Executing a ",(0,r.jsx)(n.code,{children:"TASK"})," Node"]}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.code,{children:"WorkflowThread::execute()"})," method can be used to execute a Task. It is required that the ",(0,r.jsx)(n.code,{children:"TaskDef"})," is already registered with the LH Server, and that you have a Task Worker that is polling for those tasks."]}),"\n",(0,r.jsx)(n.admonition,{type:"info",children:(0,r.jsxs)(n.p,{children:["It is perfectly acceptable for a ",(0,r.jsx)(n.code,{children:"WfSpec"})," written in one language to execute tasks that are defined and run in other languages."]})}),"\n",(0,r.jsxs)(n.p,{children:["To execute the ",(0,r.jsx)(n.code,{children:"foo"})," task, you simply do the following:"]}),"\n",(0,r.jsxs)(l.A,{children:[(0,r.jsx)(i.A,{value:"java",label:"Java",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'public void myWf(WorkflowThread thread) {\n NodeOutput output = thread.execute("foo");\n}\n'})})}),(0,r.jsx)(i.A,{value:"go",label:"Go",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'func myThreadFunc(thread *littlehorse.WorkflowThread) {\n taskOutput := thread.Execute("foo")\n}\n'})})}),(0,r.jsx)(i.A,{value:"python",label:"Python",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'def thread_function(thread: WorkflowThread) -> None:\n thread.execute("foo")\n'})})})]}),"\n",(0,r.jsx)(n.h3,{id:"task-input-variables",children:"Task Input Variables"}),"\n",(0,r.jsx)(n.p,{children:"You can pass input variables to a Task. Let's say, for example, I have a Python Task Function as follows:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'async def my_task(some_str: str, some_int: int) -> str:\n return f"Inputs were {some_str} and {some_int}"\n'})}),"\n",(0,r.jsxs)(n.p,{children:["The resulting ",(0,r.jsx)(n.code,{children:"TaskDef"})," has two input variables, one of type ",(0,r.jsx)(n.code,{children:"STR"})," and another of type ",(0,r.jsx)(n.code,{children:"INT"}),"."]}),"\n",(0,r.jsxs)(n.p,{children:["You can hard-code the input variables in a call to that ",(0,r.jsx)(n.code,{children:"TaskDef"})," as follows:"]}),"\n",(0,r.jsxs)(l.A,{children:[(0,r.jsx)(i.A,{value:"java",label:"Java",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'String inputStrVal = "input string value!";\nint inputIntVal = 54321;\nthread.execute("foo", inputStrVal, inputIntVal);\n'})})}),(0,r.jsx)(i.A,{value:"go",label:"Go",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'inputStrVal := "input string value!"\ninputIntVal := 54321\nthread.Execute("foo", inputStrVal, inputIntVal)\n'})})}),(0,r.jsx)(i.A,{value:"python",label:"Python",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'str_val = "input string value!"\nint_val = 54321\nthread.execute("foo", str_val, int_val)\n'})})})]}),"\n",(0,r.jsxs)(n.p,{children:["Alternatively, if you have a ",(0,r.jsx)(n.code,{children:"WfRunVariable"}),", you can use it as input:"]}),"\n",(0,r.jsxs)(l.A,{children:[(0,r.jsx)(i.A,{value:"java",label:"Java",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:'public void threadFunction(WorkflowThread thread) {\n WfRunVariable myStr = thread.addVariable("my-str", VariableType.STR);\n WfRunVariable myInt = thread.addVariable("my-int", VariableType.INT);\n\n thread.execute("foo", myStr, myInt);\n}\n'})})}),(0,r.jsx)(i.A,{value:"go",label:"Go",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'func threadFunction(thread *littlehorse.WorkflowThread) {\n myStr := thread.AddVariable("my-str", lhproto.VariableType_STR)\n myInt := thread.AddVariable("my-int", lhproto.VariableType_INT)\n\n thread.Execute("foo", myStr, myInt)\n}\n'})})}),(0,r.jsx)(i.A,{value:"python",label:"Python",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'def thread_function(thread: WorkflowThread) -> None:\n my_str = thread.add_variable("my-str", VariableType.STR)\n my_int = thread.add_variable("my-int", VariableType.INT)\n thread.execute("foo", my_str, my_int)\n'})})})]}),"\n",(0,r.jsx)(n.p,{children:"You can also define a input masked variables by adding some metadata to the task argument or return values. Using the\nsame Python example:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'async def my_task(some_str: Annotated[str, LHType(name="some_str", masked=True)], some_int: int) -> str:\n return f"Inputs were {some_str} and {some_int}"\n'})}),"\n",(0,r.jsx)(n.admonition,{type:"note",children:(0,r.jsx)(n.p,{children:"Any type of variable can be masked, not limited to string types."})}),"\n",(0,r.jsx)(n.h3,{id:"setting-retention-hours",children:"Setting Retention Hours"}),"\n",(0,r.jsxs)(n.p,{children:["You can use the ",(0,r.jsx)(n.code,{children:"Workflow::withRetentionHours()"})," method to set how long a ",(0,r.jsx)(n.code,{children:"WfRun"})," should stay on the system. Remember that our default system hosts ",(0,r.jsx)(n.code,{children:"WfRun"}),"s for 168 hours (7 days). For example, if the ",(0,r.jsx)(n.code,{children:"WfSpec"})," has a retention period of 2 hours, a ",(0,r.jsx)(n.code,{children:"WfRun"})," will be deleted 2 hours after it is ",(0,r.jsx)(n.code,{children:"COMPLETED"})," or ",(0,r.jsx)(n.code,{children:"ERROR"}),":"]}),"\n",(0,r.jsxs)(l.A,{children:[(0,r.jsx)(i.A,{value:"java",label:"Java",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:"Workflow wf = new WorkflowImpl(...)\nwf.withRetentionHours(23);\nwf.register(...);\n"})})}),(0,r.jsx)(i.A,{value:"go",label:"Go",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'client := ...;\nwf := littlehorse.NewWorkflow(basic.MyWorkflow, "my-workflow")\nputWf, _ := wf.Compile()\n\nhours := int32(23)\nputWf.WithRetentionHours(&hours)\nresp, err := client.PutWfSpec(putWf)\n'})})}),(0,r.jsx)(i.A,{value:"python",label:"Python",default:!0,children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'wf = Workflow("my-wf", thread_function)\nwf.retention_hours = 23\nlittlehorse.create_workflow_spec(wf, config)\n'})})})]})]})}function f(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(u,{...e})}):u(e)}},9365:(e,n,a)=>{a.d(n,{A:()=>i});a(6540);var r=a(8215);const t={tabItem:"tabItem_Ymn6"};var l=a(4848);function i(e){let{children:n,hidden:a,className:i}=e;return(0,l.jsx)("div",{role:"tabpanel",className:(0,r.A)(t.tabItem,i),hidden:a,children:n})}},1470:(e,n,a)=>{a.d(n,{A:()=>w});var r=a(6540),t=a(8215),l=a(3104),i=a(6347),o=a(205),s=a(7485),d=a(1682),c=a(679);function h(e){return r.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,r.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad LittleHorseBlockingStub client = ...;
WfRun result = client.runWf(RunWfRequest.newBuilder()
.setWfSpecName("some-wf-spec")
.setId("my-wfrun-id")
.build());
config := littlehorse.NewConfigFromEnv()
client, _ := config.GetGrpcClient()
var wfRunId string
wfRunId = "my-wfrun-id"
result, err := (*client).RunWf(context.Background(), &lhproto.RunWfRequest{
WfSpecName: "some-wf-spec",
Id: &wfRunId,
})
config = LHConfig()
stub = config.stub()
stub.RunWf(RunWfRequest(wf_spec_name="some-wf-spec", id="my-wfrun-id"))
You can pass variables into a WfRun
. To do so, set the variables
field on the RunWfRequest
.
In Java, the LHLibUtil#objToVarVal
method is a useful convenience function to convert any object into a VariableValue
.
LittleHorseBlockingStub client = ...;
WfRun result = client.runWf(RunWfRequest.newBuilder()
.setWfSpecName("some-wf-spec")
.putVariables("my-int-var", LHLibUtil.objToVarVal(1234))
.putVariables("my-str-var", LHLibUtil.objToVarVal("asdf"))
.build());
The Go SDK has a useful littlehorse.InterfaceToVarVal()
function which converts an arbitrary Go interface into a VariableValue
.
config := littlehorse.NewConfigFromEnv()
client, _ := config.GetGrpcClient()
var wfRunId string
wfRunId = "my-wfrun-id"
stringVar, err := littlehorse.InterfaceToVarVal("some-string")
intVar, err := littlehorse.InterfaceToVarVal(1234)
result, err := (*client).RunWf(context.Background(), &lhproto.RunWfRequest{
WfSpecName: "some-wf-spec",
Variables: map[string]*lhproto.VariableValue{
"my-string-var": stringVar,
"my-int-var": intVar,
},
})
In python you can use littlehorse.to_variable_value()
utility.
config = LHConfig()
stub = config.stub()
request = RunWfRequest(
wf_spec_name="some-wf-spec",
id="my-wfrun-id",
variables={
"my-string-var": littlehorse.to_variable_value("ABCD"),
"my-int-var": littlehorse.to_variable_value(1234),
},
)
stub.RunWf(request)
In Java, the LHLibUtil#objToVarVal
method is a useful convenience function to convert any object into a VariableValue
.
LittleHorseBlockingStub client = ...;
WfRun result = client.runWf(RunWfRequest.newBuilder()
.setWfSpecName("some-wf-spec")
.putVariables("my-int-var", LHLibUtil.objToVarVal(1234))
.putVariables("my-str-var", LHLibUtil.objToVarVal("asdf"))
.build());
The Go SDK has a useful littlehorse.InterfaceToVarVal()
function which converts an arbitrary Go interface into a VariableValue
.
config := littlehorse.NewConfigFromEnv()
client, _ := config.GetGrpcClient()
var wfRunId string
wfRunId = "my-wfrun-id"
stringVar, err := littlehorse.InterfaceToVarVal("some-string")
intVar, err := littlehorse.InterfaceToVarVal(1234)
result, err := (*client).RunWf(context.Background(), &lhproto.RunWfRequest{
WfSpecName: "some-wf-spec",
Variables: map[string]*lhproto.VariableValue{
"my-string-var": stringVar,
"my-int-var": intVar,
},
})
In python you can use littlehorse.to_variable_value()
utility.
config = LHConfig()
stub = config.stub()
request = RunWfRequest(
wf_spec_name="some-wf-spec",
id="my-wfrun-id",
variables={
"my-string-var": littlehorse.to_variable_value("ABCD"),
"my-int-var": littlehorse.to_variable_value(1234),
},
)
stub.RunWf(request)
You can schedule WfRun
s using Cron expressions.
LittleHorseBlockingStub client = ...;
ScheduledWfRun result = client.scheduleWf(ScheduleWfRequest.newBuilder()
.setWfSpecName("some-wf-spec")
.setMajorVersion(0)
.setRevision(2)
.setCronExpression("5 4 * * *")
.putVariables("my-int-var", LHLibUtil.objToVarVal(1234))
.build());
config := littlehorse.NewConfigFromEnv()
client, _ := config.GetGrpcClient()
var wfRunId string
wfRunId = "my-wfrun-id"
stringVar, err := littlehorse.InterfaceToVarVal("some-string")
intVar, err := littlehorse.InterfaceToVarVal(1234)
result, err := (*client).ScheduleWf(context.Background(), &lhproto.ScheduleWfRequest{
WfSpecName: "some-wf-spec",
CronExpression: "5 4 * * *",
Variables: map[string]*lhproto.VariableValue{
"my-string-var": stringVar,
},
})
config = LHConfig()
stub = config.stub()
request = ScheduleWfRequest(
wf_spec_name="some-wf-spec",
cron_expression="5 4 * * *",
variables={
"name": littlehorse.to_variable_value("bob"),
},
)
stub.RunWf(request)
Note that you are not required to pick a WfSpec
version; if you don't, the most current version will be used for every run.