-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ Adds custom project's metadata in the wbserver API (#4421)
- Loading branch information
Showing
29 changed files
with
996 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
paths: | ||
/projects/{project_id}/metadata: | ||
get: | ||
tags: | ||
- project | ||
summary: Get Project Metadata | ||
operationId: get_project_metadata | ||
parameters: | ||
- required: true | ||
schema: | ||
type: string | ||
format: uuid | ||
title: Project Id | ||
name: project_id | ||
in: path | ||
responses: | ||
'200': | ||
description: Successful Response | ||
content: | ||
application/json: | ||
schema: | ||
$ref: '#/components/schemas/Envelope_ProjectMetadataGet_' | ||
patch: | ||
tags: | ||
- project | ||
summary: Update Project Metadata | ||
operationId: update_project_metadata | ||
parameters: | ||
- required: true | ||
schema: | ||
type: string | ||
format: uuid | ||
title: Project Id | ||
name: project_id | ||
in: path | ||
requestBody: | ||
content: | ||
application/json: | ||
schema: | ||
$ref: '#/components/schemas/ProjectMetadataUpdate' | ||
required: true | ||
responses: | ||
'200': | ||
description: Successful Response | ||
content: | ||
application/json: | ||
schema: | ||
$ref: '#/components/schemas/Envelope_ProjectMetadataGet_' | ||
components: | ||
schemas: | ||
Envelope_ProjectMetadataGet_: | ||
properties: | ||
data: | ||
$ref: '#/components/schemas/ProjectMetadataGet' | ||
error: | ||
title: Error | ||
type: object | ||
title: Envelope[ProjectMetadataGet] | ||
ProjectMetadataGet: | ||
properties: | ||
projectUuid: | ||
type: string | ||
format: uuid | ||
title: Projectuuid | ||
custom: | ||
additionalProperties: | ||
anyOf: | ||
- type: boolean | ||
- type: integer | ||
- type: number | ||
- type: string | ||
type: object | ||
title: Custom | ||
description: Custom key-value map | ||
type: object | ||
required: | ||
- projectUuid | ||
title: ProjectMetadataGet | ||
ProjectMetadataUpdate: | ||
properties: | ||
custom: | ||
additionalProperties: | ||
anyOf: | ||
- type: boolean | ||
- type: integer | ||
- type: number | ||
- type: string | ||
type: object | ||
title: Custom | ||
type: object | ||
required: | ||
- custom | ||
title: ProjectMetadataUpdate |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
""" Helper script to automatically generate OAS | ||
This OAS are the source of truth | ||
""" | ||
|
||
# pylint: disable=redefined-outer-name | ||
# pylint: disable=unused-argument | ||
# pylint: disable=unused-variable | ||
# pylint: disable=too-many-arguments | ||
|
||
|
||
from enum import Enum | ||
from typing import Annotated | ||
|
||
from _common import CURRENT_DIR, create_openapi_specs | ||
from fastapi import Depends, FastAPI, status | ||
from models_library.api_schemas_webserver.projects_metadata import ( | ||
ProjectMetadataGet, | ||
ProjectMetadataUpdate, | ||
) | ||
from models_library.generics import Envelope | ||
from simcore_service_webserver.projects._metadata_handlers import ( | ||
ProjectMetadataGet, | ||
ProjectPathParams, | ||
) | ||
|
||
app = FastAPI(redoc_url=None) | ||
|
||
TAGS: list[str | Enum] = ["project"] | ||
|
||
|
||
# | ||
# API entrypoints | ||
# | ||
|
||
|
||
@app.get( | ||
"/projects/{project_id}/metadata", | ||
response_model=Envelope[ProjectMetadataGet], | ||
tags=TAGS, | ||
operation_id="get_project_metadata", | ||
status_code=status.HTTP_200_OK, | ||
) | ||
async def get_project_metadata(_params: Annotated[ProjectPathParams, Depends()]): | ||
... | ||
|
||
|
||
@app.patch( | ||
"/projects/{project_id}/metadata", | ||
response_model=Envelope[ProjectMetadataGet], | ||
tags=TAGS, | ||
operation_id="update_project_metadata", | ||
status_code=status.HTTP_200_OK, | ||
) | ||
async def update_project_metadata( | ||
_params: Annotated[ProjectPathParams, Depends()], _body: ProjectMetadataUpdate | ||
): | ||
... | ||
|
||
|
||
if __name__ == "__main__": | ||
|
||
create_openapi_specs(app, CURRENT_DIR.parent / "openapi-projects-metadata.yaml") |
21 changes: 21 additions & 0 deletions
21
packages/models-library/src/models_library/api_schemas_webserver/projects_metadata.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from typing import TypeAlias | ||
|
||
from pydantic import Field, StrictBool, StrictFloat, StrictInt | ||
|
||
from ..projects import ProjectID | ||
from ._base import InputSchema, OutputSchema | ||
|
||
# Limits metadata values | ||
MetaValueType: TypeAlias = StrictBool | StrictInt | StrictFloat | str | ||
MetadataDict: TypeAlias = dict[str, MetaValueType] | ||
|
||
|
||
class ProjectMetadataGet(OutputSchema): | ||
project_uuid: ProjectID | ||
custom: MetadataDict = Field( | ||
default_factory=dict, description="Custom key-value map" | ||
) | ||
|
||
|
||
class ProjectMetadataUpdate(InputSchema): | ||
custom: MetadataDict |
97 changes: 97 additions & 0 deletions
97
.../simcore_postgres_database/migration/versions/f3285aff5e84_new_projects_metadata_table.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
"""new projects_metadata table | ||
Revision ID: f3285aff5e84 | ||
Revises: 58b24613c3f7 | ||
Create Date: 2023-07-05 15:06:56.003418+00:00 | ||
""" | ||
from typing import Final | ||
|
||
import sqlalchemy as sa | ||
from alembic import op | ||
from sqlalchemy.dialects import postgresql | ||
|
||
# revision identifiers, used by Alembic. | ||
revision = "f3285aff5e84" | ||
down_revision = "58b24613c3f7" | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
# auto-update modified | ||
# TRIGGERS ------------------------ | ||
_TABLE_NAME: Final[str] = "projects_metadata" | ||
_TRIGGER_NAME: Final[str] = "trigger_auto_update" # NOTE: scoped on table | ||
_PROCEDURE_NAME: Final[ | ||
str | ||
] = f"{_TABLE_NAME}_auto_update_modified()" # NOTE: scoped on database | ||
modified_timestamp_trigger = sa.DDL( | ||
f""" | ||
DROP TRIGGER IF EXISTS {_TRIGGER_NAME} on {_TABLE_NAME}; | ||
CREATE TRIGGER {_TRIGGER_NAME} | ||
BEFORE INSERT OR UPDATE ON {_TABLE_NAME} | ||
FOR EACH ROW EXECUTE PROCEDURE {_PROCEDURE_NAME}; | ||
""" | ||
) | ||
|
||
# PROCEDURES ------------------------ | ||
update_modified_timestamp_procedure = sa.DDL( | ||
f""" | ||
CREATE OR REPLACE FUNCTION {_PROCEDURE_NAME} | ||
RETURNS TRIGGER AS $$ | ||
BEGIN | ||
NEW.modified := current_timestamp; | ||
RETURN NEW; | ||
END; | ||
$$ LANGUAGE plpgsql; | ||
""" | ||
) | ||
|
||
|
||
def upgrade(): | ||
# ### commands auto generated by Alembic - please adjust! ### | ||
op.create_table( | ||
"projects_metadata", | ||
sa.Column("project_uuid", sa.String(), nullable=False), | ||
sa.Column( | ||
"custom", | ||
postgresql.JSONB(astext_type=sa.Text()), | ||
server_default=sa.text("'{}'::jsonb"), | ||
nullable=False, | ||
), | ||
sa.Column( | ||
"created", | ||
sa.DateTime(timezone=True), | ||
server_default=sa.text("now()"), | ||
nullable=False, | ||
), | ||
sa.Column( | ||
"modified", | ||
sa.DateTime(timezone=True), | ||
server_default=sa.text("now()"), | ||
nullable=False, | ||
), | ||
sa.ForeignKeyConstraint( | ||
["project_uuid"], | ||
["projects.uuid"], | ||
name="fk_projects_metadata_project_uuid", | ||
onupdate="CASCADE", | ||
ondelete="CASCADE", | ||
), | ||
sa.PrimaryKeyConstraint("project_uuid"), | ||
) | ||
# ### end Alembic commands ### | ||
|
||
# custom | ||
op.execute(update_modified_timestamp_procedure) | ||
op.execute(modified_timestamp_trigger) | ||
|
||
|
||
def downgrade(): | ||
# custom | ||
op.execute(f"DROP TRIGGER IF EXISTS {_TRIGGER_NAME} on {_TABLE_NAME};") | ||
op.execute(f"DROP FUNCTION {_PROCEDURE_NAME};") | ||
|
||
# ### commands auto generated by Alembic - please adjust! ### | ||
op.drop_table("projects_metadata") | ||
# ### end Alembic commands ### |
57 changes: 57 additions & 0 deletions
57
packages/postgres-database/src/simcore_postgres_database/models/projects_metadata.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
""" | ||
These tables were designed to be controled by projects-plugin in | ||
the webserver's service | ||
""" | ||
|
||
import sqlalchemy as sa | ||
from sqlalchemy.dialects.postgresql import JSONB | ||
|
||
from ._common import ( | ||
column_created_datetime, | ||
column_modified_datetime, | ||
register_modified_datetime_auto_update_trigger, | ||
) | ||
from .base import metadata | ||
from .projects import projects | ||
|
||
projects_metadata = sa.Table( | ||
"projects_metadata", | ||
# | ||
# Keeps "third-party" metadata attached to a project | ||
# | ||
# These SHOULD NOT be actual properties of the project (e.g. uuid, name etc) | ||
# but rather information attached by third-parties that "decorate" or qualify | ||
# a project resource | ||
# | ||
# Things like 'stars', 'quality', 'classifiers', 'dev', etc (or any kind of stats) | ||
# should be moved here | ||
# | ||
metadata, | ||
sa.Column( | ||
"project_uuid", | ||
sa.String, | ||
sa.ForeignKey( | ||
projects.c.uuid, | ||
onupdate="CASCADE", | ||
ondelete="CASCADE", | ||
name="fk_projects_metadata_project_uuid", | ||
), | ||
nullable=False, | ||
primary_key=True, | ||
doc="The project unique identifier is also used to identify the associated job", | ||
), | ||
sa.Column( | ||
"custom", | ||
JSONB, | ||
nullable=False, | ||
server_default=sa.text("'{}'::jsonb"), | ||
doc="Reserved for the user to store custom metadata", | ||
), | ||
# TIME STAMPS ----ß | ||
column_created_datetime(timezone=True), | ||
column_modified_datetime(timezone=True), | ||
sa.PrimaryKeyConstraint("project_uuid"), | ||
) | ||
|
||
|
||
register_modified_datetime_auto_update_trigger(projects_metadata) |
Oops, something went wrong.