Skip to content
Merged
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
5 changes: 0 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@ repos:
hooks:
- id: biome-check
additional_dependencies: ["@biomejs/biome@1.8.3"]
- repo: https://github.com/rbubley/mirrors-prettier
rev: "v3.4.0"
hooks:
- id: prettier
files: \.(html|md|yml|yaml)$
- repo: https://github.com/djlint/djLint
rev: v1.36.1
hooks:
Expand Down
4 changes: 2 additions & 2 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,14 @@ async def test_data(db):
rollout_default = await Rollout.create(software_id=software_release.id)

device_rollout = await Device.create(
uuid="device1",
id="device1",
last_state=UpdateStateEnum.REGISTERED,
update_mode=UpdateModeEnum.ROLLOUT,
hardware=hardware,
)

device_assigned = await Device.create(
uuid="device2",
id="device2",
last_state=UpdateStateEnum.REGISTERED,
update_mode=UpdateModeEnum.ASSIGNED,
assigned_software=software_release,
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Assign specific software to a device manually. Once installed, no further update

#### 2. Automatic Update to Latest Software

Automatically updates the device to the latest compatible software, based on the reported `hw_model` and `hw_revision`. Note: versions are interpreted as [SemVer](https://semver.org) versions.
Automatically updates the device to the latest compatible software, based on the reported `hw_model` and `hw_revision`.

#### 3. Software Rollout

Expand Down
3 changes: 3 additions & 0 deletions goosebit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
# Port to host the server on, default:
#port: 60053 # GOOSE ;)

# Tenant for DDI endpoint
#tenant: DEFAULT

# Database to be used, default:
#db_uri: sqlite:///<project root>/db.sqlite3
#db_ssl_crt: /<project root>/ca-certificate.crt
Expand Down
14 changes: 7 additions & 7 deletions goosebit/api/v1/devices/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ async def devices_get(_: Request) -> DevicesResponse:
response = DevicesResponse(devices=devices)

async def set_assigned_sw(d: DeviceSchema):
device = await get_device(d.uuid)
device = await get_device(d.id)
_, target = await DeviceManager.get_update(device)
if target is not None:
await target.fetch_related("compatibility")
Expand All @@ -54,10 +54,10 @@ async def devices_delete(_: Request, config: DevicesDeleteRequest) -> StatusResp
dependencies=[Security(validate_user_permissions, scopes=["device.write"])],
)
async def devices_patch(_: Request, config: DevicesPatchRequest) -> StatusResponse:
for uuid in config.devices:
if await Device.get_or_none(uuid=uuid) is None:
raise HTTPException(404, f"Device with UUID {uuid} not found")
device = await DeviceManager.get_device(uuid)
for dev_id in config.devices:
if await Device.get_or_none(id=dev_id) is None:
raise HTTPException(404, f"Device with ID {dev_id} not found")
device = await DeviceManager.get_device(dev_id)
if config.software is not None:
if config.software == "rollout":
await DeviceManager.update_update(device, UpdateModeEnum.ROLLOUT, None)
Expand All @@ -84,8 +84,8 @@ async def devices_patch(_: Request, config: DevicesPatchRequest) -> StatusRespon
dependencies=[Security(validate_user_permissions, scopes=["device.write"])],
)
async def devices_put(_: Request, config: DevicesPutRequest) -> StatusResponse:
for uuid in config.devices:
device = await DeviceManager.get_device(uuid)
for dev_id in config.devices:
device = await DeviceManager.get_device(dev_id)
if config.software is not None:
if config.software == "rollout":
await DeviceManager.update_update(device, UpdateModeEnum.ROLLOUT, None)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from tortoise import BaseDBAsyncClient


async def upgrade(db: BaseDBAsyncClient) -> str:
return """
ALTER TABLE "device" RENAME COLUMN "uuid" TO "id";"""
Comment thread
tsagadar marked this conversation as resolved.


async def downgrade(db: BaseDBAsyncClient) -> str:
return """
ALTER TABLE "device" RENAME COLUMN "id" TO "uuid";"""
13 changes: 6 additions & 7 deletions goosebit/db/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@
from urllib.parse import unquote, urlparse
from urllib.request import url2pathname

import semver
from anyio import Path
from semver import Version
from tortoise import Model, fields
from tortoise.exceptions import ValidationError

from goosebit.api.telemetry.metrics import devices_count
from goosebit.util.version import Version


class UpdateModeEnum(IntEnum):
Expand Down Expand Up @@ -57,7 +56,7 @@ class Tag(Model):


class Device(Model):
uuid = fields.CharField(max_length=255, primary_key=True)
id = fields.CharField(max_length=255, primary_key=True)
name = fields.CharField(max_length=255, null=True)
assigned_software = fields.ForeignKeyField(
"models.Software", related_name="assigned_devices", null=True, on_delete=fields.SET_NULL
Expand Down Expand Up @@ -108,7 +107,7 @@ class Rollout(Model):
created_at = fields.DatetimeField(auto_now_add=True)
name = fields.CharField(max_length=255, null=True)
feed = fields.CharField(max_length=255, default="default")
software = fields.ForeignKeyField("models.Software", related_name="rollouts", index=True)
software = fields.ForeignKeyField("models.Software", related_name="rollouts", db_index=True)
paused = fields.BooleanField(default=False)
success_count = fields.IntField(default=0)
failure_count = fields.IntField(default=0)
Expand All @@ -134,12 +133,12 @@ class Software(Model):

@classmethod
async def latest(cls, device: Device) -> Self | None:
updates = await cls.filter(compatibility__devices__uuid=device.uuid)
updates = await cls.filter(compatibility__devices__id=device.id)
if len(updates) == 0:
return None
return sorted(
updates,
key=lambda x: semver.Version.parse(x.version, optional_minor_and_patch=True),
key=lambda x: Version.parse(x.version),
reverse=True,
)[0]

Expand All @@ -160,4 +159,4 @@ def path_user(self) -> str:

@property
def parsed_version(self) -> Version:
return semver.Version.parse(self.version, optional_minor_and_patch=True)
return Version.parse(self.version)
10 changes: 5 additions & 5 deletions goosebit/device_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ async def get_device(dev_id: str) -> Device:
hardware = (await Hardware.get_or_create(model="default", revision="default"))[0]
DeviceManager._hardware_default = hardware

device = (await Device.get_or_create(uuid=dev_id, defaults={"hardware": hardware}))[0]
result = await cache.set(device.uuid, device, ttl=600)
device = (await Device.get_or_create(id=dev_id, defaults={"hardware": hardware}))[0]
result = await cache.set(device.id, device, ttl=600)
assert result, "device being cached"

return device
Expand All @@ -57,7 +57,7 @@ async def save_device(device: Device, update_fields: list[str]):
await device.save(update_fields=update_fields)

# only update cache after a successful database save
result = await caches.get("default").set(device.uuid, device, ttl=600)
result = await caches.get("default").set(device.id, device, ttl=600)
assert result, "device being cached"

@staticmethod
Expand Down Expand Up @@ -155,7 +155,7 @@ async def get_rollout(device: Device) -> Rollout | None:
await Rollout.filter(
feed=device.feed,
paused=False,
software__compatibility__devices__uuid=device.uuid,
software__compatibility__devices__id=device.id,
)
.order_by("-created_at")
.first()
Expand Down Expand Up @@ -219,7 +219,7 @@ async def update_log(device: Device, log_data: str) -> None:

@staticmethod
async def delete_devices(ids: list[str]):
await Device.filter(uuid__in=ids).delete()
await Device.filter(id__in=ids).delete()
for dev_id in ids:
result = await caches.get("default").delete(dev_id)
assert result == 1, "device has been cached"
Expand Down
2 changes: 1 addition & 1 deletion goosebit/schema/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def enum_factory(name: str, base: type[Enum]) -> type[ConvertableEnum]:
class DeviceSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)

uuid: str
id: str
name: str | None
sw_version: str | None

Expand Down
1 change: 1 addition & 0 deletions goosebit/settings/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class GooseBitSettings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="GOOSEBIT_")

port: int = 60053 # GOOSE
tenant: str = "DEFAULT"

poll_time: str = "00:01:00"

Expand Down
2 changes: 1 addition & 1 deletion goosebit/ui/bff/common/columns.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


class DeviceColumns:
uuid = DTColumnDescription(title="UUID", data="uuid", name="uuid", searchable=True, orderable=True)
id = DTColumnDescription(title="ID", data="id", name="id", searchable=True, orderable=True)
name = DTColumnDescription(title="Name", data="name", name="name", searchable=True, orderable=True)
hw_model = DTColumnDescription(title="Model", data="hw_model")
hw_revision = DTColumnDescription(title="Revision", data="hw_revision")
Expand Down
14 changes: 7 additions & 7 deletions goosebit/ui/bff/devices/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
async def devices_get(dt_query: Annotated[DataTableRequest, Depends(parse_datatables_query)]) -> BFFDeviceResponse:
def search_filter(search_value: str):
return (
Q(uuid__icontains=search_value)
Q(id__icontains=search_value)
| Q(name__icontains=search_value)
| Q(feed__icontains=search_value)
| Q(sw_version__icontains=search_value)
Expand All @@ -47,7 +47,7 @@ def search_filter(search_value: str):
response = await BFFDeviceResponse.convert(dt_query, query, search_filter)

async def set_assigned_sw(d: DeviceSchema):
device = await get_device(d.uuid)
device = await get_device(d.id)
_, target = await DeviceManager.get_update(device)
if target is not None:
await target.fetch_related("compatibility")
Expand All @@ -63,10 +63,10 @@ async def set_assigned_sw(d: DeviceSchema):
dependencies=[Security(validate_user_permissions, scopes=["device.write"])],
)
async def devices_patch(_: Request, config: DevicesPatchRequest) -> StatusResponse:
for uuid in config.devices:
if await Device.get_or_none(uuid=uuid) is None:
raise HTTPException(404, f"Device with UUID {uuid} not found")
device = await get_device(uuid)
for dev_id in config.devices:
if await Device.get_or_none(id=dev_id) is None:
raise HTTPException(404, f"Device with ID {dev_id} not found")
device = await get_device(dev_id)
if config.software is not None:
if config.software == "rollout":
await DeviceManager.update_update(device, UpdateModeEnum.ROLLOUT, None)
Expand Down Expand Up @@ -108,7 +108,7 @@ async def devices_get_columns(request: Request) -> DTColumns:
filter(
None,
[
DeviceColumns.uuid,
DeviceColumns.id,
DeviceColumns.name,
DeviceColumns.hw_model,
DeviceColumns.hw_revision,
Expand Down
6 changes: 3 additions & 3 deletions goosebit/ui/bff/software/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
)
async def software_get(
dt_query: Annotated[DataTableRequest, Depends(parse_datatables_query)],
uuids: list[str] = Query(default=None),
ids: list[str] = Query(default=None),
) -> BFFSoftwareResponse:
filters: list[Q] = []

Expand All @@ -38,8 +38,8 @@ def search_filter(search_value):

query = Software.all().prefetch_related("compatibility")

if uuids:
hardware = await Hardware.filter(devices__uuid__in=uuids).distinct()
if ids:
hardware = await Hardware.filter(devices__id__in=ids).distinct()
filters.append(Q(*[Q(compatibility__id=c.id) for c in hardware], join_type="AND"))

return await BFFSoftwareResponse.convert(dt_query, query, search_filter, Q(*filters))
Expand Down
24 changes: 12 additions & 12 deletions goosebit/ui/static/js/devices.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ document.addEventListener("DOMContentLoaded", async () => {
paging: true,
processing: false,
serverSide: true,
order: { name: "uuid", dir: "asc" },
order: { name: "id", dir: "asc" },
scrollCollapse: true,
scroller: true,
scrollY: "65vh",
stateSave: true,
select: true,
rowId: "uuid",
rowId: "id",
ajax: {
url: "/ui/bff/devices",
data: (data) => {
Expand Down Expand Up @@ -88,7 +88,7 @@ document.addEventListener("DOMContentLoaded", async () => {
text: '<i class="bi bi-file-text"></i>',
action: () => {
const selectedDevice = dataTable.rows({ selected: true }).data().toArray()[0];
window.location.href = `/ui/logs/${selectedDevice.uuid}`;
window.location.href = `/ui/logs/${selectedDevice.id}`;
},
className: "buttons-logs",
titleAttr: "View Log",
Expand Down Expand Up @@ -117,7 +117,7 @@ document.addEventListener("DOMContentLoaded", async () => {
.rows({ selected: true })
.data()
.toArray()
.map((d) => d.uuid);
.map((d) => d.id);
await deleteDevices(selectedDevices);
},
className: "buttons-delete",
Expand All @@ -130,7 +130,7 @@ document.addEventListener("DOMContentLoaded", async () => {
.rows({ selected: true })
.data()
.toArray()
.map((d) => d.uuid);
.map((d) => d.id);
await forceUpdateDevices(selectedDevices);
},
className: "buttons-force-update",
Expand All @@ -143,7 +143,7 @@ document.addEventListener("DOMContentLoaded", async () => {
.rows({ selected: true })
.data()
.toArray()
.map((d) => d.uuid);
.map((d) => d.id);
await pinDevices(selectedDevices);
},
className: "buttons-pin",
Expand Down Expand Up @@ -283,7 +283,7 @@ async function updateDeviceName() {
.rows({ selected: true })
.data()
.toArray()
.map((d) => d.uuid);
.map((d) => d.id);
const name = document.getElementById("device-name").value;

try {
Expand All @@ -300,7 +300,7 @@ async function updateDeviceRollout() {
.rows({ selected: true })
.data()
.toArray()
.map((d) => d.uuid);
.map((d) => d.id);
const feed = document.getElementById("device-selected-feed").value;
const software = "rollout";

Expand All @@ -318,7 +318,7 @@ async function updateDeviceManualSoftware() {
.rows({ selected: true })
.data()
.toArray()
.map((d) => d.uuid);
.map((d) => d.id);
const feed = null;
const software = document.getElementById("selected-sw").value;

Expand All @@ -336,7 +336,7 @@ async function updateDeviceLatest() {
.rows({ selected: true })
.data()
.toArray()
.map((d) => d.uuid);
.map((d) => d.id);
const feed = null;
const software = "latest";

Expand Down Expand Up @@ -386,12 +386,12 @@ function updateDeviceList() {
.rows({ selected: true })
.data()
.toArray()
.map((d) => d.uuid);
.map((d) => d.id);

dataTable.ajax.reload(() => {
dataTable.rows().every(function () {
const rowData = this.data();
if (selectedRows.includes(rowData.uuid)) {
if (selectedRows.includes(rowData.id)) {
this.select();
}
});
Expand Down
2 changes: 1 addition & 1 deletion goosebit/ui/static/js/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ async function updateSoftwareSelection(devices = null) {
const url = new URL("/ui/bff/software?order[0][dir]=desc&order[0][name]=version", window.location.origin);
if (devices != null) {
for (const device of devices) {
url.searchParams.append("uuids", device.uuid);
url.searchParams.append("ids", device.id);
}
}
const response = await fetch(url.toString());
Expand Down
Loading