Skip to content

Commit

Permalink
Allow environment hiding. (#54)
Browse files Browse the repository at this point in the history
* Add endpoint to hide or unhide an environment in the frontend.

* Add admin only option to force hide an environment.

* Remove force_hide from the Environment type.
  • Loading branch information
mjkw31 authored Aug 6, 2024
1 parent aca6f9d commit b677481
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 6 deletions.
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

0 comments on commit b677481

Please sign in to comment.