Skip to content

Commit

Permalink
Merge pull request UpstreamDataInc#94 from UpstreamDataInc/dev_refact…
Browse files Browse the repository at this point in the history
…or_bff_api

Use only BFF from UI
  • Loading branch information
b-rowan authored Aug 29, 2024
2 parents 7bfc1a1 + 19e52f0 commit 2857da0
Show file tree
Hide file tree
Showing 17 changed files with 220 additions and 118 deletions.
9 changes: 0 additions & 9 deletions goosebit/api/v1/devices/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,3 @@

class DevicesDeleteRequest(BaseModel):
devices: list[str]


class DevicesPatchRequest(BaseModel):
devices: list[str]
software: str | None = None
name: str | None = None
pinned: bool | None = None
feed: str | None = None
force_update: bool | None = None
32 changes: 3 additions & 29 deletions goosebit/api/v1/devices/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

from goosebit.api.responses import StatusResponse
from goosebit.auth import validate_user_permissions
from goosebit.db.models import Device, Software, UpdateModeEnum
from goosebit.updater.manager import delete_devices, get_update_manager
from goosebit.db.models import Device
from goosebit.updater.manager import delete_devices

from . import device
from .requests import DevicesDeleteRequest, DevicesPatchRequest
from .requests import DevicesDeleteRequest
from .responses import DevicesResponse

router = APIRouter(prefix="/devices", tags=["devices"])
Expand All @@ -23,32 +23,6 @@ async def devices_get(_: Request) -> DevicesResponse:
return await DevicesResponse.convert(await Device.all().prefetch_related("assigned_software", "hardware"))


@router.patch(
"",
dependencies=[Security(validate_user_permissions, scopes=["device.write"])],
)
async def devices_patch(_: Request, config: DevicesPatchRequest) -> StatusResponse:
for uuid in config.devices:
updater = await get_update_manager(uuid)
if config.software is not None:
if config.software == "rollout":
await updater.update_update(UpdateModeEnum.ROLLOUT, None)
elif config.software == "latest":
await updater.update_update(UpdateModeEnum.LATEST, None)
else:
software = await Software.get_or_none(id=config.software)
await updater.update_update(UpdateModeEnum.ASSIGNED, software)
if config.pinned is not None:
await updater.update_update(UpdateModeEnum.PINNED, None)
if config.name is not None:
await updater.update_name(config.name)
if config.feed is not None:
await updater.update_feed(config.feed)
if config.force_update is not None:
await updater.update_force_update(config.force_update)
return StatusResponse(success=True)


@router.delete(
"",
dependencies=[Security(validate_user_permissions, scopes=["device.delete"])],
Expand Down
6 changes: 3 additions & 3 deletions goosebit/api/v1/software/requests.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from pydantic import RootModel
from pydantic import BaseModel


class SoftwareDeleteRequest(RootModel[list[int]]):
pass
class SoftwareDeleteRequest(BaseModel):
software_ids: list[int]
35 changes: 31 additions & 4 deletions goosebit/api/v1/software/routes.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from fastapi import APIRouter, HTTPException, Security
import aiofiles
from fastapi import APIRouter, File, Form, HTTPException, Security, UploadFile
from fastapi.requests import Request

from goosebit.api.responses import StatusResponse
from goosebit.auth import validate_user_permissions
from goosebit.db.models import Rollout, Software
from goosebit.settings import config
from goosebit.updates import create_software_update

from .requests import SoftwareDeleteRequest
from .responses import SoftwareResponse
Expand All @@ -23,10 +26,10 @@ async def software_get(_: Request) -> SoftwareResponse:
"",
dependencies=[Security(validate_user_permissions, scopes=["software.delete"])],
)
async def software_delete(_: Request, config: SoftwareDeleteRequest) -> StatusResponse:
async def software_delete(_: Request, delete_req: SoftwareDeleteRequest) -> StatusResponse:
success = False
for f_id in config.root:
software = await Software.get_or_none(id=f_id)
for software_id in delete_req.software_ids:
software = await Software.get_or_none(id=software_id)

if software is None:
continue
Expand All @@ -43,3 +46,27 @@ async def software_delete(_: Request, config: SoftwareDeleteRequest) -> StatusRe
await software.delete()
success = True
return StatusResponse(success=success)


@router.post(
"",
dependencies=[Security(validate_user_permissions, scopes=["software.write"])],
)
async def post_update(_: Request, file: UploadFile = File(None), url: str = Form(None)):
if url is not None:
# remote file
software = await Software.get_or_none(uri=url)
if software is not None:
rollout_count = await Rollout.filter(software=software).count()
if rollout_count == 0:
await software.delete()
else:
raise HTTPException(409, "Software with same URL already exists and is referenced by rollout")

await create_software_update(url, None)
else:
# local file
software_file = config.artifacts_dir.joinpath(file.filename)

async with aiofiles.open(software_file, mode="ab") as f:
await f.write(await file.read())
12 changes: 12 additions & 0 deletions goosebit/ui/bff/devices/requests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from __future__ import annotations

from pydantic import BaseModel


class DevicesPatchRequest(BaseModel):
devices: list[str]
software: str | None = None
name: str | None = None
pinned: bool | None = None
feed: str | None = None
force_update: bool | None = None
41 changes: 40 additions & 1 deletion goosebit/ui/bff/devices/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
from fastapi.requests import Request
from tortoise.expressions import Q

from goosebit.api.responses import StatusResponse
from goosebit.api.v1.devices import routes
from goosebit.auth import validate_user_permissions
from goosebit.db.models import Device, UpdateModeEnum, UpdateStateEnum
from goosebit.db.models import Device, Software, UpdateModeEnum, UpdateStateEnum
from goosebit.updater.manager import get_update_manager

from .requests import DevicesPatchRequest
from .responses import BFFDeviceResponse

router = APIRouter(prefix="/devices")
Expand All @@ -31,3 +35,38 @@ def search_filter(search_value):
total_records = await Device.all().count()

return await BFFDeviceResponse.convert(request, query, search_filter, total_records)


@router.patch(
"",
dependencies=[Security(validate_user_permissions, scopes=["device.write"])],
)
async def devices_patch(_: Request, config: DevicesPatchRequest) -> StatusResponse:
for uuid in config.devices:
updater = await get_update_manager(uuid)
if config.software is not None:
if config.software == "rollout":
await updater.update_update(UpdateModeEnum.ROLLOUT, None)
elif config.software == "latest":
await updater.update_update(UpdateModeEnum.LATEST, None)
else:
software = await Software.get_or_none(id=config.software)
await updater.update_update(UpdateModeEnum.ASSIGNED, software)
if config.pinned is not None:
await updater.update_update(UpdateModeEnum.PINNED, None)
if config.name is not None:
await updater.update_name(config.name)
if config.feed is not None:
await updater.update_feed(config.feed)
if config.force_update is not None:
await updater.update_force_update(config.force_update)
return StatusResponse(success=True)


router.add_api_route(
"",
routes.devices_delete,
methods=["DELETE"],
dependencies=[Security(validate_user_permissions, scopes=["device.delete"])],
name="bff_devices_delete",
)
1 change: 1 addition & 0 deletions goosebit/ui/bff/download/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .routes import router # noqa : F401
22 changes: 22 additions & 0 deletions goosebit/ui/bff/download/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from fastapi import APIRouter, HTTPException
from fastapi.requests import Request
from fastapi.responses import FileResponse, RedirectResponse

from goosebit.db.models import Software

router = APIRouter(prefix="/download", tags=["download"])


@router.get("/{file_id}")
async def download_file(_: Request, file_id: int):
software = await Software.get_or_none(id=file_id)
if software is None:
raise HTTPException(404)
if software.local:
return FileResponse(
software.path,
media_type="application/octet-stream",
filename=software.path.name,
)
else:
return RedirectResponse(url=software.uri)
28 changes: 28 additions & 0 deletions goosebit/ui/bff/rollouts/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from fastapi.requests import Request
from tortoise.expressions import Q

from goosebit.api.v1.rollouts import routes
from goosebit.auth import validate_user_permissions
from goosebit.db.models import Rollout

Expand All @@ -22,3 +23,30 @@ def search_filter(search_value):
total_records = await Rollout.all().count()

return await BFFRolloutsResponse.convert(request, query, search_filter, total_records)


router.add_api_route(
"",
routes.rollouts_put,
methods=["POST"],
dependencies=[Security(validate_user_permissions, scopes=["rollout.write"])],
name="bff_rollouts_post",
)


router.add_api_route(
"",
routes.rollouts_patch,
methods=["PATCH"],
dependencies=[Security(validate_user_permissions, scopes=["rollout.write"])],
name="bff_rollouts_patch",
)


router.add_api_route(
"",
routes.rollouts_delete,
methods=["DELETE"],
dependencies=[Security(validate_user_permissions, scopes=["rollout.delete"])],
name="bff_rollouts_delete",
)
3 changes: 2 additions & 1 deletion goosebit/ui/bff/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

from fastapi import APIRouter

from . import devices, rollouts, software
from . import devices, download, rollouts, software

router = APIRouter(prefix="/bff", tags=["bff"])
router.include_router(devices.router)
router.include_router(software.router)
router.include_router(rollouts.router)
router.include_router(download.router)
64 changes: 61 additions & 3 deletions goosebit/ui/bff/software/routes.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
from __future__ import annotations

from fastapi import APIRouter, Security
import aiofiles
from fastapi import APIRouter, Form, HTTPException, Security, UploadFile
from fastapi.requests import Request
from tortoise.expressions import Q

from goosebit.api.v1.software import routes
from goosebit.auth import validate_user_permissions
from goosebit.db.models import Software
from goosebit.ui.bff.software.responses import BFFSoftwareResponse
from goosebit.db.models import Rollout, Software
from goosebit.settings import config
from goosebit.updates import create_software_update

from .responses import BFFSoftwareResponse

router = APIRouter(prefix="/software")

Expand All @@ -23,3 +28,56 @@ def search_filter(search_value):
total_records = await Software.all().count()

return await BFFSoftwareResponse.convert(request, query, search_filter, total_records)


router.add_api_route(
"",
routes.software_delete,
methods=["DELETE"],
dependencies=[Security(validate_user_permissions, scopes=["software.delete"])],
name="bff_software_delete",
)


@router.post(
"",
dependencies=[Security(validate_user_permissions, scopes=["software.write"])],
)
async def post_update(
request: Request,
url: str = Form(default=None),
chunk: UploadFile = Form(default=None),
init: bool = Form(default=None),
done: bool = Form(default=None),
filename: str = Form(default=None),
):
if url is not None:
# remote file
software = await Software.get_or_none(uri=url)
if software is not None:
rollout_count = await Rollout.filter(software=software).count()
if rollout_count == 0:
await software.delete()
else:
raise HTTPException(409, "Software with same URL already exists and is referenced by rollout")

await create_software_update(url, None)
else:
# local file
file = config.artifacts_dir.joinpath(filename)
config.artifacts_dir.mkdir(parents=True, exist_ok=True)

temp_file = file.with_suffix(".tmp")
if init:
temp_file.unlink(missing_ok=True)

contents = await chunk.read()

async with aiofiles.open(temp_file, mode="ab") as f:
await f.write(contents)

if done:
try:
await create_software_update(file.absolute().as_uri(), temp_file)
finally:
temp_file.unlink(missing_ok=True)
Loading

0 comments on commit 2857da0

Please sign in to comment.