diff --git a/.devcontainer/onCreateCommand.sh b/.devcontainer/onCreateCommand.sh index cbbc87a8e1..1a61fc2582 100755 --- a/.devcontainer/onCreateCommand.sh +++ b/.devcontainer/onCreateCommand.sh @@ -8,4 +8,4 @@ poetry install --no-interaction --no-ansi git submodule update --init -invoke demo.pull +poetry run invoke demo.pull diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh index a574523175..be741d5d0b 100755 --- a/.devcontainer/postCreateCommand.sh +++ b/.devcontainer/postCreateCommand.sh @@ -2,4 +2,4 @@ git pull git submodule update -invoke demo.start --wait +poetry run invoke demo.start --wait diff --git a/.devcontainer/updateContentCommand.sh b/.devcontainer/updateContentCommand.sh index eb522f6425..9a5d83499d 100755 --- a/.devcontainer/updateContentCommand.sh +++ b/.devcontainer/updateContentCommand.sh @@ -1,12 +1,12 @@ #!/bin/bash export WEB_CONCURRENCY=2 -invoke demo.start +poetry run invoke demo.start sleep 120 docker logs infrahub-server-1 -invoke demo.load-infra-schema +poetry run invoke demo.load-infra-schema docker logs infrahub-server-1 sleep 90 docker logs infrahub-server-1 -invoke demo.load-infra-data -invoke demo.stop +poetry run invoke demo.load-infra-data +poetry run invoke demo.stop diff --git a/CHANGELOG.md b/CHANGELOG.md index e8023c2a5c..01bab77a68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,22 @@ This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the chang +## [Infrahub - v1.4.12](https://github.com/opsmill/infrahub/tree/infrahub-v1.4.12) - 2025-10-23 + +### Added + +- - Schema Visualizer now displays `on_delete` settings for relationships + - Fixed display of common_parent settings in relationships. + + ([#7431](https://github.com/opsmill/infrahub/issues/7431)) + +### Fixed + +- Loosen requirements for upsert mutations in the GraphQL schema so that required fields can be supplied by a template. ([#7398](https://github.com/opsmill/infrahub/issues/7398)) +- Fix a bug that could cause duplicated attributes to be created when updating a generic schema with a new attribute. Includes a migration to fix any existing duplicated attributes created by this bug. ([#7407](https://github.com/opsmill/infrahub/issues/7407)) +- Fix bug in logic to create an object from a template that would prevent existing objects in relationships of sub-templates from being correctly linked to the created object. ([#7430](https://github.com/opsmill/infrahub/issues/7430)) +- The artifact count has been removed from the Proposed Changes list view. + ## [Infrahub - v1.4.11](https://github.com/opsmill/infrahub/tree/infrahub-v1.4.11) - 2025-10-17 ### Added diff --git a/backend/infrahub/core/node/create.py b/backend/infrahub/core/node/create.py index d7bef7ade1..91a68721b4 100644 --- a/backend/infrahub/core/node/create.py +++ b/backend/infrahub/core/node/create.py @@ -57,14 +57,25 @@ async def extract_peer_data( for rel in template_peer.get_schema().relationship_names: rel_manager: RelationshipManager = getattr(template_peer, rel) - if ( - rel_manager.schema.kind not in [RelationshipKind.COMPONENT, RelationshipKind.PARENT] - or rel_manager.schema.name not in obj_peer_schema.relationship_names - ): + + if rel_manager.schema.name not in obj_peer_schema.relationship_names: continue - if list(await rel_manager.get_peers(db=db)) == [current_template.id]: + peers_map = await rel_manager.get_peers(db=db) + if rel_manager.schema.kind in [RelationshipKind.COMPONENT, RelationshipKind.PARENT] and list( + peers_map.keys() + ) == [current_template.id]: obj_peer_data[rel] = {"id": parent_obj.id} + continue + + rel_peer_ids = [] + for peer_id, peer_object in peers_map.items(): + # deeper templates are handled in the next level of recursion + if peer_object.get_schema().is_template_schema: + continue + rel_peer_ids.append({"id": peer_id}) + + obj_peer_data[rel] = rel_peer_ids return obj_peer_data diff --git a/backend/infrahub/graphql/manager.py b/backend/infrahub/graphql/manager.py index 0544a2b323..b19f6ae42d 100644 --- a/backend/infrahub/graphql/manager.py +++ b/backend/infrahub/graphql/manager.py @@ -787,10 +787,7 @@ class StatusUpsertInput(InputObjectType): attr_kind = get_attr_kind(schema, attr) attr_type = get_attribute_type(kind=attr_kind).get_graphql_update() - # A Field is not required if explicitly indicated or if a default value has been provided - required = not attr.optional if not attr.default_value else False - - attrs[attr.name] = graphene.InputField(attr_type, required=required, description=attr.description) + attrs[attr.name] = graphene.InputField(attr_type, description=attr.description) for rel in schema.relationships: if rel.internal_peer or rel.read_only: @@ -798,14 +795,11 @@ class StatusUpsertInput(InputObjectType): input_type = self._get_related_input_type(relationship=rel) - required = not rel.optional if rel.cardinality == RelationshipCardinality.ONE: - attrs[rel.name] = graphene.InputField(input_type, required=required, description=rel.description) + attrs[rel.name] = graphene.InputField(input_type, description=rel.description) elif rel.cardinality == RelationshipCardinality.MANY: - attrs[rel.name] = graphene.InputField( - graphene.List(input_type), required=required, description=rel.description - ) + attrs[rel.name] = graphene.InputField(graphene.List(input_type), description=rel.description) return type(f"{schema.kind}UpsertInput", (graphene.InputObjectType,), attrs) diff --git a/backend/tests/helpers/schema/tshirt.py b/backend/tests/helpers/schema/tshirt.py index 05fc2c1afc..3b184b685a 100644 --- a/backend/tests/helpers/schema/tshirt.py +++ b/backend/tests/helpers/schema/tshirt.py @@ -11,6 +11,7 @@ default_filter="name__value", display_labels=["name__value"], uniqueness_constraints=[["name__value"]], + generate_template=True, attributes=[ AttributeSchema(name="name", kind="Text"), AttributeSchema( diff --git a/backend/tests/unit/graphql/test_mutation_create.py b/backend/tests/unit/graphql/test_mutation_create.py index 597e103315..763b9a687b 100644 --- a/backend/tests/unit/graphql/test_mutation_create.py +++ b/backend/tests/unit/graphql/test_mutation_create.py @@ -3,7 +3,7 @@ from infrahub import config from infrahub.core import registry from infrahub.core.branch.models import Branch -from infrahub.core.constants import InfrahubKind, SchemaPathType +from infrahub.core.constants import InfrahubKind, RelationshipKind, SchemaPathType from infrahub.core.initialization import create_branch from infrahub.core.manager import NodeManager from infrahub.core.migrations.schema.node_kind_update import NodeKindUpdateMigration @@ -17,7 +17,7 @@ from infrahub.graphql.initialization import prepare_graphql_params from tests.constants import TestKind from tests.helpers.graphql import graphql -from tests.helpers.schema import DEVICE_SCHEMA +from tests.helpers.schema import CAR_SCHEMA, DEVICE_SCHEMA async def test_create_simple_object(db: InfrahubDatabase, default_branch, car_person_schema): @@ -1462,6 +1462,100 @@ async def test_create_with_object_template( assert sfp.part_number.source_id is None +async def test_create_with_object_template_and_real_object( + db: InfrahubDatabase, default_branch: Branch, register_core_models_schema: SchemaBranch, branch: Branch +): + """ + Test that relationships on sub-templates will correctly link the created sub-object to an existing object on non-component relationships + """ + updated_car_schema = CAR_SCHEMA.duplicate() + manufacturer_schema = updated_car_schema.get(name=TestKind.MANUFACTURER) + manufacturer_schema.generate_template = True + cars_rel = manufacturer_schema.get_relationship(name="cars") + cars_rel.kind = RelationshipKind.COMPONENT + person_schema = updated_car_schema.get(name=TestKind.PERSON) + person_schema.generate_template = True + car_schema = updated_car_schema.get(name=TestKind.CAR) + car_schema.generate_template = True + manufacturer_rel = car_schema.get_relationship(name="manufacturer") + manufacturer_rel.kind = RelationshipKind.PARENT + registry.schema.register_schema(schema=updated_car_schema, branch=branch.name) + + manufacturer_object = await Node.init(schema=TestKind.MANUFACTURER, db=db, branch=branch) + await manufacturer_object.new(db=db, name="Hark Motors") + await manufacturer_object.save(db=db) + + person_object = await Node.init(schema=TestKind.PERSON, db=db, branch=branch) + await person_object.new(db=db, name="John", height=180) + await person_object.save(db=db) + + car_object = await Node.init(schema=TestKind.CAR, db=db, branch=branch) + await car_object.new(db=db, name="Accord", manufacturer=manufacturer_object, owner=person_object, color="blurple") + await car_object.save(db=db) + + manufacturer_template: Node = await Node.init(schema=f"Template{TestKind.MANUFACTURER}", db=db, branch=branch) + await manufacturer_template.new(db=db, template_name="m_template", customers=[person_object]) + await manufacturer_template.save(db=db) + + car_template_with_person_object = await Node.init(schema=f"Template{TestKind.CAR}", db=db, branch=branch) + await car_template_with_person_object.new( + db=db, + template_name="c_template", + name="Civic", + color="blurple", + manufacturer=manufacturer_template, + owner=person_object, + ) + await car_template_with_person_object.save(db=db) + + create_manufacturer_with_template_query = """ + mutation CreateManufacturerWithTemplate($manufacturer_name: String!, $template_id: String!) { + TestingManufacturerCreate(data: { + name: {value: $manufacturer_name} + object_template: {id: $template_id} + }) { + ok + object { + id + } + } + } + """ + gql_params = await prepare_graphql_params(db=db, branch=branch) + result = await graphql( + schema=gql_params.schema, + source=create_manufacturer_with_template_query, + context_value=gql_params.context, + variable_values={"manufacturer_name": "Fresh Motors", "template_id": manufacturer_template.id}, + ) + assert not result.errors + new_manufacturer = await NodeManager.get_one( + db=db, + kind=TestKind.MANUFACTURER, + branch=branch, + id=result.data[f"{TestKind.MANUFACTURER}Create"]["object"]["id"], + ) + assert new_manufacturer + assert new_manufacturer.name.value == "Fresh Motors" + customers_peers = await new_manufacturer.customers.get_peers(db=db) + assert len(customers_peers) == 1 + customers_by_name = {person.name.value: person for person in customers_peers.values()} + # check non-template person + non_template_person = customers_by_name["John"] + assert non_template_person.id == person_object.id + + cars_peers = await new_manufacturer.cars.get_peers(db=db) + assert len(cars_peers) == 1 + cars_by_name = {car.name.value: car for car in cars_peers.values()} + # check car template with person object + car_template_with_person_object = cars_by_name["Civic"] + assert car_template_with_person_object.color.value == "blurple" + car_manufacturer = await car_template_with_person_object.manufacturer.get_peer(db=db) + assert car_manufacturer.id == new_manufacturer.id + car_owner = await car_template_with_person_object.owner.get_peer(db=db) + assert car_owner.id == person_object.id + + async def test_create_without_object_template( db: InfrahubDatabase, default_branch: Branch, register_core_models_schema: SchemaBranch, branch: Branch ): diff --git a/backend/tests/unit/graphql/test_mutation_upsert.py b/backend/tests/unit/graphql/test_mutation_upsert.py index 0c56781100..6047af9750 100644 --- a/backend/tests/unit/graphql/test_mutation_upsert.py +++ b/backend/tests/unit/graphql/test_mutation_upsert.py @@ -12,7 +12,7 @@ from tests.adapters.event import MemoryInfrahubEvent from tests.constants import TestKind from tests.helpers.graphql import graphql -from tests.helpers.schema import TICKET +from tests.helpers.schema import COLOR, TICKET, TSHIRT from tests.node_creation import create_and_save @@ -673,3 +673,82 @@ async def test_upsert_node_on_branch_with_hfid_on_default(db: InfrahubDatabase, in result.errors[0].message ) assert f"Please rebase this branch to access {person.id} / TestPerson" in result.errors[0].message + + +async def test_upsert_with_required_relationship_from_template( + db: InfrahubDatabase, default_branch: Branch, register_core_models_schema: None +) -> None: + """Validate that we can use a template to populate required relationships in upsert mutations. + + Steps: + - Create a color node and a Tshirt template node. + - Try to upsert a Tshirt without specifying color or template (should fail). + - Upsert a Tshirt specifying the template (should succeed and apply the color from the template). + """ + registry.schema.register_schema(schema=SchemaRoot(nodes=[TSHIRT, COLOR]), branch=default_branch.name) + + # Create a color node + color_node = await Node.init(db=db, schema="TestingColor", branch=default_branch) + await color_node.new(db=db, name="Red", description="Bright Red Color") + await color_node.save(db=db) + + # Create a Tshirt template node with the color relationship set + template_node = await Node.init(db=db, schema="TemplateTestingTShirt", branch=default_branch) + await template_node.new(db=db, template_name="Basic Red Tshirt", color=color_node) + await template_node.save(db=db) + + # Try to upsert a TShirt without specifying color or template (should fail) + query_missing_required = """ + mutation { + TestingTShirtUpsert(data: {name: {value: "My Shirt"} }) { + ok + object { + id + name { value } + color { node { id name { value } } } + } + } + } + """ + gql_params = await prepare_graphql_params(db=db, include_subscription=False, branch=default_branch) + result_missing = await graphql( + schema=gql_params.schema, + source=query_missing_required, + context_value=gql_params.context, + root_value=None, + variable_values={}, + ) + assert result_missing.errors + assert "color is mandatory for TestingTShirt at color" in str(result_missing.errors) + + # Upsert a Tshirt specifying the template (should succeed and apply the color from the template) + query_with_template = """ + mutation UpsertTShirt($template_id: String!) { + TestingTShirtUpsert(data: { + name: {value: "My Tshirt"}, + object_template: {id: $template_id} + }) { + ok + object { + id + name { value } + color { node { id name { value } } } + } + } + } + """ + + result_with_template = await graphql( + schema=gql_params.schema, + source=query_with_template, + context_value=gql_params.context, + root_value=None, + variable_values={"template_id": template_node.id}, + ) + assert result_with_template.errors is None + assert result_with_template.data + assert result_with_template.data["TestingTShirtUpsert"]["ok"] is True + tshirt_obj = result_with_template.data["TestingTShirtUpsert"]["object"] + assert tshirt_obj["name"]["value"] == "My Tshirt" + assert tshirt_obj["color"]["node"]["id"] == color_node.id + assert tshirt_obj["color"]["node"]["name"]["value"] == "Red" diff --git a/changelog/+artifact.fixed.md b/changelog/+artifact.fixed.md deleted file mode 100644 index baefaa8cbb..0000000000 --- a/changelog/+artifact.fixed.md +++ /dev/null @@ -1 +0,0 @@ -The artifact count has been removed from the Proposed Changes list view. diff --git a/changelog/+checks.fixed.md b/changelog/+checks.fixed.md new file mode 100644 index 0000000000..ba06df4153 --- /dev/null +++ b/changelog/+checks.fixed.md @@ -0,0 +1 @@ +Resolved a problem that caused generator checks to fail when retrying requests \ No newline at end of file diff --git a/changelog/7407.fixed.md b/changelog/7407.fixed.md deleted file mode 100644 index 5ba7373deb..0000000000 --- a/changelog/7407.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fix a bug that could cause duplicated attributes to be created when updating a generic schema with a new attribute. Includes a migration to fix any existing duplicated attributes created by this bug. \ No newline at end of file diff --git a/changelog/7431.added.md b/changelog/7431.added.md deleted file mode 100644 index b907bc7cd0..0000000000 --- a/changelog/7431.added.md +++ /dev/null @@ -1,2 +0,0 @@ -- Schema Visualizer now displays `on_delete` settings for relationships -- Fixed display of common_parent settings in relationships. diff --git a/docker-compose.yml b/docker-compose.yml index aaf6c990e8..50ba8e8fc3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -199,7 +199,7 @@ services: - 6362:6362 task-manager: - image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.4.11}" + image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.4.12}" command: uvicorn --host 0.0.0.0 --port 4200 --factory infrahub.prefect_server.app:create_infrahub_prefect restart: unless-stopped depends_on: @@ -232,7 +232,7 @@ services: retries: 5 infrahub-server: - image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.4.11}" + image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.4.12}" restart: unless-stopped command: > gunicorn --config backend/infrahub/serve/gunicorn_config.py @@ -278,7 +278,7 @@ services: deploy: mode: replicated replicas: 2 - image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.4.11}" + image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.4.12}" command: prefect worker start --type infrahubasync --pool infrahub-worker --with-healthcheck restart: unless-stopped depends_on: diff --git a/docs/docs/guides/change-approval-workflow.mdx b/docs/docs/guides/change-approval-workflow.mdx index 18b480cba0..64d52c4a41 100644 --- a/docs/docs/guides/change-approval-workflow.mdx +++ b/docs/docs/guides/change-approval-workflow.mdx @@ -10,6 +10,12 @@ This guide walks you through implementing a change approval workflow in Infrahub Some features in this guide require the Enterprise Edition of Infrahub. If you are using the Community Edition, the enforcement mechanisms of the change approval workflow will not be available, though you can still implement a process-based approach. ::: +:::success Change Management Workflow Blog Post + +Want to see how branches can be used in a change management workflow? Read our blog post on [Infrahub’s Change Management Workflow Is Built for Infrastructure Data](https://opsmill.com/blog/infrastructure-change-management-workflow/). + +::: + ## What you'll build By the end of this guide, you'll have a complete governance system for infrastructure changes that includes: @@ -296,3 +302,4 @@ Users with `Super Administrator` permission can: - [Managing users and permissions](../topics/permissions-roles) - [Working with branches](../topics/version-control) - [Configuring Git repositories](../guides/repository) +- [Change Management Workflow Blog Post](https://opsmill.com/blog/infrastructure-change-management-workflow/) diff --git a/docs/docs/release-notes/infrahub/release-1_4_12.mdx b/docs/docs/release-notes/infrahub/release-1_4_12.mdx new file mode 100644 index 0000000000..cf416abe6e --- /dev/null +++ b/docs/docs/release-notes/infrahub/release-1_4_12.mdx @@ -0,0 +1,35 @@ +--- +title: Release 1.4.12 +--- + + + + + + + + + + + + + + + +
Release Number1.4.12
Release DateOctober 23rd, 2025
Tag[infrahub-v1.4.12](https://github.com/opsmill/infrahub/releases/tag/infrahub-v1.4.12)
+ + +### Added + +- - Schema Visualizer now displays `on_delete` settings for relationships + - Fixed display of common_parent settings in relationships. + + ([#7431](https://github.com/opsmill/infrahub/issues/7431)) + +### Fixed + +- Loosen requirements for upsert mutations in the GraphQL schema so that required fields can be supplied by a template. ([#7398](https://github.com/opsmill/infrahub/issues/7398)) +- Fix a bug that could cause duplicated attributes to be created when updating a generic schema with a new attribute. Includes a migration to fix any existing duplicated attributes created by this bug. ([#7407](https://github.com/opsmill/infrahub/issues/7407)) +- Fix bug in logic to create an object from a template that would prevent existing objects in relationships of sub-templates from being correctly linked to the created object. ([#7430](https://github.com/opsmill/infrahub/issues/7430)) +- The artifact count has been removed from the Proposed Changes list view. + diff --git a/docs/docs/topics/branching.mdx b/docs/docs/topics/branching.mdx index e22121d6e6..3c4d92ab45 100644 --- a/docs/docs/topics/branching.mdx +++ b/docs/docs/topics/branching.mdx @@ -28,6 +28,12 @@ Branching in Infrahub supports various infrastructure management workflows: - **Experimentation**: Use branches to test new ideas or approaches without affecting the production environment. This provides a safe sandbox to validate new configurations or architectures before committing to them. - **Transaction support**: Group related changes into a single branch, ensuring atomic updates and easier rollbacks. This maintains consistency when implementing interdependent infrastructure changes. +:::success Change Management Workflow Blog Post + +Want to see how branches can be used in a change management workflow? Read our blog post on [Infrahub’s Change Management Workflow Is Built for Infrastructure Data](https://opsmill.com/blog/infrastructure-change-management-workflow/). + +::: + ## Core concepts Branches in Infrahub serve as isolated workspaces where changes can be prepared, validated, and reviewed before merging into production. Each branch maintains its own independent view of the data while sharing the underlying immutable history. @@ -192,6 +198,7 @@ This approach means creating a branch has minimal overhead, and storage grows on ## Related topics +- [Branching Blog Post](https://opsmill.com/blog/infrastructure-change-management-workflow/) - [Git Integration](./repository.mdx) - [Proposed Change](./proposed-change.mdx) - [Immutable History](./version-control.mdx) diff --git a/docs/docs/topics/proposed-change.mdx b/docs/docs/topics/proposed-change.mdx index a63350edf3..c0c12012f7 100644 --- a/docs/docs/topics/proposed-change.mdx +++ b/docs/docs/topics/proposed-change.mdx @@ -187,3 +187,4 @@ The proposed change functionality interconnects with several other key concepts - [Transformations](transformation): Understand how data transformations are managed and reviewed within proposed changes - [Generators](generator): Learn about the code generation process that can be triggered by changes in proposed changes - [Permissions and roles](permissions-roles): Understand the access controls that govern who can create, review, and merge proposed changes +- [Change Management Workflow Blog Post](https://opsmill.com/blog/infrastructure-change-management-workflow/) diff --git a/docs/docs/tutorials/getting-started/branches.mdx b/docs/docs/tutorials/getting-started/branches.mdx index b793777336..d55eb7ff04 100644 --- a/docs/docs/tutorials/getting-started/branches.mdx +++ b/docs/docs/tutorials/getting-started/branches.mdx @@ -171,6 +171,6 @@ Go back to the detailed page for the Tenant `my-first-tenant`. :::info Proposed Change -For an in-depth understanding of Infrahub's approach to handling differences between branches and merging them, please consult the [proposed change topic](../../topics/proposed-change). +For an in-depth understanding of Infrahub's approach to handling differences between branches and merging them, please consult the [proposed change topic](../../topics/proposed-change). ::: diff --git a/docs/sidebars.ts b/docs/sidebars.ts index 28631f292a..9e344f881f 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -414,6 +414,7 @@ const sidebars: SidebarsConfig = { slug: 'release-notes/infrahub', }, items: [ + 'release-notes/infrahub/release-1_4_12', 'release-notes/infrahub/release-1_4_11', 'release-notes/infrahub/release-1_4_10', 'release-notes/infrahub/release-1_4_9', diff --git a/frontend/app/src/config/constants.ts b/frontend/app/src/config/constants.ts index 12fe3d07dd..c7b5095f89 100644 --- a/frontend/app/src/config/constants.ts +++ b/frontend/app/src/config/constants.ts @@ -1,3 +1,5 @@ +import { CheckType } from "@/shared/api/graphql/generated/graphql"; + import { RelationshipKind } from "@/entities/nodes/types"; import { PROPOSED_CHANGE_OBJECT } from "@/entities/proposed-changes/constants"; @@ -115,9 +117,10 @@ export const CHECKS_LABEL = { IN_PROGRESS: "In progress", }; -export const VALIDATIONS_ENUM_MAP: { [key: string]: string } = { +export const VALIDATIONS_ENUM_MAP: { [key: string]: CheckType } = { CoreArtifactValidator: "ARTIFACT", CoreDataValidator: "DATA", + CoreGeneratorValidator: "GENERATOR", CoreRepositoryValidator: "REPOSITORY", CoreSchemaValidator: "SCHEMA", CoreUserValidator: "USER", diff --git a/frontend/app/src/entities/diff/api/getCheckDetails.ts b/frontend/app/src/entities/diff/api/get-check-details-from-api.ts similarity index 64% rename from frontend/app/src/entities/diff/api/getCheckDetails.ts rename to frontend/app/src/entities/diff/api/get-check-details-from-api.ts index 9e5fc0ce6c..74a976fa5d 100644 --- a/frontend/app/src/entities/diff/api/getCheckDetails.ts +++ b/frontend/app/src/entities/diff/api/get-check-details-from-api.ts @@ -1,8 +1,14 @@ import { gql } from "@apollo/client"; -export const GET_CHECKS = gql` - query GET_CORE_CHECKS($ids: [ID]!) { - CoreCheck(ids: $ids) { +import { + Get_Check_DetailsQuery, + Get_Check_DetailsQueryVariables, +} from "@/shared/api/graphql/generated/graphql"; +import graphqlClient from "@/shared/api/graphql/graphqlClientApollo"; + +const GET_CHECK_DETAILS = gql` + query GET_CHECK_DETAILS($id: ID!) { + CoreCheck(ids: [$id]) { edges { node { id @@ -63,3 +69,12 @@ export const GET_CHECKS = gql` } } `; + +export interface GetCheckDetailsFromApiParams extends Get_Check_DetailsQueryVariables {} + +export const getCheckDetailsFromApi = async (variables: GetCheckDetailsFromApiParams) => { + return graphqlClient.query({ + query: GET_CHECK_DETAILS, + variables, + }); +}; diff --git a/frontend/app/src/entities/diff/api/get-validators-from-api.ts b/frontend/app/src/entities/diff/api/get-validators-from-api.ts new file mode 100644 index 0000000000..0d7daff7ee --- /dev/null +++ b/frontend/app/src/entities/diff/api/get-validators-from-api.ts @@ -0,0 +1,54 @@ +import { gql } from "@apollo/client"; + +import { + Get_Check_DetailsQueryVariables, + Get_Core_ValidatorsQuery, +} from "@/shared/api/graphql/generated/graphql"; +import graphqlClient from "@/shared/api/graphql/graphqlClientApollo"; + +const GET_VALIDATORS = gql` + query GET_CORE_VALIDATORS($id: ID!) { + CoreValidator(proposed_change__ids: [$id]) { + edges { + node { + id + display_label + conclusion { + value + } + started_at { + value + } + completed_at { + value + } + state { + value + } + checks { + edges { + node { + conclusion { + value + } + severity { + value + } + } + } + } + __typename + } + } + } + } +`; + +export interface GetValidatorsFromApiParams extends Get_Check_DetailsQueryVariables {} + +export const getValidatorsFromApi = async (variables: GetValidatorsFromApiParams) => { + return graphqlClient.query({ + query: GET_VALIDATORS, + variables, + }); +}; diff --git a/frontend/app/src/entities/diff/api/getValidators.ts b/frontend/app/src/entities/diff/api/getValidators.ts deleted file mode 100644 index a7119838be..0000000000 --- a/frontend/app/src/entities/diff/api/getValidators.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { gql } from "@apollo/client"; - -export const GET_VALIDATORS = gql` - query GET_CORE_VALIDATORS($ids: [ID]!) { - CoreValidator(proposed_change__ids: $ids) { - edges { - node { - id - display_label - conclusion { - value - } - started_at { - value - } - completed_at { - value - } - state { - value - } - checks { - edges { - node { - conclusion { - value - } - severity { - value - } - } - } - } - __typename - } - } - } - } -`; diff --git a/frontend/app/src/entities/diff/api/run-check-from-api.ts b/frontend/app/src/entities/diff/api/run-check-from-api.ts new file mode 100644 index 0000000000..64c4c2b4c6 --- /dev/null +++ b/frontend/app/src/entities/diff/api/run-check-from-api.ts @@ -0,0 +1,32 @@ +import { gql } from "@apollo/client"; + +import { + Run_CheckMutation, + Run_CheckMutationVariables, +} from "@/shared/api/graphql/generated/graphql"; +import graphqlClient from "@/shared/api/graphql/graphqlClientApollo"; + +export const RUN_CHECK = gql` + mutation RUN_CHECK($proposedChangeId: String!, $checkType: CheckType) { + CoreProposedChangeRunCheck ( + data: { + id: $proposedChangeId, + check_type: $checkType + } + ) { + ok + } + } +`; + +export interface RunCheckFromApiParams extends Run_CheckMutationVariables {} + +export const runCheckFromApi = ({ proposedChangeId, checkType }: RunCheckFromApiParams) => { + return graphqlClient.mutate({ + mutation: RUN_CHECK, + variables: { + proposedChangeId, + checkType, + }, + }); +}; diff --git a/frontend/app/src/entities/diff/api/runCheck.ts b/frontend/app/src/entities/diff/api/runCheck.ts deleted file mode 100644 index 465c2dbd24..0000000000 --- a/frontend/app/src/entities/diff/api/runCheck.ts +++ /dev/null @@ -1,14 +0,0 @@ -import Handlebars from "@/shared/libs/handlebars"; - -export const runCheck = Handlebars.compile(` -mutation { - CoreProposedChangeRunCheck ( - data: { - id: "{{id}}", - check_type: {{check_type}} - } - ) { - ok - } -} -`); diff --git a/frontend/app/src/entities/diff/checks/check.tsx b/frontend/app/src/entities/diff/checks/check.tsx index 6d20e1fa14..6c8eddfd7c 100644 --- a/frontend/app/src/entities/diff/checks/check.tsx +++ b/frontend/app/src/entities/diff/checks/check.tsx @@ -1,24 +1,22 @@ import { Icon } from "@iconify-icon/react"; import { useAtomValue } from "jotai"; -import useQuery from "@/shared/api/graphql/useQuery"; import { InfoButton } from "@/shared/components/buttons/info-button"; import Accordion from "@/shared/components/display/accordion"; import { DateDisplay } from "@/shared/components/display/date-display"; import { CodeViewer } from "@/shared/components/editor/code/code-viewer"; import ErrorScreen from "@/shared/components/errors/error-screen"; -import { Skeleton } from "@/shared/components/skeleton"; +import { LoadingIndicator } from "@/shared/components/loading/loading-indicator"; import { List } from "@/shared/components/table/list"; import { Popover, PopoverContent, PopoverTrigger } from "@/shared/components/ui/popover"; import { Tooltip } from "@/shared/components/ui/tooltip"; import { classNames } from "@/shared/utils/common"; -import { GET_CHECKS } from "@/entities/diff/api/getCheckDetails"; +import { DataIntegrityConflicts } from "@/entities/diff/checks/data-integrity-conflicts"; +import { SchemaIntegrityConflicts } from "@/entities/diff/checks/schema-integrity-conflicts"; +import { useGetCheckDetails } from "@/entities/diff/domain/get-check-details.query"; import { schemaKindLabelState } from "@/entities/schema/stores/schemaKindLabel.atom"; -import { DataIntegrityConflicts } from "./data-integrity-conflicts"; -import { SchemaIntegrityConflicts } from "./schema-integrity-conflicts"; - type tCheckProps = { id: string; }; @@ -75,9 +73,23 @@ const getCheckBorderColor = (severity?: string) => { export const Check = ({ id }: tCheckProps) => { const schemaKindLabel = useAtomValue(schemaKindLabelState); - const { loading, error, data } = useQuery(GET_CHECKS, { variables: { ids: [id] } }); + const { isPending, error, data: check } = useGetCheckDetails({ checkId: id }); + + if (error) { + return ( +
+ +
+ ); + } + + if (isPending) { + return ; + } - const check = data?.CoreCheck?.edges?.[0]?.node ?? {}; + if (!check) { + return null; + } const { __typename, @@ -92,14 +104,6 @@ export const Check = ({ id }: tCheckProps) => { conflicts, } = check; - if (error) { - return ( -
- -
- ); - } - const columns = [ { name: "type", @@ -133,20 +137,12 @@ export const Check = ({ id }: tCheckProps) => {
- {loading ? ( - - ) : ( - getCheckIcon(conclusion?.value) - )} + {getCheckIcon(conclusion?.value)} - {loading ? : name?.value || display_label} + {name?.value || display_label}
- {loading ? ( - - ) : ( - created_at?.value && - )} + {created_at?.value && } diff --git a/frontend/app/src/entities/diff/checks/checks-summary.tsx b/frontend/app/src/entities/diff/checks/checks-summary.tsx index 39b10f73aa..1dcf4fb34e 100644 --- a/frontend/app/src/entities/diff/checks/checks-summary.tsx +++ b/frontend/app/src/entities/diff/checks/checks-summary.tsx @@ -1,5 +1,3 @@ -import { gql } from "@apollo/client"; -import { Icon } from "@iconify-icon/react"; import { useAtomValue } from "jotai"; import { useParams } from "react-router"; import { toast } from "react-toastify"; @@ -10,7 +8,7 @@ import { VALIDATIONS_ENUM_MAP, } from "@/config/constants"; -import graphqlClient from "@/shared/api/graphql/graphqlClientApollo"; +import { queryClient } from "@/shared/api/rest/client"; import { Button } from "@/shared/components/buttons/button-primitive"; import { Retry } from "@/shared/components/buttons/retry"; import { PieChart } from "@/shared/components/display/pie-chart"; @@ -19,24 +17,25 @@ import { ALERT_TYPES, Alert } from "@/shared/components/ui/alert"; import { classNames } from "@/shared/utils/common"; import { useAuth } from "@/entities/authentication/ui/useAuth"; -import { runCheck } from "@/entities/diff/api/runCheck"; +import { proposedChangeValidatorsKeys } from "@/entities/diff/domain/diff.query-keys"; +import { useRunCheckMutation } from "@/entities/diff/domain/run-check.mutation"; import { getValidatorsStats } from "@/entities/proposed-changes/ui/checks"; import { genericSchemasAtom } from "@/entities/schema/stores/schema.atom"; import { schemaKindLabelState } from "@/entities/schema/stores/schemaKindLabel.atom"; -type tChecksSummaryProps = { +type ChecksSummaryProps = { validators: any[]; isLoading: boolean; - refetch: Function; }; -export const ChecksSummary = (props: tChecksSummaryProps) => { - const { isLoading, validators, refetch } = props; +export const ChecksSummary = (props: ChecksSummaryProps) => { + const { isLoading, validators } = props; const { proposedChangeId } = useParams(); const schemaKindLabel = useAtomValue(schemaKindLabelState); const schemaList = useAtomValue(genericSchemasAtom); const { isAuthenticated } = useAuth(); + const { mutate, isPending } = useRunCheckMutation(); const schemaData = schemaList.find((s) => s.kind === PROPOSED_CHANGES_VALIDATOR_OBJECT); @@ -49,24 +48,20 @@ export const ChecksSummary = (props: tChecksSummaryProps) => { }, {}); const handleRetry = async (validator: string) => { - const runParams = { - id: proposedChangeId, - check_type: VALIDATIONS_ENUM_MAP[validator], - }; - - const mustationString = runCheck(runParams); - - const mutation = gql` - ${mustationString} - `; - - const result = await graphqlClient.mutate({ mutation }); - - refetch(); - - if (result?.data?.CoreProposedChangeRunCheck?.ok) { - toast(); - } + mutate( + { + proposedChangeId: proposedChangeId!, + checkType: VALIDATIONS_ENUM_MAP[validator]!, + }, + { + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: proposedChangeValidatorsKeys.allWithinProposedChange(proposedChangeId!), + }); + toast(); + }, + } + ); }; const canRetry = (stats: any) => { @@ -93,7 +88,7 @@ export const ChecksSummary = (props: tChecksSummaryProps) => { className="gap-1 hover:bg-neutral-200" > Retry all - +
@@ -103,20 +98,28 @@ export const ChecksSummary = (props: tChecksSummaryProps) => { {Object.entries(validatorsCount).map(([kind, data]: [string, any]) => (
- canRetry(data) && handleRetry(kind)}> + + +
+ + {(schemaKindLabel[kind] ?? kind)?.replace("Validator", "").trim()} + + {canRetry(data) && ( -
+
canRetry(data) && handleRetry(kind)} />
)} - - - - {schemaKindLabel[kind]?.replace("Validator", "").trim()} - +
))} diff --git a/frontend/app/src/entities/diff/checks/checks.tsx b/frontend/app/src/entities/diff/checks/checks.tsx index 3cb0958835..9988da5ceb 100644 --- a/frontend/app/src/entities/diff/checks/checks.tsx +++ b/frontend/app/src/entities/diff/checks/checks.tsx @@ -1,42 +1,39 @@ -import { forwardRef, useImperativeHandle } from "react"; import { useParams } from "react-router"; -import useQuery from "@/shared/api/graphql/useQuery"; import ErrorScreen from "@/shared/components/errors/error-screen"; +import { LoadingIndicator } from "@/shared/components/loading/loading-indicator"; -import { GET_VALIDATORS } from "@/entities/diff/api/getValidators"; +import { ChecksSummary } from "@/entities/diff/checks/checks-summary"; +import { Validator } from "@/entities/diff/checks/validator"; +import { useGetValidatorsQuery } from "@/entities/diff/domain/get-validators.query"; -import { ChecksSummary } from "./checks-summary"; -import { Validator } from "./validator"; - -export const Checks = forwardRef((_, ref) => { +export const Checks = () => { const { proposedChangeId } = useParams(); - const { loading, error, data, refetch } = useQuery(GET_VALIDATORS, { - notifyOnNetworkStatusChange: true, - variables: { - ids: [proposedChangeId], - }, + const { + isPending, + error, + data: validators, + } = useGetValidatorsQuery({ + proposedChangeId: proposedChangeId!, }); - // Provide refetch function to parent - useImperativeHandle(ref, () => ({ refetch })); - - const validators = data?.CoreValidator?.edges?.map((edge: any) => edge.node) ?? []; - if (error) { return ; } + if (isPending) { + return ; + } + return (
- - +
- {validators.map((item: any) => ( + {validators.map((item) => ( ))}
); -}); +}; diff --git a/frontend/app/src/entities/diff/checks/validator-details.tsx b/frontend/app/src/entities/diff/checks/validator-details.tsx index 3af08ae2b0..60d9004226 100644 --- a/frontend/app/src/entities/diff/checks/validator-details.tsx +++ b/frontend/app/src/entities/diff/checks/validator-details.tsx @@ -56,7 +56,6 @@ export const ValidatorDetails = ({ id }: tValidatorDetails) => { {!validator?.checks?.edges?.length && }
- {!!validator?.checks?.edges?.length && }
); diff --git a/frontend/app/src/entities/diff/domain/diff.query-keys.ts b/frontend/app/src/entities/diff/domain/diff.query-keys.ts index ed5e1f733d..94d0bb328e 100644 --- a/frontend/app/src/entities/diff/domain/diff.query-keys.ts +++ b/frontend/app/src/entities/diff/domain/diff.query-keys.ts @@ -9,3 +9,14 @@ export const treeQueryKeys = { export const updateDiffMutationKeys = { all: ["update-diff"] as const, }; + +export const getCheckQueryKeys = { + all: ["checks"] as const, + details: (checkId: string) => [...getCheckQueryKeys.all, checkId] as const, +}; + +export const proposedChangeValidatorsKeys = { + all: ["proposed-change-validators"] as const, + allWithinProposedChange: (proposedChangeId: string) => + [...proposedChangeValidatorsKeys.all, proposedChangeId] as const, +}; diff --git a/frontend/app/src/entities/diff/domain/get-check-details.query.ts b/frontend/app/src/entities/diff/domain/get-check-details.query.ts new file mode 100644 index 0000000000..06b7551951 --- /dev/null +++ b/frontend/app/src/entities/diff/domain/get-check-details.query.ts @@ -0,0 +1,13 @@ +import { queryOptions, useQuery } from "@tanstack/react-query"; + +import { getCheckQueryKeys } from "@/entities/diff/domain/diff.query-keys"; +import { GetCheckDetailsParams, getCheckDetails } from "@/entities/diff/domain/get-check-details"; + +export const useGetCheckDetails = (params: GetCheckDetailsParams) => { + return useQuery( + queryOptions({ + queryKey: getCheckQueryKeys.details(params.checkId), + queryFn: () => getCheckDetails(params), + }) + ); +}; diff --git a/frontend/app/src/entities/diff/domain/get-check-details.ts b/frontend/app/src/entities/diff/domain/get-check-details.ts new file mode 100644 index 0000000000..edf6790a4f --- /dev/null +++ b/frontend/app/src/entities/diff/domain/get-check-details.ts @@ -0,0 +1,11 @@ +import { getCheckDetailsFromApi } from "@/entities/diff/api/get-check-details-from-api"; + +export type GetCheckDetailsParams = { checkId: string }; + +export const getCheckDetails = async ({ checkId }: GetCheckDetailsParams) => { + const { data, error } = await getCheckDetailsFromApi({ id: checkId }); + + if (error) throw error; + + return data?.CoreCheck?.edges?.[0]?.node ?? null; +}; diff --git a/frontend/app/src/entities/diff/domain/get-validators.query.ts b/frontend/app/src/entities/diff/domain/get-validators.query.ts new file mode 100644 index 0000000000..357b59df7b --- /dev/null +++ b/frontend/app/src/entities/diff/domain/get-validators.query.ts @@ -0,0 +1,13 @@ +import { queryOptions, useQuery } from "@tanstack/react-query"; + +import { proposedChangeValidatorsKeys } from "@/entities/diff/domain/diff.query-keys"; +import { GetValidatorsParams, getValidators } from "@/entities/diff/domain/get-validators"; + +export const useGetValidatorsQuery = (params: GetValidatorsParams) => { + return useQuery( + queryOptions({ + queryKey: proposedChangeValidatorsKeys.allWithinProposedChange(params.proposedChangeId), + queryFn: () => getValidators(params), + }) + ); +}; diff --git a/frontend/app/src/entities/diff/domain/get-validators.ts b/frontend/app/src/entities/diff/domain/get-validators.ts new file mode 100644 index 0000000000..0b290fff38 --- /dev/null +++ b/frontend/app/src/entities/diff/domain/get-validators.ts @@ -0,0 +1,11 @@ +import { getValidatorsFromApi } from "@/entities/diff/api/get-validators-from-api"; + +export type GetValidatorsParams = { proposedChangeId: string }; + +export const getValidators = async ({ proposedChangeId }: GetValidatorsParams) => { + const { data, error } = await getValidatorsFromApi({ id: proposedChangeId }); + + if (error) throw error; + + return data?.CoreValidator?.edges?.map((edge) => edge.node)?.filter((node) => !!node) ?? []; +}; diff --git a/frontend/app/src/entities/diff/domain/run-check.mutation.ts b/frontend/app/src/entities/diff/domain/run-check.mutation.ts new file mode 100644 index 0000000000..d8cec9f9cc --- /dev/null +++ b/frontend/app/src/entities/diff/domain/run-check.mutation.ts @@ -0,0 +1,9 @@ +import { useMutation } from "@tanstack/react-query"; + +import { runCheck } from "@/entities/diff/domain/run-check"; + +export function useRunCheckMutation() { + return useMutation({ + mutationFn: runCheck, + }); +} diff --git a/frontend/app/src/entities/diff/domain/run-check.ts b/frontend/app/src/entities/diff/domain/run-check.ts new file mode 100644 index 0000000000..67de85928e --- /dev/null +++ b/frontend/app/src/entities/diff/domain/run-check.ts @@ -0,0 +1,13 @@ +import { CheckType } from "@/shared/api/graphql/generated/graphql"; + +import { runCheckFromApi } from "@/entities/diff/api/run-check-from-api"; + +export type RunCheckParams = { + proposedChangeId: string; + checkType: CheckType; +}; +export type RunCheck = (params: RunCheckParams) => Promise; + +export const runCheck: RunCheck = async (params) => { + await runCheckFromApi(params); +}; diff --git a/frontend/app/src/shared/components/display/pie-chart.tsx b/frontend/app/src/shared/components/display/pie-chart.tsx index cc6366d1fe..91151aa03e 100644 --- a/frontend/app/src/shared/components/display/pie-chart.tsx +++ b/frontend/app/src/shared/components/display/pie-chart.tsx @@ -1,8 +1,7 @@ import { Cell, Pie, PieChart as RPieChart, Tooltip } from "recharts"; -type tPieChart = { +type PieChartProps = { data: any[]; - children?: any; onClick?: Function; }; @@ -14,14 +13,14 @@ const renderCustomizedTooltip = (props: any) => { } return ( - +
{data.name}: {data.value} - +
); }; -export const PieChart = (props: tPieChart) => { - const { data, children, onClick } = props; +export const PieChart = (props: PieChartProps) => { + const { data, onClick } = props; const handleClick = () => { if (!onClick) return; @@ -30,30 +29,22 @@ export const PieChart = (props: tPieChart) => { }; return ( -
-
- {children} -
- +
- - {data.map((entry, index) => ( ))} + +
); diff --git a/poetry.lock b/poetry.lock index b0a0b8a1cc..9c60b27a05 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "aio-pika" @@ -681,7 +681,7 @@ files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {dev = "sys_platform == \"win32\" or platform_system == \"Windows\""} +markers = {dev = "platform_system == \"Windows\" or sys_platform == \"win32\""} [[package]] name = "contourpy" @@ -5248,6 +5248,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76"}, {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6"}, {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a"}, {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da"}, {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6"}, @@ -5256,6 +5257,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632"}, @@ -5264,6 +5266,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a"}, @@ -5272,6 +5275,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987"}, @@ -5280,6 +5284,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win32.whl", hash = "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b"}, {file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"}, @@ -5928,31 +5933,31 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uv" -version = "0.8.13" +version = "0.9.5" description = "An extremely fast Python package and project manager, written in Rust." optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "uv-0.8.13-py3-none-linux_armv6l.whl", hash = "sha256:3b5c6e44238007ec1d25212cafe1b37a8506d425d1dd74a267cb9072a61930f9"}, - {file = "uv-0.8.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2945c32b8fcf23807ef1f74c390795e2b00371c53b94c015cc6e7b0cfbab9d94"}, - {file = "uv-0.8.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:73459fe1403b1089853071db6770450dc03e4058848f7146d88cff5f1c352743"}, - {file = "uv-0.8.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:854c4e75024a4894477bf61684b2872b83c77ca87d1bad62692bcc31200619c3"}, - {file = "uv-0.8.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:28c8d4560c673ff5c798f2f4422281840728f46ebf1946345b65d065f8344c03"}, - {file = "uv-0.8.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6c508aa9c5210577008e1919b532e38356fe68712179399f00462b3e78fd845"}, - {file = "uv-0.8.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3bac51ea503d97f371222f23e845fc4ab95465ac3e958c7589d6743c75445b71"}, - {file = "uv-0.8.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a6d37547947fcae57244b4d1f3b62fba55f4a85d3e45e7284a93b6cd5bedca4"}, - {file = "uv-0.8.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3735a452cdc3168932d128891d7e8866b4a2d052283c6da5ccfe0b038d1cf8bd"}, - {file = "uv-0.8.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2113cd877974b68ea2af64a2f2cc23708ba97066046e78efb72ba94e5fef617a"}, - {file = "uv-0.8.13-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:4c2c5e5962239ecaff6444d5bc22422a9bd2da25a80adc6ab14cb42e4461b1cf"}, - {file = "uv-0.8.13-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:eb90089624d92d57b8582f708973db8988e09dba6bae83991dba20731d82eb6a"}, - {file = "uv-0.8.13-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:cf3ce98404ddc1e11cd2c2604668f8f81219cf00bb1227b792fdf5dbb4faf31a"}, - {file = "uv-0.8.13-py3-none-musllinux_1_1_i686.whl", hash = "sha256:8a3739540f8b0b5258869b1671185d55daacfa4609eaffd235573ac938ec01a6"}, - {file = "uv-0.8.13-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:18a502328545af511039c7b7c602a0aa89eeff23b1221a1f56d99b3a3fecfddd"}, - {file = "uv-0.8.13-py3-none-win32.whl", hash = "sha256:d22fa55580b224779279b98e0b23cbc45e51837e1fac616d7c5d03aff668a998"}, - {file = "uv-0.8.13-py3-none-win_amd64.whl", hash = "sha256:20862f612de38f6dea55d40467a29f3cb621b256a4b5891ae55debbbdf1db2b4"}, - {file = "uv-0.8.13-py3-none-win_arm64.whl", hash = "sha256:404ca19b2d860ab661e1d78633f594e994f8422af8772ad237d763fe353da2ab"}, - {file = "uv-0.8.13.tar.gz", hash = "sha256:a4438eca3d301183c52994a6d2baff70fd1840421a83446f3cabb1d0d0b50aff"}, + {file = "uv-0.9.5-py3-none-linux_armv6l.whl", hash = "sha256:f8eb34ebebac4b45334ce7082cca99293b71fb32b164651f1727c8a640e5b387"}, + {file = "uv-0.9.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:922cd784cce36bbdc7754b590d28c276698c85791c18cd4c6a7e917db4480440"}, + {file = "uv-0.9.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8603bb902e578463c50c3ddd4ee376ba4172ccdf4979787f8948747d1bb0e18b"}, + {file = "uv-0.9.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:48a3542835d37882ff57d1ff91b757085525d98756712fa61cf9941d3dda8ebf"}, + {file = "uv-0.9.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:21452ece590ddb90e869a478ca4c2ba70be180ec0d6716985ee727b9394c8aa5"}, + {file = "uv-0.9.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb31c9896dc2c88f6a9f1d693be2409fe2fc2e3d90827956e4341c2b2171289"}, + {file = "uv-0.9.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:02db727beb94a2137508cee5a785c3465d150954ca9abdff2d8157c76dea163e"}, + {file = "uv-0.9.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c465f2e342cab908849b8ce83e14fd4cf75f5bed55802d0acf1399f9d02f92d9"}, + {file = "uv-0.9.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:133e2614e1ff3b34c2606595d8ae55710473ebb7516bfa5708afc00315730cd1"}, + {file = "uv-0.9.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6507bbbcd788553ec4ad5a96fa19364dc0f58b023e31d79868773559a83ec181"}, + {file = "uv-0.9.5-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:6a046c2e833169bf26f461286aab58a2ba8d48ed2220bfcf119dcfaf87163116"}, + {file = "uv-0.9.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9fc13b4b943d19adac52d7dcd2159e96ab2e837ac49a79e20714ed25f1f1b7f9"}, + {file = "uv-0.9.5-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:5bb4996329ba47e7e775baba4a47e85092aa491d708a66e63b564e9b306bfb7e"}, + {file = "uv-0.9.5-py3-none-musllinux_1_1_i686.whl", hash = "sha256:6452eb6257e37e1ebd97430b5f5e10419da2c3ca35b4086540ec4163b4b2f25c"}, + {file = "uv-0.9.5-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:3a4ecbfdcbd3dae4190428874762c791e05d2c97ff2872bf6c0a30ed5c4ea9ca"}, + {file = "uv-0.9.5-py3-none-win32.whl", hash = "sha256:0316493044035098666d6e99c14bd61b352555d9717d57269f4ce531855330fa"}, + {file = "uv-0.9.5-py3-none-win_amd64.whl", hash = "sha256:48a12390421f91af8a8993cf15c38297c0bb121936046286e287975b2fbf1789"}, + {file = "uv-0.9.5-py3-none-win_arm64.whl", hash = "sha256:c966e3a4fe4de3b0a6279d0a835c79f9cddbb3693f52d140910cbbed177c5742"}, + {file = "uv-0.9.5.tar.gz", hash = "sha256:d8835d2c034421ac2235fb658bb4f669a301a0f1eb00a8430148dd8461b65641"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index ee814c9411..9b40d39329 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "infrahub-server" -version = "1.4.11" +version = "1.4.12" description = "Infrahub is taking a new approach to Infrastructure Management by providing a new generation of datastore to organize and control all the data that defines how an infrastructure should run." authors = ["OpsMill "] readme = "README.md" diff --git a/python_testcontainers/pyproject.toml b/python_testcontainers/pyproject.toml index 3de01449ae..69f667943f 100644 --- a/python_testcontainers/pyproject.toml +++ b/python_testcontainers/pyproject.toml @@ -1,11 +1,11 @@ [project] name = "infrahub-testcontainers" -version = "1.4.11" +version = "1.4.12" requires-python = ">=3.9" [tool.poetry] name = "infrahub-testcontainers" -version = "1.4.11" +version = "1.4.12" description = "Testcontainers instance for Infrahub to easily build integration tests" authors = ["OpsMill "] readme = "README.md"