We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Create basic, no-op `pipeline: {}` block in new `dev-vm` resource
This task is to enable future work on the `pipeline: {}` without interfering with existing functionality.

- [x] Copy the `guacamole-azure-linuxvm` to `guacamole-dev-vm`
- [x] Add a basic 3-step `pipeline: {}` to the schema to update the `display_name` of the firewall shared service, run main, then update the firewall name again.
- [x] Make sure the `pipeline: {}` block is ignored/removed during schema validation.
- [x] Start a doc to describe what the `pipeline:{}` is for. This will be updated as features are added in subsequent tasks.




--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `api_app/db/repositories/resource_templates.py`
Content:
```
1 import uuid
2 from typing import List, Union
3 
4 from azure.cosmos import CosmosClient
5 from pydantic import parse_obj_as
6 
7 from core import config
8 from db.errors import DuplicateEntity, EntityDoesNotExist, EntityVersionExist
9 from db.repositories.base import BaseRepository
10 from models.domain.resource import ResourceType
11 from models.domain.resource_template import ResourceTemplate
12 from models.domain.user_resource_template import UserResourceTemplate
13 from models.schemas.resource_template import ResourceTemplateInCreate, ResourceTemplateInformation
14 from services.schema_service import enrich_shared_service_template, enrich_workspace_template, enrich_workspace_service_template, enrich_user_resource_template
15 
16 
17 class ResourceTemplateRepository(BaseRepository):
18     def __init__(self, client: CosmosClient):
19         super().__init__(client, config.STATE_STORE_RESOURCE_TEMPLATES_CONTAINER)
20 
21     @staticmethod
22     def _template_by_name_query(name: str, resource_type: ResourceType) -> str:
23         return f'SELECT * FROM c WHERE c.resourceType = "{resource_type}" AND c.name = "{name}"'
24 
25     @staticmethod
26     def enrich_template(template: ResourceTemplate, is_update: bool = False) -> dict:
27         if template.resourceType == ResourceType.Workspace:
28             return enrich_workspace_template(template, is_update=is_update)
29         elif template.resourceType == ResourceType.WorkspaceService:
30             return enrich_workspace_service_template(template, is_update=is_update)
31         elif template.resourceType == ResourceType.SharedService:
32             return enrich_shared_service_template(template, is_update=is_update)
33         else:
34             return enrich_user_resource_template(template, is_update=is_update)
35 
36     def get_templates_information(self, resource_type: ResourceType, parent_service_name: str = "") -> List[ResourceTemplateInformation]:
37         """
38         Returns name/title/description for all current resource_type templates
39         """
40         query = f'SELECT c.name, c.title, c.description FROM c WHERE c.resourceType = "{resource_type}" AND c.current = true'
41         if resource_type == ResourceType.UserResource:
42             query += f' AND c.parentWorkspaceService = "{parent_service_name}"'
43         template_infos = self.query(query=query)
44         return [parse_obj_as(ResourceTemplateInformation, info) for info in template_infos]
45 
46     def get_current_template(self, template_name: str, resource_type: ResourceType, parent_service_name: str = "") -> Union[ResourceTemplate, UserResourceTemplate]:
47         """
48         Returns full template for the current version of the 'template_name' template
49         """
50         query = self._template_by_name_query(template_name, resource_type) + ' AND c.current = true'
51         if resource_type == ResourceType.UserResource:
52             query += f' AND c.parentWorkspaceService = "{parent_service_name}"'
53         templates = self.query(query=query)
54         if len(templates) == 0:
55             raise EntityDoesNotExist
56         if len(templates) > 1:
57             raise DuplicateEntity
58         if resource_type == ResourceType.UserResource:
59             return parse_obj_as(UserResourceTemplate, templates[0])
60         else:
61             return parse_obj_as(ResourceTemplate, templates[0])
62 
63     def get_template_by_name_and_version(self, name: str, version: str, resource_type: ResourceType, parent_service_name: str = None) -> Union[ResourceTemplate, UserResourceTemplate]:
64         """
65         Returns full template for the 'resource_type' template defined by 'template_name' and 'version'
66 
67         For UserResource templates, you also need to pass in 'parent_service_name' as a parameter
68         """
69         query = self._template_by_name_query(name, resource_type) + f' AND c.version = "{version}"'
70 
71         # If querying for a user resource, we also need to add the parentWorkspaceService (name) to the query
72         if resource_type == ResourceType.UserResource:
73             if parent_service_name:
74                 query += f' AND c.parentWorkspaceService = "{parent_service_name}"'
75             else:
76                 raise Exception("When getting a UserResource template, you must pass in a 'parent_service_name'")
77 
78         # Execute the query and handle results
79         templates = self.query(query=query)
80         if len(templates) != 1:
81             raise EntityDoesNotExist
82         if resource_type == ResourceType.UserResource:
83             return parse_obj_as(UserResourceTemplate, templates[0])
84         else:
85             return parse_obj_as(ResourceTemplate, templates[0])
86 
87     def create_template(self, template_input: ResourceTemplateInCreate, resource_type: ResourceType, parent_service_name: str = "") -> Union[ResourceTemplate, UserResourceTemplate]:
88         """
89         creates a template based on the input (workspace and workspace-services template)
90         """
91         template = {
92             "id": str(uuid.uuid4()),
93             "name": template_input.name,
94             "title": template_input.json_schema["title"],
95             "description": template_input.json_schema["description"],
96             "version": template_input.version,
97             "resourceType": resource_type,
98             "current": template_input.current,
99             "required": template_input.json_schema["required"],
100             "properties": template_input.json_schema["properties"],
101             "customActions": template_input.customActions
102         }
103 
104         if resource_type == ResourceType.UserResource:
105             template["parentWorkspaceService"] = parent_service_name
106             template = parse_obj_as(UserResourceTemplate, template)
107         else:
108             template = parse_obj_as(ResourceTemplate, template)
109 
110         self.save_item(template)
111         return template
112 
113     def create_and_validate_template(self, template_input: ResourceTemplateInCreate, resource_type: ResourceType, workspace_service_template_name: str = "") -> dict:
114         """
115         Validates that we don't have a version conflict
116         Updates the current version for the template
117         Saves to the database and returns the enriched template
118         """
119         try:
120             template = self.get_template_by_name_and_version(template_input.name, template_input.version, resource_type, workspace_service_template_name)
121             if template:
122                 raise EntityVersionExist
123         except EntityDoesNotExist:
124             try:
125                 template = self.get_current_template(template_input.name, resource_type, workspace_service_template_name)
126                 if template_input.current:
127                     template.current = False
128                     self.update_item(template)
129             except EntityDoesNotExist:
130                 # first registration
131                 template_input.current = True  # For first time registration, template is always marked current
132             created_template = self.create_template(template_input, resource_type, workspace_service_template_name)
133             return self.enrich_template(created_template)
134
```

Path: `api_app/models/domain/resource_template.py`
Content:
```
1 from typing import Dict, Any, List, Optional
2 
3 from pydantic import Field
4 
5 from models.domain.azuretremodel import AzureTREModel
6 from models.domain.resource import ResourceType
7 
8 
9 class Property(AzureTREModel):
10     type: str = Field(title="Property type")
11     title: str = Field("", title="Property description")
12     description: str = Field("", title="Property description")
13     default: Any = Field(None, title="Default value for the property")
14     enum: Optional[List[str]] = Field(None, title="Enum values")
15     const: Optional[Any] = Field(None, title="Constant value")
16     multipleOf: Optional[float] = Field(None, title="Multiple of")
17     maximum: Optional[float] = Field(None, title="Maximum value")
18     exclusiveMaximum: Optional[float] = Field(None, title="Exclusive maximum value")
19     minimum: Optional[float] = Field(None, title="Minimum value")
20     exclusiveMinimum: Optional[float] = Field(None, title="Exclusive minimum value")
21     maxLength: Optional[int] = Field(None, title="Maximum length")
22     minLength: Optional[int] = Field(None, title="Minimum length")
23     pattern: Optional[str] = Field(None, title="Pattern")
24     updateable: Optional[bool] = Field(None, title="Indicates that the field can be updated")
25     readOnly: Optional[bool] = Field(None, title="Indicates the field is read-only")
26 
27 
28 class CustomAction(AzureTREModel):
29     name: str = Field(None, title="Custom action name")
30     description: str = Field("", title="Action description")
31 
32 
33 class ResourceTemplate(AzureTREModel):
34     id: str
35     name: str = Field(title="Unique template name")
36     title: str = Field("", title="Template title or friendly name")
37     description: str = Field(title="Template description")
38     version: str = Field(title="Template version")
39     resourceType: ResourceType = Field(title="Type of resource this template is for (workspace/service)")
40     current: bool = Field(title="Is this the current version of this template")
41     type: str = "object"
42     required: List[str] = Field(title="List of properties which must be provided")
43     properties: Dict[str, Property] = Field(title="Template properties")
44     actions: List[CustomAction] = Field(default=[], title="Template actions")
45     customActions: List[CustomAction] = Field(default=[], title="Template custom actions")
46 
47     # setting this to false means if extra, unexpected fields are supplied, the request is invalidated
48     additionalProperties: bool = Field(default=False, title="Prevent unspecified properties being applied")
49
```

Path: `api_app/_version.py`
Content:
```
1 __version__ = "0.2.12"
2
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/api_app/_version.py b/api_app/_version.py
--- a/api_app/_version.py
+++ b/api_app/_version.py
@@ -1 +1 @@
-__version__ = "0.2.12"
+__version__ = "0.2.13"
diff --git a/api_app/db/repositories/resource_templates.py b/api_app/db/repositories/resource_templates.py
--- a/api_app/db/repositories/resource_templates.py
+++ b/api_app/db/repositories/resource_templates.py
@@ -101,6 +101,9 @@
             "customActions": template_input.customActions
         }
 
+        if "pipeline" in template_input.json_schema:
+            template["pipeline"] = template_input.json_schema["pipeline"]
+
         if resource_type == ResourceType.UserResource:
             template["parentWorkspaceService"] = parent_service_name
             template = parse_obj_as(UserResourceTemplate, template)
diff --git a/api_app/models/domain/resource_template.py b/api_app/models/domain/resource_template.py
--- a/api_app/models/domain/resource_template.py
+++ b/api_app/models/domain/resource_template.py
@@ -43,6 +43,7 @@
     properties: Dict[str, Property] = Field(title="Template properties")
     actions: List[CustomAction] = Field(default=[], title="Template actions")
     customActions: List[CustomAction] = Field(default=[], title="Template custom actions")
+    pipeline: Optional[dict] = Field(default=None, title="Template pipeline to define updates to other resources")
 
     # setting this to false means if extra, unexpected fields are supplied, the request is invalidated
     additionalProperties: bool = Field(default=False, title="Prevent unspecified properties being applied")

{"golden_diff": "diff --git a/api_app/_version.py b/api_app/_version.py\n--- a/api_app/_version.py\n+++ b/api_app/_version.py\n@@ -1 +1 @@\n-__version__ = \"0.2.12\"\n+__version__ = \"0.2.13\"\ndiff --git a/api_app/db/repositories/resource_templates.py b/api_app/db/repositories/resource_templates.py\n--- a/api_app/db/repositories/resource_templates.py\n+++ b/api_app/db/repositories/resource_templates.py\n@@ -101,6 +101,9 @@\n             \"customActions\": template_input.customActions\n         }\n \n+        if \"pipeline\" in template_input.json_schema:\n+            template[\"pipeline\"] = template_input.json_schema[\"pipeline\"]\n+\n         if resource_type == ResourceType.UserResource:\n             template[\"parentWorkspaceService\"] = parent_service_name\n             template = parse_obj_as(UserResourceTemplate, template)\ndiff --git a/api_app/models/domain/resource_template.py b/api_app/models/domain/resource_template.py\n--- a/api_app/models/domain/resource_template.py\n+++ b/api_app/models/domain/resource_template.py\n@@ -43,6 +43,7 @@\n     properties: Dict[str, Property] = Field(title=\"Template properties\")\n     actions: List[CustomAction] = Field(default=[], title=\"Template actions\")\n     customActions: List[CustomAction] = Field(default=[], title=\"Template custom actions\")\n+    pipeline: Optional[dict] = Field(default=None, title=\"Template pipeline to define updates to other resources\")\n \n     # setting this to false means if extra, unexpected fields are supplied, the request is invalidated\n     additionalProperties: bool = Field(default=False, title=\"Prevent unspecified properties being applied\")\n", "issue": "Create basic, no-op `pipeline: {}` block in new `dev-vm` resource\nThis task is to enable future work on the `pipeline: {}` without interfering with existing functionality.\r\n\r\n- [x] Copy the `guacamole-azure-linuxvm` to `guacamole-dev-vm`\r\n- [x] Add a basic 3-step `pipeline: {}` to the schema to update the `display_name` of the firewall shared service, run main, then update the firewall name again.\r\n- [x] Make sure the `pipeline: {}` block is ignored/removed during schema validation.\r\n- [x] Start a doc to describe what the `pipeline:{}` is for. This will be updated as features are added in subsequent tasks.\r\n\r\n\r\n\n", "before_files": [{"content": "import uuid\nfrom typing import List, Union\n\nfrom azure.cosmos import CosmosClient\nfrom pydantic import parse_obj_as\n\nfrom core import config\nfrom db.errors import DuplicateEntity, EntityDoesNotExist, EntityVersionExist\nfrom db.repositories.base import BaseRepository\nfrom models.domain.resource import ResourceType\nfrom models.domain.resource_template import ResourceTemplate\nfrom models.domain.user_resource_template import UserResourceTemplate\nfrom models.schemas.resource_template import ResourceTemplateInCreate, ResourceTemplateInformation\nfrom services.schema_service import enrich_shared_service_template, enrich_workspace_template, enrich_workspace_service_template, enrich_user_resource_template\n\n\nclass ResourceTemplateRepository(BaseRepository):\n    def __init__(self, client: CosmosClient):\n        super().__init__(client, config.STATE_STORE_RESOURCE_TEMPLATES_CONTAINER)\n\n    @staticmethod\n    def _template_by_name_query(name: str, resource_type: ResourceType) -> str:\n        return f'SELECT * FROM c WHERE c.resourceType = \"{resource_type}\" AND c.name = \"{name}\"'\n\n    @staticmethod\n    def enrich_template(template: ResourceTemplate, is_update: bool = False) -> dict:\n        if template.resourceType == ResourceType.Workspace:\n            return enrich_workspace_template(template, is_update=is_update)\n        elif template.resourceType == ResourceType.WorkspaceService:\n            return enrich_workspace_service_template(template, is_update=is_update)\n        elif template.resourceType == ResourceType.SharedService:\n            return enrich_shared_service_template(template, is_update=is_update)\n        else:\n            return enrich_user_resource_template(template, is_update=is_update)\n\n    def get_templates_information(self, resource_type: ResourceType, parent_service_name: str = \"\") -> List[ResourceTemplateInformation]:\n        \"\"\"\n        Returns name/title/description for all current resource_type templates\n        \"\"\"\n        query = f'SELECT c.name, c.title, c.description FROM c WHERE c.resourceType = \"{resource_type}\" AND c.current = true'\n        if resource_type == ResourceType.UserResource:\n            query += f' AND c.parentWorkspaceService = \"{parent_service_name}\"'\n        template_infos = self.query(query=query)\n        return [parse_obj_as(ResourceTemplateInformation, info) for info in template_infos]\n\n    def get_current_template(self, template_name: str, resource_type: ResourceType, parent_service_name: str = \"\") -> Union[ResourceTemplate, UserResourceTemplate]:\n        \"\"\"\n        Returns full template for the current version of the 'template_name' template\n        \"\"\"\n        query = self._template_by_name_query(template_name, resource_type) + ' AND c.current = true'\n        if resource_type == ResourceType.UserResource:\n            query += f' AND c.parentWorkspaceService = \"{parent_service_name}\"'\n        templates = self.query(query=query)\n        if len(templates) == 0:\n            raise EntityDoesNotExist\n        if len(templates) > 1:\n            raise DuplicateEntity\n        if resource_type == ResourceType.UserResource:\n            return parse_obj_as(UserResourceTemplate, templates[0])\n        else:\n            return parse_obj_as(ResourceTemplate, templates[0])\n\n    def get_template_by_name_and_version(self, name: str, version: str, resource_type: ResourceType, parent_service_name: str = None) -> Union[ResourceTemplate, UserResourceTemplate]:\n        \"\"\"\n        Returns full template for the 'resource_type' template defined by 'template_name' and 'version'\n\n        For UserResource templates, you also need to pass in 'parent_service_name' as a parameter\n        \"\"\"\n        query = self._template_by_name_query(name, resource_type) + f' AND c.version = \"{version}\"'\n\n        # If querying for a user resource, we also need to add the parentWorkspaceService (name) to the query\n        if resource_type == ResourceType.UserResource:\n            if parent_service_name:\n                query += f' AND c.parentWorkspaceService = \"{parent_service_name}\"'\n            else:\n                raise Exception(\"When getting a UserResource template, you must pass in a 'parent_service_name'\")\n\n        # Execute the query and handle results\n        templates = self.query(query=query)\n        if len(templates) != 1:\n            raise EntityDoesNotExist\n        if resource_type == ResourceType.UserResource:\n            return parse_obj_as(UserResourceTemplate, templates[0])\n        else:\n            return parse_obj_as(ResourceTemplate, templates[0])\n\n    def create_template(self, template_input: ResourceTemplateInCreate, resource_type: ResourceType, parent_service_name: str = \"\") -> Union[ResourceTemplate, UserResourceTemplate]:\n        \"\"\"\n        creates a template based on the input (workspace and workspace-services template)\n        \"\"\"\n        template = {\n            \"id\": str(uuid.uuid4()),\n            \"name\": template_input.name,\n            \"title\": template_input.json_schema[\"title\"],\n            \"description\": template_input.json_schema[\"description\"],\n            \"version\": template_input.version,\n            \"resourceType\": resource_type,\n            \"current\": template_input.current,\n            \"required\": template_input.json_schema[\"required\"],\n            \"properties\": template_input.json_schema[\"properties\"],\n            \"customActions\": template_input.customActions\n        }\n\n        if resource_type == ResourceType.UserResource:\n            template[\"parentWorkspaceService\"] = parent_service_name\n            template = parse_obj_as(UserResourceTemplate, template)\n        else:\n            template = parse_obj_as(ResourceTemplate, template)\n\n        self.save_item(template)\n        return template\n\n    def create_and_validate_template(self, template_input: ResourceTemplateInCreate, resource_type: ResourceType, workspace_service_template_name: str = \"\") -> dict:\n        \"\"\"\n        Validates that we don't have a version conflict\n        Updates the current version for the template\n        Saves to the database and returns the enriched template\n        \"\"\"\n        try:\n            template = self.get_template_by_name_and_version(template_input.name, template_input.version, resource_type, workspace_service_template_name)\n            if template:\n                raise EntityVersionExist\n        except EntityDoesNotExist:\n            try:\n                template = self.get_current_template(template_input.name, resource_type, workspace_service_template_name)\n                if template_input.current:\n                    template.current = False\n                    self.update_item(template)\n            except EntityDoesNotExist:\n                # first registration\n                template_input.current = True  # For first time registration, template is always marked current\n            created_template = self.create_template(template_input, resource_type, workspace_service_template_name)\n            return self.enrich_template(created_template)\n", "path": "api_app/db/repositories/resource_templates.py"}, {"content": "from typing import Dict, Any, List, Optional\n\nfrom pydantic import Field\n\nfrom models.domain.azuretremodel import AzureTREModel\nfrom models.domain.resource import ResourceType\n\n\nclass Property(AzureTREModel):\n    type: str = Field(title=\"Property type\")\n    title: str = Field(\"\", title=\"Property description\")\n    description: str = Field(\"\", title=\"Property description\")\n    default: Any = Field(None, title=\"Default value for the property\")\n    enum: Optional[List[str]] = Field(None, title=\"Enum values\")\n    const: Optional[Any] = Field(None, title=\"Constant value\")\n    multipleOf: Optional[float] = Field(None, title=\"Multiple of\")\n    maximum: Optional[float] = Field(None, title=\"Maximum value\")\n    exclusiveMaximum: Optional[float] = Field(None, title=\"Exclusive maximum value\")\n    minimum: Optional[float] = Field(None, title=\"Minimum value\")\n    exclusiveMinimum: Optional[float] = Field(None, title=\"Exclusive minimum value\")\n    maxLength: Optional[int] = Field(None, title=\"Maximum length\")\n    minLength: Optional[int] = Field(None, title=\"Minimum length\")\n    pattern: Optional[str] = Field(None, title=\"Pattern\")\n    updateable: Optional[bool] = Field(None, title=\"Indicates that the field can be updated\")\n    readOnly: Optional[bool] = Field(None, title=\"Indicates the field is read-only\")\n\n\nclass CustomAction(AzureTREModel):\n    name: str = Field(None, title=\"Custom action name\")\n    description: str = Field(\"\", title=\"Action description\")\n\n\nclass ResourceTemplate(AzureTREModel):\n    id: str\n    name: str = Field(title=\"Unique template name\")\n    title: str = Field(\"\", title=\"Template title or friendly name\")\n    description: str = Field(title=\"Template description\")\n    version: str = Field(title=\"Template version\")\n    resourceType: ResourceType = Field(title=\"Type of resource this template is for (workspace/service)\")\n    current: bool = Field(title=\"Is this the current version of this template\")\n    type: str = \"object\"\n    required: List[str] = Field(title=\"List of properties which must be provided\")\n    properties: Dict[str, Property] = Field(title=\"Template properties\")\n    actions: List[CustomAction] = Field(default=[], title=\"Template actions\")\n    customActions: List[CustomAction] = Field(default=[], title=\"Template custom actions\")\n\n    # setting this to false means if extra, unexpected fields are supplied, the request is invalidated\n    additionalProperties: bool = Field(default=False, title=\"Prevent unspecified properties being applied\")\n", "path": "api_app/models/domain/resource_template.py"}, {"content": "__version__ = \"0.2.12\"\n", "path": "api_app/_version.py"}], "after_files": [{"content": "import uuid\nfrom typing import List, Union\n\nfrom azure.cosmos import CosmosClient\nfrom pydantic import parse_obj_as\n\nfrom core import config\nfrom db.errors import DuplicateEntity, EntityDoesNotExist, EntityVersionExist\nfrom db.repositories.base import BaseRepository\nfrom models.domain.resource import ResourceType\nfrom models.domain.resource_template import ResourceTemplate\nfrom models.domain.user_resource_template import UserResourceTemplate\nfrom models.schemas.resource_template import ResourceTemplateInCreate, ResourceTemplateInformation\nfrom services.schema_service import enrich_shared_service_template, enrich_workspace_template, enrich_workspace_service_template, enrich_user_resource_template\n\n\nclass ResourceTemplateRepository(BaseRepository):\n    def __init__(self, client: CosmosClient):\n        super().__init__(client, config.STATE_STORE_RESOURCE_TEMPLATES_CONTAINER)\n\n    @staticmethod\n    def _template_by_name_query(name: str, resource_type: ResourceType) -> str:\n        return f'SELECT * FROM c WHERE c.resourceType = \"{resource_type}\" AND c.name = \"{name}\"'\n\n    @staticmethod\n    def enrich_template(template: ResourceTemplate, is_update: bool = False) -> dict:\n        if template.resourceType == ResourceType.Workspace:\n            return enrich_workspace_template(template, is_update=is_update)\n        elif template.resourceType == ResourceType.WorkspaceService:\n            return enrich_workspace_service_template(template, is_update=is_update)\n        elif template.resourceType == ResourceType.SharedService:\n            return enrich_shared_service_template(template, is_update=is_update)\n        else:\n            return enrich_user_resource_template(template, is_update=is_update)\n\n    def get_templates_information(self, resource_type: ResourceType, parent_service_name: str = \"\") -> List[ResourceTemplateInformation]:\n        \"\"\"\n        Returns name/title/description for all current resource_type templates\n        \"\"\"\n        query = f'SELECT c.name, c.title, c.description FROM c WHERE c.resourceType = \"{resource_type}\" AND c.current = true'\n        if resource_type == ResourceType.UserResource:\n            query += f' AND c.parentWorkspaceService = \"{parent_service_name}\"'\n        template_infos = self.query(query=query)\n        return [parse_obj_as(ResourceTemplateInformation, info) for info in template_infos]\n\n    def get_current_template(self, template_name: str, resource_type: ResourceType, parent_service_name: str = \"\") -> Union[ResourceTemplate, UserResourceTemplate]:\n        \"\"\"\n        Returns full template for the current version of the 'template_name' template\n        \"\"\"\n        query = self._template_by_name_query(template_name, resource_type) + ' AND c.current = true'\n        if resource_type == ResourceType.UserResource:\n            query += f' AND c.parentWorkspaceService = \"{parent_service_name}\"'\n        templates = self.query(query=query)\n        if len(templates) == 0:\n            raise EntityDoesNotExist\n        if len(templates) > 1:\n            raise DuplicateEntity\n        if resource_type == ResourceType.UserResource:\n            return parse_obj_as(UserResourceTemplate, templates[0])\n        else:\n            return parse_obj_as(ResourceTemplate, templates[0])\n\n    def get_template_by_name_and_version(self, name: str, version: str, resource_type: ResourceType, parent_service_name: str = None) -> Union[ResourceTemplate, UserResourceTemplate]:\n        \"\"\"\n        Returns full template for the 'resource_type' template defined by 'template_name' and 'version'\n\n        For UserResource templates, you also need to pass in 'parent_service_name' as a parameter\n        \"\"\"\n        query = self._template_by_name_query(name, resource_type) + f' AND c.version = \"{version}\"'\n\n        # If querying for a user resource, we also need to add the parentWorkspaceService (name) to the query\n        if resource_type == ResourceType.UserResource:\n            if parent_service_name:\n                query += f' AND c.parentWorkspaceService = \"{parent_service_name}\"'\n            else:\n                raise Exception(\"When getting a UserResource template, you must pass in a 'parent_service_name'\")\n\n        # Execute the query and handle results\n        templates = self.query(query=query)\n        if len(templates) != 1:\n            raise EntityDoesNotExist\n        if resource_type == ResourceType.UserResource:\n            return parse_obj_as(UserResourceTemplate, templates[0])\n        else:\n            return parse_obj_as(ResourceTemplate, templates[0])\n\n    def create_template(self, template_input: ResourceTemplateInCreate, resource_type: ResourceType, parent_service_name: str = \"\") -> Union[ResourceTemplate, UserResourceTemplate]:\n        \"\"\"\n        creates a template based on the input (workspace and workspace-services template)\n        \"\"\"\n        template = {\n            \"id\": str(uuid.uuid4()),\n            \"name\": template_input.name,\n            \"title\": template_input.json_schema[\"title\"],\n            \"description\": template_input.json_schema[\"description\"],\n            \"version\": template_input.version,\n            \"resourceType\": resource_type,\n            \"current\": template_input.current,\n            \"required\": template_input.json_schema[\"required\"],\n            \"properties\": template_input.json_schema[\"properties\"],\n            \"customActions\": template_input.customActions\n        }\n\n        if \"pipeline\" in template_input.json_schema:\n            template[\"pipeline\"] = template_input.json_schema[\"pipeline\"]\n\n        if resource_type == ResourceType.UserResource:\n            template[\"parentWorkspaceService\"] = parent_service_name\n            template = parse_obj_as(UserResourceTemplate, template)\n        else:\n            template = parse_obj_as(ResourceTemplate, template)\n\n        self.save_item(template)\n        return template\n\n    def create_and_validate_template(self, template_input: ResourceTemplateInCreate, resource_type: ResourceType, workspace_service_template_name: str = \"\") -> dict:\n        \"\"\"\n        Validates that we don't have a version conflict\n        Updates the current version for the template\n        Saves to the database and returns the enriched template\n        \"\"\"\n        try:\n            template = self.get_template_by_name_and_version(template_input.name, template_input.version, resource_type, workspace_service_template_name)\n            if template:\n                raise EntityVersionExist\n        except EntityDoesNotExist:\n            try:\n                template = self.get_current_template(template_input.name, resource_type, workspace_service_template_name)\n                if template_input.current:\n                    template.current = False\n                    self.update_item(template)\n            except EntityDoesNotExist:\n                # first registration\n                template_input.current = True  # For first time registration, template is always marked current\n            created_template = self.create_template(template_input, resource_type, workspace_service_template_name)\n            return self.enrich_template(created_template)\n", "path": "api_app/db/repositories/resource_templates.py"}, {"content": "from typing import Dict, Any, List, Optional\n\nfrom pydantic import Field\n\nfrom models.domain.azuretremodel import AzureTREModel\nfrom models.domain.resource import ResourceType\n\n\nclass Property(AzureTREModel):\n    type: str = Field(title=\"Property type\")\n    title: str = Field(\"\", title=\"Property description\")\n    description: str = Field(\"\", title=\"Property description\")\n    default: Any = Field(None, title=\"Default value for the property\")\n    enum: Optional[List[str]] = Field(None, title=\"Enum values\")\n    const: Optional[Any] = Field(None, title=\"Constant value\")\n    multipleOf: Optional[float] = Field(None, title=\"Multiple of\")\n    maximum: Optional[float] = Field(None, title=\"Maximum value\")\n    exclusiveMaximum: Optional[float] = Field(None, title=\"Exclusive maximum value\")\n    minimum: Optional[float] = Field(None, title=\"Minimum value\")\n    exclusiveMinimum: Optional[float] = Field(None, title=\"Exclusive minimum value\")\n    maxLength: Optional[int] = Field(None, title=\"Maximum length\")\n    minLength: Optional[int] = Field(None, title=\"Minimum length\")\n    pattern: Optional[str] = Field(None, title=\"Pattern\")\n    updateable: Optional[bool] = Field(None, title=\"Indicates that the field can be updated\")\n    readOnly: Optional[bool] = Field(None, title=\"Indicates the field is read-only\")\n\n\nclass CustomAction(AzureTREModel):\n    name: str = Field(None, title=\"Custom action name\")\n    description: str = Field(\"\", title=\"Action description\")\n\n\nclass ResourceTemplate(AzureTREModel):\n    id: str\n    name: str = Field(title=\"Unique template name\")\n    title: str = Field(\"\", title=\"Template title or friendly name\")\n    description: str = Field(title=\"Template description\")\n    version: str = Field(title=\"Template version\")\n    resourceType: ResourceType = Field(title=\"Type of resource this template is for (workspace/service)\")\n    current: bool = Field(title=\"Is this the current version of this template\")\n    type: str = \"object\"\n    required: List[str] = Field(title=\"List of properties which must be provided\")\n    properties: Dict[str, Property] = Field(title=\"Template properties\")\n    actions: List[CustomAction] = Field(default=[], title=\"Template actions\")\n    customActions: List[CustomAction] = Field(default=[], title=\"Template custom actions\")\n    pipeline: Optional[dict] = Field(default=None, title=\"Template pipeline to define updates to other resources\")\n\n    # setting this to false means if extra, unexpected fields are supplied, the request is invalidated\n    additionalProperties: bool = Field(default=False, title=\"Prevent unspecified properties being applied\")\n", "path": "api_app/models/domain/resource_template.py"}, {"content": "__version__ = \"0.2.13\"\n", "path": "api_app/_version.py"}]}