orchestrator-core 2.10.0rc1__py3-none-any.whl → 3.0.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- orchestrator/__init__.py +1 -1
- orchestrator/api/api_v1/api.py +24 -3
- orchestrator/api/api_v1/endpoints/processes.py +1 -1
- orchestrator/api/api_v1/endpoints/product_blocks.py +56 -0
- orchestrator/api/api_v1/endpoints/products.py +28 -1
- orchestrator/api/api_v1/endpoints/resource_types.py +56 -0
- orchestrator/api/api_v1/endpoints/workflows.py +54 -0
- orchestrator/app.py +3 -2
- orchestrator/cli/generator/generator/product_block.py +1 -9
- orchestrator/cli/generator/templates/create_product.j2 +2 -1
- orchestrator/cli/generator/templates/modify_product.j2 +2 -1
- orchestrator/cli/generator/templates/shared_workflows.j2 +2 -1
- orchestrator/cli/generator/templates/terminate_product.j2 +1 -1
- orchestrator/cli/generator/templates/test_create_workflow.j2 +0 -1
- orchestrator/cli/generator/templates/test_modify_workflow.j2 +1 -2
- orchestrator/cli/generator/templates/test_terminate_workflow.j2 +1 -1
- orchestrator/cli/generator/templates/validate_product.j2 +3 -1
- orchestrator/cli/helpers/print_helpers.py +1 -1
- orchestrator/config/assignee.py +1 -1
- orchestrator/devtools/populator.py +1 -1
- orchestrator/devtools/scripts/migrate_20.py +11 -106
- orchestrator/devtools/scripts/migrate_30.py +61 -0
- orchestrator/devtools/scripts/shared.py +108 -0
- orchestrator/domain/base.py +1 -2
- orchestrator/domain/lifecycle.py +2 -1
- orchestrator/migrations/helpers.py +1 -1
- orchestrator/schemas/engine_settings.py +1 -1
- orchestrator/schemas/product.py +4 -0
- orchestrator/schemas/product_block.py +4 -0
- orchestrator/schemas/resource_type.py +4 -0
- orchestrator/schemas/subscription.py +2 -1
- orchestrator/schemas/workflow.py +4 -0
- orchestrator/services/celery.py +1 -1
- orchestrator/services/processes.py +2 -1
- orchestrator/services/products.py +1 -1
- orchestrator/services/subscriptions.py +2 -1
- orchestrator/services/tasks.py +2 -1
- orchestrator/settings.py +1 -1
- orchestrator/targets.py +1 -1
- orchestrator/types.py +8 -43
- orchestrator/utils/errors.py +2 -1
- orchestrator/utils/state.py +2 -1
- orchestrator/workflow.py +3 -1
- orchestrator/workflows/modify_note.py +1 -2
- orchestrator/workflows/steps.py +2 -1
- orchestrator/workflows/tasks/cleanup_tasks_log.py +1 -1
- orchestrator/workflows/tasks/resume_workflows.py +1 -1
- orchestrator/workflows/tasks/validate_product_type.py +1 -1
- orchestrator/workflows/tasks/validate_products.py +1 -1
- orchestrator/workflows/utils.py +2 -2
- {orchestrator_core-2.10.0rc1.dist-info → orchestrator_core-3.0.0rc1.dist-info}/METADATA +1 -1
- {orchestrator_core-2.10.0rc1.dist-info → orchestrator_core-3.0.0rc1.dist-info}/RECORD +54 -49
- {orchestrator_core-2.10.0rc1.dist-info → orchestrator_core-3.0.0rc1.dist-info}/LICENSE +0 -0
- {orchestrator_core-2.10.0rc1.dist-info → orchestrator_core-3.0.0rc1.dist-info}/WHEEL +0 -0
orchestrator/__init__.py
CHANGED
orchestrator/api/api_v1/api.py
CHANGED
|
@@ -19,12 +19,15 @@ from fastapi.routing import APIRouter
|
|
|
19
19
|
from orchestrator.api.api_v1.endpoints import (
|
|
20
20
|
health,
|
|
21
21
|
processes,
|
|
22
|
+
product_blocks,
|
|
22
23
|
products,
|
|
24
|
+
resource_types,
|
|
23
25
|
settings,
|
|
24
26
|
subscription_customer_descriptions,
|
|
25
27
|
subscriptions,
|
|
26
28
|
translations,
|
|
27
29
|
user,
|
|
30
|
+
workflows,
|
|
28
31
|
ws,
|
|
29
32
|
)
|
|
30
33
|
from orchestrator.security import authorize
|
|
@@ -34,14 +37,32 @@ api_router = APIRouter()
|
|
|
34
37
|
api_router.include_router(
|
|
35
38
|
processes.router, prefix="/processes", tags=["Core", "Processes"], dependencies=[Depends(authorize)]
|
|
36
39
|
)
|
|
40
|
+
api_router.include_router(
|
|
41
|
+
subscriptions.router,
|
|
42
|
+
prefix="/subscriptions",
|
|
43
|
+
tags=["Core", "Subscriptions"],
|
|
44
|
+
dependencies=[Depends(authorize)],
|
|
45
|
+
)
|
|
37
46
|
api_router.include_router(processes.ws_router, prefix="/processes", tags=["Core", "Processes"])
|
|
38
47
|
api_router.include_router(
|
|
39
48
|
products.router, prefix="/products", tags=["Core", "Product"], dependencies=[Depends(authorize)]
|
|
40
49
|
)
|
|
41
50
|
api_router.include_router(
|
|
42
|
-
|
|
43
|
-
prefix="/
|
|
44
|
-
tags=["Core", "
|
|
51
|
+
product_blocks.router,
|
|
52
|
+
prefix="/product_blocks",
|
|
53
|
+
tags=["Core", "Product Blocks"],
|
|
54
|
+
dependencies=[Depends(authorize)],
|
|
55
|
+
)
|
|
56
|
+
api_router.include_router(
|
|
57
|
+
resource_types.router,
|
|
58
|
+
prefix="/resource_types",
|
|
59
|
+
tags=["Core", "Resource Types"],
|
|
60
|
+
dependencies=[Depends(authorize)],
|
|
61
|
+
)
|
|
62
|
+
api_router.include_router(
|
|
63
|
+
workflows.router,
|
|
64
|
+
prefix="/workflows",
|
|
65
|
+
tags=["Core", "Workflows"],
|
|
45
66
|
dependencies=[Depends(authorize)],
|
|
46
67
|
)
|
|
47
68
|
api_router.include_router(
|
|
@@ -61,7 +61,6 @@ from orchestrator.services.processes import (
|
|
|
61
61
|
)
|
|
62
62
|
from orchestrator.services.settings import get_engine_settings
|
|
63
63
|
from orchestrator.settings import app_settings
|
|
64
|
-
from orchestrator.types import JSON, State
|
|
65
64
|
from orchestrator.utils.enrich_process import enrich_process
|
|
66
65
|
from orchestrator.websocket import (
|
|
67
66
|
WS_CHANNELS,
|
|
@@ -70,6 +69,7 @@ from orchestrator.websocket import (
|
|
|
70
69
|
websocket_manager,
|
|
71
70
|
)
|
|
72
71
|
from orchestrator.workflow import ProcessStatus
|
|
72
|
+
from pydantic_forms.types import JSON, State
|
|
73
73
|
|
|
74
74
|
router = APIRouter()
|
|
75
75
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Copyright 2019-2020 SURF.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
|
|
14
|
+
from http import HTTPStatus
|
|
15
|
+
from uuid import UUID
|
|
16
|
+
|
|
17
|
+
from fastapi.param_functions import Body
|
|
18
|
+
from fastapi.routing import APIRouter
|
|
19
|
+
|
|
20
|
+
from orchestrator.api.error_handling import raise_status
|
|
21
|
+
from orchestrator.db import db
|
|
22
|
+
from orchestrator.db.models import ProductBlockTable
|
|
23
|
+
from orchestrator.schemas.product_block import ProductBlockPatchSchema, ProductBlockSchema
|
|
24
|
+
|
|
25
|
+
router = APIRouter()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@router.get("/{product_block_id}", response_model=ProductBlockSchema)
|
|
29
|
+
def get_product_block_description(product_block_id: UUID) -> str:
|
|
30
|
+
product_block = db.session.get(ProductBlockTable, product_block_id)
|
|
31
|
+
if product_block is None:
|
|
32
|
+
raise_status(HTTPStatus.NOT_FOUND)
|
|
33
|
+
return product_block
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@router.patch("/{product_block_id}", status_code=HTTPStatus.CREATED, response_model=ProductBlockSchema)
|
|
37
|
+
async def patch_product_block_by_id(
|
|
38
|
+
product_block_id: UUID, data: ProductBlockPatchSchema = Body(...)
|
|
39
|
+
) -> ProductBlockTable:
|
|
40
|
+
product_block = db.session.get(ProductBlockTable, product_block_id)
|
|
41
|
+
if not product_block:
|
|
42
|
+
raise_status(HTTPStatus.NOT_FOUND, f"Product_block id {product_block_id} not found")
|
|
43
|
+
|
|
44
|
+
return await _patch_product_block_description(data, product_block)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def _patch_product_block_description(
|
|
48
|
+
data: ProductBlockPatchSchema,
|
|
49
|
+
product_block: ProductBlockTable,
|
|
50
|
+
) -> ProductBlockTable:
|
|
51
|
+
|
|
52
|
+
updated_properties = data.model_dump(exclude_unset=True)
|
|
53
|
+
description = updated_properties.get("description", product_block.description)
|
|
54
|
+
product_block.description = description
|
|
55
|
+
db.session.commit()
|
|
56
|
+
return product_block
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
from http import HTTPStatus
|
|
15
15
|
from uuid import UUID
|
|
16
16
|
|
|
17
|
+
from fastapi.param_functions import Body
|
|
17
18
|
from fastapi.routing import APIRouter
|
|
18
19
|
from sqlalchemy import select
|
|
19
20
|
from sqlalchemy.orm import joinedload, selectinload
|
|
@@ -21,6 +22,7 @@ from sqlalchemy.orm import joinedload, selectinload
|
|
|
21
22
|
from orchestrator.api.error_handling import raise_status
|
|
22
23
|
from orchestrator.db import ProductBlockTable, ProductTable, db
|
|
23
24
|
from orchestrator.schemas import ProductSchema
|
|
25
|
+
from orchestrator.schemas.product import ProductPatchSchema
|
|
24
26
|
|
|
25
27
|
router = APIRouter()
|
|
26
28
|
|
|
@@ -48,6 +50,13 @@ def fetch(tag: str | None = None, product_type: str | None = None) -> list[Produ
|
|
|
48
50
|
response_model=ProductSchema,
|
|
49
51
|
)
|
|
50
52
|
def product_by_id(product_id: UUID) -> ProductTable:
|
|
53
|
+
product = _product_by_id(product_id)
|
|
54
|
+
if not product:
|
|
55
|
+
raise_status(HTTPStatus.NOT_FOUND, f"Product id {product_id} not found")
|
|
56
|
+
return product
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _product_by_id(product_id: UUID) -> ProductTable | None:
|
|
51
60
|
stmt = (
|
|
52
61
|
select(ProductTable)
|
|
53
62
|
.options(
|
|
@@ -57,7 +66,25 @@ def product_by_id(product_id: UUID) -> ProductTable:
|
|
|
57
66
|
)
|
|
58
67
|
.filter(ProductTable.product_id == product_id)
|
|
59
68
|
)
|
|
60
|
-
|
|
69
|
+
return db.session.scalars(stmt).unique().one_or_none()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@router.patch("/{product_id}", status_code=HTTPStatus.CREATED, response_model=ProductSchema)
|
|
73
|
+
async def patch_product_by_id(product_id: UUID, data: ProductPatchSchema = Body(...)) -> ProductTable:
|
|
74
|
+
product = _product_by_id(product_id)
|
|
61
75
|
if not product:
|
|
62
76
|
raise_status(HTTPStatus.NOT_FOUND, f"Product id {product_id} not found")
|
|
77
|
+
|
|
78
|
+
return await _patch_product_description(data, product)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async def _patch_product_description(
|
|
82
|
+
data: ProductPatchSchema,
|
|
83
|
+
product: ProductTable,
|
|
84
|
+
) -> ProductTable:
|
|
85
|
+
|
|
86
|
+
updated_properties = data.model_dump(exclude_unset=True)
|
|
87
|
+
description = updated_properties.get("description", product.description)
|
|
88
|
+
product.description = description
|
|
89
|
+
db.session.commit()
|
|
63
90
|
return product
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Copyright 2019-2020 SURF.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
|
|
14
|
+
from http import HTTPStatus
|
|
15
|
+
from uuid import UUID
|
|
16
|
+
|
|
17
|
+
from fastapi.param_functions import Body
|
|
18
|
+
from fastapi.routing import APIRouter
|
|
19
|
+
|
|
20
|
+
from orchestrator.api.error_handling import raise_status
|
|
21
|
+
from orchestrator.db import db
|
|
22
|
+
from orchestrator.db.models import ResourceTypeTable
|
|
23
|
+
from orchestrator.schemas.resource_type import ResourceTypePatchSchema, ResourceTypeSchema
|
|
24
|
+
|
|
25
|
+
router = APIRouter()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@router.get("/{resource_type_id}", response_model=ResourceTypeSchema)
|
|
29
|
+
def get_resource_type_description(resource_type_id: UUID) -> str:
|
|
30
|
+
resource_type = db.session.get(ResourceTypeTable, resource_type_id)
|
|
31
|
+
if resource_type is None:
|
|
32
|
+
raise_status(HTTPStatus.NOT_FOUND)
|
|
33
|
+
return resource_type
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@router.patch("/{resource_type_id}", status_code=HTTPStatus.CREATED, response_model=ResourceTypeSchema)
|
|
37
|
+
async def patch_resource_type_by_id(
|
|
38
|
+
resource_type_id: UUID, data: ResourceTypePatchSchema = Body(...)
|
|
39
|
+
) -> ResourceTypeTable:
|
|
40
|
+
resource_type = db.session.get(ResourceTypeTable, resource_type_id)
|
|
41
|
+
if not resource_type:
|
|
42
|
+
raise_status(HTTPStatus.NOT_FOUND, f"ResourceType id {resource_type_id} not found")
|
|
43
|
+
|
|
44
|
+
return await _patch_resource_type_description(data, resource_type)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def _patch_resource_type_description(
|
|
48
|
+
data: ResourceTypePatchSchema,
|
|
49
|
+
resource_type: ResourceTypeTable,
|
|
50
|
+
) -> ResourceTypeTable:
|
|
51
|
+
|
|
52
|
+
updated_properties = data.model_dump(exclude_unset=True)
|
|
53
|
+
description = updated_properties.get("description", resource_type.description)
|
|
54
|
+
resource_type.description = description
|
|
55
|
+
db.session.commit()
|
|
56
|
+
return resource_type
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Copyright 2019-2020 SURF.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
|
|
14
|
+
from http import HTTPStatus
|
|
15
|
+
from uuid import UUID
|
|
16
|
+
|
|
17
|
+
from fastapi.param_functions import Body
|
|
18
|
+
from fastapi.routing import APIRouter
|
|
19
|
+
|
|
20
|
+
from orchestrator.api.error_handling import raise_status
|
|
21
|
+
from orchestrator.db import db
|
|
22
|
+
from orchestrator.db.models import WorkflowTable
|
|
23
|
+
from orchestrator.schemas.workflow import WorkflowPatchSchema, WorkflowSchema
|
|
24
|
+
|
|
25
|
+
router = APIRouter()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@router.get("/{workflow_id}", response_model=WorkflowSchema)
|
|
29
|
+
def get_workflow_description(workflow_id: UUID) -> str:
|
|
30
|
+
workflow = db.session.get(WorkflowTable, workflow_id)
|
|
31
|
+
if workflow is None:
|
|
32
|
+
raise_status(HTTPStatus.NOT_FOUND)
|
|
33
|
+
return workflow
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@router.patch("/{workflow_id}", status_code=HTTPStatus.CREATED, response_model=WorkflowSchema)
|
|
37
|
+
async def patch_workflow_by_id(workflow_id: UUID, data: WorkflowPatchSchema = Body(...)) -> WorkflowTable:
|
|
38
|
+
workflow = db.session.get(WorkflowTable, workflow_id)
|
|
39
|
+
if not workflow:
|
|
40
|
+
raise_status(HTTPStatus.NOT_FOUND, f"Workflow id {workflow_id} not found")
|
|
41
|
+
|
|
42
|
+
return await _patch_workflow_description(data, workflow)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async def _patch_workflow_description(
|
|
46
|
+
data: WorkflowPatchSchema,
|
|
47
|
+
workflow: WorkflowTable,
|
|
48
|
+
) -> WorkflowTable:
|
|
49
|
+
|
|
50
|
+
updated_properties = data.model_dump(exclude_unset=True)
|
|
51
|
+
description = updated_properties.get("description", workflow.description)
|
|
52
|
+
workflow.description = description
|
|
53
|
+
db.session.commit()
|
|
54
|
+
return workflow
|
orchestrator/app.py
CHANGED
|
@@ -199,8 +199,9 @@ class OrchestratorCore(FastAPI):
|
|
|
199
199
|
|
|
200
200
|
def register_graphql(
|
|
201
201
|
self: "OrchestratorCore",
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
# mypy 1.9 cannot properly inspect these, fixed in 1.15
|
|
203
|
+
query: Any = Query, # type: ignore
|
|
204
|
+
mutation: Any = Mutation, # type: ignore
|
|
204
205
|
register_models: bool = True,
|
|
205
206
|
subscription_interface: Any = SubscriptionInterface,
|
|
206
207
|
graphql_models: StrawberryModelType | None = None,
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
# See the License for the specific language governing permissions and
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
|
|
14
|
-
import re
|
|
15
14
|
from collections import ChainMap
|
|
16
15
|
from collections.abc import Mapping
|
|
17
16
|
from pathlib import Path
|
|
@@ -56,15 +55,8 @@ def get_product_block_path(product_block: dict) -> Path:
|
|
|
56
55
|
|
|
57
56
|
|
|
58
57
|
def enrich_product_block(product_block: dict) -> dict:
|
|
59
|
-
def to_block_name() -> str:
|
|
60
|
-
"""Separate block name into words."""
|
|
61
|
-
type = product_block["type"]
|
|
62
|
-
name = re.sub("(.)([A-Z][a-z]+)", r"\1 \2", type)
|
|
63
|
-
return re.sub("([a-z0-9])([A-Z])", r"\1 \2", name)
|
|
64
|
-
|
|
65
58
|
fields = get_all_fields(product_block)
|
|
66
|
-
block_name = product_block.get("block_name",
|
|
67
|
-
|
|
59
|
+
block_name = product_block.get("block_name", product_block.get("type"))
|
|
68
60
|
return product_block | {
|
|
69
61
|
"fields": fields,
|
|
70
62
|
"block_name": block_name,
|
|
@@ -7,11 +7,12 @@ from typing import Annotated
|
|
|
7
7
|
|
|
8
8
|
import structlog
|
|
9
9
|
from pydantic import AfterValidator, ConfigDict, model_validator
|
|
10
|
+
from pydantic_forms.types import FormGenerator, State, UUIDstr
|
|
10
11
|
|
|
11
12
|
from orchestrator.forms import FormPage
|
|
12
13
|
from orchestrator.forms.validators import Divider, Label, CustomerId, MigrationSummary
|
|
13
14
|
from orchestrator.targets import Target
|
|
14
|
-
from orchestrator.types import
|
|
15
|
+
from orchestrator.types import SubscriptionLifecycle
|
|
15
16
|
from orchestrator.workflow import StepList, begin, step
|
|
16
17
|
from orchestrator.workflows.steps import store_process_subscription
|
|
17
18
|
from orchestrator.workflows.utils import create_workflow
|
|
@@ -6,11 +6,12 @@ from typing import Annotated
|
|
|
6
6
|
|
|
7
7
|
import structlog
|
|
8
8
|
from pydantic import AfterValidator, ConfigDict, model_validator
|
|
9
|
+
from pydantic_forms.types import FormGenerator, State, UUIDstr
|
|
9
10
|
from pydantic_forms.validators import ReadOnlyField
|
|
10
11
|
|
|
11
12
|
from orchestrator.forms import FormPage
|
|
12
13
|
from orchestrator.forms.validators import CustomerId, Divider
|
|
13
|
-
from orchestrator.types import
|
|
14
|
+
from orchestrator.types import SubscriptionLifecycle
|
|
14
15
|
from orchestrator.workflow import StepList, begin, step
|
|
15
16
|
from orchestrator.workflows.steps import set_status
|
|
16
17
|
from orchestrator.workflows.utils import modify_workflow
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import structlog
|
|
4
4
|
from pydantic import AfterValidator, ConfigDict, model_validator
|
|
5
|
+
from pydantic_forms.types import InputForm, State, UUIDstr
|
|
5
6
|
|
|
6
7
|
from orchestrator.forms import FormPage
|
|
7
8
|
from orchestrator.forms.validators import DisplaySubscription
|
|
8
|
-
from orchestrator.types import InputForm, State, UUIDstr
|
|
9
9
|
from orchestrator.workflow import StepList, begin, step
|
|
10
10
|
from orchestrator.workflows.utils import terminate_workflow
|
|
11
11
|
|
|
@@ -4,7 +4,6 @@ from orchestrator.db import ProductTable
|
|
|
4
4
|
from orchestrator.forms import FormValidationError
|
|
5
5
|
|
|
6
6
|
from test.unit_tests.workflows import assert_complete, extract_state, run_workflow
|
|
7
|
-
|
|
8
7
|
from {{ product_types_module }}.{{ product.variable }} import {{ product.type }}
|
|
9
8
|
|
|
10
9
|
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
from orchestrator.forms import FormValidationError
|
|
3
|
-
|
|
4
3
|
from orchestrator.types import SubscriptionLifecycle
|
|
5
|
-
from test.unit_tests.workflows import assert_complete, extract_state, run_workflow
|
|
6
4
|
|
|
5
|
+
from test.unit_tests.workflows import assert_complete, extract_state, run_workflow
|
|
7
6
|
from {{ product_types_module }}.{{ product.variable }} import {{ product.type }}
|
|
8
7
|
|
|
9
8
|
|
|
@@ -4,8 +4,8 @@ import pytest
|
|
|
4
4
|
from orchestrator.forms import FormValidationError
|
|
5
5
|
{% endif %}
|
|
6
6
|
from orchestrator.types import SubscriptionLifecycle
|
|
7
|
-
from test.unit_tests.workflows import assert_complete, extract_state, run_workflow
|
|
8
7
|
|
|
8
|
+
from test.unit_tests.workflows import assert_complete, extract_state, run_workflow
|
|
9
9
|
from {{ product_types_module }}.{{ product.variable }} import {{ product.type }}
|
|
10
10
|
|
|
11
11
|
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
import structlog
|
|
4
4
|
{% if product.nso_service_id_path %}
|
|
5
5
|
from deepdiff import DeepDiff
|
|
6
|
+
{% endif %}
|
|
7
|
+
from pydantic_forms.types import State
|
|
8
|
+
{% if product.nso_service_id_path %}
|
|
6
9
|
from surf.products.services.nso.nso import build_payload
|
|
7
10
|
{% endif %}
|
|
8
|
-
from orchestrator.types import State
|
|
9
11
|
from orchestrator.workflow import StepList, begin, step
|
|
10
12
|
from orchestrator.workflows.utils import validate_workflow
|
|
11
13
|
|
orchestrator/config/assignee.py
CHANGED
|
@@ -26,8 +26,8 @@ import structlog
|
|
|
26
26
|
from more_itertools import first, first_true
|
|
27
27
|
|
|
28
28
|
from nwastdlib.url import URL
|
|
29
|
-
from orchestrator.types import State
|
|
30
29
|
from pydantic_forms.types import InputForm as LegacyInputForm
|
|
30
|
+
from pydantic_forms.types import State
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class JSONSubSchema(TypedDict, total=False):
|
|
@@ -6,61 +6,17 @@ Refer to the 2.0 migration guide documentation for background.
|
|
|
6
6
|
import re
|
|
7
7
|
import sys
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from subprocess import run
|
|
10
9
|
from typing import Iterable
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
rgx = r"(from %s import \b%s\b(\s*#[^\n]*)*\n)" % (re.escape(module), symbol)
|
|
22
|
-
text = re.sub(rgx, "", text)
|
|
23
|
-
|
|
24
|
-
# middle or last of multiple imports from module -> strip symbol
|
|
25
|
-
rgx = r"(from %s import .+)(, \b%s\b)" % (re.escape(module), symbol)
|
|
26
|
-
text = re.sub(rgx, r"\1", text)
|
|
27
|
-
|
|
28
|
-
# first of multiple imports from same module -> strip symbol
|
|
29
|
-
rgx = r"(from %s import )\b%s\b, " % (re.escape(module), symbol)
|
|
30
|
-
text = re.sub(rgx, r"\1", text)
|
|
31
|
-
|
|
32
|
-
# multiline import -> remove line with symbol
|
|
33
|
-
rgx_verbose = r"""(?P<before>^from\s%s\simport\s*\([^\n]*\n(?:^[^\n]+,\n)*)
|
|
34
|
-
(^\s*\b%s\b,[^\n]*\n)
|
|
35
|
-
(?P<after>(?:^[^\n]+,\n)*\)[^\n]*$)"""
|
|
36
|
-
text = re.sub(rgx_verbose % (re.escape(module), symbol), r"\g<before>\g<after>", text, flags=re.M | re.X)
|
|
37
|
-
return text, text_orig != text
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def insert_import(text: str, import_stmt: str) -> str:
|
|
41
|
-
# Find the first import line and add our line above that
|
|
42
|
-
# Rely on ruff & black for formatting
|
|
43
|
-
return re.sub(r"(^(?:from .+|import .+)$)", f"{import_stmt}\n" + r"\1", text, count=1, flags=re.M)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def find_and_remove_aliases(text: str, symbol: str) -> tuple[str, list[str]]:
|
|
47
|
-
"""In the given text find aliases of the given symbol and remove them.
|
|
48
|
-
|
|
49
|
-
Return updated text and aliases removed.
|
|
50
|
-
"""
|
|
51
|
-
rgx = r"(\b%s as (\w+))" % (symbol,)
|
|
52
|
-
aliases = [aliasgroup for fullgroup, aliasgroup in re.findall(rgx, text)]
|
|
53
|
-
newtext = re.sub(rgx, symbol, text)
|
|
54
|
-
return newtext, aliases
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def replace_words(text: str, words: list[str], replace: str) -> str:
|
|
58
|
-
rgx = r"\b(%s)\b" % ("|".join(words),)
|
|
59
|
-
return re.sub(rgx, replace, text)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def has_word(text: str, word: str) -> bool:
|
|
63
|
-
return bool(re.search(r"\b%s\b" % (word,), text))
|
|
11
|
+
from orchestrator.devtools.scripts.shared import (
|
|
12
|
+
find_and_remove_aliases,
|
|
13
|
+
has_word,
|
|
14
|
+
insert_import,
|
|
15
|
+
migrate,
|
|
16
|
+
move_import,
|
|
17
|
+
remove_imports,
|
|
18
|
+
replace_words,
|
|
19
|
+
)
|
|
64
20
|
|
|
65
21
|
|
|
66
22
|
def rewrite_subscription_instance_lists(f: Path) -> list[str]:
|
|
@@ -124,17 +80,6 @@ def rewrite_subscription_instance_lists(f: Path) -> list[str]:
|
|
|
124
80
|
return names
|
|
125
81
|
|
|
126
82
|
|
|
127
|
-
def move_import(f: Path, symbol: str, old_module: str, new_module: str) -> bool:
|
|
128
|
-
text = f.read_text()
|
|
129
|
-
text, changed = remove_imports(text, old_module, symbol)
|
|
130
|
-
if not changed:
|
|
131
|
-
return False
|
|
132
|
-
text = insert_import(text, f"from {new_module} import {symbol}")
|
|
133
|
-
with f.open(mode="w"):
|
|
134
|
-
f.write_text(text)
|
|
135
|
-
return True
|
|
136
|
-
|
|
137
|
-
|
|
138
83
|
re_serializable_property = re.compile(r"^(\s+)(@serializable_property)([^\n]*)\n", flags=re.MULTILINE)
|
|
139
84
|
|
|
140
85
|
|
|
@@ -167,7 +112,7 @@ def replace_serializable_props(f: Path) -> bool:
|
|
|
167
112
|
return True
|
|
168
113
|
|
|
169
114
|
|
|
170
|
-
def migrate_file(f: Path) ->
|
|
115
|
+
def migrate_file(f: Path) -> bool:
|
|
171
116
|
imports = {
|
|
172
117
|
"SI": move_import(f, "SI", "orchestrator.domain.base", "orchestrator.types"),
|
|
173
118
|
"VlanRanges": move_import(f, "VlanRanges", "orchestrator.utils.vlans", "nwastdlib.vlans"),
|
|
@@ -189,46 +134,6 @@ def migrate_file(f: Path) -> int:
|
|
|
189
134
|
return bool(lines)
|
|
190
135
|
|
|
191
136
|
|
|
192
|
-
def run_tool(*args: str) -> bool:
|
|
193
|
-
cmd = " ".join(args)
|
|
194
|
-
try:
|
|
195
|
-
r = run(args, capture_output=True) # noqa: S603
|
|
196
|
-
if r.returncode == 0:
|
|
197
|
-
return True
|
|
198
|
-
print(f"{cmd} failed:", r.stdout, r.stderr)
|
|
199
|
-
except FileNotFoundError:
|
|
200
|
-
print(f"{cmd }failed: could not find executable in the current venv")
|
|
201
|
-
return False
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def migrate(target_dir: Path) -> bool:
|
|
205
|
-
abs_path = str(target_dir.resolve())
|
|
206
|
-
|
|
207
|
-
def run_tools() -> bool:
|
|
208
|
-
return run_tool("ruff", "--fix", abs_path) and run_tool("black", "--quiet", abs_path)
|
|
209
|
-
|
|
210
|
-
print(f"\n### Verifing files in {abs_path}... ", end="")
|
|
211
|
-
if not run_tools():
|
|
212
|
-
print("Failed to verify files, aborting migration. Please resolve the errors.")
|
|
213
|
-
return False
|
|
214
|
-
print("Ok")
|
|
215
|
-
|
|
216
|
-
files_migrated = files_checked = 0
|
|
217
|
-
print(f"\n### Migrating files in {abs_path}")
|
|
218
|
-
try:
|
|
219
|
-
for f in target_dir.glob("**/*.py"):
|
|
220
|
-
if migrate_file(f):
|
|
221
|
-
files_migrated += 1
|
|
222
|
-
files_checked += 1
|
|
223
|
-
except KeyboardInterrupt:
|
|
224
|
-
print("Interrupted...")
|
|
225
|
-
|
|
226
|
-
print(f"\n### Migrated {files_migrated}/{files_checked} files in {abs_path}")
|
|
227
|
-
|
|
228
|
-
print(f"\n### Formatting files in {abs_path}")
|
|
229
|
-
return run_tools()
|
|
230
|
-
|
|
231
|
-
|
|
232
137
|
if __name__ == "__main__":
|
|
233
138
|
try:
|
|
234
139
|
_target_dir = Path(sys.argv[1])
|
|
@@ -237,4 +142,4 @@ if __name__ == "__main__":
|
|
|
237
142
|
print("Need a directory as parameter")
|
|
238
143
|
sys.exit(1)
|
|
239
144
|
|
|
240
|
-
sys.exit(0 if migrate(_target_dir) else 1)
|
|
145
|
+
sys.exit(0 if migrate(_target_dir, migrate_file) else 1)
|