Skip to content

Commit

Permalink
Rename 'secrets' to 'credentials' in tool parsing
Browse files Browse the repository at this point in the history
and related methods and
use the discussed XML format #19196
  • Loading branch information
arash77 committed Nov 28, 2024
1 parent bdf8667 commit f2a773c
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 98 deletions.
4 changes: 2 additions & 2 deletions lib/galaxy/tool_util/cwl/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,8 @@ def software_requirements(self) -> List:
def resource_requirements(self) -> List:
return self.hints_or_requirements_of_class("ResourceRequirement")

def secrets(self) -> List:
return self.hints_or_requirements_of_class("Secrets")
def credentials(self) -> List:
return self.hints_or_requirements_of_class("Credentials")


class CommandLineToolProxy(ToolProxy):
Expand Down
125 changes: 87 additions & 38 deletions lib/galaxy/tool_util/deps/requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,66 +306,113 @@ def resource_requirements_from_list(requirements: Iterable[Dict[str, Any]]) -> L
return rr


class SecretsRequirement:
class SecretOrVariable:
def __init__(
self,
type: str,
user_preferences_key: str,
name: str,
inject_as_env: str,
label: Optional[str] = "",
required: Optional[bool] = False,
label: str = "",
description: str = "",
) -> None:
self.type = type
self.user_preferences_key = user_preferences_key
self.name = name
self.inject_as_env = inject_as_env
self.label = label
self.required = required
if not self.user_preferences_key:
raise ValueError("Missing user_preferences_key")
seperated_key = user_preferences_key.split("/")
if len(seperated_key) != 2 or not seperated_key[0] or not seperated_key[1]:
raise ValueError("Invalid user_preferences_key")
if self.type not in {"vault"}:
raise ValueError(f"Invalid secret type '{self.type}'")
self.description = description
if self.type not in {"secret", "variable"}:
raise ValueError(f"Invalid credential type '{self.type}'")
if not self.inject_as_env:
raise ValueError("Missing inject_as_env")

def to_dict(self) -> Dict[str, Any]:
return {
"type": self.type,
"user_preferences_key": self.user_preferences_key,
"name": self.name,
"inject_as_env": self.inject_as_env,
"label": self.label,
"required": self.required,
"description": self.description,
}

@classmethod
def from_dict(cls, dict: Dict[str, Any]) -> "SecretsRequirement":
def from_element(cls, elem) -> "SecretOrVariable":
return cls(
type=elem.tag,
name=elem.get("name"),
inject_as_env=elem.get("inject_as_env"),
label=elem.get("label", ""),
description=elem.get("description", ""),
)

@classmethod
def from_dict(cls, dict: Dict[str, Any]) -> "SecretOrVariable":
type = dict["type"]
user_preferences_key = dict["user_preferences_key"]
name = dict["name"]
inject_as_env = dict["inject_as_env"]
label = dict.get("label", "")
description = dict.get("description", "")
return cls(type=type, name=name, inject_as_env=inject_as_env, label=label, description=description)


class CredentialsRequirement:
def __init__(
self,
name: str,
reference: str,
required: bool = False,
label: str = "",
description: str = "",
secrets_and_variables: Optional[List[SecretOrVariable]] = None,
) -> None:
self.name = name
self.reference = reference
self.required = required
self.label = label
self.description = description
self.secrets_and_variables = secrets_and_variables if secrets_and_variables is not None else []

if not self.reference:
raise ValueError("Missing reference")

def to_dict(self) -> Dict[str, Any]:
return {
"name": self.name,
"reference": self.reference,
"required": self.required,
"label": self.label,
"description": self.description,
"secrets_and_variables": [s.to_dict() for s in self.secrets_and_variables],
}

@classmethod
def from_dict(cls, dict: Dict[str, Any]) -> "CredentialsRequirement":
name = dict["name"]
reference = dict["reference"]
required = dict.get("required", False)
label = dict.get("label", "")
description = dict.get("description", "")
secrets_and_variables = [SecretOrVariable.from_dict(s) for s in dict.get("secrets_and_variables", [])]
return cls(
type=type,
user_preferences_key=user_preferences_key,
inject_as_env=inject_as_env,
label=label,
name=name,
reference=reference,
required=required,
label=label,
description=description,
secrets_and_variables=secrets_and_variables,
)


def parse_requirements_from_lists(
software_requirements: List[Union[ToolRequirement, Dict[str, Any]]],
containers: Iterable[Dict[str, Any]],
resource_requirements: Iterable[Dict[str, Any]],
secrets: Iterable[Dict[str, Any]],
) -> Tuple[ToolRequirements, List[ContainerDescription], List[ResourceRequirement], List[SecretsRequirement]]:
credentials: Iterable[Dict[str, Any]],
) -> Tuple[ToolRequirements, List[ContainerDescription], List[ResourceRequirement], List[CredentialsRequirement]]:
return (
ToolRequirements.from_list(software_requirements),
[ContainerDescription.from_dict(c) for c in containers],
resource_requirements_from_list(resource_requirements),
[SecretsRequirement.from_dict(s) for s in secrets],
[CredentialsRequirement.from_dict(s) for s in credentials],
)


Expand Down Expand Up @@ -413,9 +460,9 @@ def parse_requirements_from_xml(xml_root, parse_resources_and_secrets: bool = Fa
if parse_resources_and_secrets:
resource_elems = requirements_elem.findall("resource") if requirements_elem is not None else []
resources = [resource_from_element(r) for r in resource_elems]
secret_elems = requirements_elem.findall("secret") if requirements_elem is not None else []
secrets = [secret_from_element(s) for s in secret_elems]
return requirements, containers, resources, secrets
credentials_elems = requirements_elem.findall("credentials") if requirements_elem is not None else []
credentials = [credentials_from_element(s) for s in credentials_elems]
return requirements, containers, resources, credentials

return requirements, containers

Expand All @@ -440,16 +487,18 @@ def container_from_element(container_elem) -> ContainerDescription:
return container


def secret_from_element(secret_elem) -> SecretsRequirement:
type = secret_elem.get("type")
user_preferences_key = secret_elem.get("user_preferences_key")
inject_as_env = secret_elem.get("inject_as_env")
label = secret_elem.get("label", "")
required = string_as_bool(secret_elem.get("required", "false"))
return SecretsRequirement(
type=type,
user_preferences_key=user_preferences_key,
inject_as_env=inject_as_env,
label=label,
def credentials_from_element(credentials_elem) -> CredentialsRequirement:
name = credentials_elem.get("name")
reference = credentials_elem.get("reference")
required = string_as_bool(credentials_elem.get("required", "false"))
label = credentials_elem.get("label", "")
description = credentials_elem.get("description", "")
secrets_and_variables = [SecretOrVariable.from_element(elem) for elem in credentials_elem.findall("*")]
return CredentialsRequirement(
name=name,
reference=reference,
required=required,
label=label,
description=description,
secrets_and_variables=secrets_and_variables,
)
8 changes: 4 additions & 4 deletions lib/galaxy/tool_util/linters/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ class RequirementNameMissing(Linter):
@classmethod
def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"):
_, tool_node = _tool_xml_and_root(tool_source)
requirements, containers, resource_requirements, secrets = tool_source.parse_requirements_and_containers()
requirements, *_ = tool_source.parse_requirements_and_containers()
for r in requirements:
if r.type != "package":
continue
Expand All @@ -195,7 +195,7 @@ class RequirementVersionMissing(Linter):
@classmethod
def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"):
_, tool_node = _tool_xml_and_root(tool_source)
requirements, containers, resource_requirements, secrets = tool_source.parse_requirements_and_containers()
requirements, *_ = tool_source.parse_requirements_and_containers()
for r in requirements:
if r.type != "package":
continue
Expand All @@ -207,7 +207,7 @@ class RequirementVersionWhitespace(Linter):
@classmethod
def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"):
_, tool_node = _tool_xml_and_root(tool_source)
requirements, containers, resource_requirements, secrets = tool_source.parse_requirements_and_containers()
requirements, *_ = tool_source.parse_requirements_and_containers()
for r in requirements:
if r.type != "package":
continue
Expand All @@ -223,7 +223,7 @@ class ResourceRequirementExpression(Linter):
@classmethod
def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"):
_, tool_node = _tool_xml_and_root(tool_source)
requirements, containers, resource_requirements, secrets = tool_source.parse_requirements_and_containers()
*_, resource_requirements, _ = tool_source.parse_requirements_and_containers()
for rr in resource_requirements:
if rr.runtime_required:
lint_ctx.warn(
Expand Down
4 changes: 2 additions & 2 deletions lib/galaxy/tool_util/parser/cwl.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,12 @@ def parse_requirements_and_containers(self):

software_requirements = self.tool_proxy.software_requirements()
resource_requirements = self.tool_proxy.resource_requirements()
secrets = self.tool_proxy.secrets()
credentials = self.tool_proxy.credentials()
return requirements.parse_requirements_from_lists(
software_requirements=[{"name": r[0], "version": r[1], "type": "package"} for r in software_requirements],
containers=containers,
resource_requirements=resource_requirements,
secrets=secrets,
credentials=credentials,
)

def parse_profile(self):
Expand Down
4 changes: 2 additions & 2 deletions lib/galaxy/tool_util/parser/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
if TYPE_CHECKING:
from galaxy.tool_util.deps.requirements import (
ContainerDescription,
CredentialsRequirement,
ResourceRequirement,
SecretsRequirement,
ToolRequirements,
)

Expand Down Expand Up @@ -309,7 +309,7 @@ def parse_required_files(self) -> Optional["RequiredFiles"]:
def parse_requirements_and_containers(
self,
) -> Tuple[
"ToolRequirements", List["ContainerDescription"], List["ResourceRequirement"], List["SecretsRequirement"]
"ToolRequirements", List["ContainerDescription"], List["ResourceRequirement"], List["CredentialsRequirement"]
]:
"""Return triple of ToolRequirement, ContainerDescription, ResourceRequirement, and SecretsRequirement objects."""

Expand Down
2 changes: 1 addition & 1 deletion lib/galaxy/tool_util/parser/yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def parse_requirements_and_containers(self):
software_requirements=[r for r in mixed_requirements if r.get("type") != "resource"],
containers=self.root_dict.get("containers", []),
resource_requirements=[r for r in mixed_requirements if r.get("type") == "resource"],
secrets=self.root_dict.get("secrets", []),
credentials=self.root_dict.get("credentials", []),
)

def parse_input_pages(self) -> PagesSource:
Expand Down
Loading

0 comments on commit f2a773c

Please sign in to comment.