Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow environment hiding. #54

Merged
merged 10 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type Environment {
packages: [Package!]!
state: State
tags: [String!]!
hidden: Boolean!
requested: DateTime
buildStart: DateTime
buildDone: DateTime
Expand Down Expand Up @@ -72,6 +73,12 @@ type Group {
name: String!
}

union HiddenResponse = HiddenSuccess | InvalidInputError | EnvironmentNotFoundError

type HiddenSuccess implements Success {
message: String!
}

type InvalidInputError implements Error {
message: String!
}
Expand All @@ -95,6 +102,7 @@ type SchemaMutation {
createEnvironment(env: EnvironmentInput!): CreateResponse!
deleteEnvironment(name: String!, path: String!): DeleteResponse!
addTag(name: String!, path: String!, tag: String!): AddTagResponse!
setHidden(name: String!, path: String!, hidden: Boolean!): HiddenResponse!
createFromModule(file: Upload!, modulePath: String!, environmentPath: String!): CreateResponse!
updateFromModule(file: Upload!, modulePath: String!, environmentPath: String!): UpdateResponse!
}
Expand Down
19 changes: 17 additions & 2 deletions softpack_core/artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,27 @@ def spec(self) -> Box:
map(lambda p: Package.from_name(p), info.packages)
)

metadata = self.metadata()

info["tags"] = getattr(metadata, "tags", [])
info["hidden"] = getattr(metadata, "hidden", False)
info["force_hidden"] = getattr(metadata, "force_hidden", False)

return info

def metadata(self) -> Box:
"""Returns the metadata for an Environment.

Should contain keys:
- tags: list[string]
- hidden: boolean
- force_hidden: boolean
"""
meta = Box()
if Artifacts.meta_file in self.obj:
meta = Box.from_yaml(self.obj[Artifacts.meta_file].data)
info["tags"] = getattr(meta, "tags", [])

return info
return meta

def __iter__(self) -> Iterator["Artifacts.Object"]:
"""A generator for returning items under an artifacts.
Expand Down
80 changes: 76 additions & 4 deletions softpack_core/schemas/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import starlette.datastructures
import strawberry
import yaml
from box import Box
from fastapi import UploadFile
from strawberry.file_uploads import Upload

Expand Down Expand Up @@ -57,6 +58,11 @@ class AddTagSuccess(Success):
"""Successfully added tag to environment."""


@strawberry.type
class HiddenSuccess(Success):
"""Successfully set hidden status on environment."""


@strawberry.type
class DeleteEnvironmentSuccess(Success):
"""Environment successfully deleted."""
Expand Down Expand Up @@ -124,6 +130,15 @@ class BuilderError(Error):
],
)

HiddenResponse = strawberry.union(
"HiddenResponse",
[
HiddenSuccess,
InvalidInputError,
EnvironmentNotFoundError,
],
)

DeleteResponse = strawberry.union(
"DeleteResponse",
[
Expand Down Expand Up @@ -306,6 +321,7 @@ class Environment:
packages: list[Package]
state: Optional[State]
tags: list[str]
hidden: bool

requested: Optional[datetime.datetime] = None
build_start: Optional[datetime.datetime] = None
Expand Down Expand Up @@ -364,6 +380,9 @@ def from_artifact(cls, obj: Artifacts.Object) -> Optional["Environment"]:
"""
try:
spec = obj.spec()
if spec.force_hidden:
return None

return Environment(
id=obj.oid,
name=obj.name,
Expand All @@ -374,6 +393,7 @@ def from_artifact(cls, obj: Artifacts.Object) -> Optional["Environment"]:
readme=spec.get("readme", ""),
type=spec.get("type", ""),
tags=spec.tags,
hidden=spec.hidden,
)
except KeyError:
return None
Expand Down Expand Up @@ -586,12 +606,63 @@ def add_tag(
return AddTagSuccess(message="Tag already present")
tags.add(tag)

metadata = yaml.dump({"tags": sorted(tags)})
metadata = cls.read_metadata(path, name)
metadata.tags = sorted(tags)

cls.store_metadata(environment_path, metadata)

return AddTagSuccess(message="Tag successfully added")

@classmethod
def read_metadata(cls, path: str, name: str) -> Box:
"""Read an environments metadata.

This method returns the metadata for an environment with the given
path and name.
"""
arts = artifacts.get(Path(path), name)

if arts is not None:
return arts.metadata()

return Box()

@classmethod
def store_metadata(cls, environment_path: Path, metadata: Box) -> None:
"""Store an environments metadata.

This method writes the given metadata to the repo for the
environment path given.
"""
tree_oid = artifacts.create_file(
environment_path, artifacts.meta_file, metadata, overwrite=True
environment_path,
artifacts.meta_file,
metadata.to_yaml(),
overwrite=True,
)
artifacts.commit_and_push(tree_oid, "create environment folder")
return AddTagSuccess(message="Tag successfully added")

artifacts.commit_and_push(tree_oid, "update metadata")

@classmethod
def set_hidden(
cls, name: str, path: str, hidden: bool
) -> HiddenResponse: # type: ignore
"""This method sets the hidden status for the given environment."""
environment_path = Path(path, name)
response: Optional[Error] = cls.check_env_exists(environment_path)
if response is not None:
return response

metadata = cls.read_metadata(path, name)

if metadata.get("hidden") == hidden:
return HiddenSuccess(message="Hidden metadata already set")

metadata.hidden = hidden

cls.store_metadata(environment_path, metadata)

return HiddenSuccess(message="Hidden metadata set")

@classmethod
def delete(cls, name: str, path: str) -> DeleteResponse: # type: ignore
Expand Down Expand Up @@ -847,6 +918,7 @@ class Mutation:
createEnvironment: CreateResponse = Environment.create # type: ignore
deleteEnvironment: DeleteResponse = Environment.delete # type: ignore
addTag: AddTagResponse = Environment.add_tag # type: ignore
setHidden: HiddenResponse = Environment.set_hidden # type: ignore
# writeArtifact: WriteArtifactResponse = ( # type: ignore
# Environment.write_artifact
# )
Expand Down
50 changes: 50 additions & 0 deletions tests/integration/test_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
EnvironmentAlreadyExistsError,
EnvironmentInput,
EnvironmentNotFoundError,
HiddenSuccess,
InvalidInputError,
Package,
State,
Expand Down Expand Up @@ -553,3 +554,52 @@ def test_tagging(httpx_post, testable_env_input: EnvironmentInput) -> None:

example_env = Environment.iter()[0]
assert example_env.tags == ["second test", "test"]


def test_hidden(httpx_post, testable_env_input: EnvironmentInput) -> None:
example_env = Environment.iter()[0]
assert not example_env.hidden
name, path = example_env.name, example_env.path

result = Environment.set_hidden(name, path, True)
assert isinstance(result, HiddenSuccess)
assert result.message == "Hidden metadata set"
example_env = Environment.iter()[0]
assert example_env.hidden

result = Environment.set_hidden(name, path, True)
assert isinstance(result, HiddenSuccess)
assert result.message == "Hidden metadata already set"
example_env = Environment.iter()[0]
assert example_env.hidden

result = Environment.set_hidden(name, path, False)
assert isinstance(result, HiddenSuccess)
assert result.message == "Hidden metadata set"
example_env = Environment.iter()[0]
assert not example_env.hidden

result = Environment.set_hidden(name, path, False)
assert isinstance(result, HiddenSuccess)
assert result.message == "Hidden metadata already set"
example_env = Environment.iter()[0]
assert not example_env.hidden

result = Environment.set_hidden(name, path, True)
assert isinstance(result, HiddenSuccess)
assert result.message == "Hidden metadata set"
example_env = Environment.iter()[0]
assert example_env.hidden


def test_force_hidden(
httpx_post, testable_env_input: EnvironmentInput
) -> None:
first_env = Environment.iter()[0]
metadata = Environment.read_metadata(first_env.path, first_env.name)
metadata.force_hidden = True
Environment.store_metadata(Path(first_env.path, first_env.name), metadata)

new_first = Environment.iter()[0]

assert first_env.path != new_first.path or first_env.name != new_first.name
Loading