Skip to content
Open
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
64 changes: 63 additions & 1 deletion components/renku_data_services/data_connectors/api.spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,33 @@ paths:
$ref: "#/components/responses/Error"
tags:
- data_connectors
/data_connectors/search:
get:
summary: Get data connector details by DOI
parameters:
- in: query
name: doi
required: true
schema:
$ref: "#/components/schemas/DOI"
description: The DOI of the data connector
responses:
"200":
description: The data connector
content:
application/json:
schema:
$ref: "#/components/schemas/DataConnector"
"404":
description: The data connector with the given DOI does not exist or user does not have access to it
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
default:
$ref: "#/components/responses/Error"
tags:
- data_connectors
/data_connectors/{data_connector_id}:
parameters:
- in: path
Expand Down Expand Up @@ -289,13 +316,42 @@ paths:
description: the ID of the data connector
get:
summary: Get all links from a given data connector to projects
parameters:
- in: query
description: query parameters
name: params
style: form
explode: true
schema:
$ref: "#/components/schemas/PaginationRequest"
responses:
"200":
description: List of data connector to project links
content:
application/json:
schema:
$ref: "#/components/schemas/DataConnectorToProjectLinksList"
headers:
page:
description: The index of the current page (starting at 1).
required: true
schema:
type: integer
per-page:
description: The number of items per page.
required: true
schema:
type: integer
total:
description: The total number of items.
required: true
schema:
type: integer
total-pages:
description: The total number of pages.
required: true
schema:
type: integer
default:
$ref: "#/components/responses/Error"
tags:
Expand Down Expand Up @@ -639,6 +695,10 @@ components:
type: array
items:
$ref: "#/components/schemas/DataConnectorToProjectLink"
ProjectPath:
description: The path to the project page
type: string
example: "namespace/project-slug"
DataConnectorToProjectLink:
description: A link from a data connector to a project in Renku 2.0
type: object
Expand All @@ -650,6 +710,8 @@ components:
$ref: "#/components/schemas/Ulid"
project_id:
$ref: "#/components/schemas/Ulid"
project_path:
$ref: "#/components/schemas/ProjectPath"
creation_date:
$ref: "#/components/schemas/CreationDate"
created_by:
Expand All @@ -658,6 +720,7 @@ components:
- id
- data_connector_id
- project_id
- project_path
- creation_date
- created_by
DataConnectorToProjectLinkPost:
Expand Down Expand Up @@ -961,7 +1024,6 @@ components:
type: integer
minimum: 0
description: The number of data links the user does not have access to

responses:
Error:
description: The schema for all 4xx and 5xx responses
Expand Down
15 changes: 14 additions & 1 deletion components/renku_data_services/data_connectors/apispec.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# generated by datamodel-codegen:
# filename: api.spec.yaml
# timestamp: 2025-12-03T09:49:11+00:00
# timestamp: 2026-01-27T08:42:00+00:00

from __future__ import annotations

Expand Down Expand Up @@ -129,6 +129,14 @@ class InaccessibleDataConnectorLinks(BaseAPISpec):
)


class DataConnectorsSearchGetParametersQuery(BaseAPISpec):
doi: str = Field(..., description="A DOI.", examples=["10.16904/envidat.33"])


class DataConnectorsDataConnectorIdProjectLinksGetParametersQuery(BaseAPISpec):
params: Optional[PaginationRequest] = None


class CloudStorageCore(BaseAPISpec):
model_config = ConfigDict(
extra="forbid",
Expand Down Expand Up @@ -241,6 +249,11 @@ class DataConnectorToProjectLink(BaseAPISpec):
min_length=26,
pattern="^[0-7][0-9A-HJKMNP-TV-Z]{25}$",
)
project_path: str = Field(
...,
description="The path to the project page",
examples=["namespace/project-slug"],
)
creation_date: datetime = Field(
...,
description="The date and time the resource was created (in UTC and ISO-8601 format)",
Expand Down
34 changes: 29 additions & 5 deletions components/renku_data_services/data_connectors/blueprints.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,20 +308,43 @@ async def _get_permissions(_: Request, user: base_models.APIUser, data_connector

return "/data_connectors/<data_connector_id:ulid>/permissions", ["GET"], _get_permissions

def get_one_by_doi(self) -> BlueprintFactoryResponse:
"""Get data connector by DOI."""

@authenticate(self.authenticator)
@validate(query=apispec.DataConnectorsSearchGetParametersQuery)
async def _get_one_by_doi(
_: Request,
user: base_models.APIUser,
query: apispec.DataConnectorsSearchGetParametersQuery,
validator: RCloneValidator,
) -> JSONResponse:
data_connector = await self.data_connector_repo.get_data_connector_by_doi(user=user, doi=query.doi)
return validated_json(
apispec.DataConnector,
self._dump_data_connector(data_connector, validator=validator),
)

return "/data_connectors/search", ["GET"], _get_one_by_doi

def get_all_project_links(self) -> BlueprintFactoryResponse:
"""List all links from a given data connector to projects."""

@authenticate(self.authenticator)
@paginate
async def _get_all_project_links(
_: Request,
user: base_models.APIUser,
data_connector_id: ULID,
) -> JSONResponse:
links = await self.data_connector_repo.get_links_from(user=user, data_connector_id=data_connector_id)
return validated_json(
apispec.DataConnectorToProjectLinksList,
[self._dump_data_connector_to_project_link(link) for link in links],
pagination: PaginationRequest,
) -> tuple[list[dict[str, Any]], int]:
links, total_num = await self.data_connector_repo.get_links_from(
user=user, data_connector_id=data_connector_id, pagination=pagination
)
return [
validate_and_dump(apispec.DataConnectorToProjectLink, self._dump_data_connector_to_project_link(link))
for link in links
], total_num

return "/data_connectors/<data_connector_id:ulid>/project_links", ["GET"], _get_all_project_links

Expand Down Expand Up @@ -529,6 +552,7 @@ def _dump_data_connector_to_project_link(link: models.DataConnectorToProjectLink
id=str(link.id),
data_connector_id=str(link.data_connector_id),
project_id=str(link.project_id),
project_path=link.project_path,
creation_date=link.creation_date,
created_by=link.created_by,
)
Expand Down
43 changes: 40 additions & 3 deletions components/renku_data_services/data_connectors/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,33 @@ async def get_data_connector_by_slug(

return data_connector.dump()

async def get_data_connector_by_doi(
self,
user: base_models.APIUser,
doi: str,
) -> models.DataConnector | models.GlobalDataConnector:
"""Get a data connector from the database by DOI."""
not_found_msg = f"Data connector with DOI '{doi}' does not exist or you do not have access to it."

async with self.session_maker() as session:
stmt = select(schemas.DataConnectorORM).where(schemas.DataConnectorORM.doi == doi)
result = await session.scalars(stmt)
data_connector = result.one_or_none()

if data_connector is None:
raise errors.MissingResourceError(message=not_found_msg)

authorized = await self.authz.has_permission(
user=user,
resource_type=ResourceType.data_connector,
resource_id=data_connector.id,
scope=Scope.READ,
)
if not authorized:
raise errors.MissingResourceError(message=not_found_msg)

return data_connector.dump()

async def get_global_data_connector_by_slug(
self,
user: base_models.APIUser,
Expand Down Expand Up @@ -612,8 +639,8 @@ async def get_data_connector_permissions(
return permissions

async def get_links_from(
self, user: base_models.APIUser, data_connector_id: ULID
) -> list[models.DataConnectorToProjectLink]:
self, user: base_models.APIUser, data_connector_id: ULID, pagination: PaginationRequest
) -> tuple[list[models.DataConnectorToProjectLink], int]:
"""Get links from a given data connector."""
authorized = await self.authz.has_permission(user, ResourceType.data_connector, data_connector_id, Scope.READ)
if not authorized:
Expand All @@ -628,10 +655,20 @@ async def get_links_from(
select(schemas.DataConnectorToProjectLinkORM)
.where(schemas.DataConnectorToProjectLinkORM.data_connector_id == data_connector_id)
.where(schemas.DataConnectorToProjectLinkORM.project_id.in_(project_ids))
.limit(pagination.per_page)
.offset(pagination.offset)
.order_by(schemas.DataConnectorToProjectLinkORM.id.desc())
)
stmt_count = (
select(func.count())
.select_from(schemas.DataConnectorToProjectLinkORM)
.where(schemas.DataConnectorToProjectLinkORM.data_connector_id == data_connector_id)
.where(schemas.DataConnectorToProjectLinkORM.project_id.in_(project_ids))
)
result = await session.scalars(stmt)
links_orm = result.all()
return [link.dump() for link in links_orm]
total_elements = await session.scalar(stmt_count) or 0
return [link.dump() for link in links_orm], total_elements

async def get_links_to(
self, user: base_models.APIUser, project_id: ULID
Expand Down
1 change: 1 addition & 0 deletions components/renku_data_services/data_connectors/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ class DataConnectorToProjectLink(UnsavedDataConnectorToProjectLink):
"""A link from a data connector to a project."""

id: ULID
project_path: str
created_by: str
creation_date: datetime
updated_at: datetime
Expand Down
6 changes: 6 additions & 0 deletions components/renku_data_services/data_connectors/orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,18 @@ class DataConnectorToProjectLinkORM(BaseORM):
nullable=False,
)

project: Mapped["ProjectORM"] = relationship(init=False, repr=False, viewonly=True, lazy="joined")
"""The project this link points to."""

def dump(self) -> models.DataConnectorToProjectLink:
"""Create a link model from the DataConnectorProjectLinkORM."""
project_path = f"{self.project.slug.namespace.slug}/{self.project.slug.slug}"

return models.DataConnectorToProjectLink(
id=self.id,
data_connector_id=self.data_connector_id,
project_id=self.project_id,
project_path=project_path,
created_by=self.created_by_id,
creation_date=self.creation_date,
updated_at=self.updated_at,
Expand Down
26 changes: 0 additions & 26 deletions components/renku_data_services/project/api.spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -992,32 +992,6 @@ components:
type: string
description: Entity Tag
example: "9EE498F9D565D0C41E511377425F32F3"
DataConnectorToProjectLinksList:
description: A list of links from a data connector to a project
type: array
items:
$ref: "#/components/schemas/DataConnectorToProjectLink"
DataConnectorToProjectLink:
description: A link from a data connector to a project in Renku 2.0
type: object
additionalProperties: false
properties:
id:
$ref: "#/components/schemas/Ulid"
data_connector_id:
$ref: "#/components/schemas/Ulid"
project_id:
$ref: "#/components/schemas/Ulid"
creation_date:
$ref: "#/components/schemas/CreationDate"
created_by:
$ref: "#/components/schemas/UserId"
required:
- id
- data_connector_id
- project_id
- creation_date
- created_by
ProjectGetQuery:
description: Query params for project get request
allOf:
Expand Down
Loading