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

Add metrics endpoint #515

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
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
24 changes: 14 additions & 10 deletions datameta/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from pyramid.config import Configurator
from pyramid.view import view_config
from pyramid.request import Request
from pyramid.httpexceptions import HTTPFound

from dataclasses import dataclass
from dataclasses_json import dataclass_json, LetterCase
import os
from dataclasses import dataclass

import yaml
from dataclasses_json import LetterCase, dataclass_json
from pyramid.config import Configurator
from pyramid.httpexceptions import HTTPFound
from pyramid.request import Request
from pyramid.view import view_config

openapi_spec_path = os.path.join(os.path.dirname(__file__), "openapi.yaml")
# read base url from openapi.yaml:
Expand All @@ -34,6 +34,7 @@
@dataclass
class DataHolderBase:
"""Base class for data classes intended to be used as API response bodies"""

def __json__(self, request):
return self.to_dict()

Expand All @@ -49,6 +50,7 @@ def includeme(config: Configurator) -> None:
config.add_route("totp_secret_id", base_url + "/users/{id}/totp-secret")
config.add_route("rpc_whoami", base_url + "/rpc/whoami")
config.add_route("user_id", base_url + "/users/{id}")
config.add_route("metrics", base_url + "/metrics")
config.add_route("metadata", base_url + "/metadata")
config.add_route("metadata_id", base_url + "/metadata/{id}")
config.add_route("metadatasets", base_url + "/metadatasets")
Expand All @@ -64,11 +66,13 @@ def includeme(config: Configurator) -> None:
config.add_route("rpc_delete_files", base_url + "/rpc/delete-files")
config.add_route("rpc_delete_metadatasets", base_url + "/rpc/delete-metadatasets")
config.add_route("rpc_get_file_url", base_url + "/rpc/get-file-url/{id}")
config.add_route('register_submit', base_url + "/registrations")
config.add_route("register_submit", base_url + "/registrations")
config.add_route("register_settings", base_url + "/registrationsettings")
config.add_route("services", base_url + "/services")
config.add_route("services_id", base_url + "/services/{id}")
config.add_route("service_execution", base_url + "/service-execution/{serviceId}/{metadatasetId}")
config.add_route(
"service_execution", base_url + "/service-execution/{serviceId}/{metadatasetId}"
)

# Endpoint outside of openapi
config.add_route("upload", base_url + "/upload/{id}")
Expand All @@ -77,7 +81,7 @@ def includeme(config: Configurator) -> None:

@view_config(
route_name="api",
renderer='json',
renderer="json",
request_method="GET",
)
def get(request: Request):
Expand Down
59 changes: 59 additions & 0 deletions datameta/api/metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright 2021 Universität Tübingen, DKFZ and EMBL for the German Human Genome-Phenome Archive (GHGA)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
from dataclasses import dataclass

from pyramid.request import Request
from pyramid.view import view_config

from ..models import MetaDataSet
from . import DataHolderBase

log = logging.getLogger(__name__)


@dataclass
class MetricsBase(DataHolderBase):
"""Base class for Metrics communication to OpenApi"""

pass


@dataclass
class MetricsResponse(MetricsBase):
"""MetricsResponse container for OpenApi communication"""

metadatasets_submitted_count: int


@view_config(
route_name="metrics",
renderer="json",
request_method="GET",
openapi=True,
)
def metrics(request: Request) -> MetricsResponse:
"""Get metrics of the server."""
db = request.dbsession

query = db.query(MetaDataSet).filter(MetaDataSet.submission_id.isnot(None))

metadatasets_submitted_count = int(query.count())

log.info("Server metrics requested.")

return MetricsResponse(
metadatasets_submitted_count=metadatasets_submitted_count,
)
28 changes: 27 additions & 1 deletion datameta/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
openapi: 3.0.0
info:
description: DataMeta
version: 1.5.0
version: 1.5.1
title: DataMeta

servers:
Expand Down Expand Up @@ -1242,6 +1242,23 @@ paths:
$ref: "#/components/schemas/ServerInfoResponse"
"500":
description: Internal Server Error

/metrics:
get:
summary: Get server metrics
description: Get metrics of the DataMeta server serving this API
tags:
- Server
operationId: GetMetrics
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/MetricsResponse"
"500":
description: Internal Server Error

/registrations:
post:
Expand Down Expand Up @@ -1895,6 +1912,15 @@ components:
items:
$ref: "#/components/schemas/MetaDataResponse"

MetricsResponse:
type: object
properties:
metadatasetsSubmittedCount:
type: integer
required:
- metadatasetsSubmittedCount
additionalProperties: false

ServerInfoResponse:
type: object
properties:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@

setup(
name = 'datameta',
version = '1.1.1',
version = '1.1.2',
description = 'DataMeta - submission server for data and associated metadata',
long_description = README + '\n\n' + CHANGES,
author = 'Leon Kuchenbecker',
Expand Down
82 changes: 82 additions & 0 deletions tests/integration/test_metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Copyright 2021 Universität Tübingen, DKFZ and EMBL for the German Human Genome-Phenome Archive (GHGA)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from datameta.api import base_url

from . import BaseIntegrationTest

FIXTURES_LOAD = [
"groups",
"users",
"apikeys",
"services",
"metadata",
"files_msets",
"submissions",
"metadatasets",
"serviceexecutions",
]

# Required, with empty dict 401 is returned for a not registered user
EMPTY_AUTH_HEADER = {"Authorization": f"Bearer {None}"}


class TestGetMetricsEmptyServer(BaseIntegrationTest):
"""Test get metrics with an empty server."""

def setUp(self):
super().setUp()

def test_metrics(
self,
):
response = self.testapp.get(
url=f"{base_url}/metrics",
status=200,
headers=EMPTY_AUTH_HEADER,
)

assert response.json["metadatasetsSubmittedCount"] == 0


class TestGetMetricsServer(BaseIntegrationTest):
"""Test get metrics with data."""

def setUp(self):
super().setUp()
for fixture in FIXTURES_LOAD:
self.fixture_manager.load_fixtureset(fixture)
self.fixture_manager.copy_files_to_storage()
self.fixture_manager.populate_metadatasets()

def test_metrics(
self,
):
# Get submitted metadatasets
user = self.fixture_manager.get_fixture("users", "admin")
auth_headers = self.apikey_auth(user) if user else {}

response_metadatasets = self.testapp.get(
url=f"{base_url}/metadatasets", headers=auth_headers, status=200
)

response_metrics = self.testapp.get(
url=f"{base_url}/metrics",
status=200,
headers=EMPTY_AUTH_HEADER,
)

assert response_metrics.json["metadatasetsSubmittedCount"] == len(
response_metadatasets.json
)