Skip to content

Commit

Permalink
Artefact links (#52)
Browse files Browse the repository at this point in the history
* Pass correct source when creating a dummy artefact

* Add get artefacts endpoint

* Remove families endpoint as it's no longer needed

* Return stage name with artefact

* Add fetch artefact endpoint

* Refactor to get links to artefacts

* Some renaming

* Pass query parameters as a dictionary

* Fix navbar not highlighting correct family

* Fix selecting deb artefact navigates to snap board

* Pass query parameters as a map
  • Loading branch information
omar-selo authored Oct 24, 2023
1 parent 544c0ed commit 599526f
Show file tree
Hide file tree
Showing 24 changed files with 322 additions and 347 deletions.
31 changes: 27 additions & 4 deletions backend/test_observer/controllers/artefacts/artefacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,40 @@
# Written by:
# Omar Selo <[email protected]>
# Nadzeya Hutsko <[email protected]>
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from test_observer.data_access.models import ArtefactBuild
from test_observer.data_access.setup import get_db

from .models import ArtefactBuildDTO
from test_observer.data_access.models import Artefact, ArtefactBuild, Family, Stage
from test_observer.data_access.models_enums import FamilyName
from test_observer.data_access.setup import get_db

from .models import ArtefactBuildDTO, ArtefactDTO

router = APIRouter()


@router.get("/", response_model=list[ArtefactDTO])
def get_artefacts(family: FamilyName | None = None, db: Session = Depends(get_db)):
"""Get latest artefacts by family"""
query = db.query(Stage)
if family:
query = query.filter(Stage.family.has(Family.name == family))
stages = query.all()

return [artefact for stage in stages for artefact in stage.latest_artefacts]


@router.get("/{artefact_id}", response_model=ArtefactDTO)
def get_artefact(artefact_id: int, db: Session = Depends(get_db)):
"""Get an artefact by id"""
artefact = db.query(Artefact).get(artefact_id)

if artefact is None:
raise HTTPException(status_code=404, detail="Artefact not found")

return artefact


@router.get("/{artefact_id}/builds", response_model=list[ArtefactBuildDTO])
def get_artefact_builds(artefact_id: int, db: Session = Depends(get_db)):
"""Get latest artefact builds of an artefact together with their test executions"""
Expand Down
12 changes: 11 additions & 1 deletion backend/test_observer/controllers/artefacts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,21 @@
# Written by:
# Omar Selo <[email protected]>
# Nadzeya Hutsko <[email protected]>
from pydantic import BaseModel, ConfigDict
from pydantic import AliasPath, BaseModel, ConfigDict, Field

from test_observer.data_access.models_enums import TestExecutionStatus


class ArtefactDTO(BaseModel):
model_config = ConfigDict(from_attributes=True)

id: int
name: str
version: str
source: dict[str, int | str]
stage: str = Field(validation_alias=AliasPath("stage", "name"))


class EnvironmentDTO(BaseModel):
model_config = ConfigDict(from_attributes=True)

Expand Down
Empty file.
35 changes: 0 additions & 35 deletions backend/test_observer/controllers/families/families.py

This file was deleted.

48 changes: 0 additions & 48 deletions backend/test_observer/controllers/families/models.py

This file was deleted.

2 changes: 0 additions & 2 deletions backend/test_observer/controllers/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,11 @@

from .application import version
from .artefacts import artefacts
from .families import families
from .promoter import promoter
from .test_executions import test_executions

router = APIRouter()
router.include_router(promoter.router)
router.include_router(families.router, prefix="/v1/families")
router.include_router(version.router, prefix="/v1/version")
router.include_router(test_executions.router, prefix="/v1/test-executions")
router.include_router(artefacts.router, prefix="/v1/artefacts")
Expand Down
42 changes: 41 additions & 1 deletion backend/tests/controllers/artefacts/test_artefacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,53 @@
# Written by:
# Omar Selo <[email protected]>
# Nadzeya Hutsko <[email protected]>
from datetime import timedelta

from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from test_observer.data_access.models import ArtefactBuild, Environment, TestExecution

from test_observer.data_access.models import ArtefactBuild, Environment, TestExecution
from tests.helpers import create_artefact


def test_get_latest_artefacts_by_family(db_session: Session, test_client: TestClient):
"""Should only get latest artefacts and only ones that belong to given family"""
relevant_artefact = create_artefact(db_session, "edge", version="2")

old_timestamp = relevant_artefact.created_at - timedelta(days=1)
create_artefact(db_session, "edge", created_at=old_timestamp, version="1")
create_artefact(db_session, "proposed")

response = test_client.get("/v1/artefacts", params={"family": "snap"})

assert response.status_code == 200
assert response.json() == [
{
"id": relevant_artefact.id,
"name": relevant_artefact.name,
"version": relevant_artefact.version,
"source": relevant_artefact.source,
"stage": relevant_artefact.stage.name,
}
]


def test_get_artefact(db_session: Session, test_client: TestClient):
"""Should be able to fetch an existing artefact"""
artefact = create_artefact(db_session, "edge")

response = test_client.get(f"/v1/artefacts/{artefact.id}")

assert response.status_code == 200
assert response.json() == {
"id": artefact.id,
"name": artefact.name,
"version": artefact.version,
"source": artefact.source,
"stage": artefact.stage.name,
}


def test_get_artefact_builds(db_session: Session, test_client: TestClient):
artefact = create_artefact(db_session, "beta")
artefact_build = ArtefactBuild(architecture="amd64", artefact=artefact, revision=1)
Expand Down
88 changes: 0 additions & 88 deletions backend/tests/controllers/families/test_families.py

This file was deleted.

10 changes: 9 additions & 1 deletion backend/tests/helpers.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
from datetime import datetime

from sqlalchemy.orm import Session

from test_observer.data_access.models import Artefact, Stage


def create_artefact(db_session: Session, stage_name: str, **kwargs) -> Artefact:
"""Create a dummy artefact"""
stage = db_session.query(Stage).filter(Stage.name == stage_name).first()
source = kwargs.get("source")
if not source:
if stage_name in ("edge", "beta", "candidate", "stable"):
source = {"store": "ubuntu", "track": "latest"}
else:
source = {"pocket": "proposed", "series": "jammy"}

artefact = Artefact(
name=kwargs.get("name", ""),
stage=stage,
version=kwargs.get("version", "1.1.1"),
source=kwargs.get("source", {"store": "ubuntu"}),
source=source,
created_at=kwargs.get("created_at", datetime.utcnow()),
)
db_session.add(artefact)
Expand Down
3 changes: 3 additions & 0 deletions frontend/lib/models/artefact.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:freezed_annotation/freezed_annotation.dart';

import 'stage_name.dart';

part 'artefact.freezed.dart';
part 'artefact.g.dart';

Expand All @@ -10,6 +12,7 @@ class Artefact with _$Artefact {
required String name,
required String version,
required Map<String, dynamic> source,
required StageName stage,
}) = _Artefact;

factory Artefact.fromJson(Map<String, Object?> json) =>
Expand Down
16 changes: 0 additions & 16 deletions frontend/lib/models/stage.dart

This file was deleted.

20 changes: 20 additions & 0 deletions frontend/lib/models/stage_name.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'family_name.dart';

enum StageName { edge, beta, candidate, stable, proposed, updates }

List<StageName> familyStages(FamilyName family) {
switch (family) {
case FamilyName.snap:
return [
StageName.edge,
StageName.beta,
StageName.candidate,
StageName.stable,
];
case FamilyName.deb:
return [
StageName.proposed,
StageName.updates,
];
}
}
Loading

0 comments on commit 599526f

Please sign in to comment.