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

Enable server-side pagination for base_images_collections and system_models #793

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
271 changes: 237 additions & 34 deletions frontend/src/api/schema.graphql

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion frontend/src/api/schema.graphql.license
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
SPDX-FileCopyrightText: 2021-2023 SECO Mind Srl
SPDX-FileCopyrightText: 2021-2025 SECO Mind Srl

SPDX-License-Identifier: Apache-2.0
54 changes: 31 additions & 23 deletions frontend/src/components/BaseImageCollectionsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This file is part of Edgehog.

Copyright 2023 SECO Mind Srl
Copyright 2023-2025 SECO Mind Srl

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -19,8 +19,9 @@
*/

import { FormattedMessage } from "react-intl";
import { graphql, useFragment } from "react-relay/hooks";
import { graphql, usePaginationFragment } from "react-relay/hooks";

import type { BaseImageCollectionsTable_PaginationQuery } from "api/__generated__/BaseImageCollectionsTable_PaginationQuery.graphql";
import type {
BaseImageCollectionsTable_BaseImageCollectionFragment$data,
BaseImageCollectionsTable_BaseImageCollectionFragment$key,
Expand All @@ -32,19 +33,29 @@ import { Link, Route } from "Navigation";
// We use graphql fields below in columns configuration
/* eslint-disable relay/unused-fields */
const BASE_IMAGE_COLLECTIONS_TABLE_FRAGMENT = graphql`
fragment BaseImageCollectionsTable_BaseImageCollectionFragment on BaseImageCollection
@relay(plural: true) {
id
name
handle
systemModel {
name
fragment BaseImageCollectionsTable_BaseImageCollectionFragment on RootQueryType
@refetchable(queryName: "BaseImageCollectionsTable_PaginationQuery") {
baseImageCollections(first: $first, after: $after)
@connection(key: "BaseImageCollectionsTable_baseImageCollections") {
edges {
node {
id
name
handle
systemModel {
name
}
}
}
}
}
`;

type TableRecord =
BaseImageCollectionsTable_BaseImageCollectionFragment$data[number];
type TableRecord = NonNullable<
NonNullable<
BaseImageCollectionsTable_BaseImageCollectionFragment$data["baseImageCollections"]
>["edges"]
>[number]["node"];

const columnHelper = createColumnHelper<TableRecord>();
const columns = [
Expand Down Expand Up @@ -96,18 +107,15 @@ const BaseImageCollectionsTable = ({
className,
baseImageCollectionsRef,
}: Props) => {
const baseImageCollections = useFragment(
BASE_IMAGE_COLLECTIONS_TABLE_FRAGMENT,
baseImageCollectionsRef,
);

return (
<Table
className={className}
columns={columns}
data={baseImageCollections}
/>
);
const { data } = usePaginationFragment<
BaseImageCollectionsTable_PaginationQuery,
BaseImageCollectionsTable_BaseImageCollectionFragment$key
>(BASE_IMAGE_COLLECTIONS_TABLE_FRAGMENT, baseImageCollectionsRef);

const tableData =
data.baseImageCollections?.edges?.map((edge) => edge.node) ?? [];

return <Table className={className} columns={columns} data={tableData} />;
};

export default BaseImageCollectionsTable;
27 changes: 15 additions & 12 deletions frontend/src/components/BaseImageSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This file is part of Edgehog.

Copyright 2023 SECO Mind Srl
Copyright 2023-2025 SECO Mind Srl

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -38,15 +38,21 @@ import Stack from "components/Stack";

type SelectProps = ComponentProps<typeof Form.Select>;
type BaseImage = NonNullable<
BaseImageSelect_getBaseImages_Query$data["baseImageCollection"]
>["baseImages"][number];
NonNullable<
BaseImageSelect_getBaseImages_Query$data["baseImageCollection"]
>["baseImages"]["edges"]
>[number]["node"];

const GET_BASE_IMAGES_QUERY = graphql`
query BaseImageSelect_getBaseImages_Query($baseImageCollectionId: ID!) {
baseImageCollection(id: $baseImageCollectionId) {
baseImages {
id
name
edges {
node {
id
name
}
}
}
}
}
Expand Down Expand Up @@ -97,13 +103,10 @@ const BaseImageSelectContent = forwardRef<
return notFoundComponent;
}

return (
<BaseImageSelect
{...selectProps}
baseImages={baseImageCollection.baseImages}
ref={ref}
/>
);
const baseImages =
baseImageCollection.baseImages.edges?.map((edge) => edge.node) ?? [];

return <BaseImageSelect {...selectProps} baseImages={baseImages} ref={ref} />;
});
BaseImageSelectContent.displayName = "BaseImageSelectContent";

Expand Down
49 changes: 28 additions & 21 deletions frontend/src/components/BaseImagesTable.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This file is part of Edgehog.

Copyright 2023-2024 SECO Mind Srl
Copyright 2023-2025 SECO Mind Srl

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -20,8 +20,9 @@

import { useMemo } from "react";
import { FormattedMessage } from "react-intl";
import { graphql, useFragment } from "react-relay/hooks";
import { graphql, usePaginationFragment } from "react-relay/hooks";

import type { BaseImagesTable_PaginationQuery } from "api/__generated__/BaseImagesTable_PaginationQuery.graphql";
import type {
BaseImagesTable_BaseImagesFragment$data,
BaseImagesTable_BaseImagesFragment$key,
Expand All @@ -33,22 +34,29 @@ import { Link, Route } from "Navigation";
// We use graphql fields below in columns configuration
/* eslint-disable relay/unused-fields */
const BASE_IMAGES_TABLE_FRAGMENT = graphql`
fragment BaseImagesTable_BaseImagesFragment on BaseImageCollection {
fragment BaseImagesTable_BaseImagesFragment on BaseImageCollection
@refetchable(queryName: "BaseImagesTable_PaginationQuery") {
id
baseImages {
id
version
startingVersionRequirement
localizedReleaseDisplayNames {
value
languageTag
baseImages(first: $first, after: $after)
@connection(key: "BaseImagesTable_baseImages") {
edges {
node {
id
version
startingVersionRequirement
localizedReleaseDisplayNames {
value
languageTag
}
}
}
}
}
`;

type TableRecord =
BaseImagesTable_BaseImagesFragment$data["baseImages"][number];
type TableRecord = NonNullable<
NonNullable<BaseImagesTable_BaseImagesFragment$data["baseImages"]>["edges"]
>[number]["node"];

const columnHelper = createColumnHelper<TableRecord>();
const getColumnsDefinition = (baseImageCollectionId: string) => [
Expand Down Expand Up @@ -110,21 +118,20 @@ const BaseImagesTable = ({
baseImageCollectionRef,
hideSearch = false,
}: Props) => {
const baseImageCollection = useFragment(
BASE_IMAGES_TABLE_FRAGMENT,
baseImageCollectionRef,
);
const { data } = usePaginationFragment<
BaseImagesTable_PaginationQuery,
BaseImagesTable_BaseImagesFragment$key
>(BASE_IMAGES_TABLE_FRAGMENT, baseImageCollectionRef);

const columns = useMemo(
() => getColumnsDefinition(baseImageCollection.id),
[baseImageCollection.id],
);
const tableData = data.baseImages?.edges?.map((edge) => edge.node) ?? [];

const columns = useMemo(() => getColumnsDefinition(data.id), [data.id]);

return (
<Table
className={className}
columns={columns}
data={baseImageCollection.baseImages}
data={tableData}
hideSearch={hideSearch}
/>
);
Expand Down
10 changes: 7 additions & 3 deletions frontend/src/components/DevicesTable.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This file is part of Edgehog.

Copyright 2021-2024 SECO Mind Srl
Copyright 2021-2025 SECO Mind Srl

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -49,7 +49,11 @@ const DEVICES_TABLE_FRAGMENT = graphql`
}
}
tags {
name
edges {
node {
name
}
}
}
}
`;
Expand Down Expand Up @@ -145,7 +149,7 @@ const columns = [
),
cell: ({ getValue }) => (
<>
{getValue().map(({ name: tag }) => (
{getValue().edges?.map(({ node: { name: tag } }) => (
<Tag key={tag} className="me-2">
{tag}
</Tag>
Expand Down
41 changes: 27 additions & 14 deletions frontend/src/components/OperationTable.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This file is part of Edgehog.

Copyright 2022-2023 SECO Mind Srl
Copyright 2022-2025 SECO Mind Srl

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -19,8 +19,9 @@
*/

import { defineMessages, FormattedDate, FormattedMessage } from "react-intl";
import { graphql, useFragment } from "react-relay/hooks";
import { graphql, usePaginationFragment } from "react-relay/hooks";

import type { OperationTable_PaginationQuery } from "api/__generated__/OperationTable_PaginationQuery.graphql";
import type {
OtaOperationStatus,
OperationTable_otaOperations$data,
Expand All @@ -33,12 +34,18 @@ import Table, { createColumnHelper } from "components/Table";
// We use graphql fields below in columns configuration
/* eslint-disable relay/unused-fields */
const OPERATION_TABLE_FRAGMENT = graphql`
fragment OperationTable_otaOperations on Device {
otaOperations {
baseImageUrl
createdAt
status
updatedAt
fragment OperationTable_otaOperations on Device
@refetchable(queryName: "OperationTable_PaginationQuery") {
otaOperations(first: $first, after: $after)
@connection(key: "Device_otaOperations") {
edges {
node {
baseImageUrl
createdAt
status
updatedAt
}
}
}
}
`;
Expand All @@ -51,7 +58,9 @@ const isOtaOperationFinalStatus = (
): status is OtaOperationFinalStatus =>
(otaOperationFinalStatuses as readonly string[]).includes(status);

type OtaOperation = OperationTable_otaOperations$data["otaOperations"][number];
type OtaOperation = NonNullable<
NonNullable<OperationTable_otaOperations$data["otaOperations"]>["edges"]
>[number]["node"];
type OtaOperationWithFinalStatus = Omit<OtaOperation, "status"> & {
readonly status: OtaOperationFinalStatus;
};
Expand Down Expand Up @@ -154,11 +163,15 @@ type OperationTableProps = {
const initialSortedColumns = [{ id: "updatedAt", desc: true }];

const OperationTable = ({ className, deviceRef }: OperationTableProps) => {
const data = useFragment(OPERATION_TABLE_FRAGMENT, deviceRef);

const otaOperations = data.otaOperations.filter(
isOtaOperationWithFinalStatus,
);
const { data } = usePaginationFragment<
OperationTable_PaginationQuery,
OperationTable_otaOperations$key
>(OPERATION_TABLE_FRAGMENT, deviceRef);

const otaOperations =
data.otaOperations?.edges
?.map((edge) => edge.node)
.filter(isOtaOperationWithFinalStatus) ?? [];

if (!otaOperations) {
return (
Expand Down
Loading
Loading