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: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:r}=e;return(0,t.useMemo)((()=>{const e=n??function(e){return d(e).map((e=>{let{props:{value:n,label:r,attributes:t,default:s}}=e;return{value:n,label:r,attributes:t,default:s}}))}(r);return function(e){const n=(0,c.XI)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,r])}function f(e){let{value:n,tabValues:r}=e;return r.some((e=>e.value===n))}function p(e){let{queryString:n=!1,groupId:r}=e;const s=(0,l.W6)(),a=function(e){let{queryString:n=!1,groupId:r}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!r)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return r??null}({queryString:n,groupId:r});return[(0,o.aZ)(a),(0,t.useCallback)((e=>{if(!a)return;const n=new URLSearchParams(s.location.search);n.set(a,e),s.replace({...s.location,search:n.toString()})}),[a,s])]}function g(e){const{defaultValue:n,queryString:r=!1,groupId:s}=e,a=h(e),[l,o]=(0,t.useState)((()=>function(e){let{defaultValue:n,tabValues:r}=e;if(0===r.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!f({value:n,tabValues:r}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${r.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const t=r.find((e=>e.default))??r[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:a}))),[c,d]=p({queryString:r,groupId:s}),[g,j]=function(e){let{groupId:n}=e;const r=function(e){return e?`docusaurus.tab.${e}`:null}(n),[s,a]=(0,u.Dv)(r);return[s,(0,t.useCallback)((e=>{r&&a.set(e)}),[r,a])]}({groupId:s}),m=(()=>{const e=c??g;return f({value:e,tabValues:a})?e:null})();(0,i.A)((()=>{m&&o(m)}),[m]);return{selectedValue:l,selectValue:(0,t.useCallback)((e=>{if(!f({value:e,tabValues:a}))throw new Error(`Can't select invalid tab value=${e}`);o(e),d(e),j(e)}),[d,j,a]),tabValues:a}}var j=r(2303);const m={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var v=r(4848);function x(e){let{className:n,block:r,selectedValue:t,selectValue:l,tabValues:i}=e;const o=[],{blockElementScrollPositionUntilNextRender:c}=(0,a.a_)(),u=e=>{const n=e.currentTarget,r=o.indexOf(n),s=i[r].value;s!==t&&(c(n),l(s))},d=e=>{let n=null;switch(e.key){case"Enter":u(e);break;case"ArrowRight":{const r=o.indexOf(e.currentTarget)+1;n=o[r]??o[0];break}case"ArrowLeft":{const r=o.indexOf(e.currentTarget)-1;n=o[r]??o[o.length-1];break}}n?.focus()};return(0,v.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":r},n),children:i.map((e=>{let{value:n,label:r,attributes:a}=e;return(0,v.jsx)("li",{role:"tab",tabIndex:t===n?0:-1,"aria-selected":t===n,ref:e=>o.push(e),onKeyDown:d,onClick:u,...a,className:(0,s.A)("tabs__item",m.tabItem,a?.className,{"tabs__item--active":t===n}),children:r??n},n)}))})}function b(e){let{lazy:n,children:r,selectedValue:a}=e;const l=(Array.isArray(r)?r:[r]).filter(Boolean);if(n){const e=l.find((e=>e.props.value===a));return e?(0,t.cloneElement)(e,{className:(0,s.A)("margin-top--md",e.props.className)}):null}return(0,v.jsx)("div",{className:"margin-top--md",children:l.map(((e,n)=>(0,t.cloneElement)(e,{key:n,hidden:e.props.value!==a})))})}function w(e){const n=g(e);return(0,v.jsxs)("div",{className:(0,s.A)("tabs-container",m.tabList),children:[(0,v.jsx)(x,{...n,...e}),(0,v.jsx)(b,{...n,...e})]})}function R(e){const n=(0,j.A)();return(0,v.jsx)(w,{...e,children:d(e.children)},String(n))}},8453:(e,n,r)=>{r.d(n,{R:()=>l,x:()=>i});var t=r(6540);const s={},a=t.createContext(s);function l(e){const n=t.useContext(a);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function i(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:l(e.components),t.createElement(a.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/62796615.ec8840ea.js b/assets/js/62796615.ec8840ea.js deleted file mode 100644 index 64e6b2aec..000000000 --- a/assets/js/62796615.ec8840ea.js +++ /dev/null @@ -1 +0,0 @@ -"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:()=>l,metadata:()=>c,toc:()=>d});var t=r(4848),s=r(8453),a=r(1470),i=r(9365);const l={},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}];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)(i.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)(i.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)(i.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)(i.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)(i.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)(i.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)(i.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)(i.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)(i.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)(i.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)(i.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)(i.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)(i.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)(i.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)(i.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'})})]})]})]})}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:()=>i});r(6540);var t=r(8215);const s={tabItem:"tabItem_Ymn6"};var a=r(4848);function i(e){let{children:n,hidden:r,className:i}=e;return(0,a.jsx)("div",{role:"tabpanel",className:(0,t.A)(s.tabItem,i),hidden:r,children:n})}},1470:(e,n,r)=>{r.d(n,{A:()=>R});var t=r(6540),s=r(8215),a=r(3104),i=r(6347),l=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: Bad child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:r}=e;return(0,t.useMemo)((()=>{const e=n??function(e){return d(e).map((e=>{let{props:{value:n,label:r,attributes:t,default:s}}=e;return{value:n,label:r,attributes:t,default:s}}))}(r);return function(e){const n=(0,c.XI)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,r])}function f(e){let{value:n,tabValues:r}=e;return r.some((e=>e.value===n))}function p(e){let{queryString:n=!1,groupId:r}=e;const s=(0,i.W6)(),a=function(e){let{queryString:n=!1,groupId:r}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!r)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return r??null}({queryString:n,groupId:r});return[(0,o.aZ)(a),(0,t.useCallback)((e=>{if(!a)return;const n=new URLSearchParams(s.location.search);n.set(a,e),s.replace({...s.location,search:n.toString()})}),[a,s])]}function g(e){const{defaultValue:n,queryString:r=!1,groupId:s}=e,a=h(e),[i,o]=(0,t.useState)((()=>function(e){let{defaultValue:n,tabValues:r}=e;if(0===r.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!f({value:n,tabValues:r}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${r.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const t=r.find((e=>e.default))??r[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:a}))),[c,d]=p({queryString:r,groupId:s}),[g,m]=function(e){let{groupId:n}=e;const r=function(e){return e?`docusaurus.tab.${e}`:null}(n),[s,a]=(0,u.Dv)(r);return[s,(0,t.useCallback)((e=>{r&&a.set(e)}),[r,a])]}({groupId:s}),j=(()=>{const e=c??g;return f({value:e,tabValues:a})?e:null})();(0,l.A)((()=>{j&&o(j)}),[j]);return{selectedValue:i,selectValue:(0,t.useCallback)((e=>{if(!f({value:e,tabValues:a}))throw new Error(`Can't select invalid tab value=${e}`);o(e),d(e),m(e)}),[d,m,a]),tabValues:a}}var m=r(2303);const j={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var v=r(4848);function x(e){let{className:n,block:r,selectedValue:t,selectValue:i,tabValues:l}=e;const o=[],{blockElementScrollPositionUntilNextRender:c}=(0,a.a_)(),u=e=>{const n=e.currentTarget,r=o.indexOf(n),s=l[r].value;s!==t&&(c(n),i(s))},d=e=>{let n=null;switch(e.key){case"Enter":u(e);break;case"ArrowRight":{const r=o.indexOf(e.currentTarget)+1;n=o[r]??o[0];break}case"ArrowLeft":{const r=o.indexOf(e.currentTarget)-1;n=o[r]??o[o.length-1];break}}n?.focus()};return(0,v.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":r},n),children:l.map((e=>{let{value:n,label:r,attributes:a}=e;return(0,v.jsx)("li",{role:"tab",tabIndex:t===n?0:-1,"aria-selected":t===n,ref:e=>o.push(e),onKeyDown:d,onClick:u,...a,className:(0,s.A)("tabs__item",j.tabItem,a?.className,{"tabs__item--active":t===n}),children:r??n},n)}))})}function b(e){let{lazy:n,children:r,selectedValue:a}=e;const i=(Array.isArray(r)?r:[r]).filter(Boolean);if(n){const e=i.find((e=>e.props.value===a));return e?(0,t.cloneElement)(e,{className:(0,s.A)("margin-top--md",e.props.className)}):null}return(0,v.jsx)("div",{className:"margin-top--md",children:i.map(((e,n)=>(0,t.cloneElement)(e,{key:n,hidden:e.props.value!==a})))})}function w(e){const n=g(e);return(0,v.jsxs)("div",{className:(0,s.A)("tabs-container",j.tabList),children:[(0,v.jsx)(x,{...n,...e}),(0,v.jsx)(b,{...n,...e})]})}function R(e){const n=(0,m.A)();return(0,v.jsx)(w,{...e,children:d(e.children)},String(n))}},8453:(e,n,r)=>{r.d(n,{R:()=>i,x:()=>l});var t=r(6540);const s={},a=t.createContext(s);function i(e){const n=t.useContext(a);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:i(e.components),t.createElement(a.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/a06c0eb2.13eb8aee.js b/assets/js/a06c0eb2.13eb8aee.js deleted file mode 100644 index 22d477020..000000000 --- a/assets/js/a06c0eb2.13eb8aee.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunklh_site=self.webpackChunklh_site||[]).push([[300],{4898:(e,n,s)=>{s.r(n),s.d(n,{assets:()=>o,contentTitle:()=>l,default:()=>h,frontMatter:()=>i,metadata:()=>r,toc:()=>t});var c=s(4848),d=s(8453);const i={sidebar_label:"LittleHorse CLI"},l="LittleHorse CLI",r={id:"developer-guide/lhctl",title:"LittleHorse CLI",description:"lhctl is the LittleHorse CLI. It allows you to manage metadata in your system, observe and analyze your WfRuns, and also perform rudimentary actions such as running a WfRun.",source:"@site/docs/05-developer-guide/03-lhctl.md",sourceDirName:"05-developer-guide",slug:"/developer-guide/lhctl",permalink:"/docs/developer-guide/lhctl",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_label:"LittleHorse CLI"},sidebar:"tutorialSidebar",previous:{title:"Configuring the Clients",permalink:"/docs/developer-guide/client-configuration"},next:{title:"Developing Task Workers",permalink:"/docs/developer-guide/task-worker-development"}},o={},t=[{value:"Create Metadata",id:"create-metadata",level:2},{value:"View Objects",id:"view-objects",level:2},{value:"Common Notes",id:"common-notes",level:3},{value:"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 WfRuns",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} \n"})}),"\n",(0,c.jsxs)(n.p,{children:["The ",(0,c.jsx)(n.code,{children:""})," parameter is expected to be a JSON-formatted printout of the corresponding protobuf. If your file is in the binary form, you can deploy using the ",(0,c.jsx)(n.code,{children:"--proto"})," flag."]}),"\n",(0,c.jsxs)(n.p,{children:["The following creates a simple ",(0,c.jsx)(n.code,{children:"TaskDef"}),":"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:'-> cat < /tmp/taskDef.json\n{\n "name": "my-task",\n "inputVars": [{\n\t "type": "STR",\n\t "name": "my-input-var"\n }]\n}\n\n-> lhctl deploy taskDef /tmp/taskDef.json\n{\n "code": "OK",\n "result": {\n "name": "my-task",\n "inputVars": [\n {\n "type": "STR",\n "name": "my-input-var",\n "required": false\n }\n ],\n "createdAt": "2023-04-18T18:54:23.263Z"\n }\n}\n\n'})}),"\n",(0,c.jsxs)(n.p,{children:["The same syntax can be used for creating ",(0,c.jsx)(n.code,{children:"WfSpec"}),", ",(0,c.jsx)(n.code,{children:"UserTaskDef"}),", and ",(0,c.jsx)(n.code,{children:"ExternalEventDef"})," objects."]}),"\n",(0,c.jsx)(n.h2,{id:"view-objects",children:"View Objects"}),"\n",(0,c.jsxs)(n.p,{children:["The ",(0,c.jsx)(n.code,{children:"lhctl get"})," command allows you to inspect the state of an API Object; ",(0,c.jsx)(n.code,{children:"lhctl search"})," allows you to find the ID's of API Object which match certain criteria; and ",(0,c.jsx)(n.code,{children:"lhctl list"})," allows you to retrieve multiple API Objects at one time."]}),"\n",(0,c.jsx)(n.p,{children:"The following sections describe how to interact with each type of API Object."}),"\n",(0,c.jsx)(n.h3,{id:"common-notes",children:"Common Notes"}),"\n",(0,c.jsxs)(n.p,{children:["In ",(0,c.jsx)(n.code,{children:"lhctl search"}),", there are two global flags:"]}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"bookmark"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"limit"})}),"\n"]}),"\n",(0,c.jsxs)(n.p,{children:["The ",(0,c.jsx)(n.code,{children:"limit"})," flag is quite self-explanatory as it simply limits the number of results for a request."]}),"\n",(0,c.jsxs)(n.p,{children:["All ",(0,c.jsx)(n.code,{children:"lhctl search"})," responses have an optional ",(0,c.jsx)(n.code,{children:"bookmark"})," field, which is a base64-encoded pagination token. If all results that match the search have been returned, then no ",(0,c.jsx)(n.code,{children:"bookmark"})," is provided. Otherwise, if you wish to retrieve more results starting from where you left off, just pass in the provided ",(0,c.jsx)(n.code,{children:"bookmark"})," by copying and pasting the base64-encoded data."]}),"\n",(0,c.jsx)(n.h3,{id:"wfspec",children:(0,c.jsx)(n.code,{children:"WfSpec"})}),"\n",(0,c.jsxs)(n.p,{children:[(0,c.jsx)(n.code,{children:"WfSpec"}),"s are versioned objects, meaning that their ID comprises a ",(0,c.jsx)(n.code,{children:"name"})," and a ",(0,c.jsx)(n.code,{children:"version"}),". When a new ",(0,c.jsx)(n.code,{children:"WfSpec"})," is created with the same ",(0,c.jsx)(n.code,{children:"name"})," as an older one, it gets an incremented ",(0,c.jsx)(n.code,{children:"version"})," number and lives in the API as its own separate object."]}),"\n",(0,c.jsxs)(n.p,{children:["You can retrieve the latest ",(0,c.jsx)(n.code,{children:"WfSpec"})," named ",(0,c.jsx)(n.code,{children:"foo"})," by doing:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get wfSpec foo\n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can retrieve a list of all ",(0,c.jsx)(n.code,{children:"WfSpec"}),"s named ",(0,c.jsx)(n.code,{children:"foo"})," by doing:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:'-> lhctl search wfSpec --name foo\n{\n "results": [\n {\n "name": "foo",\n "version": 0\n },\n {\n "name": "foo",\n "version": 1\n }\n ],\n}\n'})}),"\n",(0,c.jsxs)(n.p,{children:["To get an old version (eg. ",(0,c.jsx)(n.code,{children:"0"}),") of a ",(0,c.jsx)(n.code,{children:"WfSpec"})," named ",(0,c.jsx)(n.code,{children:"foo"}),", you can:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get wfSpec foo --v 0\n"})}),"\n",(0,c.jsx)(n.h3,{id:"wfrun",children:(0,c.jsx)(n.code,{children:"WfRun"})}),"\n",(0,c.jsxs)(n.p,{children:["To get a ",(0,c.jsx)(n.code,{children:"WfRun"})," with id ",(0,c.jsx)(n.code,{children:""})," you can:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get wfRun \n"})}),"\n",(0,c.jsxs)(n.h4,{id:"specific-wfspec-version",children:["Specific ",(0,c.jsx)(n.code,{children:"WfSpec"})," Version"]}),"\n",(0,c.jsxs)(n.p,{children:["You can search for ",(0,c.jsx)(n.code,{children:"WfRun"}),"s by providing the name and version of the ",(0,c.jsx)(n.code,{children:"WfSpec"})," and the status of the ",(0,c.jsx)(n.code,{children:"WfRun"}),". For example, if you want to find all failed ",(0,c.jsx)(n.code,{children:"WfRun"}),"'s from the ",(0,c.jsx)(n.code,{children:"foo"})," WfSpec, version ",(0,c.jsx)(n.code,{children:"9"}),", you would do the following:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search wfRun --wfSpecName foo --wfSpecVersion 9 --status ERROR\n"})}),"\n",(0,c.jsxs)(n.h4,{id:"by-wfspecname-and-status",children:["By ",(0,c.jsx)(n.code,{children:"wfSpecName"})," and ",(0,c.jsx)(n.code,{children:"status"})]}),"\n",(0,c.jsxs)(n.p,{children:["You can search for ",(0,c.jsx)(n.code,{children:"WfRun"}),"s by providing the name of the ",(0,c.jsx)(n.code,{children:"WfSpec"})," and the ",(0,c.jsx)(n.code,{children:"status"})," of the ",(0,c.jsx)(n.code,{children:"WfRun"}),". This retrieves results from all versions of the ",(0,c.jsx)(n.code,{children:"WfSpec"}),":"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search wfRun --wfSpecName foo --status ERROR\n"})}),"\n",(0,c.jsxs)(n.h4,{id:"by-wfspecname",children:["By ",(0,c.jsx)(n.code,{children:"wfSpecName"})]}),"\n",(0,c.jsxs)(n.p,{children:["If you only specify ",(0,c.jsx)(n.code,{children:"--wfSpecName"}),", ",(0,c.jsx)(n.code,{children:"WfRun"}),"s with any status and any version of the provided ",(0,c.jsx)(n.code,{children:"WfSpec"})," are returned:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search wfRun --wfSpecName foo\n"})}),"\n",(0,c.jsx)(n.h4,{id:"by-time",children:"By Time"}),"\n",(0,c.jsxs)(n.p,{children:["Every flavor of ",(0,c.jsx)(n.code,{children:"lhctl search wfRun"})," shown above also allows you to filter based on the ",(0,c.jsxs)(n.em,{children:["time that the ",(0,c.jsx)(n.code,{children:"WfRun"})," was launched"]})," via the following options:"]}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--earliestMinutesAgo"}),": Only show ",(0,c.jsx)(n.code,{children:"WfRun"}),"s more recent than this configuration."]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--latestMinutesAgo"}),": only show ",(0,c.jsx)(n.code,{children:"WfRun"}),"'s less recent than this configuration."]}),"\n"]}),"\n",(0,c.jsxs)(n.p,{children:["For example, to find all workflows started between 10 and 15 minutes ago that are in the ",(0,c.jsx)(n.code,{children:"COMPLETED"})," state, we would do:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search wfRun --wfSpecName foo --status COMPLETED --earliestMinutesAgo 15 --latestMinutesAgo 10\n"})}),"\n",(0,c.jsx)(n.h3,{id:"noderun",children:(0,c.jsx)(n.code,{children:"NodeRun"})}),"\n",(0,c.jsxs)(n.p,{children:["A ",(0,c.jsx)(n.code,{children:"NodeRun"})," has a composite ID consisting of the ",(0,c.jsx)(n.code,{children:"wfRunId"}),", ",(0,c.jsx)(n.code,{children:"threadRunNumber"}),", and ",(0,c.jsx)(n.code,{children:"position"}),"."]}),"\n",(0,c.jsx)(n.p,{children:"To get all NodeRun associated to an specific WfRun:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl list nodeRun \n"})}),"\n",(0,c.jsx)(n.p,{children:"Get an simplified response as follows:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search nodeRun --wfRunId \n"})}),"\n",(0,c.jsxs)(n.p,{children:["Use ",(0,c.jsx)(n.code,{children:"lhctl get nodeRun"})," to find an specific NodeRun, The syntax for this is as follows:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get nodeRun \n"})}),"\n",(0,c.jsxs)(n.p,{children:["To get the second (zero-indexed) ",(0,c.jsx)(n.code,{children:"NodeRun"})," for the entrypoint thread (Thread Run ",(0,c.jsx)(n.code,{children:"0"}),") of the ",(0,c.jsx)(n.code,{children:"WfRun"})," with id ",(0,c.jsx)(n.code,{children:"123foo"}),", you can:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get nodeRun 123foo 0 2\n"})}),"\n",(0,c.jsxs)(n.h3,{id:"taskdef-and-externaleventdef",children:[(0,c.jsx)(n.code,{children:"TaskDef"})," and ",(0,c.jsx)(n.code,{children:"ExternalEventDef"})]}),"\n",(0,c.jsxs)(n.p,{children:["The ",(0,c.jsx)(n.code,{children:"lhctl"})," syntax for ",(0,c.jsx)(n.code,{children:"TaskDef"})," and ",(0,c.jsx)(n.code,{children:"ExternalEventDef"})," is identical. You can get a specific ",(0,c.jsx)(n.code,{children:"TaskDef"})," or ",(0,c.jsx)(n.code,{children:"ExternalEventDef"})," by its ",(0,c.jsx)(n.code,{children:"name"})," using:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get taskDef \n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can list all ",(0,c.jsx)(n.code,{children:"TaskDef"}),"s or ",(0,c.jsx)(n.code,{children:"ExternalEventDef"}),"s as follows:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search externalEventDef\n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can search ",(0,c.jsx)(n.code,{children:"TaskDef"}),"s and ",(0,c.jsx)(n.code,{children:"ExternalEventDef"}),"s with prefixes as follows:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search taskDef --prefix some-pref\n"})}),"\n",(0,c.jsx)(n.h3,{id:"taskrun",children:(0,c.jsx)(n.code,{children:"TaskRun"})}),"\n",(0,c.jsxs)(n.p,{children:["A ",(0,c.jsx)(n.code,{children:"TaskRun"})," is a running instance of a Task in LittleHorse. A ",(0,c.jsx)(n.code,{children:"TaskRun"})," is associated with a ",(0,c.jsx)(n.code,{children:"TaskDef"}),"."]}),"\n",(0,c.jsxs)(n.p,{children:["To retrieve all ",(0,c.jsx)(n.code,{children:"TaskRun"}),"'s associated with a ",(0,c.jsx)(n.code,{children:"TaskDef"}),", use the following command:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search taskRun --taskDefName \n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can also filter ",(0,c.jsx)(n.code,{children:"TaskRun"}),"s by specifying a particular ",(0,c.jsx)(n.code,{children:"TaskRun"})," status:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search taskRun --taskDefName --status \n"})}),"\n",(0,c.jsx)(n.p,{children:"Possible values for the status parameter are as follows:"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"TASK_SCHEDULED"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"TASK_RUNNING"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"TASK_SUCCESS"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"TASK_FAILED"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"TASK_TIMEOUT"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"TASK_OUTPUT_SERIALIZING_ERROR"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"TASK_INPUT_VAR_SUB_ERROR"})}),"\n"]}),"\n",(0,c.jsxs)(n.p,{children:["Just like ",(0,c.jsx)(n.code,{children:"lhctl search wfRun"}),", each flavor of ",(0,c.jsx)(n.code,{children:"lhctl search taskRun"})," shown above also allows you to filter based on the ",(0,c.jsxs)(n.em,{children:["time that the ",(0,c.jsx)(n.code,{children:"taskRun"})," was scheduled"]})," via the following options:"]}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--earliestMinutesAgo"}),": Only show ",(0,c.jsx)(n.code,{children:"TaskRun"}),"s more recent than this configuration."]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--latestMinutesAgo"}),": only show ",(0,c.jsx)(n.code,{children:"TaskRun"}),"'s less recent than this configuration."]}),"\n"]}),"\n",(0,c.jsxs)(n.p,{children:["A ",(0,c.jsx)(n.code,{children:"TaskRun"})," has a composite id consisting of the ",(0,c.jsx)(n.code,{children:"id"})," of its ",(0,c.jsx)(n.code,{children:"WfRun"}),", and a ",(0,c.jsx)(n.code,{children:"taskGuid"}),". To get a ",(0,c.jsx)(n.code,{children:"TaskRun"}),", you can use:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get taskRun \n\n# This is equivalent:\nlhctl get taskRun /\n"})}),"\n",(0,c.jsx)(n.h3,{id:"usertaskrun",children:(0,c.jsx)(n.code,{children:"UserTaskRun"})}),"\n",(0,c.jsxs)(n.p,{children:["A ",(0,c.jsx)(n.code,{children:"UserTaskRun"})," is an instance of a ",(0,c.jsx)(n.code,{children:"UserTaskDef"})," in LittleHorse, in which a human is assigned a Task and the ",(0,c.jsx)(n.code,{children:"WfRun"})," blocks until that Task is completed. The ID of a ",(0,c.jsx)(n.code,{children:"UserTaskRun"})," is a composite ID consisting of the ID of the ",(0,c.jsx)(n.code,{children:"WfRun"})," and a Guid."]}),"\n",(0,c.jsxs)(n.p,{children:["To get a ",(0,c.jsx)(n.code,{children:"UserTaskRun"}),", you can run:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get userTaskRun \n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can search for ",(0,c.jsx)(n.code,{children:"UserTaskRun"}),"'s with multiple combinations of flags. Supported flags for a ",(0,c.jsx)(n.code,{children:"UserTaskRun"})," search are:"]}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"--earliestMinutesAgo"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"--latestMinutesAgo"})}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--userTaskStatus"}),", which is the status of the ",(0,c.jsx)(n.code,{children:"UserTaskRun"}),". Valid values are:","\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"UNASSIGNED"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"ASSIGNED"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"DONE"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"CANCELLED"})}),"\n"]}),"\n"]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--userId"}),", or the ID of the User to whom the Task is assigned."]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--userGroup"}),", or the ID of the User Group to whom the Task is assigned."]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--userTaskDefName"}),", or the name of the ",(0,c.jsx)(n.code,{children:"UserTaskDef"})," that the Task comes from."]}),"\n"]}),"\n",(0,c.jsx)(n.h3,{id:"usertaskdef",children:(0,c.jsx)(n.code,{children:"UserTaskDef"})}),"\n",(0,c.jsxs)(n.p,{children:["A ",(0,c.jsx)(n.code,{children:"UserTaskDef"})," is equivalent to a ",(0,c.jsx)(n.code,{children:"TaskDef"})," but for ",(0,c.jsx)(n.code,{children:"UserTaskRun"}),"s instead of ",(0,c.jsx)(n.code,{children:"TaskRun"}),"s. To find all ",(0,c.jsx)(n.code,{children:"UserTaskDef"}),"s, you can:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search userTaskDef\n\n# By Prefix\nlhctl search userTaskDef --prefix some-prefix-\n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can get a ",(0,c.jsx)(n.code,{children:"userTaskDef"})," as follows:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get userTaskDef my-user-task-def\n\n# optionally specify version. If version not set, defaults to latest.\nlhctl get userTaskDef my-user-task-def --v 2\n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can delete a ",(0,c.jsx)(n.code,{children:"UserTaskDef"})," as follows:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl delete userTaskDef my-user-task-def 2 # version is required\n"})}),"\n",(0,c.jsx)(n.h3,{id:"externalevent",children:(0,c.jsx)(n.code,{children:"ExternalEvent"})}),"\n",(0,c.jsxs)(n.p,{children:["An ",(0,c.jsx)(n.code,{children:"ExternalEvent"})," has a composite ID consisting of:"]}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["The associated ",(0,c.jsx)(n.code,{children:"wfRunId"}),"."]}),"\n",(0,c.jsxs)(n.li,{children:["The ",(0,c.jsx)(n.code,{children:"name"})," of the ",(0,c.jsx)(n.code,{children:"ExternalEventDef"}),"."]}),"\n",(0,c.jsxs)(n.li,{children:["A unique ",(0,c.jsx)(n.code,{children:"guid"})," for that ",(0,c.jsx)(n.code,{children:"ExternalEvent"})," instance."]}),"\n"]}),"\n",(0,c.jsxs)(n.p,{children:["To get an ",(0,c.jsx)(n.code,{children:"ExternalEvent"}),", you can:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search externalEvent \n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can list all ",(0,c.jsx)(n.code,{children:"ExternalEvent"}),"s for a given ",(0,c.jsx)(n.code,{children:"wfRunId"})," via the following:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search externalEvent --wfRunId \n"})}),"\n",(0,c.jsx)(n.h3,{id:"variable",children:(0,c.jsx)(n.code,{children:"Variable"})}),"\n",(0,c.jsxs)(n.p,{children:["A ",(0,c.jsx)(n.code,{children:"Variable"})," has a composite ID consisting of:"]}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["The associated ",(0,c.jsx)(n.code,{children:"wfRunId"}),"."]}),"\n",(0,c.jsxs)(n.li,{children:["The ",(0,c.jsx)(n.code,{children:"threadRunNumber"})," of the owning ThreadRun."]}),"\n",(0,c.jsxs)(n.li,{children:["The ",(0,c.jsx)(n.code,{children:"name"})," of the Variable."]}),"\n"]}),"\n",(0,c.jsx)(n.p,{children:"You can get a specific variable via:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get variable \n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can list all ",(0,c.jsx)(n.code,{children:"Variable"}),"s for a given ",(0,c.jsx)(n.code,{children:"WfRun"})," via:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search variable --wfRunId \n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can search for ",(0,c.jsx)(n.code,{children:"Variable"}),"s with a certain value (not supported for ",(0,c.jsx)(n.code,{children:"JSON_OBJ"}),", ",(0,c.jsx)(n.code,{children:"BYTES"}),", and ",(0,c.jsx)(n.code,{children:"JSON_ARR"})," variables). You must pass in the Variable ",(0,c.jsx)(n.code,{children:"name"}),", the type, and the value. For example, to search for ",(0,c.jsx)(n.code,{children:"email-address"})," variable's with the value ",(0,c.jsx)(n.code,{children:"foo@bar.com"}),", you would:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search variable --varType STR --value 'foo@bar.com' --name email-address\n"})}),"\n",(0,c.jsx)(n.p,{children:"Supported variable types for searching are:"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"STR"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"INT"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"DOUBLE"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"BOOL"})}),"\n"]}),"\n",(0,c.jsx)(n.h3,{id:"metrics",children:"Metrics"}),"\n",(0,c.jsx)(n.p,{children:"The LittleHorse CLI lets you view metrics in a rudimentary manner. It is recommended to use the Admin Dashboard to better visualize these metrics; however, you can still view metrics through the CLI."}),"\n",(0,c.jsxs)(n.p,{children:['LittleHorse exposes two types of metrics: "Task Metrics", which are aggregated by ',(0,c.jsx)(n.code,{children:"TaskDef"}),', and "Workflow Metrics", which are aggregated by ',(0,c.jsx)(n.code,{children:"WfSpec"}),"."]}),"\n",(0,c.jsx)(n.p,{children:"Metrics are collected and aggregated on tumbling time windows. There are three sizes of windows which you can use:"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"MINUTES_5"}),","]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"HOURS_2"}),", and"]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"DAYS_1"}),"."]}),"\n"]}),"\n",(0,c.jsxs)(n.p,{children:["To get the last 10 windows of size ",(0,c.jsx)(n.code,{children:"MINUTES_5"})," of metrics for the ",(0,c.jsx)(n.code,{children:"foo-task"})," TaskDef, you can:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl taskDefMetrics foo-task MINUTES_5 10\n"})}),"\n",(0,c.jsxs)(n.p,{children:["To get the last 5 windows of size ",(0,c.jsx)(n.code,{children:"DAYS_1"})," for the second version of the ",(0,c.jsx)(n.code,{children:"foo-wf"})," WfSpec, you can:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl wfSpecMetrics foo-wf 2 DAYS_1 5\n"})}),"\n",(0,c.jsxs)(n.h2,{id:"manage-wfruns",children:["Manage ",(0,c.jsx)(n.code,{children:"WfRun"}),"s"]}),"\n",(0,c.jsxs)(n.p,{children:[(0,c.jsx)(n.code,{children:"lhctl"})," allows you to perform basic actions around running, stopping, and resuming ",(0,c.jsx)(n.code,{children:"WfRun"}),"s and also creating ",(0,c.jsx)(n.code,{children:"ExternalEvent"}),"s."]}),"\n",(0,c.jsxs)(n.h3,{id:"run-a-wfrun",children:["Run a ",(0,c.jsx)(n.code,{children:"WfRun"})]}),"\n",(0,c.jsxs)(n.p,{children:["You can run a ",(0,c.jsx)(n.code,{children:"WfRun"})," using the ",(0,c.jsx)(n.code,{children:"lhctl run"})," command. The syntax is:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl run ...args\n"})}),"\n",(0,c.jsxs)(n.p,{children:["All positional arguments after the WfSpec Name are interpreted as pairs of\n",(0,c.jsx)(n.code,{children:"{Variable Name, Variable Value}"}),". The variable values are intelligently deserialized\nto their appropriate types; for example, if var 'foo' is of type 'JSON_OBJ', then\nthe argument ",(0,c.jsx)(n.code,{children:'\'{"bar":"baz"}\''})," will be unmarshalled as a JSON object."]}),"\n",(0,c.jsxs)(n.p,{children:['To run the "my-workflow" ',(0,c.jsx)(n.code,{children:"WfSpec"})," with two input parameters,"]}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"foo"})," set to the Json Object ",(0,c.jsx)(n.code,{children:'{"bar":"baz"}'})]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"my-int"})," set to ",(0,c.jsx)(n.code,{children:"123"})]}),"\n"]}),"\n",(0,c.jsx)(n.p,{children:"you can:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:'lhctl run my-workflow foo \'{"bar":"baz"}\' my-int 123\n'})}),"\n",(0,c.jsxs)(n.p,{children:["You can also set the ID of the ",(0,c.jsx)(n.code,{children:"WfRun"})," using the ",(0,c.jsx)(n.code,{children:"--wfRunId"})," flag. Note that there can only be one ",(0,c.jsx)(n.code,{children:"WfRun"})," with a given ID. This can be used to guarantee idempotence."]}),"\n",(0,c.jsxs)(n.h3,{id:"stop-and-resume-a-wfrun",children:["Stop and Resume a ",(0,c.jsx)(n.code,{children:"WfRun"})]}),"\n",(0,c.jsx)(n.p,{children:"You can use:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl stop wfRun \n"})}),"\n",(0,c.jsxs)(n.p,{children:["to stop a ",(0,c.jsx)(n.code,{children:"WfRun"}),", and then resume it with:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl resume wfRun \n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can optionally stop or resume a child thread (without affecting the entrypoint ThreadRun) by using the ",(0,c.jsx)(n.code,{children:"--threadRunNumber"})," argument."]}),"\n",(0,c.jsxs)(n.h3,{id:"post-an-externalevent",children:["Post an ",(0,c.jsx)(n.code,{children:"ExternalEvent"})]}),"\n",(0,c.jsxs)(n.p,{children:[(0,c.jsx)(n.code,{children:"lhctl postEvent"})," allows you to post an ExternalEvent of a specified Event Type and Variable Type to a WfRun. Specifying the Variable Type for the external event is currently required as ExternalEventDef's currently do not carry Schema information (this will change in a future release). The payload is deserialized according to the type. JSON objects should be provided as\na string; BYTES objects should be b64-encoded."]}),"\n",(0,c.jsxs)(n.p,{children:["To send an External Event of type ",(0,c.jsx)(n.code,{children:"my-event"})," with a String value ",(0,c.jsx)(n.code,{children:'"my-event-content"'})," to the ",(0,c.jsx)(n.code,{children:"WfRun"}),' given by id "my-wf-id", you can:']}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl postEvent my-wf-id my-event STR my-event-content\n"})}),"\n",(0,c.jsx)(n.p,{children:"As a refresher, the valid variable types in LittleHorse are:"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"STR"}),"\n",(0,c.jsx)(n.li,{children:"INT"}),"\n",(0,c.jsx)(n.li,{children:"DOUBLE"}),"\n",(0,c.jsx)(n.li,{children:"BOOL"}),"\n",(0,c.jsx)(n.li,{children:"JSON_OBJ"}),"\n",(0,c.jsx)(n.li,{children:"JSON_ARR"}),"\n",(0,c.jsx)(n.li,{children:"BYTES"}),"\n"]}),"\n",(0,c.jsxs)(n.p,{children:["You can optionally specify the ",(0,c.jsx)(n.code,{children:"--guid"})," flag to guarantee idempotence of this request, as only one ",(0,c.jsx)(n.code,{children:"ExternalEvent"})," can exist with the same ",(0,c.jsx)(n.code,{children:"guid"}),", ",(0,c.jsx)(n.code,{children:"wfRunId"}),", and ",(0,c.jsx)(n.code,{children:"externalEventDefName"}),"."]}),"\n",(0,c.jsxs)(n.p,{children:["You can optionally assign the ",(0,c.jsx)(n.code,{children:"ExternalEvent"})," to a specific ",(0,c.jsx)(n.code,{children:"NodeRun"})," or ",(0,c.jsx)(n.code,{children:"ThreadRun"})," using the ",(0,c.jsx)(n.code,{children:"--nodeRunPosition"})," and ",(0,c.jsx)(n.code,{children:"--threadRunNumber"})," flags, respectively. The ",(0,c.jsx)(n.code,{children:"--nodeRunPosition"})," flag is only valid if the ",(0,c.jsx)(n.code,{children:"--threadRunNumber"})," flag is also set."]})]})}function h(e={}){const{wrapper:n}={...(0,d.R)(),...e.components};return n?(0,c.jsx)(n,{...e,children:(0,c.jsx)(a,{...e})}):a(e)}},8453:(e,n,s)=>{s.d(n,{R:()=>l,x:()=>r});var c=s(6540);const d={},i=c.createContext(d);function l(e){const n=c.useContext(i);return c.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(d):e.components||d:l(e.components),c.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/a06c0eb2.47a7d66e.js b/assets/js/a06c0eb2.47a7d66e.js new file mode 100644 index 000000000..bb327c842 --- /dev/null +++ b/assets/js/a06c0eb2.47a7d66e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunklh_site=self.webpackChunklh_site||[]).push([[300],{4898:(e,n,s)=>{s.r(n),s.d(n,{assets:()=>o,contentTitle:()=>l,default:()=>h,frontMatter:()=>i,metadata:()=>r,toc:()=>t});var c=s(4848),d=s(8453);const i={sidebar_label:"LittleHorse CLI"},l="LittleHorse CLI",r={id:"developer-guide/lhctl",title:"LittleHorse CLI",description:"lhctl is the LittleHorse CLI. It allows you to manage metadata in your system, observe and analyze your WfRuns, and also perform rudimentary actions such as running a WfRun.",source:"@site/docs/05-developer-guide/03-lhctl.md",sourceDirName:"05-developer-guide",slug:"/developer-guide/lhctl",permalink:"/docs/developer-guide/lhctl",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_label:"LittleHorse CLI"},sidebar:"tutorialSidebar",previous:{title:"Configuring the Clients",permalink:"/docs/developer-guide/client-configuration"},next:{title:"Developing Task Workers",permalink:"/docs/developer-guide/task-worker-development"}},o={},t=[{value:"Create Metadata",id:"create-metadata",level:2},{value:"View Objects",id:"view-objects",level:2},{value:"Common Notes",id:"common-notes",level:3},{value:"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 WfRuns",id:"manage-wfruns",level:2},{value:"Run a WfRun",id:"run-a-wfrun",level:3},{value:"Schedule a WfRuns",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} \n"})}),"\n",(0,c.jsxs)(n.p,{children:["The ",(0,c.jsx)(n.code,{children:""})," parameter is expected to be a JSON-formatted printout of the corresponding protobuf. If your file is in the binary form, you can deploy using the ",(0,c.jsx)(n.code,{children:"--proto"})," flag."]}),"\n",(0,c.jsxs)(n.p,{children:["The following creates a simple ",(0,c.jsx)(n.code,{children:"TaskDef"}),":"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:'-> cat < /tmp/taskDef.json\n{\n "name": "my-task",\n "inputVars": [{\n\t "type": "STR",\n\t "name": "my-input-var"\n }]\n}\n\n-> lhctl deploy taskDef /tmp/taskDef.json\n{\n "code": "OK",\n "result": {\n "name": "my-task",\n "inputVars": [\n {\n "type": "STR",\n "name": "my-input-var",\n "required": false\n }\n ],\n "createdAt": "2023-04-18T18:54:23.263Z"\n }\n}\n\n'})}),"\n",(0,c.jsxs)(n.p,{children:["The same syntax can be used for creating ",(0,c.jsx)(n.code,{children:"WfSpec"}),", ",(0,c.jsx)(n.code,{children:"UserTaskDef"}),", and ",(0,c.jsx)(n.code,{children:"ExternalEventDef"})," objects."]}),"\n",(0,c.jsx)(n.h2,{id:"view-objects",children:"View Objects"}),"\n",(0,c.jsxs)(n.p,{children:["The ",(0,c.jsx)(n.code,{children:"lhctl get"})," command allows you to inspect the state of an API Object; ",(0,c.jsx)(n.code,{children:"lhctl search"})," allows you to find the ID's of API Object which match certain criteria; and ",(0,c.jsx)(n.code,{children:"lhctl list"})," allows you to retrieve multiple API Objects at one time."]}),"\n",(0,c.jsx)(n.p,{children:"The following sections describe how to interact with each type of API Object."}),"\n",(0,c.jsx)(n.h3,{id:"common-notes",children:"Common Notes"}),"\n",(0,c.jsxs)(n.p,{children:["In ",(0,c.jsx)(n.code,{children:"lhctl search"}),", there are two global flags:"]}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"bookmark"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"limit"})}),"\n"]}),"\n",(0,c.jsxs)(n.p,{children:["The ",(0,c.jsx)(n.code,{children:"limit"})," flag is quite self-explanatory as it simply limits the number of results for a request."]}),"\n",(0,c.jsxs)(n.p,{children:["All ",(0,c.jsx)(n.code,{children:"lhctl search"})," responses have an optional ",(0,c.jsx)(n.code,{children:"bookmark"})," field, which is a base64-encoded pagination token. If all results that match the search have been returned, then no ",(0,c.jsx)(n.code,{children:"bookmark"})," is provided. Otherwise, if you wish to retrieve more results starting from where you left off, just pass in the provided ",(0,c.jsx)(n.code,{children:"bookmark"})," by copying and pasting the base64-encoded data."]}),"\n",(0,c.jsx)(n.h3,{id:"wfspec",children:(0,c.jsx)(n.code,{children:"WfSpec"})}),"\n",(0,c.jsxs)(n.p,{children:[(0,c.jsx)(n.code,{children:"WfSpec"}),"s are versioned objects, meaning that their ID comprises a ",(0,c.jsx)(n.code,{children:"name"})," and a ",(0,c.jsx)(n.code,{children:"version"}),". When a new ",(0,c.jsx)(n.code,{children:"WfSpec"})," is created with the same ",(0,c.jsx)(n.code,{children:"name"})," as an older one, it gets an incremented ",(0,c.jsx)(n.code,{children:"version"})," number and lives in the API as its own separate object."]}),"\n",(0,c.jsxs)(n.p,{children:["You can retrieve the latest ",(0,c.jsx)(n.code,{children:"WfSpec"})," named ",(0,c.jsx)(n.code,{children:"foo"})," by doing:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get wfSpec foo\n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can retrieve a list of all ",(0,c.jsx)(n.code,{children:"WfSpec"}),"s named ",(0,c.jsx)(n.code,{children:"foo"})," by doing:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:'-> lhctl search wfSpec --name foo\n{\n "results": [\n {\n "name": "foo",\n "version": 0\n },\n {\n "name": "foo",\n "version": 1\n }\n ],\n}\n'})}),"\n",(0,c.jsxs)(n.p,{children:["To get an old version (eg. ",(0,c.jsx)(n.code,{children:"0"}),") of a ",(0,c.jsx)(n.code,{children:"WfSpec"})," named ",(0,c.jsx)(n.code,{children:"foo"}),", you can:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get wfSpec foo --v 0\n"})}),"\n",(0,c.jsx)(n.h3,{id:"wfrun",children:(0,c.jsx)(n.code,{children:"WfRun"})}),"\n",(0,c.jsxs)(n.p,{children:["To get a ",(0,c.jsx)(n.code,{children:"WfRun"})," with id ",(0,c.jsx)(n.code,{children:""})," you can:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get wfRun \n"})}),"\n",(0,c.jsxs)(n.h4,{id:"specific-wfspec-version",children:["Specific ",(0,c.jsx)(n.code,{children:"WfSpec"})," Version"]}),"\n",(0,c.jsxs)(n.p,{children:["You can search for ",(0,c.jsx)(n.code,{children:"WfRun"}),"s by providing the name and version of the ",(0,c.jsx)(n.code,{children:"WfSpec"})," and the status of the ",(0,c.jsx)(n.code,{children:"WfRun"}),". For example, if you want to find all failed ",(0,c.jsx)(n.code,{children:"WfRun"}),"'s from the ",(0,c.jsx)(n.code,{children:"foo"})," WfSpec, version ",(0,c.jsx)(n.code,{children:"9"}),", you would do the following:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search wfRun --wfSpecName foo --majorVersion 9 --revision 0 --status ERROR\n"})}),"\n",(0,c.jsxs)(n.h4,{id:"by-wfspecname-and-status",children:["By ",(0,c.jsx)(n.code,{children:"wfSpecName"})," and ",(0,c.jsx)(n.code,{children:"status"})]}),"\n",(0,c.jsxs)(n.p,{children:["You can search for ",(0,c.jsx)(n.code,{children:"WfRun"}),"s by providing the name of the ",(0,c.jsx)(n.code,{children:"WfSpec"})," and the ",(0,c.jsx)(n.code,{children:"status"})," of the ",(0,c.jsx)(n.code,{children:"WfRun"}),". This retrieves results from all versions of the ",(0,c.jsx)(n.code,{children:"WfSpec"}),":"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search wfRun --wfSpecName foo --status ERROR\n"})}),"\n",(0,c.jsxs)(n.h4,{id:"by-wfspecname",children:["By ",(0,c.jsx)(n.code,{children:"wfSpecName"})]}),"\n",(0,c.jsxs)(n.p,{children:["If you only specify ",(0,c.jsx)(n.code,{children:"--wfSpecName"}),", ",(0,c.jsx)(n.code,{children:"WfRun"}),"s with any status and any version of the provided ",(0,c.jsx)(n.code,{children:"WfSpec"})," are returned:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search wfRun --wfSpecName foo\n"})}),"\n",(0,c.jsx)(n.h4,{id:"by-time",children:"By Time"}),"\n",(0,c.jsxs)(n.p,{children:["Every flavor of ",(0,c.jsx)(n.code,{children:"lhctl search wfRun"})," shown above also allows you to filter based on the ",(0,c.jsxs)(n.em,{children:["time that the ",(0,c.jsx)(n.code,{children:"WfRun"})," was launched"]})," via the following options:"]}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--earliestMinutesAgo"}),": Only show ",(0,c.jsx)(n.code,{children:"WfRun"}),"s more recent than this configuration."]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--latestMinutesAgo"}),": only show ",(0,c.jsx)(n.code,{children:"WfRun"}),"'s less recent than this configuration."]}),"\n"]}),"\n",(0,c.jsxs)(n.p,{children:["For example, to find all workflows started between 10 and 15 minutes ago that are in the ",(0,c.jsx)(n.code,{children:"COMPLETED"})," state, we would do:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search wfRun --wfSpecName foo --status COMPLETED --earliestMinutesAgo 15 --latestMinutesAgo 10\n"})}),"\n",(0,c.jsx)(n.h3,{id:"noderun",children:(0,c.jsx)(n.code,{children:"NodeRun"})}),"\n",(0,c.jsxs)(n.p,{children:["A ",(0,c.jsx)(n.code,{children:"NodeRun"})," has a composite ID consisting of the ",(0,c.jsx)(n.code,{children:"wfRunId"}),", ",(0,c.jsx)(n.code,{children:"threadRunNumber"}),", and ",(0,c.jsx)(n.code,{children:"position"}),"."]}),"\n",(0,c.jsx)(n.p,{children:"To get all NodeRun associated to an specific WfRun:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl list nodeRun \n"})}),"\n",(0,c.jsx)(n.p,{children:"Get an simplified response as follows:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search nodeRun --wfRunId \n"})}),"\n",(0,c.jsxs)(n.p,{children:["Use ",(0,c.jsx)(n.code,{children:"lhctl get nodeRun"})," to find an specific NodeRun, The syntax for this is as follows:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get nodeRun \n"})}),"\n",(0,c.jsxs)(n.p,{children:["To get the second (zero-indexed) ",(0,c.jsx)(n.code,{children:"NodeRun"})," for the entrypoint thread (Thread Run ",(0,c.jsx)(n.code,{children:"0"}),") of the ",(0,c.jsx)(n.code,{children:"WfRun"})," with id ",(0,c.jsx)(n.code,{children:"123foo"}),", you can:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get nodeRun 123foo 0 2\n"})}),"\n",(0,c.jsxs)(n.h3,{id:"taskdef-and-externaleventdef",children:[(0,c.jsx)(n.code,{children:"TaskDef"})," and ",(0,c.jsx)(n.code,{children:"ExternalEventDef"})]}),"\n",(0,c.jsxs)(n.p,{children:["The ",(0,c.jsx)(n.code,{children:"lhctl"})," syntax for ",(0,c.jsx)(n.code,{children:"TaskDef"})," and ",(0,c.jsx)(n.code,{children:"ExternalEventDef"})," is identical. You can get a specific ",(0,c.jsx)(n.code,{children:"TaskDef"})," or ",(0,c.jsx)(n.code,{children:"ExternalEventDef"})," by its ",(0,c.jsx)(n.code,{children:"name"})," using:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get taskDef \n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can list all ",(0,c.jsx)(n.code,{children:"TaskDef"}),"s or ",(0,c.jsx)(n.code,{children:"ExternalEventDef"}),"s as follows:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search externalEventDef\n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can search ",(0,c.jsx)(n.code,{children:"TaskDef"}),"s and ",(0,c.jsx)(n.code,{children:"ExternalEventDef"}),"s with prefixes as follows:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search taskDef --prefix some-pref\n"})}),"\n",(0,c.jsx)(n.h3,{id:"taskrun",children:(0,c.jsx)(n.code,{children:"TaskRun"})}),"\n",(0,c.jsxs)(n.p,{children:["A ",(0,c.jsx)(n.code,{children:"TaskRun"})," is a running instance of a Task in LittleHorse. A ",(0,c.jsx)(n.code,{children:"TaskRun"})," is associated with a ",(0,c.jsx)(n.code,{children:"TaskDef"}),"."]}),"\n",(0,c.jsxs)(n.p,{children:["To retrieve all ",(0,c.jsx)(n.code,{children:"TaskRun"}),"'s associated with a ",(0,c.jsx)(n.code,{children:"TaskDef"}),", use the following command:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search taskRun --taskDefName \n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can also filter ",(0,c.jsx)(n.code,{children:"TaskRun"}),"s by specifying a particular ",(0,c.jsx)(n.code,{children:"TaskRun"})," status:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search taskRun --taskDefName --status \n"})}),"\n",(0,c.jsx)(n.p,{children:"Possible values for the status parameter are as follows:"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"TASK_SCHEDULED"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"TASK_RUNNING"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"TASK_SUCCESS"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"TASK_FAILED"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"TASK_TIMEOUT"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"TASK_OUTPUT_SERIALIZING_ERROR"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"TASK_INPUT_VAR_SUB_ERROR"})}),"\n"]}),"\n",(0,c.jsxs)(n.p,{children:["Just like ",(0,c.jsx)(n.code,{children:"lhctl search wfRun"}),", each flavor of ",(0,c.jsx)(n.code,{children:"lhctl search taskRun"})," shown above also allows you to filter based on the ",(0,c.jsxs)(n.em,{children:["time that the ",(0,c.jsx)(n.code,{children:"taskRun"})," was scheduled"]})," via the following options:"]}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--earliestMinutesAgo"}),": Only show ",(0,c.jsx)(n.code,{children:"TaskRun"}),"s more recent than this configuration."]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--latestMinutesAgo"}),": only show ",(0,c.jsx)(n.code,{children:"TaskRun"}),"'s less recent than this configuration."]}),"\n"]}),"\n",(0,c.jsxs)(n.p,{children:["A ",(0,c.jsx)(n.code,{children:"TaskRun"})," has a composite id consisting of the ",(0,c.jsx)(n.code,{children:"id"})," of its ",(0,c.jsx)(n.code,{children:"WfRun"}),", and a ",(0,c.jsx)(n.code,{children:"taskGuid"}),". To get a ",(0,c.jsx)(n.code,{children:"TaskRun"}),", you can use:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get taskRun \n\n# This is equivalent:\nlhctl get taskRun /\n"})}),"\n",(0,c.jsx)(n.h3,{id:"usertaskrun",children:(0,c.jsx)(n.code,{children:"UserTaskRun"})}),"\n",(0,c.jsxs)(n.p,{children:["A ",(0,c.jsx)(n.code,{children:"UserTaskRun"})," is an instance of a ",(0,c.jsx)(n.code,{children:"UserTaskDef"})," in LittleHorse, in which a human is assigned a Task and the ",(0,c.jsx)(n.code,{children:"WfRun"})," blocks until that Task is completed. The ID of a ",(0,c.jsx)(n.code,{children:"UserTaskRun"})," is a composite ID consisting of the ID of the ",(0,c.jsx)(n.code,{children:"WfRun"})," and a Guid."]}),"\n",(0,c.jsxs)(n.p,{children:["To get a ",(0,c.jsx)(n.code,{children:"UserTaskRun"}),", you can run:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get userTaskRun \n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can search for ",(0,c.jsx)(n.code,{children:"UserTaskRun"}),"'s with multiple combinations of flags. Supported flags for a ",(0,c.jsx)(n.code,{children:"UserTaskRun"})," search are:"]}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"--earliestMinutesAgo"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"--latestMinutesAgo"})}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--userTaskStatus"}),", which is the status of the ",(0,c.jsx)(n.code,{children:"UserTaskRun"}),". Valid values are:","\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"UNASSIGNED"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"ASSIGNED"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"DONE"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"CANCELLED"})}),"\n"]}),"\n"]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--userId"}),", or the ID of the User to whom the Task is assigned."]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--userGroup"}),", or the ID of the User Group to whom the Task is assigned."]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"--userTaskDefName"}),", or the name of the ",(0,c.jsx)(n.code,{children:"UserTaskDef"})," that the Task comes from."]}),"\n"]}),"\n",(0,c.jsx)(n.h3,{id:"usertaskdef",children:(0,c.jsx)(n.code,{children:"UserTaskDef"})}),"\n",(0,c.jsxs)(n.p,{children:["A ",(0,c.jsx)(n.code,{children:"UserTaskDef"})," is equivalent to a ",(0,c.jsx)(n.code,{children:"TaskDef"})," but for ",(0,c.jsx)(n.code,{children:"UserTaskRun"}),"s instead of ",(0,c.jsx)(n.code,{children:"TaskRun"}),"s. To find all ",(0,c.jsx)(n.code,{children:"UserTaskDef"}),"s, you can:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search userTaskDef\n\n# By Prefix\nlhctl search userTaskDef --prefix some-prefix-\n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can get a ",(0,c.jsx)(n.code,{children:"userTaskDef"})," as follows:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get userTaskDef my-user-task-def\n\n# optionally specify version. If version not set, defaults to latest.\nlhctl get userTaskDef my-user-task-def --v 2\n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can delete a ",(0,c.jsx)(n.code,{children:"UserTaskDef"})," as follows:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl delete userTaskDef my-user-task-def 2 # version is required\n"})}),"\n",(0,c.jsx)(n.h3,{id:"externalevent",children:(0,c.jsx)(n.code,{children:"ExternalEvent"})}),"\n",(0,c.jsxs)(n.p,{children:["An ",(0,c.jsx)(n.code,{children:"ExternalEvent"})," has a composite ID consisting of:"]}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["The associated ",(0,c.jsx)(n.code,{children:"wfRunId"}),"."]}),"\n",(0,c.jsxs)(n.li,{children:["The ",(0,c.jsx)(n.code,{children:"name"})," of the ",(0,c.jsx)(n.code,{children:"ExternalEventDef"}),"."]}),"\n",(0,c.jsxs)(n.li,{children:["A unique ",(0,c.jsx)(n.code,{children:"guid"})," for that ",(0,c.jsx)(n.code,{children:"ExternalEvent"})," instance."]}),"\n"]}),"\n",(0,c.jsxs)(n.p,{children:["To get an ",(0,c.jsx)(n.code,{children:"ExternalEvent"}),", you can:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search externalEvent \n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can list all ",(0,c.jsx)(n.code,{children:"ExternalEvent"}),"s for a given ",(0,c.jsx)(n.code,{children:"wfRunId"})," via the following:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search externalEvent --wfRunId \n"})}),"\n",(0,c.jsx)(n.h3,{id:"variable",children:(0,c.jsx)(n.code,{children:"Variable"})}),"\n",(0,c.jsxs)(n.p,{children:["A ",(0,c.jsx)(n.code,{children:"Variable"})," has a composite ID consisting of:"]}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["The associated ",(0,c.jsx)(n.code,{children:"wfRunId"}),"."]}),"\n",(0,c.jsxs)(n.li,{children:["The ",(0,c.jsx)(n.code,{children:"threadRunNumber"})," of the owning ThreadRun."]}),"\n",(0,c.jsxs)(n.li,{children:["The ",(0,c.jsx)(n.code,{children:"name"})," of the Variable."]}),"\n"]}),"\n",(0,c.jsx)(n.p,{children:"You can get a specific variable via:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get variable \n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can list all ",(0,c.jsx)(n.code,{children:"Variable"}),"s for a given ",(0,c.jsx)(n.code,{children:"WfRun"})," via:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search variable --wfRunId \n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can search for ",(0,c.jsx)(n.code,{children:"Variable"}),"s with a certain value (not supported for ",(0,c.jsx)(n.code,{children:"JSON_OBJ"}),", ",(0,c.jsx)(n.code,{children:"BYTES"}),", and ",(0,c.jsx)(n.code,{children:"JSON_ARR"})," variables). You must pass in the Variable ",(0,c.jsx)(n.code,{children:"name"}),", the type, and the value. For example, to search for ",(0,c.jsx)(n.code,{children:"email-address"})," variable's with the value ",(0,c.jsx)(n.code,{children:"foo@bar.com"}),", you would:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search variable --varType STR --value 'foo@bar.com' --name email-address\n"})}),"\n",(0,c.jsx)(n.p,{children:"Supported variable types for searching are:"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"STR"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"INT"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"DOUBLE"})}),"\n",(0,c.jsx)(n.li,{children:(0,c.jsx)(n.code,{children:"BOOL"})}),"\n"]}),"\n",(0,c.jsx)(n.h3,{id:"metrics",children:"Metrics"}),"\n",(0,c.jsx)(n.p,{children:"The LittleHorse CLI lets you view metrics in a rudimentary manner. It is recommended to use the Admin Dashboard to better visualize these metrics; however, you can still view metrics through the CLI."}),"\n",(0,c.jsxs)(n.p,{children:['LittleHorse exposes two types of metrics: "Task Metrics", which are aggregated by ',(0,c.jsx)(n.code,{children:"TaskDef"}),', and "Workflow Metrics", which are aggregated by ',(0,c.jsx)(n.code,{children:"WfSpec"}),"."]}),"\n",(0,c.jsx)(n.p,{children:"Metrics are collected and aggregated on tumbling time windows. There are three sizes of windows which you can use:"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"MINUTES_5"}),","]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"HOURS_2"}),", and"]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"DAYS_1"}),"."]}),"\n"]}),"\n",(0,c.jsxs)(n.p,{children:["To get the last 10 windows of size ",(0,c.jsx)(n.code,{children:"MINUTES_5"})," of metrics for the ",(0,c.jsx)(n.code,{children:"foo-task"})," TaskDef, you can:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl taskDefMetrics foo-task MINUTES_5 10\n"})}),"\n",(0,c.jsxs)(n.p,{children:["To get the last 5 windows of size ",(0,c.jsx)(n.code,{children:"DAYS_1"})," for the second version of the ",(0,c.jsx)(n.code,{children:"foo-wf"})," WfSpec, you can:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl wfSpecMetrics foo-wf 2 DAYS_1 5\n"})}),"\n",(0,c.jsxs)(n.h2,{id:"manage-wfruns",children:["Manage ",(0,c.jsx)(n.code,{children:"WfRun"}),"s"]}),"\n",(0,c.jsxs)(n.p,{children:[(0,c.jsx)(n.code,{children:"lhctl"})," allows you to perform basic actions around running, stopping, scheduling, and resuming ",(0,c.jsx)(n.code,{children:"WfRun"}),"s and also creating ",(0,c.jsx)(n.code,{children:"ExternalEvent"}),"s."]}),"\n",(0,c.jsxs)(n.h3,{id:"run-a-wfrun",children:["Run a ",(0,c.jsx)(n.code,{children:"WfRun"})]}),"\n",(0,c.jsxs)(n.p,{children:["You can run a ",(0,c.jsx)(n.code,{children:"WfRun"})," using the ",(0,c.jsx)(n.code,{children:"lhctl run"})," command. The syntax is:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl run ...args\n"})}),"\n",(0,c.jsxs)(n.p,{children:["All positional arguments after the WfSpec Name are interpreted as pairs of\n",(0,c.jsx)(n.code,{children:"{Variable Name, Variable Value}"}),". The variable values are intelligently deserialized\nto their appropriate types; for example, if var 'foo' is of type 'JSON_OBJ', then\nthe argument ",(0,c.jsx)(n.code,{children:'\'{"bar":"baz"}\''})," will be unmarshalled as a JSON object."]}),"\n",(0,c.jsxs)(n.p,{children:['To run the "my-workflow" ',(0,c.jsx)(n.code,{children:"WfSpec"})," with two input parameters,"]}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"foo"})," set to the Json Object ",(0,c.jsx)(n.code,{children:'{"bar":"baz"}'})]}),"\n",(0,c.jsxs)(n.li,{children:[(0,c.jsx)(n.code,{children:"my-int"})," set to ",(0,c.jsx)(n.code,{children:"123"})]}),"\n"]}),"\n",(0,c.jsx)(n.p,{children:"you can:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:'lhctl run my-workflow foo \'{"bar":"baz"}\' my-int 123\n'})}),"\n",(0,c.jsxs)(n.p,{children:["You can also set the ID of the ",(0,c.jsx)(n.code,{children:"WfRun"})," using the ",(0,c.jsx)(n.code,{children:"--wfRunId"})," flag. Note that there can only be one ",(0,c.jsx)(n.code,{children:"WfRun"})," with a given ID. This can be used to guarantee idempotence."]}),"\n",(0,c.jsxs)(n.h3,{id:"schedule-a-wfruns",children:["Schedule a ",(0,c.jsx)(n.code,{children:"WfRun"}),"s"]}),"\n",(0,c.jsxs)(n.p,{children:["You can schedule a ",(0,c.jsx)(n.code,{children:"WfSpec"})," to trigger a new ",(0,c.jsx)(n.code,{children:"WfRun"})," periodically based on a unix cron expression (see ",(0,c.jsx)(n.a,{href:"https://crontab.guru/",children:"https://crontab.guru/"}),")"]}),"\n",(0,c.jsx)(n.p,{children:"You can use:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:'lhctl schedule run "* * * * *" foo --majorVersion 3 --revision 0\n'})}),"\n",(0,c.jsxs)(n.p,{children:["To run a new ",(0,c.jsx)(n.code,{children:"WfRun"})," every minute"]}),"\n",(0,c.jsx)(n.admonition,{type:"tip",children:(0,c.jsxs)(n.p,{children:["If you pass a specific version, all subsequent ",(0,c.jsx)(n.code,{children:"WfRun"}),"s will automatically be linked to that version.\nWhen scheduling ",(0,c.jsx)(n.code,{children:"WfRun"}),"s, it is recommended to omit the version parameter if you want to use the latest ",(0,c.jsx)(n.code,{children:"WfSpec"})," version."]})}),"\n",(0,c.jsxs)(n.p,{children:["You can search current schedules for a ",(0,c.jsx)(n.code,{children:"WfSpec"}),":"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl search schedule --wfSpecName foo --majorVersion 3 --revision 0\n"})}),"\n",(0,c.jsx)(n.p,{children:"You can get a schedule by id:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl get scheduled \n"})}),"\n",(0,c.jsx)(n.p,{children:"You can delete a schedule:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl delete schedule \n"})}),"\n",(0,c.jsxs)(n.p,{children:["After calling this command, no further ",(0,c.jsx)(n.code,{children:"WfRun"}),"s will be executed for this schedule"]}),"\n",(0,c.jsxs)(n.h3,{id:"stop-and-resume-a-wfrun",children:["Stop and Resume a ",(0,c.jsx)(n.code,{children:"WfRun"})]}),"\n",(0,c.jsx)(n.p,{children:"You can use:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl stop wfRun \n"})}),"\n",(0,c.jsxs)(n.p,{children:["to stop a ",(0,c.jsx)(n.code,{children:"WfRun"}),", and then resume it with:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl resume wfRun \n"})}),"\n",(0,c.jsxs)(n.p,{children:["You can optionally stop or resume a child thread (without affecting the entrypoint ThreadRun) by using the ",(0,c.jsx)(n.code,{children:"--threadRunNumber"})," argument."]}),"\n",(0,c.jsxs)(n.h3,{id:"post-an-externalevent",children:["Post an ",(0,c.jsx)(n.code,{children:"ExternalEvent"})]}),"\n",(0,c.jsxs)(n.p,{children:[(0,c.jsx)(n.code,{children:"lhctl postEvent"})," allows you to post an ExternalEvent of a specified Event Type and Variable Type to a WfRun. Specifying the Variable Type for the external event is currently required as ExternalEventDef's currently do not carry Schema information (this will change in a future release). The payload is deserialized according to the type. JSON objects should be provided as\na string; BYTES objects should be b64-encoded."]}),"\n",(0,c.jsxs)(n.p,{children:["To send an External Event of type ",(0,c.jsx)(n.code,{children:"my-event"})," with a String value ",(0,c.jsx)(n.code,{children:'"my-event-content"'})," to the ",(0,c.jsx)(n.code,{children:"WfRun"}),' given by id "my-wf-id", you can:']}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{children:"lhctl postEvent my-wf-id my-event STR my-event-content\n"})}),"\n",(0,c.jsx)(n.p,{children:"As a refresher, the valid variable types in LittleHorse are:"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"STR"}),"\n",(0,c.jsx)(n.li,{children:"INT"}),"\n",(0,c.jsx)(n.li,{children:"DOUBLE"}),"\n",(0,c.jsx)(n.li,{children:"BOOL"}),"\n",(0,c.jsx)(n.li,{children:"JSON_OBJ"}),"\n",(0,c.jsx)(n.li,{children:"JSON_ARR"}),"\n",(0,c.jsx)(n.li,{children:"BYTES"}),"\n"]}),"\n",(0,c.jsxs)(n.p,{children:["You can optionally specify the ",(0,c.jsx)(n.code,{children:"--guid"})," flag to guarantee idempotence of this request, as only one ",(0,c.jsx)(n.code,{children:"ExternalEvent"})," can exist with the same ",(0,c.jsx)(n.code,{children:"guid"}),", ",(0,c.jsx)(n.code,{children:"wfRunId"}),", and ",(0,c.jsx)(n.code,{children:"externalEventDefName"}),"."]}),"\n",(0,c.jsxs)(n.p,{children:["You can optionally assign the ",(0,c.jsx)(n.code,{children:"ExternalEvent"})," to a specific ",(0,c.jsx)(n.code,{children:"NodeRun"})," or ",(0,c.jsx)(n.code,{children:"ThreadRun"})," using the ",(0,c.jsx)(n.code,{children:"--nodeRunPosition"})," and ",(0,c.jsx)(n.code,{children:"--threadRunNumber"})," flags, respectively. The ",(0,c.jsx)(n.code,{children:"--nodeRunPosition"})," flag is only valid if the ",(0,c.jsx)(n.code,{children:"--threadRunNumber"})," flag is also set."]})]})}function h(e={}){const{wrapper:n}={...(0,d.R)(),...e.components};return n?(0,c.jsx)(n,{...e,children:(0,c.jsx)(a,{...e})}):a(e)}},8453:(e,n,s)=>{s.d(n,{R:()=>l,x:()=>r});var c=s(6540);const d={},i=c.createContext(d);function l(e){const n=c.useContext(i);return c.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(d):e.components||d:l(e.components),c.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/e33a6d67.34ee8db2.js b/assets/js/e33a6d67.34ee8db2.js deleted file mode 100644 index 9110ef0fc..000000000 --- a/assets/js/e33a6d67.34ee8db2.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunklh_site=self.webpackChunklh_site||[]).push([[3324],{7720:(e,n,a)=>{a.r(n),a.d(n,{assets:()=>c,contentTitle:()=>s,default:()=>f,frontMatter:()=>i,metadata:()=>d,toc:()=>h});var r=a(4848),t=a(8453),l=a(1470),o=a(9365);const i={},s="Basics",d={id:"developer-guide/wfspec-development/basics",title:"Basics",description:"To develop a WfSpec in LittleHorse, you can use the Workflow struct or object in our SDK's. Generally, the Workflow entity constructor requires two arguments:",source:"@site/docs/05-developer-guide/08-wfspec-development/01-basics.md",sourceDirName:"05-developer-guide/08-wfspec-development",slug:"/developer-guide/wfspec-development/basics",permalink:"/docs/developer-guide/wfspec-development/basics",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"WfSpec Development",permalink:"/docs/developer-guide/wfspec-development/"},next:{title:"Conditionals and Loops",permalink:"/docs/developer-guide/wfspec-development/conditionals"}},c={},h=[{value:"Quickstart",id:"quickstart",level:2},{value:"Defining a 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 child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function u(e){const{values:n,children:a}=e;return(0,r.useMemo)((()=>{const e=n??function(e){return h(e).map((e=>{let{props:{value:n,label:a,attributes:r,default:t}}=e;return{value:n,label:a,attributes:r,default:t}}))}(a);return function(e){const n=(0,d.XI)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,a])}function f(e){let{value:n,tabValues:a}=e;return a.some((e=>e.value===n))}function p(e){let{queryString:n=!1,groupId:a}=e;const t=(0,o.W6)(),l=function(e){let{queryString:n=!1,groupId:a}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!a)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return a??null}({queryString:n,groupId:a});return[(0,s.aZ)(l),(0,r.useCallback)((e=>{if(!l)return;const n=new URLSearchParams(t.location.search);n.set(l,e),t.replace({...t.location,search:n.toString()})}),[l,t])]}function x(e){const{defaultValue:n,queryString:a=!1,groupId:t}=e,l=u(e),[o,s]=(0,r.useState)((()=>function(e){let{defaultValue:n,tabValues:a}=e;if(0===a.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!f({value:n,tabValues:a}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${a.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const r=a.find((e=>e.default))??a[0];if(!r)throw new Error("Unexpected error: 0 tabValues");return r.value}({defaultValue:n,tabValues:l}))),[d,h]=p({queryString:a,groupId:t}),[x,m]=function(e){let{groupId:n}=e;const a=function(e){return e?`docusaurus.tab.${e}`:null}(n),[t,l]=(0,c.Dv)(a);return[t,(0,r.useCallback)((e=>{a&&l.set(e)}),[a,l])]}({groupId:t}),j=(()=>{const e=d??x;return f({value:e,tabValues:l})?e:null})();(0,i.A)((()=>{j&&s(j)}),[j]);return{selectedValue:o,selectValue:(0,r.useCallback)((e=>{if(!f({value:e,tabValues:l}))throw new Error(`Can't select invalid tab value=${e}`);s(e),h(e),m(e)}),[h,m,l]),tabValues:l}}var m=a(2303);const j={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var b=a(4848);function g(e){let{className:n,block:a,selectedValue:r,selectValue:o,tabValues:i}=e;const s=[],{blockElementScrollPositionUntilNextRender:d}=(0,l.a_)(),c=e=>{const n=e.currentTarget,a=s.indexOf(n),t=i[a].value;t!==r&&(d(n),o(t))},h=e=>{let n=null;switch(e.key){case"Enter":c(e);break;case"ArrowRight":{const a=s.indexOf(e.currentTarget)+1;n=s[a]??s[0];break}case"ArrowLeft":{const a=s.indexOf(e.currentTarget)-1;n=s[a]??s[s.length-1];break}}n?.focus()};return(0,b.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,t.A)("tabs",{"tabs--block":a},n),children:i.map((e=>{let{value:n,label:a,attributes:l}=e;return(0,b.jsx)("li",{role:"tab",tabIndex:r===n?0:-1,"aria-selected":r===n,ref:e=>s.push(e),onKeyDown:h,onClick:c,...l,className:(0,t.A)("tabs__item",j.tabItem,l?.className,{"tabs__item--active":r===n}),children:a??n},n)}))})}function v(e){let{lazy:n,children:a,selectedValue:l}=e;const o=(Array.isArray(a)?a:[a]).filter(Boolean);if(n){const e=o.find((e=>e.props.value===l));return e?(0,r.cloneElement)(e,{className:(0,t.A)("margin-top--md",e.props.className)}):null}return(0,b.jsx)("div",{className:"margin-top--md",children:o.map(((e,n)=>(0,r.cloneElement)(e,{key:n,hidden:e.props.value!==l})))})}function y(e){const n=x(e);return(0,b.jsxs)("div",{className:(0,t.A)("tabs-container",j.tabList),children:[(0,b.jsx)(g,{...n,...e}),(0,b.jsx)(v,{...n,...e})]})}function w(e){const n=(0,m.A)();return(0,b.jsx)(y,{...e,children:h(e.children)},String(n))}},8453:(e,n,a)=>{a.d(n,{R:()=>o,x:()=>i});var r=a(6540);const t={},l=r.createContext(t);function o(e){const n=r.useContext(l);return r.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function i(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:o(e.components),r.createElement(l.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/e33a6d67.82468289.js b/assets/js/e33a6d67.82468289.js new file mode 100644 index 000000000..44d4af534 --- /dev/null +++ b/assets/js/e33a6d67.82468289.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunklh_site=self.webpackChunklh_site||[]).push([[3324],{7720:(e,n,a)=>{a.r(n),a.d(n,{assets:()=>c,contentTitle:()=>s,default:()=>f,frontMatter:()=>o,metadata:()=>d,toc:()=>h});var r=a(4848),t=a(8453),l=a(1470),i=a(9365);const o={},s="Basics",d={id:"developer-guide/wfspec-development/basics",title:"Basics",description:"To develop a WfSpec in LittleHorse, you can use the Workflow struct or object in our SDK's. Generally, the Workflow entity constructor requires two arguments:",source:"@site/docs/05-developer-guide/08-wfspec-development/01-basics.md",sourceDirName:"05-developer-guide/08-wfspec-development",slug:"/developer-guide/wfspec-development/basics",permalink:"/docs/developer-guide/wfspec-development/basics",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"WfSpec Development",permalink:"/docs/developer-guide/wfspec-development/"},next:{title:"Conditionals and Loops",permalink:"/docs/developer-guide/wfspec-development/conditionals"}},c={},h=[{value:"Quickstart",id:"quickstart",level:2},{value:"Defining a 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 child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the component should be , and every should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function u(e){const{values:n,children:a}=e;return(0,r.useMemo)((()=>{const e=n??function(e){return h(e).map((e=>{let{props:{value:n,label:a,attributes:r,default:t}}=e;return{value:n,label:a,attributes:r,default:t}}))}(a);return function(e){const n=(0,d.XI)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in . Every value needs to be unique.`)}(e),e}),[n,a])}function f(e){let{value:n,tabValues:a}=e;return a.some((e=>e.value===n))}function p(e){let{queryString:n=!1,groupId:a}=e;const t=(0,i.W6)(),l=function(e){let{queryString:n=!1,groupId:a}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!a)throw new Error('Docusaurus error: The component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return a??null}({queryString:n,groupId:a});return[(0,s.aZ)(l),(0,r.useCallback)((e=>{if(!l)return;const n=new URLSearchParams(t.location.search);n.set(l,e),t.replace({...t.location,search:n.toString()})}),[l,t])]}function m(e){const{defaultValue:n,queryString:a=!1,groupId:t}=e,l=u(e),[i,s]=(0,r.useState)((()=>function(e){let{defaultValue:n,tabValues:a}=e;if(0===a.length)throw new Error("Docusaurus error: the component requires at least one children component");if(n){if(!f({value:n,tabValues:a}))throw new Error(`Docusaurus error: The has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${a.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const r=a.find((e=>e.default))??a[0];if(!r)throw new Error("Unexpected error: 0 tabValues");return r.value}({defaultValue:n,tabValues:l}))),[d,h]=p({queryString:a,groupId:t}),[m,x]=function(e){let{groupId:n}=e;const a=function(e){return e?`docusaurus.tab.${e}`:null}(n),[t,l]=(0,c.Dv)(a);return[t,(0,r.useCallback)((e=>{a&&l.set(e)}),[a,l])]}({groupId:t}),j=(()=>{const e=d??m;return f({value:e,tabValues:l})?e:null})();(0,o.A)((()=>{j&&s(j)}),[j]);return{selectedValue:i,selectValue:(0,r.useCallback)((e=>{if(!f({value:e,tabValues:l}))throw new Error(`Can't select invalid tab value=${e}`);s(e),h(e),x(e)}),[h,x,l]),tabValues:l}}var x=a(2303);const j={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var b=a(4848);function g(e){let{className:n,block:a,selectedValue:r,selectValue:i,tabValues:o}=e;const s=[],{blockElementScrollPositionUntilNextRender:d}=(0,l.a_)(),c=e=>{const n=e.currentTarget,a=s.indexOf(n),t=o[a].value;t!==r&&(d(n),i(t))},h=e=>{let n=null;switch(e.key){case"Enter":c(e);break;case"ArrowRight":{const a=s.indexOf(e.currentTarget)+1;n=s[a]??s[0];break}case"ArrowLeft":{const a=s.indexOf(e.currentTarget)-1;n=s[a]??s[s.length-1];break}}n?.focus()};return(0,b.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,t.A)("tabs",{"tabs--block":a},n),children:o.map((e=>{let{value:n,label:a,attributes:l}=e;return(0,b.jsx)("li",{role:"tab",tabIndex:r===n?0:-1,"aria-selected":r===n,ref:e=>s.push(e),onKeyDown:h,onClick:c,...l,className:(0,t.A)("tabs__item",j.tabItem,l?.className,{"tabs__item--active":r===n}),children:a??n},n)}))})}function v(e){let{lazy:n,children:a,selectedValue:l}=e;const i=(Array.isArray(a)?a:[a]).filter(Boolean);if(n){const e=i.find((e=>e.props.value===l));return e?(0,r.cloneElement)(e,{className:(0,t.A)("margin-top--md",e.props.className)}):null}return(0,b.jsx)("div",{className:"margin-top--md",children:i.map(((e,n)=>(0,r.cloneElement)(e,{key:n,hidden:e.props.value!==l})))})}function y(e){const n=m(e);return(0,b.jsxs)("div",{className:(0,t.A)("tabs-container",j.tabList),children:[(0,b.jsx)(g,{...n,...e}),(0,b.jsx)(v,{...n,...e})]})}function w(e){const n=(0,x.A)();return(0,b.jsx)(y,{...e,children:h(e.children)},String(n))}},8453:(e,n,a)=>{a.d(n,{R:()=>i,x:()=>o});var r=a(6540);const t={},l=r.createContext(t);function i(e){const n=r.useContext(l);return r.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:i(e.components),r.createElement(l.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.3f9a0c72.js b/assets/js/runtime~main.93137d14.js similarity index 97% rename from assets/js/runtime~main.3f9a0c72.js rename to assets/js/runtime~main.93137d14.js index 5ba765d54..a8192a33b 100644 --- a/assets/js/runtime~main.3f9a0c72.js +++ b/assets/js/runtime~main.93137d14.js @@ -1 +1 @@ -(()=>{"use strict";var e,a,f,c,d,b={},t={};function r(e){var a=t[e];if(void 0!==a)return a.exports;var f=t[e]={exports:{}};return b[e].call(f.exports,f,f.exports,r),f.exports}r.m=b,e=[],r.O=(a,f,c,d)=>{if(!f){var b=1/0;for(i=0;i=d)&&Object.keys(r.O).every((e=>r.O[e](f[o])))?f.splice(o--,1):(t=!1,d0&&e[i-1][2]>d;i--)e[i]=e[i-1];e[i]=[f,c,d]},r.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return r.d(a,{a:a}),a},f=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,c){if(1&c&&(e=this(e)),8&c)return e;if("object"==typeof e&&e){if(4&c&&e.__esModule)return e;if(16&c&&"function"==typeof e.then)return e}var d=Object.create(null);r.r(d);var b={};a=a||[null,f({}),f([]),f(f)];for(var t=2&c&&e;"object"==typeof t&&!~a.indexOf(t);t=f(t))Object.getOwnPropertyNames(t).forEach((a=>b[a]=()=>e[a]));return b.default=()=>e,r.d(d,b),d},r.d=(e,a)=>{for(var f in a)r.o(a,f)&&!r.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:a[f]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((a,f)=>(r.f[f](e,a),a)),[])),r.u=e=>"assets/js/"+({216:"f23abd89",300:"a06c0eb2",312:"209570fd",538:"5b3e9818",826:"e3618154",832:"dd97924f",849:"0058b4c6",867:"33fc5bb8",957:"c141421f",1173:"0fad6d0a",1235:"a7456010",1561:"b23d33c8",1634:"32279f3c",1642:"66680c26",1715:"81a46808",1872:"c9736c35",1886:"7f375326",1903:"acecf23e",1946:"5d0b0e70",2138:"1a4e3797",2296:"d0f4e7d1",2558:"f21a8f01",2634:"c4f5d8e4",2711:"9e4087bc",2775:"57cb429f",3133:"8bb7c884",3234:"eed23361",3249:"ccc49370",3324:"e33a6d67",3361:"c69aaf89",3372:"9a24bfa0",3408:"bd28587a",3457:"5b77cc54",3484:"bead5408",3828:"8bc86172",3896:"971c8a60",3983:"e359e6f5",4098:"141346fb",4120:"9b53f530",4134:"393be207",4212:"621db11d",4325:"366d7c2c",4372:"015df829",4635:"f447dd38",4723:"83879120",4792:"150c26e9",4813:"6875c492",4982:"524a0089",5174:"a957f15c",5739:"7f97f38f",5742:"aba21aa0",5808:"49cd91b3",6051:"f13b4cb1",6061:"1f391b9e",6309:"80b6e621",6417:"6ed84113",6583:"d61070d0",6848:"24a81a13",6928:"6ddfedc5",6958:"2494aaaa",7080:"df0890b4",7098:"a7bd4aaa",7130:"d5e335f6",7142:"fb7e2344",7173:"a86a7ed2",7316:"8dbae9ea",7434:"23f588f5",7472:"814f3328",7506:"845dc8ad",7643:"a6aa9e1f",7746:"7e3e0d30",7757:"84954295",7762:"b136319f",7784:"d9c5bba9",7828:"2a162317",7948:"ddb4e1f1",7962:"5263bac3",8035:"5a5f8fd5",8046:"be5744c7",8090:"74974c25",8121:"3a2db09e",8130:"f81c1134",8146:"c15d9823",8209:"01a85c17",8401:"17896441",8648:"5af1e9f6",8892:"4781d3d2",8947:"ef8b811a",9045:"a1824316",9048:"a94703ab",9291:"5a6d38a2",9371:"0bd106d9",9403:"d52af3ba",9445:"62796615",9615:"b770e739",9647:"5e95c892",9685:"b1ad0a29",9858:"36994c47",9905:"59f5fc7c",9964:"1d85cfc3",9993:"477ea5a4"}[e]||e)+"."+{216:"0404a672",300:"13eb8aee",312:"a04cc24b",416:"bd6759d1",538:"5bdb5f9d",826:"9b13d064",832:"9ef757fe",849:"0ee5924f",867:"40807a62",957:"541d7437",1173:"41f42246",1235:"fbb20977",1561:"59f8b0c9",1634:"8627ffc2",1642:"23896f8d",1715:"1d30f3e3",1872:"d4158810",1886:"7d79d881",1903:"8f73abf1",1946:"e68fd9dc",2138:"3ec61d80",2237:"68217b8a",2296:"8a374718",2558:"939e0c35",2634:"3870273d",2711:"0ef30c75",2775:"2b0514fd",3133:"e8f7f9e6",3234:"a7f55e7e",3249:"16f4a0e3",3324:"34ee8db2",3347:"7a0c4bea",3361:"7f98a7e0",3372:"b979efa0",3408:"f68ac721",3457:"17f618c6",3484:"2d15433d",3828:"566493d9",3896:"e472b2fd",3983:"73afef77",4098:"75fa4823",4120:"715210c2",4134:"b2bf3017",4212:"3dca9c5a",4325:"0f6833c6",4372:"c4bfbae7",4635:"2cd44f06",4723:"a8a9ae07",4792:"dfc9c07e",4813:"174f4a92",4982:"b73f52a6",5174:"f75c6662",5739:"38383c7b",5742:"382f6066",5808:"ccdd0eae",6051:"083ab604",6061:"1887799c",6309:"eec88f9d",6417:"dde390bc",6583:"9bde1063",6848:"397031be",6928:"5755d482",6958:"684a0a3d",7080:"a0b8ea9e",7098:"03a92e34",7130:"91c474a6",7142:"21ec2458",7173:"ee6a52f0",7316:"4c7f823c",7434:"3c383517",7472:"a5db8ea4",7506:"baeb401a",7643:"44dbae04",7746:"7cf38925",7757:"588f31c1",7762:"fb8d8543",7784:"b1e3c7dc",7828:"3a272e13",7948:"82873e64",7962:"24658d99",8035:"bd685f79",8046:"b55b8036",8090:"138c33f9",8121:"29ad30f3",8130:"3437bee0",8146:"55c527c7",8158:"26510717",8209:"471c1b01",8401:"8c1290d9",8498:"14012ec5",8648:"578d9587",8892:"e1d5b690",8913:"25f0add6",8947:"a4c3f802",9045:"f72768dd",9048:"da01bcc7",9291:"90b0091b",9371:"3cea8738",9403:"cc882533",9445:"ec8840ea",9615:"c86f3b46",9647:"60173cbc",9685:"9cdbe99c",9858:"73ae0329",9905:"767a5198",9964:"e965be8b",9993:"ada62a64"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),c={},d="lh-site:",r.l=(e,a,f,b)=>{if(c[e])c[e].push(a);else{var t,o;if(void 0!==f)for(var n=document.getElementsByTagName("script"),i=0;i{t.onerror=t.onload=null,clearTimeout(s);var d=c[e];if(delete c[e],t.parentNode&&t.parentNode.removeChild(t),d&&d.forEach((e=>e(f))),a)return a(f)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/",r.gca=function(e){return e={17896441:"8401",62796615:"9445",83879120:"4723",84954295:"7757",f23abd89:"216",a06c0eb2:"300","209570fd":"312","5b3e9818":"538",e3618154:"826",dd97924f:"832","0058b4c6":"849","33fc5bb8":"867",c141421f:"957","0fad6d0a":"1173",a7456010:"1235",b23d33c8:"1561","32279f3c":"1634","66680c26":"1642","81a46808":"1715",c9736c35:"1872","7f375326":"1886",acecf23e:"1903","5d0b0e70":"1946","1a4e3797":"2138",d0f4e7d1:"2296",f21a8f01:"2558",c4f5d8e4:"2634","9e4087bc":"2711","57cb429f":"2775","8bb7c884":"3133",eed23361:"3234",ccc49370:"3249",e33a6d67:"3324",c69aaf89:"3361","9a24bfa0":"3372",bd28587a:"3408","5b77cc54":"3457",bead5408:"3484","8bc86172":"3828","971c8a60":"3896",e359e6f5:"3983","141346fb":"4098","9b53f530":"4120","393be207":"4134","621db11d":"4212","366d7c2c":"4325","015df829":"4372",f447dd38:"4635","150c26e9":"4792","6875c492":"4813","524a0089":"4982",a957f15c:"5174","7f97f38f":"5739",aba21aa0:"5742","49cd91b3":"5808",f13b4cb1:"6051","1f391b9e":"6061","80b6e621":"6309","6ed84113":"6417",d61070d0:"6583","24a81a13":"6848","6ddfedc5":"6928","2494aaaa":"6958",df0890b4:"7080",a7bd4aaa:"7098",d5e335f6:"7130",fb7e2344:"7142",a86a7ed2:"7173","8dbae9ea":"7316","23f588f5":"7434","814f3328":"7472","845dc8ad":"7506",a6aa9e1f:"7643","7e3e0d30":"7746",b136319f:"7762",d9c5bba9:"7784","2a162317":"7828",ddb4e1f1:"7948","5263bac3":"7962","5a5f8fd5":"8035",be5744c7:"8046","74974c25":"8090","3a2db09e":"8121",f81c1134:"8130",c15d9823:"8146","01a85c17":"8209","5af1e9f6":"8648","4781d3d2":"8892",ef8b811a:"8947",a1824316:"9045",a94703ab:"9048","5a6d38a2":"9291","0bd106d9":"9371",d52af3ba:"9403",b770e739:"9615","5e95c892":"9647",b1ad0a29:"9685","36994c47":"9858","59f5fc7c":"9905","1d85cfc3":"9964","477ea5a4":"9993"}[e]||e,r.p+r.u(e)},(()=>{var e={5354:0,1869:0};r.f.j=(a,f)=>{var c=r.o(e,a)?e[a]:void 0;if(0!==c)if(c)f.push(c[2]);else if(/^(1869|5354)$/.test(a))e[a]=0;else{var d=new Promise(((f,d)=>c=e[a]=[f,d]));f.push(c[2]=d);var b=r.p+r.u(a),t=new Error;r.l(b,(f=>{if(r.o(e,a)&&(0!==(c=e[a])&&(e[a]=void 0),c)){var d=f&&("load"===f.type?"missing":f.type),b=f&&f.target&&f.target.src;t.message="Loading chunk "+a+" failed.\n("+d+": "+b+")",t.name="ChunkLoadError",t.type=d,t.request=b,c[1](t)}}),"chunk-"+a,a)}},r.O.j=a=>0===e[a];var a=(a,f)=>{var c,d,b=f[0],t=f[1],o=f[2],n=0;if(b.some((a=>0!==e[a]))){for(c in t)r.o(t,c)&&(r.m[c]=t[c]);if(o)var i=o(r)}for(a&&a(f);n{"use strict";var e,a,f,c,d,b={},t={};function r(e){var a=t[e];if(void 0!==a)return a.exports;var f=t[e]={exports:{}};return b[e].call(f.exports,f,f.exports,r),f.exports}r.m=b,e=[],r.O=(a,f,c,d)=>{if(!f){var b=1/0;for(i=0;i=d)&&Object.keys(r.O).every((e=>r.O[e](f[o])))?f.splice(o--,1):(t=!1,d0&&e[i-1][2]>d;i--)e[i]=e[i-1];e[i]=[f,c,d]},r.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return r.d(a,{a:a}),a},f=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,c){if(1&c&&(e=this(e)),8&c)return e;if("object"==typeof e&&e){if(4&c&&e.__esModule)return e;if(16&c&&"function"==typeof e.then)return e}var d=Object.create(null);r.r(d);var b={};a=a||[null,f({}),f([]),f(f)];for(var t=2&c&&e;"object"==typeof t&&!~a.indexOf(t);t=f(t))Object.getOwnPropertyNames(t).forEach((a=>b[a]=()=>e[a]));return b.default=()=>e,r.d(d,b),d},r.d=(e,a)=>{for(var f in a)r.o(a,f)&&!r.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:a[f]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((a,f)=>(r.f[f](e,a),a)),[])),r.u=e=>"assets/js/"+({216:"f23abd89",300:"a06c0eb2",312:"209570fd",538:"5b3e9818",826:"e3618154",832:"dd97924f",849:"0058b4c6",867:"33fc5bb8",957:"c141421f",1173:"0fad6d0a",1235:"a7456010",1561:"b23d33c8",1634:"32279f3c",1642:"66680c26",1715:"81a46808",1872:"c9736c35",1886:"7f375326",1903:"acecf23e",1946:"5d0b0e70",2138:"1a4e3797",2296:"d0f4e7d1",2558:"f21a8f01",2634:"c4f5d8e4",2711:"9e4087bc",2775:"57cb429f",3133:"8bb7c884",3234:"eed23361",3249:"ccc49370",3324:"e33a6d67",3361:"c69aaf89",3372:"9a24bfa0",3408:"bd28587a",3457:"5b77cc54",3484:"bead5408",3828:"8bc86172",3896:"971c8a60",3983:"e359e6f5",4098:"141346fb",4120:"9b53f530",4134:"393be207",4212:"621db11d",4325:"366d7c2c",4372:"015df829",4635:"f447dd38",4723:"83879120",4792:"150c26e9",4813:"6875c492",4982:"524a0089",5174:"a957f15c",5739:"7f97f38f",5742:"aba21aa0",5808:"49cd91b3",6051:"f13b4cb1",6061:"1f391b9e",6309:"80b6e621",6417:"6ed84113",6583:"d61070d0",6848:"24a81a13",6928:"6ddfedc5",6958:"2494aaaa",7080:"df0890b4",7098:"a7bd4aaa",7130:"d5e335f6",7142:"fb7e2344",7173:"a86a7ed2",7316:"8dbae9ea",7434:"23f588f5",7472:"814f3328",7506:"845dc8ad",7643:"a6aa9e1f",7746:"7e3e0d30",7757:"84954295",7762:"b136319f",7784:"d9c5bba9",7828:"2a162317",7948:"ddb4e1f1",7962:"5263bac3",8035:"5a5f8fd5",8046:"be5744c7",8090:"74974c25",8121:"3a2db09e",8130:"f81c1134",8146:"c15d9823",8209:"01a85c17",8401:"17896441",8648:"5af1e9f6",8892:"4781d3d2",8947:"ef8b811a",9045:"a1824316",9048:"a94703ab",9291:"5a6d38a2",9371:"0bd106d9",9403:"d52af3ba",9445:"62796615",9615:"b770e739",9647:"5e95c892",9685:"b1ad0a29",9858:"36994c47",9905:"59f5fc7c",9964:"1d85cfc3",9993:"477ea5a4"}[e]||e)+"."+{216:"0404a672",300:"47a7d66e",312:"a04cc24b",416:"bd6759d1",538:"5bdb5f9d",826:"9b13d064",832:"9ef757fe",849:"0ee5924f",867:"40807a62",957:"541d7437",1173:"41f42246",1235:"fbb20977",1561:"59f8b0c9",1634:"8627ffc2",1642:"23896f8d",1715:"1d30f3e3",1872:"d4158810",1886:"7d79d881",1903:"8f73abf1",1946:"e68fd9dc",2138:"3ec61d80",2237:"68217b8a",2296:"8a374718",2558:"939e0c35",2634:"3870273d",2711:"0ef30c75",2775:"2b0514fd",3133:"e8f7f9e6",3234:"a7f55e7e",3249:"16f4a0e3",3324:"82468289",3347:"7a0c4bea",3361:"7f98a7e0",3372:"b979efa0",3408:"f68ac721",3457:"17f618c6",3484:"2d15433d",3828:"566493d9",3896:"e472b2fd",3983:"73afef77",4098:"75fa4823",4120:"715210c2",4134:"b2bf3017",4212:"3dca9c5a",4325:"0f6833c6",4372:"c4bfbae7",4635:"2cd44f06",4723:"a8a9ae07",4792:"dfc9c07e",4813:"174f4a92",4982:"b73f52a6",5174:"f75c6662",5739:"38383c7b",5742:"382f6066",5808:"ccdd0eae",6051:"083ab604",6061:"1887799c",6309:"eec88f9d",6417:"dde390bc",6583:"9bde1063",6848:"397031be",6928:"5755d482",6958:"684a0a3d",7080:"a0b8ea9e",7098:"03a92e34",7130:"91c474a6",7142:"21ec2458",7173:"ee6a52f0",7316:"4c7f823c",7434:"3c383517",7472:"a5db8ea4",7506:"baeb401a",7643:"44dbae04",7746:"7cf38925",7757:"588f31c1",7762:"fb8d8543",7784:"b1e3c7dc",7828:"3a272e13",7948:"82873e64",7962:"24658d99",8035:"bd685f79",8046:"b55b8036",8090:"138c33f9",8121:"29ad30f3",8130:"3437bee0",8146:"55c527c7",8158:"26510717",8209:"471c1b01",8401:"8c1290d9",8498:"14012ec5",8648:"578d9587",8892:"e1d5b690",8913:"25f0add6",8947:"a4c3f802",9045:"f72768dd",9048:"da01bcc7",9291:"90b0091b",9371:"3cea8738",9403:"cc882533",9445:"08ee0677",9615:"c86f3b46",9647:"60173cbc",9685:"9cdbe99c",9858:"73ae0329",9905:"767a5198",9964:"e965be8b",9993:"ada62a64"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),c={},d="lh-site:",r.l=(e,a,f,b)=>{if(c[e])c[e].push(a);else{var t,o;if(void 0!==f)for(var n=document.getElementsByTagName("script"),i=0;i{t.onerror=t.onload=null,clearTimeout(s);var d=c[e];if(delete c[e],t.parentNode&&t.parentNode.removeChild(t),d&&d.forEach((e=>e(f))),a)return a(f)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/",r.gca=function(e){return e={17896441:"8401",62796615:"9445",83879120:"4723",84954295:"7757",f23abd89:"216",a06c0eb2:"300","209570fd":"312","5b3e9818":"538",e3618154:"826",dd97924f:"832","0058b4c6":"849","33fc5bb8":"867",c141421f:"957","0fad6d0a":"1173",a7456010:"1235",b23d33c8:"1561","32279f3c":"1634","66680c26":"1642","81a46808":"1715",c9736c35:"1872","7f375326":"1886",acecf23e:"1903","5d0b0e70":"1946","1a4e3797":"2138",d0f4e7d1:"2296",f21a8f01:"2558",c4f5d8e4:"2634","9e4087bc":"2711","57cb429f":"2775","8bb7c884":"3133",eed23361:"3234",ccc49370:"3249",e33a6d67:"3324",c69aaf89:"3361","9a24bfa0":"3372",bd28587a:"3408","5b77cc54":"3457",bead5408:"3484","8bc86172":"3828","971c8a60":"3896",e359e6f5:"3983","141346fb":"4098","9b53f530":"4120","393be207":"4134","621db11d":"4212","366d7c2c":"4325","015df829":"4372",f447dd38:"4635","150c26e9":"4792","6875c492":"4813","524a0089":"4982",a957f15c:"5174","7f97f38f":"5739",aba21aa0:"5742","49cd91b3":"5808",f13b4cb1:"6051","1f391b9e":"6061","80b6e621":"6309","6ed84113":"6417",d61070d0:"6583","24a81a13":"6848","6ddfedc5":"6928","2494aaaa":"6958",df0890b4:"7080",a7bd4aaa:"7098",d5e335f6:"7130",fb7e2344:"7142",a86a7ed2:"7173","8dbae9ea":"7316","23f588f5":"7434","814f3328":"7472","845dc8ad":"7506",a6aa9e1f:"7643","7e3e0d30":"7746",b136319f:"7762",d9c5bba9:"7784","2a162317":"7828",ddb4e1f1:"7948","5263bac3":"7962","5a5f8fd5":"8035",be5744c7:"8046","74974c25":"8090","3a2db09e":"8121",f81c1134:"8130",c15d9823:"8146","01a85c17":"8209","5af1e9f6":"8648","4781d3d2":"8892",ef8b811a:"8947",a1824316:"9045",a94703ab:"9048","5a6d38a2":"9291","0bd106d9":"9371",d52af3ba:"9403",b770e739:"9615","5e95c892":"9647",b1ad0a29:"9685","36994c47":"9858","59f5fc7c":"9905","1d85cfc3":"9964","477ea5a4":"9993"}[e]||e,r.p+r.u(e)},(()=>{var e={5354:0,1869:0};r.f.j=(a,f)=>{var c=r.o(e,a)?e[a]:void 0;if(0!==c)if(c)f.push(c[2]);else if(/^(1869|5354)$/.test(a))e[a]=0;else{var d=new Promise(((f,d)=>c=e[a]=[f,d]));f.push(c[2]=d);var b=r.p+r.u(a),t=new Error;r.l(b,(f=>{if(r.o(e,a)&&(0!==(c=e[a])&&(e[a]=void 0),c)){var d=f&&("load"===f.type?"missing":f.type),b=f&&f.target&&f.target.src;t.message="Loading chunk "+a+" failed.\n("+d+": "+b+")",t.name="ChunkLoadError",t.type=d,t.request=b,c[1](t)}}),"chunk-"+a,a)}},r.O.j=a=>0===e[a];var a=(a,f)=>{var c,d,b=f[0],t=f[1],o=f[2],n=0;if(b.some((a=>0!==e[a]))){for(c in t)r.o(t,c)&&(r.m[c]=t[c]);if(o)var i=o(r)}for(a&&a(f);n