Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 Fixes access rights fields in web-api's PATCH services input model #6180

Merged
merged 13 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from datetime import datetime
from typing import Any, ClassVar, TypeAlias

from models_library.rpc_pagination import PageRpc
from pydantic import BaseModel, Extra, Field, HttpUrl, NonNegativeInt

from ..boot_options import BootOptions
from ..emails import LowerCaseEmailStr
from ..services_access import ServiceAccessRights
from ..services_access import ServiceAccessRights, ServiceGroupAccessRightsV2
from ..services_authoring import Author
from ..services_enums import ServiceType
from ..services_history import ServiceRelease
Expand Down Expand Up @@ -108,14 +109,22 @@ class Config:
"owner": "[email protected]",
}

_EXAMPLE_FILEPICKER_V2 = {
**_EXAMPLE_FILEPICKER,
"accessRights": {
"1": {"execute": True, "write": False},
"4": {"execute": True, "write": True},
},
}


_EXAMPLE_SLEEPER: dict[str, Any] = {
"name": "sleeper",
"thumbnail": None,
"description": "A service which awaits for time to pass, two times.",
"classifiers": [],
"quality": {},
"accessRights": {"1": {"execute_access": True, "write_access": False}},
"accessRights": {"1": {"execute": True, "write": False}},
"key": "simcore/services/comp/itis/sleeper",
"version": "2.2.1",
"version_display": "2 Xtreme",
Expand Down Expand Up @@ -204,15 +213,6 @@ class Config:
}


class ServiceGroupAccessRightsV2(BaseModel):
execute: bool = False
write: bool = False

class Config:
alias_generator = snake_to_camel
allow_population_by_field_name = True


class ServiceGetV2(BaseModel):
key: ServiceKey
version: ServiceVersion
Expand Down Expand Up @@ -291,10 +291,10 @@ class Config:
],
},
{
**_EXAMPLE_FILEPICKER,
**_EXAMPLE_FILEPICKER_V2,
"history": [
{
"version": _EXAMPLE_FILEPICKER["version"],
"version": _EXAMPLE_FILEPICKER_V2["version"],
"version_display": "Odei Release",
"released": "2025-03-25T00:00:00",
}
Expand All @@ -310,3 +310,28 @@ class Config:
]

ServiceResourcesGet: TypeAlias = ServiceResourcesDict


class ServiceUpdateV2(BaseModel):
name: str | None = None
thumbnail: HttpUrl | None = None

description: str | None = None
version_display: str | None = None

deprecated: datetime | None = None

classifiers: list[str] | None = None
quality: dict[str, Any] = {}

access_rights: dict[GroupID, ServiceGroupAccessRightsV2] | None = None

class Config:
extra = Extra.forbid
pcrespov marked this conversation as resolved.
Show resolved Hide resolved
alias_generator = snake_to_camel
allow_population_by_field_name = True


assert set(ServiceUpdateV2.__fields__.keys()) - set( # nosec
ServiceGetV2.__fields__.keys()
) == {"deprecated"}
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,6 @@ class Config(OutputSchema.Config):
}


class CatalogServiceUpdate(api_schemas_catalog_services.ServiceUpdate):
class CatalogServiceUpdate(api_schemas_catalog_services.ServiceUpdateV2):
class Config(InputSchema.Config):
...
14 changes: 12 additions & 2 deletions packages/models-library/src/models_library/services_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

"""


from pydantic import BaseModel, Field
from pydantic import BaseModel, Extra, Field

from .users import GroupID
from .utils.change_case import snake_to_camel


class ServiceGroupAccessRights(BaseModel):
Expand All @@ -18,6 +18,16 @@ class ServiceGroupAccessRights(BaseModel):
)


class ServiceGroupAccessRightsV2(BaseModel):
execute: bool = False
write: bool = False

class Config:
alias_generator = snake_to_camel
allow_population_by_field_name = True
extra = Extra.forbid


class ServiceAccessRights(BaseModel):
access_rights: dict[GroupID, ServiceGroupAccessRights] | None = Field(
None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import Any, cast

from models_library.api_schemas_catalog import CATALOG_RPC_NAMESPACE
from models_library.api_schemas_catalog.services import ServiceGetV2, ServiceUpdate
from models_library.api_schemas_catalog.services import ServiceGetV2, ServiceUpdateV2
from models_library.products import ProductName
from models_library.rabbitmq_basic_types import RPCMethodName
from models_library.rpc_pagination import (
Expand Down Expand Up @@ -115,7 +115,7 @@ async def update_service(
user_id: UserID,
service_key: ServiceKey,
service_version: ServiceVersion,
update: ServiceUpdate,
update: ServiceUpdateV2,
) -> ServiceGetV2:
"""Updates editable fields of a service

Expand All @@ -131,7 +131,7 @@ async def _call(
user_id: UserID,
service_key: ServiceKey,
service_version: ServiceVersion,
update: ServiceUpdate,
update: ServiceUpdateV2,
):
return await rpc_client.request(
CATALOG_RPC_NAMESPACE,
Expand Down
4 changes: 2 additions & 2 deletions services/catalog/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2079,7 +2079,7 @@
"type": "string",
"format": "date-time",
"title": "Deprecated",
"description": "Owner can set the date to retire the service. Three possibilities:If None, the service is marked as `published`;If now<deprecated the service is marked as dprecated;If now>=deprecated, the service is retired"
"description": "Owner can set the date to retire the service. Three possibilities:If None, the service is marked as `published`;If now<deprecated the service is marked as deprecated;If now>=deprecated, the service is retired"
},
"classifiers": {
"items": {
Expand Down Expand Up @@ -2558,7 +2558,7 @@
"type": "string",
"format": "date-time",
"title": "Deprecated",
"description": "Owner can set the date to retire the service. Three possibilities:If None, the service is marked as `published`;If now<deprecated the service is marked as dprecated;If now>=deprecated, the service is retired"
"description": "Owner can set the date to retire the service. Three possibilities:If None, the service is marked as `published`;If now<deprecated the service is marked as deprecated;If now>=deprecated, the service is retired"
},
"classifiers": {
"items": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from models_library.api_schemas_catalog.services import (
PageRpcServicesGetV2,
ServiceGetV2,
ServiceUpdate,
ServiceUpdateV2,
)
from models_library.products import ProductName
from models_library.rpc_pagination import DEFAULT_NUMBER_OF_ITEMS_PER_PAGE, PageLimitInt
Expand Down Expand Up @@ -124,7 +124,7 @@ async def update_service(
user_id: UserID,
service_key: ServiceKey,
service_version: ServiceVersion,
update: ServiceUpdate,
update: ServiceUpdateV2,
) -> ServiceGetV2:
"""Updates editable fields of a service"""

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import logging

from models_library.api_schemas_catalog.services import (
ServiceGetV2,
ServiceGroupAccessRightsV2,
ServiceUpdate,
)
from models_library.api_schemas_catalog.services import ServiceGetV2, ServiceUpdateV2
from models_library.emails import LowerCaseEmailStr
from models_library.products import ProductName
from models_library.rest_pagination import PageLimitInt
from models_library.services_access import ServiceGroupAccessRightsV2
from models_library.services_enums import ServiceType
from models_library.services_history import Compatibility, ServiceRelease
from models_library.services_metadata_published import ServiceMetaDataPublished
Expand Down Expand Up @@ -219,7 +216,7 @@ async def update_service(
user_id: UserID,
service_key: ServiceKey,
service_version: ServiceVersion,
update: ServiceUpdate,
update: ServiceUpdateV2,
) -> ServiceGetV2:

if is_function_service(service_key):
Expand Down Expand Up @@ -273,8 +270,8 @@ async def update_service(
key=service_key,
version=service_version,
gid=gid,
execute_access=rights.execute_access,
write_access=rights.write_access,
execute_access=rights.execute,
write_access=rights.write,
product_name=product_name,
)
for gid, rights in update.access_rights.items()
Expand Down
2 changes: 1 addition & 1 deletion services/web/server/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.40.5
0.41.0
6 changes: 3 additions & 3 deletions services/web/server/setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.40.4
current_version = 0.41.0
commit = True
message = services/webserver api version: {current_version} → {new_version}
tag = False
Expand All @@ -12,13 +12,13 @@ commit_args = --no-verify
[tool:pytest]
addopts = --strict-markers
asyncio_mode = auto
markers =
markers =
slow: marks tests as slow (deselect with '-m "not slow"')
acceptance_test: "marks tests as 'acceptance tests' i.e. does the system do what the user expects? Typically those are workflows."
testit: "marks test to run during development"
heavy_load: "mark tests that require large amount of data"

[mypy]
plugins =
plugins =
pydantic.mypy
sqlalchemy.ext.mypy.plugin
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ openapi: 3.0.2
info:
title: simcore-service-webserver
description: Main service with an interface (http-API & websockets) to the web front-end
version: 0.40.5
version: 0.41.0
servers:
- url: ''
description: webserver
Expand Down Expand Up @@ -6070,12 +6070,6 @@ components:
title: CatalogServiceUpdate
type: object
properties:
accessRights:
title: Accessrights
type: object
additionalProperties:
$ref: '#/components/schemas/ServiceGroupAccessRights'
description: service access rights per group id
name:
title: Name
type: string
Expand All @@ -6094,9 +6088,6 @@ components:
deprecated:
title: Deprecated
type: string
description: Owner can set the date to retire the service. Three possibilities:If
None, the service is marked as `published`;If now<deprecated the service
is marked as deprecated;If now>=deprecated, the service is retired
format: date-time
classifiers:
title: Classifiers
Expand All @@ -6107,63 +6098,11 @@ components:
title: Quality
type: object
default: {}
example:
accessRights:
1:
execute_access: false
write_access: false
2:
execute_access: true
write_access: true
44:
execute_access: false
write_access: false
name: My Human Readable Service Name
description: An interesting service that does something
classifiers:
- RRID:SCR_018997
- RRID:SCR_019001
quality:
tsr:
r01:
level: 3
references: ''
r02:
level: 2
references: ''
r03:
level: 0
references: ''
r04:
level: 0
references: ''
r05:
level: 2
references: ''
r06:
level: 0
references: ''
r07:
level: 0
references: ''
r08:
level: 1
references: ''
r09:
level: 0
references: ''
r10:
level: 0
references: ''
enabled: true
annotations:
vandv: ''
purpose: ''
standards: ''
limitations: ''
documentation: ''
certificationLink: ''
certificationStatus: Uncertified
title: Accessrights
type: object
additionalProperties:
$ref: '#/components/schemas/ServiceGroupAccessRightsV2'
pcrespov marked this conversation as resolved.
Show resolved Hide resolved
ChangePasswordBody:
title: ChangePasswordBody
required:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from aiohttp import web
from aiohttp.web import Request
from models_library.api_schemas_catalog.services import ServiceUpdate
from models_library.api_schemas_catalog.services import ServiceUpdateV2
from models_library.api_schemas_webserver.catalog import (
ServiceInputGet,
ServiceInputKey,
Expand Down Expand Up @@ -156,7 +156,7 @@ async def dev_update_service(
user_id=user_id,
service_key=service_key,
service_version=service_version,
update=ServiceUpdate.parse_obj(update_data),
update=ServiceUpdateV2.parse_obj(update_data),
)

data = jsonable_encoder(service, exclude_unset=True)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ async def test_dev_get_and_patch_service(
description="bar",
classifiers=None,
versionDisplay="Some nice name",
accessRights={1: {"execute": True, "write": True}},
)
response = await client.patch(
f"{url}", json=jsonable_encoder(update, exclude_unset=True)
Expand All @@ -205,9 +206,10 @@ async def test_dev_get_and_patch_service(
model = parse_obj_as(CatalogServiceGet, data)
assert model.key == service_key
assert model.version == service_version
assert model.name == "foo"
assert model.description == "bar"
assert model.version_display == "Some nice name"
assert model.name == update.name
assert model.description == update.description
assert model.version_display == update.version_display
assert model.access_rights == update.access_rights

assert mocked_rpc_catalog_service_api["get_service"].call_count == 1
assert mocked_rpc_catalog_service_api["update_service"].call_count == 1
Loading