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

Support for relationship expiration in Playground UI #53

Merged
merged 3 commits into from
Jan 28, 2025
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
4 changes: 2 additions & 2 deletions public/static/main.wasm
Git LFS file not shown
4 changes: 2 additions & 2 deletions public/static/zed.wasm
Git LFS file not shown
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
import {
ParseRelationshipError,
parseRelationshipsWithComments,
parseRelationshipWithError,
} from "../../parsing";
import DataEditor, {
CompactSelection,
EditableGridCell,
Expand Down Expand Up @@ -37,13 +32,18 @@
useMemo,
useRef,
useState,
type ReactNode,
type KeyboardEvent,
type ReactNode,
} from "react";
import { useCookies } from "react-cookie";
import { ThemeProvider } from "styled-components";
import { useDeepCompareEffect, useDeepCompareMemo } from "use-deep-compare";
import { Resolver } from "../../parsers/dsl/resolution";
import {
ParseRelationshipError,
parseRelationshipsWithComments,
parseRelationshipWithError,
} from "../../parsing";
import { RelationTuple as Relationship } from "../../protodefs/core/v1/core";
import { useRelationshipsService } from "../../services/relationshipsservice";
import {
Expand Down Expand Up @@ -81,6 +81,8 @@
CaveatContextCell,
CAVEATNAME_CELL_KIND,
CaveatNameCell,
EXPIRATION_CELL_KIND,
ExpirationCell,
OBJECTID_CELL_KIND,
ObjectIdCell,
RELATION_CELL_KIND,
Expand Down Expand Up @@ -187,7 +189,7 @@

// NOTE: we do not want to rerun this if the dataUpdated callback has changed (which it should
// not, ideally).
}, [data]);

Check warning on line 192 in src/spicedb-common/components/relationshipeditor/RelationshipEditor.tsx

View workflow job for this annotation

GitHub Actions / Run Linters and Typechecking

React Hook useEffect has a missing dependency: 'dataUpdated'. Either include it or remove the dependency array

// relationships holds a filtered form of the grid, containing only valid relationships.
const relationships = useDeepCompareMemo(() => {
Expand Down Expand Up @@ -216,7 +218,7 @@
relData.subjectRelation ? `#${relData.subjectRelation}` : ""
}${
relData.caveatName ? `[${relData.caveatName}${caveatContext}]` : ""
}`;
}${relData.expiration ? `[expiration:${relData.expiration}]` : ""}`;
return parseRelationshipWithError(str);
})
.filter((value: Relationship | ParseRelationshipError) => {
Expand Down Expand Up @@ -571,6 +573,19 @@
copyData: data[row].columnData[col],
} as CaveatContextCell;

case DataKind.EXPIRATION:
return {
kind: GridCellKind.Custom,
data: {
kind: EXPIRATION_CELL_KIND,
row: row,
col: col,
dataValue: data[row].columnData[col],
},
allowOverlay: true,
copyData: data[row].columnData[col],
} as ExpirationCell;

default:
return {
kind: GridCellKind.Text,
Expand Down
20 changes: 20 additions & 0 deletions src/spicedb-common/components/relationshipeditor/columns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export enum DataKind {
SUBJECT_RELATION = 6,
CAVEAT_NAME = 7,
CAVEAT_CONTEXT = 8,
EXPIRATION = 9,
}

export type DataValidator = RegExp | ((input: string) => boolean);
Expand Down Expand Up @@ -57,6 +58,14 @@ export const DataRegex: Record<DataKind, DataValidator> = {
return false;
}
},
[DataKind.EXPIRATION]: (input: string) => {
try {
Date.parse(input);
return true;
} catch {
return false;
}
},
};

export const DataTitle: Record<DataKind, string> = {
Expand All @@ -69,13 +78,15 @@ export const DataTitle: Record<DataKind, string> = {
[DataKind.SUBJECT_RELATION]: "",
[DataKind.CAVEAT_NAME]: "",
[DataKind.CAVEAT_CONTEXT]: "",
[DataKind.EXPIRATION]: "",
};

export enum RelationshipSection {
NONE = 0,
RESOURCE = 1,
SUBJECT = 2,
CAVEAT = 3,
EXPIRATION = 4,
}

export type Column = SizedGridColumn & {
Expand Down Expand Up @@ -167,4 +178,13 @@ export const COLUMNS: Column[] = [
section: RelationshipSection.CAVEAT,
dataDescription: "JSON",
},
{
title: "Date/Time",
id: "expiration",
width: DEFAULT_COLUMN_WIDTH,
group: "Expiration (optional)",
dataKind: DataKind.EXPIRATION,
section: RelationshipSection.EXPIRATION,
dataDescription: "datetime",
},
];
30 changes: 29 additions & 1 deletion src/spicedb-common/components/relationshipeditor/customcells.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
CaveatContextCellRenderer,
CaveatNameCell,
CaveatNameCellRenderer,
ExpirationCell,
ExpirationCellRenderer,
ObjectIdCell,
ObjectIdCellRenderer,
RelationCell,
Expand All @@ -33,7 +35,8 @@ export type RelEditorCustomCell =
| ObjectIdCell
| RelationCell
| CaveatNameCell
| CaveatContextCell;
| CaveatContextCell
| ExpirationCell;

// Copied from: https://github.com/glideapps/glide-data-grid/blob/6b0a04f9d6550378890580b4db1e1168e4268c54/packages/cells/src/index.ts#L12
export type DrawCallback = NonNullable<DataEditorProps["drawCell"]>;
Expand Down Expand Up @@ -179,6 +182,28 @@ export function useCustomCells(
return { caveatcontext: annotatedData[row].columnData[col] };
}, [gridSelection, annotatedData]);

const selectedExpiration = useMemo(() => {
if (!gridSelection?.current?.cell) {
return undefined;
}

const [col, row] = gridSelection.current.cell;
if (row >= annotatedData.length) {
return undefined;
}

if (col >= COLUMNS.length) {
return undefined;
}

const dataKind = COLUMNS[col].dataKind;
if (dataKind !== DataKind.EXPIRATION) {
return undefined;
}

return { expiration: annotatedData[row].columnData[col] };
}, [gridSelection, annotatedData]);

const props = useRef({
relationshipsService: relationshipsService,
annotatedData: annotatedData,
Expand All @@ -190,6 +215,7 @@ export function useCustomCells(
selectedRelation: selectedRelation,
selectedCaveatName: selectedCaveatName,
selectedCaveatContext: selectedCaveatContext,
selectedExpiration: selectedExpiration,
},
similarHighlighting: similarHighlighting,
columnsWithWidths: columnsWithWidths,
Expand All @@ -208,6 +234,7 @@ export function useCustomCells(
selectedRelation: selectedRelation,
selectedCaveatName: selectedCaveatName,
selectedCaveatContext: selectedCaveatContext,
selectedExpiration: selectedExpiration,
},
similarHighlighting: similarHighlighting,
columnsWithWidths: columnsWithWidths,
Expand All @@ -222,6 +249,7 @@ export function useCustomCells(
RelationCellRenderer(props),
CaveatNameCellRenderer(props),
CaveatContextCellRenderer(props),
ExpirationCellRenderer(props),
];
}, []);

Expand Down
19 changes: 18 additions & 1 deletion src/spicedb-common/components/relationshipeditor/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export type PartialRelationship = {
subjectRelation: string;
caveatName: string;
caveatContext: string;
expiration: string;
};

/**
Expand Down Expand Up @@ -94,6 +95,7 @@ export function emptyAnnotatedDatum(
subjectRelation: "",
caveatName: "",
caveatContext: "",
expiration: "",
},
index,
);
Expand Down Expand Up @@ -143,11 +145,14 @@ export function toPartialRelationshipString(
const caveat = annotated.caveatName
? `[${annotated.caveatName}${caveatContext}]`
: "";
const expiration = annotated.expiration
? `[expiration:${annotated.expiration}]`
: "";
return `${annotated.resourceType}:${annotated.resourceId}#${
annotated.relation
}@${annotated.subjectType}:${annotated.subjectId}${
annotated.subjectRelation ? `#${annotated.subjectRelation}` : ""
}${caveat}`;
}${caveat}${expiration}`;
}

/**
Expand Down Expand Up @@ -181,6 +186,7 @@ export function getColumnData(datum: RelationshipDatum) {
datum.subjectRelation ?? "",
datum.caveatName ?? "",
datum.caveatContext ?? "",
datum.expiration ?? "",
];

return colData;
Expand All @@ -206,6 +212,11 @@ export function relationshipToDatum(rel: Relationship): PartialRelationship {
caveatContext: rel.caveat?.context
? Struct.toJsonString(rel.caveat?.context)
: "",
expiration: rel.optionalExpirationTime
? new Date(parseFloat(rel.optionalExpirationTime.seconds) * 1000)
.toISOString()
.replace(".000", "")
: "",
};
}

Expand All @@ -228,6 +239,7 @@ function fromColumnData(columnData: ColumnData): PartialRelationship | Comment {
subjectRelation: columnData[5] ?? "",
caveatName: columnData[6] ?? "",
caveatContext: columnData[7] ?? "",
expiration: columnData[8] ?? "",
};
}

Expand Down Expand Up @@ -257,6 +269,11 @@ export function relationshipToColumnData(
userRel,
relationship.caveat?.caveatName ?? "",
caveatContext,
relationship.optionalExpirationTime
? new Date(parseFloat(relationship.optionalExpirationTime.seconds) * 1000)
.toISOString()
.replace(".000", "")
: "",
];

if (columnData.length !== COLUMNS.length) {
Expand Down
21 changes: 21 additions & 0 deletions src/spicedb-common/components/relationshipeditor/fieldcell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const OBJECTID_CELL_KIND = "objectid-field-cell";
export const RELATION_CELL_KIND = "relation-field-cell";
export const CAVEATNAME_CELL_KIND = "caveatname-field-cell";
export const CAVEATCONTEXT_CELL_KIND = "caveatcontext-field-cell";
export const EXPIRATION_CELL_KIND = "expiration-field-cell";

interface FieldCellProps {
readonly dataValue: string;
Expand Down Expand Up @@ -54,11 +55,17 @@ type CaveatContextCellProps = FieldCellProps & {
readonly readonly: false;
};

type ExpirationCellProps = FieldCellProps & {
readonly kind: "expiration-field-cell";
readonly readonly: false;
};

export type TypeCell = CustomCell<TypeCellProps>;
export type ObjectIdCell = CustomCell<ObjectIdCellProps>;
export type RelationCell = CustomCell<RelationCellProps>;
export type CaveatNameCell = CustomCell<CaveatNameCellProps>;
export type CaveatContextCell = CustomCell<CaveatContextCellProps>;
export type ExpirationCell = CustomCell<ExpirationCellProps>;

type SelectedType = {
type: string;
Expand All @@ -72,6 +79,8 @@ type SelectedCaveatName = SelectedRelation & { caveatname: string };

type SelectedCaveatContext = { caveatcontext: string };

type SelectedExpiration = { expiration: string };

export interface FieldCellRendererProps {
relationshipsService: RelationshipsService;
annotatedData: AnnotatedData;
Expand All @@ -83,6 +92,7 @@ export interface FieldCellRendererProps {
selectedRelation: SelectedRelation | undefined;
selectedCaveatName: SelectedCaveatName | undefined;
selectedCaveatContext: SelectedCaveatContext | undefined;
selectedExpiration: SelectedExpiration | undefined;
};
similarHighlighting: boolean;
columnsWithWidths: Column[];
Expand Down Expand Up @@ -208,6 +218,9 @@ function fieldCellRenderer<T extends CustomCell<Q>, Q extends FieldCellProps>(
case DataKind.CAVEAT_CONTEXT:
// No highlighting for context values
break;
case DataKind.EXPIRATION:
// No highlighting for expiration
break;
}
}

Expand Down Expand Up @@ -451,3 +464,11 @@ export const CaveatContextCellRenderer = fieldCellRenderer<
// No autocomplete support
return [];
});

export const ExpirationCellRenderer = fieldCellRenderer<
ExpirationCell,
ExpirationCellProps
>(EXPIRATION_CELL_KIND, () => {
// No autocomplete support
return [];
});
10 changes: 8 additions & 2 deletions src/spicedb-common/lang/dslang.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as monacoEditor from "monaco-editor";
import { Position, editor, languages } from "monaco-editor";
import { findReferenceNode, parse } from "../parsers/dsl/dsl";
import { ResolvedReference, Resolver } from "../parsers/dsl/resolution";
import { Position, editor, languages } from "monaco-editor";
import * as monacoEditor from "monaco-editor";

export const DS_LANGUAGE_NAME = "dsl";
export const DS_THEME_NAME = "dsl-theme";
Expand Down Expand Up @@ -95,6 +95,7 @@ export default function registerDSLanguage(monaco: typeof monacoEditor) {
/caveat/,
{ token: "keyword.caveat", bracket: "@open", next: "@caveat" },
],
[/use expiration$/, { token: "keyword.expiration" }],
[
/permission/,
{
Expand Down Expand Up @@ -282,6 +283,9 @@ export default function registerDSLanguage(monaco: typeof monacoEditor) {
],
[/\w+#/, { token: "@rematch", next: "@relationref" }],
[/\w+:/, { token: "@rematch", next: "@wildcardref" }],
[/expiration/, { token: "keyword.expiration" }],
[/and/, { token: "keyword.and" }],
[/with/, { token: "keyword.with" }],
[/\w+/, { token: "type.identifier" }],
{ include: "@whitespace" },
],
Expand Down Expand Up @@ -607,6 +611,7 @@ export default function registerDSLanguage(monaco: typeof monacoEditor) {

{ token: "identifier.relorperm", foreground: "666666" },

{ token: "keyword.expiration", foreground: "ddaaaa" },
{ token: "keyword.permission", foreground: "158a64" },
{ token: "keyword.relation", foreground: "883425" },
{ token: "keyword.definition", foreground: "4242ff" },
Expand Down Expand Up @@ -652,6 +657,7 @@ export default function registerDSLanguage(monaco: typeof monacoEditor) {

{ token: "identifier.relorperm", foreground: "cccccc" },

{ token: "keyword.expiration", foreground: "ddaaaa" },
{ token: "keyword.permission", foreground: "1acc92" },
{ token: "keyword.relation", foreground: "ffa887" },
{ token: "keyword.definition", foreground: "8787ff" },
Expand Down
4 changes: 2 additions & 2 deletions src/wasm-config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"spicedb": "v1.37.1",
"zed": "v0.22.0"
"spicedb": "v1.40.0",
"zed": "v0.25.0"
}
Loading