Skip to content

Commit

Permalink
fix(go-client): support setVariable, setResource, setState, getState
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenfiszel committed Nov 19, 2022
1 parent ab2d480 commit f85c7f0
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 44 deletions.
24 changes: 23 additions & 1 deletion backend/windmill-common/src/variables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,24 @@ pub fn get_reserved_variables(
flow_id: Option<String>,
flow_path: Option<String>,
schedule_path: Option<String>,
) -> [ContextualVariable; 11] {
) -> [ContextualVariable; 12] {
let state_path = {
let flow_path = flow_path
.clone()
.unwrap_or_else(|| "NO_FLOW_PATH".to_string());
let script_path = path.clone().unwrap_or_else(|| "NO_JOB_PATH".to_string());
let schedule_path = schedule_path
.clone()
.map(|x| format!("/{x}"))
.unwrap_or_else(String::new);

let script_path = if script_path.ends_with("/") {
"NO_NAME".to_string()
} else {
script_path
};
format!("{permissioned_as}/{flow_path}/{script_path}{schedule_path}")
};
[
ContextualVariable {
name: "WM_WORKSPACE".to_string(),
Expand Down Expand Up @@ -114,5 +131,10 @@ pub fn get_reserved_variables(
value: permissioned_as.to_string(),
description: "Fully Qualified (u/g) owner name of executor of the job".to_string(),
},
ContextualVariable {
name: "WM_STATE_PATH".to_string(),
value: state_path,
description: "State resource path unique to a script and its trigger".to_string(),
},
]
}
15 changes: 4 additions & 11 deletions deno-client/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,11 @@ export async function getResource(path?: string, undefinedIfEmpty?: boolean): Pr
}

export function getStatePath(): string {
const env_flow_path = Deno.env.get("WM_FLOW_PATH")
const env_job_path = Deno.env.get("WM_JOB_PATH")
const permissioned_as = Deno.env.get("WM_PERMISSIONED_AS")
const flow_path = env_flow_path != undefined && env_flow_path != "" ? env_flow_path : 'NO_FLOW_PATH'
const script_path = env_job_path != undefined && env_job_path != "" ? env_job_path : 'NO_JOB_PATH'
const env_schedule_path = Deno.env.get("WM_SCHEDULE_PATH")
const schedule_path = env_schedule_path != undefined && env_schedule_path != "" ? `/${env_schedule_path}` : ''

if (script_path.slice(script_path.length - 1) === '/') {
throw Error(`The script path must not end with '/', give a name to your script!`)
const state_path = Deno.env.get("WM_STATE_PATH")
if (state_path === undefined) {
throw Error("State path not set")
}
return `${permissioned_as}/${flow_path}/${script_path}${schedule_path}`
return state_path
}

/**
Expand Down
68 changes: 64 additions & 4 deletions go-client/windmill.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@ package windmill

import (
"context"
"errors"
"fmt"
"net/http"
"os"

api "github.com/windmill-labs/windmill-go-client/api"
)

func hello_world() {
fmt.Println("Windmill")
}

type ClientWithWorkspace struct {
Client *api.ClientWithResponses
Workspace string
Expand Down Expand Up @@ -47,6 +44,9 @@ func GetVariable(path string) (string, error) {
res, err := client.Client.GetVariableWithResponse(context.Background(), client.Workspace, path, &api.GetVariableParams{
DecryptSecret: newBool(true),
})
if res.StatusCode()/100 != 2 {
return "", errors.New(string(res.Body))
}
if err != nil {
return "", err
}
Expand All @@ -59,8 +59,68 @@ func GetResource(path string) (interface{}, error) {
return nil, err
}
res, err := client.Client.GetResourceWithResponse(context.Background(), client.Workspace, path)
if res.StatusCode()/100 != 2 {
return nil, errors.New(string(res.Body))
}
if err != nil {
return nil, err
}
return *res.JSON200.Value, nil
}

func SetResource(path string, value interface{}) error {
client, err := GetClient()
if err != nil {
return err
}
res, err := client.Client.UpdateResourceWithResponse(context.Background(), client.Workspace, path, api.EditResource{Value: &value})
if err != nil {
return err
}
if res.StatusCode()/100 != 2 {
return errors.New(string(res.Body))
}
return nil
}

func SetVariable(path string, value string) error {
client, err := GetClient()
if err != nil {
return err
}
res, err := client.Client.UpdateVariableWithResponse(context.Background(), client.Workspace, path, api.EditVariable{Value: &value})
if err != nil {
return err
}
if res.StatusCode()/100 != 2 {
return errors.New(string(res.Body))
}
return nil
}

func GetStatePath() string {
return os.Getenv("WM_STATE_PATH")
}

func GetState() (interface{}, error) {
return GetResource(GetStatePath())
}

func SetState(state interface{}) error {
err := SetResource(GetStatePath(), state)
if err != nil {
client, err := GetClient()
if err != nil {
return err
}
res, err := client.Client.CreateResourceWithResponse(context.Background(), client.Workspace, api.CreateResource{Value: &state})
if err != nil {
return err
}
if res.StatusCode()/100 != 2 {
return errors.New(string(res.Body))
}
return nil
}
return nil
}
77 changes: 49 additions & 28 deletions python-client/wmill/wmill/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ class JobStatus(Enum):
_client: "AuthenticatedClient | None" = None


def create_client(base_url: "str | None" = None, token: "str | None" = None) -> AuthenticatedClient:
def create_client(
base_url: "str | None" = None, token: "str | None" = None
) -> AuthenticatedClient:
env_base_url = os.environ.get("BASE_INTERNAL_URL")

if env_base_url is not None:
Expand All @@ -63,7 +65,9 @@ def create_client(base_url: "str | None" = None, token: "str | None" = None) ->
token_: str = token or os.environ.get("WM_TOKEN") or ""
global _client
if _client is None:
_client = AuthenticatedClient(base_url=base_url_, token=token_, timeout=30, verify_ssl=False)
_client = AuthenticatedClient(
base_url=base_url_, token=token_, timeout=30, verify_ssl=False
)
return _client


Expand All @@ -78,7 +82,9 @@ def get_version() -> str:
"""
Returns the current version of the backend
"""
return backend_version.sync_detailed(client=create_client()).content.decode("us-ascii")
return backend_version.sync_detailed(client=create_client()).content.decode(
"us-ascii"
)


def run_script_async(
Expand All @@ -99,7 +105,9 @@ def run_script_async(
).content.decode("us-ascii")


def run_script_sync(hash: str, args: Dict[str, Any] = {}, verbose: bool = False) -> Dict[str, Any]:
def run_script_sync(
hash: str, args: Dict[str, Any] = {}, verbose: bool = False
) -> Dict[str, Any]:
"""
Run a script, wait for it to complete and return the result of the launched script
"""
Expand All @@ -120,7 +128,9 @@ def get_job_status(job_id: str) -> JobStatus:
"""
Returns the status of a queued or completed job
"""
res = get_job.sync_detailed(client=create_client(), workspace=get_workspace(), id=job_id).parsed
res = get_job.sync_detailed(
client=create_client(), workspace=get_workspace(), id=job_id
).parsed
if not res:
raise Exception(f"Job {job_id} not found")
elif not res.type:
Expand All @@ -140,7 +150,9 @@ def get_result(job_id: str) -> Dict[str, Any]:
"""
Returns the result of a completed job
"""
res = get_completed_job.sync_detailed(client=create_client(), workspace=get_workspace(), id=job_id).parsed
res = get_completed_job.sync_detailed(
client=create_client(), workspace=get_workspace(), id=job_id
).parsed
if not res:
raise Exception(f"Job {job_id} not found")
if not res.result:
Expand All @@ -154,12 +166,16 @@ def get_resource(path: str | None = None, none_if_undefined: bool = False) -> An
Returns the resource at a given path
"""
path = path or get_state_path()
parsed = get_resource_api.sync_detailed(workspace=get_workspace(), path=path, client=create_client()).parsed
parsed = get_resource_api.sync_detailed(
workspace=get_workspace(), path=path, client=create_client()
).parsed
if parsed is None:
if none_if_undefined:
return None
else:
raise Exception(f"Resource at path {path} does not exist or you do not have read permissions on it")
raise Exception(
f"Resource at path {path} does not exist or you do not have read permissions on it"
)

if isinstance(parsed.value, Unset):
return None
Expand All @@ -175,18 +191,24 @@ def get_state() -> Any:
return get_resource(None, True)


def set_resource(value: Any, path: str | None = None, resource_type: str = "state") -> None:
def set_resource(
value: Any, path: str | None = None, resource_type: str = "state"
) -> None:
"""
Set the resource at a given path as a string, creating it if it does not exist
"""
path = path or get_state_path()
workspace = get_workspace()
client = create_client()
if not exists_resource.sync_detailed(workspace=workspace, path=path, client=client).parsed:
if not exists_resource.sync_detailed(
workspace=workspace, path=path, client=client
).parsed:
create_resource.sync_detailed(
workspace=workspace,
client=client,
json_body=CreateResourceJsonBody(path=path, value=value, resource_type=resource_type),
json_body=CreateResourceJsonBody(
path=path, value=value, resource_type=resource_type
),
)
else:
update_resource.sync_detailed(
Expand All @@ -208,9 +230,13 @@ def get_variable(path: str) -> str:
"""
Returns the variable at a given path as a string
"""
res = get_variable_api.sync_detailed(workspace=get_workspace(), path=path, client=create_client()).parsed
res = get_variable_api.sync_detailed(
workspace=get_workspace(), path=path, client=create_client()
).parsed
if res is None:
raise Exception(f"Variable at path {path} does not exist or you do not have read permissions on it")
raise Exception(
f"Variable at path {path} does not exist or you do not have read permissions on it"
)

return res.value # type: ignore

Expand All @@ -221,11 +247,15 @@ def set_variable(path: str, value: str) -> None:
"""
workspace = get_workspace()
client = create_client()
if not exists_variable.sync_detailed(workspace=workspace, path=path, client=client).parsed:
if not exists_variable.sync_detailed(
workspace=workspace, path=path, client=client
).parsed:
create_variable.sync_detailed(
workspace=workspace,
client=client,
json_body=CreateVariableJsonBody(path=path, value=value, is_secret=False, description=""),
json_body=CreateVariableJsonBody(
path=path, value=value, is_secret=False, description=""
),
)
else:
update_variable.sync_detailed(
Expand All @@ -237,19 +267,10 @@ def set_variable(path: str, value: str) -> None:


def get_state_path() -> str:
"""
Get a stable path for next execution of the script to point to the same resource
"""
permissioned_as = os.environ.get("WM_PERMISSIONED_AS")
flow_path = os.environ.get("WM_FLOW_PATH") or "NO_FLOW_PATH"
script_path = os.environ.get("WM_JOB_PATH") or "NO_JOB_PATH"
env_schedule_path = os.environ.get("WM_SCHEDULE_PATH")
schedule_path = "" if env_schedule_path is None or env_schedule_path == "" else f"/{env_schedule_path}"

if script_path.endswith("/"):
raise Exception("The script path must not end with '/', give a name to your script!")

return f"{permissioned_as}/{flow_path}/{script_path}{schedule_path}"
state_path = os.environ.get("WM_STATE_PATH")
if state_path is None:
raise Exception("State path not found")
return state_path


def _transform_leaves(d: Dict[str, Any]) -> Dict[str, Any]:
Expand Down

0 comments on commit f85c7f0

Please sign in to comment.