diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bfbcc5d..e27307f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,7 +120,7 @@ jobs: path: "dist" - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@v1.10.3 + uses: pypa/gh-action-pypi-publish@v1.12.3 with: # Note that this is currently being pushed to the 'crossplane' PyPI # user (not org). See @negz if you need access - PyPI requires 2FA to diff --git a/crossplane/function/resource.py b/crossplane/function/resource.py index c33e7aa..039af46 100644 --- a/crossplane/function/resource.py +++ b/crossplane/function/resource.py @@ -39,7 +39,15 @@ def update(r: fnv1.Resource, source: dict | structpb.Struct | pydantic.BaseModel """ match source: case pydantic.BaseModel(): - r.resource.update(source.model_dump(exclude_defaults=True, warnings=False)) + data = source.model_dump(exclude_defaults=True, warnings=False) + # In Pydantic, exclude_defaults=True in model_dump excludes fields + # that have their value equal to the default. If a field like + # apiVersion is set to its default value 's3.aws.upbound.io/v1beta2' + # (and not explicitly provided during initialization), it will be + # excluded from the serialized output. + data['apiVersion'] = source.apiVersion + data['kind'] = source.kind + r.resource.update(data) case structpb.Struct(): # TODO(negz): Use struct_to_dict and update to match other semantics? r.resource.MergeFrom(source) @@ -106,10 +114,10 @@ def get_condition(resource: structpb.Struct, typ: str) -> Condition: """ unknown = Condition(typ=typ, status="Unknown") - if "status" not in resource: + if not resource or "status" not in resource: return unknown - if "conditions" not in resource["status"]: + if not resource["status"] or "conditions" not in resource["status"]: return unknown for c in resource["status"]["conditions"]: @@ -145,9 +153,9 @@ class Credentials: def get_credentials(req: structpb.Struct, name: str) -> Credentials: """Get the supplied credentials.""" empty = Credentials(type="data", data={}) - if "credentials" not in req: + if not req or "credentials" not in req: return empty - if name not in req["credentials"]: + if not req["credentials"] or name not in req["credentials"]: return empty return Credentials( type=req["credentials"][name]["type"], diff --git a/pyproject.toml b/pyproject.toml index 5cf2e67..ef0f00e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ classifiers = [ dependencies = [ "grpcio==1.67.0", "grpcio-reflection==1.*", - "protobuf==5.27.2", + "protobuf==5.29.3", "pydantic==2.*", "structlog==24.*", ] @@ -38,13 +38,13 @@ validate-bump = false # Allow going from 0.0.0.dev0+x to 0 [tool.hatch.envs.default] type = "virtual" path = ".venv-default" -dependencies = ["ipython==8.28.0"] +dependencies = ["ipython==8.31.0"] [tool.hatch.envs.generate] type = "virtual" detached = true path = ".venv-generate" -dependencies = ["grpcio-tools==1.67.0"] +dependencies = ["grpcio-tools==1.69.0"] [tool.hatch.envs.generate.scripts] protoc = "python -m grpc_tools.protoc --proto_path=. --python_out=. --pyi_out=. --grpc_python_out=. crossplane/function/proto/v1beta1/run_function.proto crossplane/function/proto/v1/run_function.proto" @@ -62,7 +62,7 @@ packages = ["crossplane"] # This special environment is used by hatch fmt. [tool.hatch.envs.hatch-static-analysis] -dependencies = ["ruff==0.6.9"] +dependencies = ["ruff==0.9.1"] config-path = "none" # Disable Hatch's default Ruff config. [tool.ruff] diff --git a/tests/test_resource.py b/tests/test_resource.py index 52de5f5..010fed8 100644 --- a/tests/test_resource.py +++ b/tests/test_resource.py @@ -90,7 +90,11 @@ class TestCase: ), want=fnv1.Resource( resource=resource.dict_to_struct( - {"spec": {"forProvider": {"region": "us-west-2"}}} + { + "apiVersion": "s3.aws.upbound.io/v1beta2", + "kind": "Bucket", + "spec": {"forProvider": {"region": "us-west-2"}}, + } ), ), ), diff --git a/tests/testdata/models/io/upbound/aws/s3/v1beta2.py b/tests/testdata/models/io/upbound/aws/s3/v1beta2.py index 0a678e0..d6253ff 100644 --- a/tests/testdata/models/io/upbound/aws/s3/v1beta2.py +++ b/tests/testdata/models/io/upbound/aws/s3/v1beta2.py @@ -759,11 +759,11 @@ class Status(BaseModel): class Bucket(BaseModel): - apiVersion: Optional[str] = None + apiVersion: Optional[str] = 's3.aws.upbound.io/v1beta2' """ APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources """ - kind: Optional[str] = None + kind: Optional[str] = 'Bucket' """ Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds """