From bbf09fcdba78f97f8bd8912176430d0189c2987f Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 31 Jul 2024 13:30:14 -0400 Subject: [PATCH 1/4] Add missing `key` on examples --- playground/src/components/ExamplesDropdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playground/src/components/ExamplesDropdown.tsx b/playground/src/components/ExamplesDropdown.tsx index 64b57e4..6fb58a4 100644 --- a/playground/src/components/ExamplesDropdown.tsx +++ b/playground/src/components/ExamplesDropdown.tsx @@ -87,7 +87,7 @@ export function ExamplesDropdown(props: { {examples !== undefined && examples.map((example) => { return ( - exampleSelected(example)}> + exampleSelected(example)} key={example.id}> Date: Wed, 31 Jul 2024 13:30:41 -0400 Subject: [PATCH 2/4] Add support for named arrows in parser and syntax highlighting --- .../src/components/graph/builder.ts | 3 ++ spicedb-common/src/lang/dslang.ts | 22 ++++++++++- spicedb-common/src/parsers/dsl/dsl.test.ts | 38 +++++++++++++++++++ spicedb-common/src/parsers/dsl/dsl.ts | 33 ++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) diff --git a/spicedb-common/src/components/graph/builder.ts b/spicedb-common/src/components/graph/builder.ts index 337142a..4d5c3be 100644 --- a/spicedb-common/src/components/graph/builder.ts +++ b/spicedb-common/src/components/graph/builder.ts @@ -615,6 +615,9 @@ function collectUnionedRelations( nodes: [...left.nodes, ...right.nodes], edges: [...left.edges, ...right.edges], }; + + case 'namedarrow': + // fallthrough case 'arrow': const { nodes, edges } = generateExpressionGraph( diff --git a/spicedb-common/src/lang/dslang.ts b/spicedb-common/src/lang/dslang.ts index 02e1a29..3b2b254 100644 --- a/spicedb-common/src/lang/dslang.ts +++ b/spicedb-common/src/lang/dslang.ts @@ -264,10 +264,26 @@ export default function registerDSLanguage(monaco: any) { [/}/, { token: '@rematch', next: '@popall' }], [/relation/, { token: '@rematch', next: '@popall' }], [/permission/, { token: '@rematch', next: '@popall' }], + [/any/, { token: 'keyword.any', next: '@arrowopen' }], + [/all/, { token: 'keyword.all', next: '@arrowopen' }], [/nil/, { token: 'keyword.nil' }], [/\w+/, { token: 'identifier.relorperm' }], { include: '@whitespace' }, ], + arrowopen: [ + [/$/, { token: 'close', next: '@popall' }], + [/}/, { token: '@rematch', next: '@popall' }], + [/\)/, { token: 'close', next: '@pop' }], + [/\(/, { token: '@rematch', next: '@arrowrel' }], + { include: '@whitespace' }, + ], + arrowrel: [ + [/$/, { token: 'close', next: '@popall' }], + [/}/, { token: '@rematch', next: '@popall' }], + [/\)/, { token: 'close', next: '@pop' }], + [/\w+/, { token: 'identifier.relorperm' }], + { include: '@whitespace' }, + ], relation: [ [/[a-z0-9_]+/, { token: 'identifier.relation', next: '@allowed' }], ], @@ -620,7 +636,9 @@ export default function registerDSLanguage(monaco: any) { { token: 'keyword.definition', foreground: '4242ff' }, { token: 'keyword.caveat', foreground: 'ff4271' }, { token: 'keyword.nil', foreground: '999999' }, - + { token: 'keyword.any', foreground: '23974d' }, + { token: 'keyword.all', foreground: '972323' }, + { token: 'identifier.type-prefix', foreground: 'aaaaaa' }, { token: 'identifier.definition-prefix', foreground: 'aaaaaa' }, @@ -662,6 +680,8 @@ export default function registerDSLanguage(monaco: any) { { token: 'keyword.definition', foreground: '8787ff' }, { token: 'keyword.caveat', foreground: 'ff87a6' }, { token: 'keyword.nil', foreground: 'cccccc' }, + { token: 'keyword.any', foreground: 'abe5ff' }, + { token: 'keyword.all', foreground: 'ffabab' }, { token: 'identifier.type-prefix', foreground: 'aaaaaa' }, { token: 'identifier.definition-prefix', foreground: 'aaaaaa' }, diff --git a/spicedb-common/src/parsers/dsl/dsl.test.ts b/spicedb-common/src/parsers/dsl/dsl.test.ts index 5a89b90..f6c190d 100644 --- a/spicedb-common/src/parsers/dsl/dsl.test.ts +++ b/spicedb-common/src/parsers/dsl/dsl.test.ts @@ -2,6 +2,7 @@ import { ParsedArrowExpression, ParsedBinaryExpression, ParsedCaveatDefinition, + ParsedNamedArrowExpression, ParsedNilExpression, ParsedObjectDefinition, ParsedRelationRefExpression, @@ -452,6 +453,43 @@ describe('parsing', () => { expect(rightExpr.targetRelationOrPermission).toEqual('admin'); }); + it('parses a named arrow expression', () => { + const schema = `definition user {} + + definition organization { + relation admin: user; + } + + definition document { + relation reader: user + relation org: organization + + permission read = reader + org.any(admin) + }`; + + const parsed = parseSchema(schema); + expect(parsed?.definitions?.length).toEqual(3); + + const definition = parsed?.definitions[2]! as ParsedObjectDefinition; + expect(definition.name).toEqual('document'); + expect(definition.relations.length).toEqual(2); + expect(definition.permissions.length).toEqual(1); + + const read = definition.permissions[0]!; + expect(read.name).toEqual('read'); + + const expr = read.expr as ParsedBinaryExpression; + const leftExpr = expr.left as ParsedRelationRefExpression; + expect(leftExpr.relationName).toEqual('reader'); + + const rightExpr = expr.right as ParsedNamedArrowExpression; + expect( + (rightExpr.sourceRelation as ParsedRelationRefExpression).relationName + ).toEqual('org'); + expect(rightExpr.functionName).toEqual('any'); + expect(rightExpr.targetRelationOrPermission).toEqual('admin'); + }); + it('parses an example with multiple comments', () => { const schema = ` /** diff --git a/spicedb-common/src/parsers/dsl/dsl.ts b/spicedb-common/src/parsers/dsl/dsl.ts index a34cbee..08abb93 100644 --- a/spicedb-common/src/parsers/dsl/dsl.ts +++ b/spicedb-common/src/parsers/dsl/dsl.ts @@ -102,6 +102,9 @@ export function flatMapExpression( walker: ExprWalker ): T[] { switch (expr.kind) { + case 'namedarrow': + // fallthrough + case 'arrow': const arrowResult = walker(expr); const childResults = flatMapExpression(expr.sourceRelation, walker); @@ -231,6 +234,7 @@ export type ParsedExpression = | ParsedBinaryExpression | ParsedRelationRefExpression | ParsedArrowExpression + | ParsedNamedArrowExpression | ParsedNilExpression; export interface ParsedArrowExpression { @@ -240,6 +244,15 @@ export interface ParsedArrowExpression { range: TextRange; } +export interface ParsedNamedArrowExpression { + kind: 'namedarrow'; + sourceRelation: ParsedRelationRefExpression; + functionName: string; + targetRelationOrPermission: string; + range: TextRange; +} + + export interface ParsedRelationRefExpression { kind: 'relationref'; relationName: string; @@ -357,6 +370,7 @@ const rcaret = lexeme(string('>')); const arrow = lexeme(string('->')); const hash = lexeme(string('#')); const comma = lexeme(string(',')); +const dot = lexeme(string('.')); const terminator: any = newline.or(semicolon); @@ -447,6 +461,24 @@ const arrowExpr: any = Parsimmon.lazy(() => { ); }); + +const namedArrowExpr: any = Parsimmon.lazy(() => { + return Parsimmon.seqMap( + Parsimmon.index, + seq(relationReference, dot, identifier, lparen, identifier, rparen), + Parsimmon.index, + function (startIndex, data, endIndex) { + return { + kind: 'namedarrow', + sourceRelation: data[0], + functionName: data[2], + targetRelationOrPermission: data[4], + range: { startIndex: startIndex, endIndex: endIndex }, + }; + } + ); +}); + const nilExpr: any = Parsimmon.lazy(() => { return Parsimmon.seqMap( Parsimmon.index, @@ -467,6 +499,7 @@ const parensExpr: any = Parsimmon.lazy(() => .then(expr) .skip(string(')')) .or(arrowExpr) + .or(namedArrowExpr) .or(nilExpr) .or(relationReference) ); From 2a03b49a06e11f0029eb97b1ebf83c65b1e0d72a Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 31 Jul 2024 13:31:17 -0400 Subject: [PATCH 3/4] Update README with instructions for development locally --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 687beab..acaf5f7 100644 --- a/README.md +++ b/README.md @@ -62,13 +62,13 @@ NODE_OPTIONS=--openssl-legacy-provider vercel build vercel deploy --prebuilt ``` -> ℹ️ Git Large File Storage (LFS) must be enabled in your Vercel project settings. +> ℹ️ Git Large File Storage (LFS) must be enabled in your Vercel project settings. ### NodeJS The `build` directory in the project root directory after running `yarn build` will contain an optimized production React application that can be served using your preferred NodeJS hosting method. -> ℹ️ Node v18.x is required. +> ℹ️ Node v18.x is required. For example: @@ -84,6 +84,11 @@ Setup git submodules: `git submodule update --init --recursive` Run `yarn install` in the _root_ project directory. +## Running for development + +1. Copy the files in the `wasm` root directory into `playground/public/static` +2. Run `yarn start` from the `playground` subdirectory + ## Updating wasm dependencies The project contains prebuilt WASM files for versions of both SpiceDB and zed. To update the versions, edit the [wasm-config.json] file with the desired tag/commit hash and then run from the project root: From c8894105881d4073d05a6405a60f0e81764a3606 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 31 Jul 2024 14:33:02 -0400 Subject: [PATCH 4/4] Add support for warnings and update SpiceDB and zed WASM bundles --- playground/src/components/EditorDisplay.tsx | 25 +- playground/src/components/FullPlayground.tsx | 17 +- .../src/components/panels/base/common.tsx | 6 +- .../src/components/panels/errordisplays.tsx | 27 ++ playground/src/components/panels/problems.tsx | 34 +- playground/src/services/check.ts | 18 +- playground/src/services/problem.ts | 31 +- playground/src/services/validation.ts | 10 +- spicedb-common/package.json | 3 +- .../src/protodevdefs/core/v1/core.ts | 449 +++++++++++++++++- .../protodevdefs/developer/v1/developer.ts | 218 ++++++++- .../dispatch/v1/dispatch.client.ts | 13 + .../src/protodevdefs/dispatch/v1/dispatch.ts | 430 ++++++++++++++++- .../src/services/developerservice.ts | 25 + spicedb-common/wasm-config.json | 4 +- wasm/main.wasm | 4 +- wasm/zed.wasm | 4 +- yarn.lock | 6 +- 18 files changed, 1274 insertions(+), 50 deletions(-) diff --git a/playground/src/components/EditorDisplay.tsx b/playground/src/components/EditorDisplay.tsx index bdb4ec3..8c4b087 100644 --- a/playground/src/components/EditorDisplay.tsx +++ b/playground/src/components/EditorDisplay.tsx @@ -6,7 +6,7 @@ import registerDSLanguage, { import { useDebouncedChecker } from '@code/playground-ui/src/debouncer'; import { TextRange } from '@code/spicedb-common/src/include/protobuf-parser'; import { RelationshipFound } from '@code/spicedb-common/src/parsing'; -import { DeveloperError } from '@code/spicedb-common/src/protodevdefs/developer/v1/developer'; +import { DeveloperError, DeveloperWarning } from '@code/spicedb-common/src/protodevdefs/developer/v1/developer'; import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; import useMediaQuery from '@material-ui/core/useMediaQuery'; import Editor, { DiffEditor, useMonaco } from '@monaco-editor/react'; @@ -208,6 +208,7 @@ export function EditorDisplay(props: EditorDisplayProps) { return; } + markers.push({ startLineNumber: invalid.lineNumber + 1, startColumn: 0, @@ -220,9 +221,29 @@ export function EditorDisplay(props: EditorDisplayProps) { ); } - // Generate markers for all other kinds of errors. const contents = currentItem?.editableContents ?? ''; const finder = lineColumn(contents); + const lines = contents.split('\n'); + + // Generate markers for warnings. + if (currentItem.kind === DataStoreItemKind.SCHEMA) { + props.services.problemService.warnings.forEach( + (warning: DeveloperWarning) => { + const line = lines[warning.line -1]; + const index = line.indexOf(warning.sourceCode, warning.column - 1); + markers.push({ + startLineNumber: warning.line, + startColumn: index + 1, + endLineNumber: warning.line, + endColumn: index + warning.sourceCode.length + 1, + message: warning.message, + severity: monacoRef.MarkerSeverity.Warning, + }); + } + ); + } + + // Generate markers for all other kinds of errors. const allErrors = [ ...props.services.problemService.requestErrors, ...props.services.problemService.validationErrors, diff --git a/playground/src/components/FullPlayground.tsx b/playground/src/components/FullPlayground.tsx index fbaddc6..538aef9 100644 --- a/playground/src/components/FullPlayground.tsx +++ b/playground/src/components/FullPlayground.tsx @@ -1159,18 +1159,27 @@ const TabLabelWithCount = (props: { }) => { const classes = useSummaryStyles(); const problemService = props.problemService; - const problemCount = problemService.getProblemCount(props.kind); + const errorCount = problemService.getErrorCount(props.kind); + const warningCount = props.kind === DataStoreItemKind.SCHEMA ? problemService.warnings.length : 0; return (
0 ? 'inline-flex' : 'none' }} + style={{ display: errorCount > 0 ? 'inline-flex' : 'none' }} className={clsx(classes.badge, { - [classes.failBadge]: problemCount > 0, + [classes.failBadge]: errorCount > 0, })} > - {problemCount} + {errorCount} + + 0 ? 'inline-flex' : 'none' }} + className={clsx(classes.badge, { + [classes.warningBadge]: warningCount > 0, + })} + > + {warningCount}
); diff --git a/playground/src/components/panels/base/common.tsx b/playground/src/components/panels/base/common.tsx index a54be47..c7831a7 100644 --- a/playground/src/components/panels/base/common.tsx +++ b/playground/src/components/panels/base/common.tsx @@ -83,6 +83,10 @@ export const useSummaryStyles = makeStyles((theme: Theme) => backgroundColor: theme.palette.error.dark, color: theme.palette.getContrastText(theme.palette.error.dark), }, + warningBadge: { + backgroundColor: theme.palette.warning.dark, + color: theme.palette.getContrastText(theme.palette.warning.dark), + }, checkTab: { display: 'grid', alignItems: 'center', @@ -93,7 +97,7 @@ export const useSummaryStyles = makeStyles((theme: Theme) => }, problemTab: { display: 'grid', - gridTemplateColumns: 'auto auto', + gridTemplateColumns: 'auto auto auto', columnGap: '10px', alignItems: 'center', }, diff --git a/playground/src/components/panels/errordisplays.tsx b/playground/src/components/panels/errordisplays.tsx index 21d39ce..5e6bc2e 100644 --- a/playground/src/components/panels/errordisplays.tsx +++ b/playground/src/components/panels/errordisplays.tsx @@ -1,6 +1,7 @@ import { DeveloperError, DeveloperError_Source, + DeveloperWarning, } from '@code/spicedb-common/src/protodevdefs/developer/v1/developer'; import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; import Alert from '@material-ui/lab/Alert'; @@ -89,6 +90,19 @@ export function DeveloperErrorDisplay(props: { error: DeveloperError }) { ); } +export function DeveloperWarningDisplay(props: { warning: DeveloperWarning }) { + const classes = useErrorDisplayStyles(); + return ( + + {props.warning.message} + + ); +} + const useSourceDisplayStyles = makeStyles((theme: Theme) => createStyles({ link: { @@ -101,6 +115,19 @@ const useSourceDisplayStyles = makeStyles((theme: Theme) => }) ); +export function DeveloperWarningSourceDisplay(props: {warning: DeveloperWarning}) { + const vw = props.warning; + const classes = useSourceDisplayStyles(); + + return
+ In{' '} + + Schema + + : +
+} + export function DeveloperSourceDisplay(props: { error: DeveloperError }) { const ve = props.error; const classes = useSourceDisplayStyles(); diff --git a/playground/src/components/panels/problems.tsx b/playground/src/components/panels/problems.tsx index 7eaef6b..1771e6f 100644 --- a/playground/src/components/panels/problems.tsx +++ b/playground/src/components/panels/problems.tsx @@ -1,6 +1,6 @@ import TabLabel from '@code/playground-ui/src/TabLabel'; import { RelationshipFound } from '@code/spicedb-common/src/parsing'; -import { DeveloperError } from '@code/spicedb-common/src/protodevdefs/developer/v1/developer'; +import { DeveloperError, DeveloperWarning } from '@code/spicedb-common/src/protodevdefs/developer/v1/developer'; import Paper from '@material-ui/core/Paper'; import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; import ErrorOutlineIcon from '@material-ui/icons/ErrorOutline'; @@ -11,7 +11,7 @@ import { Link } from 'react-router-dom'; import { DataStorePaths } from '../../services/datastore'; import { TourElementClass } from '../GuidedTour'; import { PanelProps, PanelSummaryProps, useSummaryStyles } from './base/common'; -import { DeveloperErrorDisplay, DeveloperSourceDisplay } from './errordisplays'; +import { DeveloperErrorDisplay, DeveloperSourceDisplay, DeveloperWarningDisplay, DeveloperWarningSourceDisplay } from './errordisplays'; import { PlaygroundPanelLocation } from './panels'; var _ = React; @@ -51,24 +51,32 @@ export function ProblemsSummary( props: PanelSummaryProps ) { const classes = useSummaryStyles(); - const problemCount = props.services.problemService.problemCount; + const errorCount = props.services.problemService.errorCount; + const warningCount = props.services.problemService.warnings.length; return (
0 ? '' : 'grey'} /> } title="Problems" /> 0, + [classes.failBadge]: errorCount > 0, })} > - {problemCount} + {errorCount} + + 0, + })} + > + {warningCount}
); @@ -85,7 +93,7 @@ export function ProblemsPanel(props: PanelProps) { {props.services.problemService.invalidRelationships.map( (invalid: RelationshipFound, index: number) => { if (!('errorMessage' in invalid.parsed)) { - return; + return
; } return ( @@ -122,6 +130,18 @@ export function ProblemsPanel(props: PanelProps) { ); } )} + {props.services.problemService.warnings.map( + (dw: DeveloperWarning, index: number) => { + return ( + +
+ + +
+
+ ); + } + )}
); } diff --git a/playground/src/services/check.ts b/playground/src/services/check.ts index 84a3abe..20b68a2 100644 --- a/playground/src/services/check.ts +++ b/playground/src/services/check.ts @@ -4,6 +4,7 @@ import { CheckOperationsResult_Membership, DeveloperError, DeveloperResponse, + DeveloperWarning, } from '@code/spicedb-common/src/protodevdefs/developer/v1/developer'; import { DeveloperService, @@ -47,6 +48,7 @@ export interface LiveCheckRunState { lastRun?: Date; requestErrors?: DeveloperError[]; serverErr?: DeveloperServiceError; + warnings?: DeveloperWarning[]; } export interface LiveCheckService { @@ -73,7 +75,7 @@ function runEditCheckWasm( developerService: DeveloperService, datastore: DataStore, items: LiveCheckItem[] -): DeveloperResponse | undefined { +): [DeveloperResponse, DeveloperWarning[]] | undefined { const schema = datastore.getSingletonByKind(DataStoreItemKind.SCHEMA).editableContents ?? ''; @@ -86,6 +88,12 @@ function runEditCheckWasm( return; } + // Add a check for warnings. + let warnings: DeveloperWarning[] = []; + request.schemaWarnings((result) => { + warnings = result.warnings; + }); + // Build the relationships to be checked, validating as we go. items.forEach((item: LiveCheckItem) => { const parsed = parseRelationship(liveCheckItemToString(item)); @@ -130,7 +138,7 @@ function runEditCheckWasm( ); }); - return request.execute(); + return [request.execute(), warnings]; } /** @@ -154,12 +162,12 @@ export function useLiveCheckService( } setState({ status: LiveCheckStatus.CHECKING }); - const response = runEditCheckWasm( + const r = runEditCheckWasm( developerService, datastore, itemsToCheck ); - if (response === undefined) { + if (r === undefined) { setState({ status: LiveCheckStatus.NOT_CHECKING }); if (itemsToCheck.length > 0) { @@ -174,6 +182,7 @@ export function useLiveCheckService( return; } + const [response, warnings] = r; const serverErr: string | undefined = response.internalError || undefined; const devErrs: DeveloperError[] = response.developerErrors ? response.developerErrors.inputErrors @@ -189,6 +198,7 @@ export function useLiveCheckService( lastRun: new Date(), requestErrors: devErrs, serverErr: serverErr, + warnings: warnings, }); }, [developerService, devServiceStatus, datastore] diff --git a/playground/src/services/problem.ts b/playground/src/services/problem.ts index bc034c2..d996f86 100644 --- a/playground/src/services/problem.ts +++ b/playground/src/services/problem.ts @@ -1,5 +1,5 @@ import { RelationshipFound } from '@code/spicedb-common/src/parsing'; -import { DeveloperError } from '@code/spicedb-common/src/protodevdefs/developer/v1/developer'; +import { DeveloperError, DeveloperWarning } from '@code/spicedb-common/src/protodevdefs/developer/v1/developer'; import { ERROR_SOURCE_TO_ITEM } from '../components/panels/errordisplays'; import { LiveCheckService, LiveCheckStatus } from './check'; import { DataStoreItemKind } from './datastore'; @@ -33,23 +33,29 @@ export interface ProblemService { invalidRelationships: RelationshipFound[]; /** - * hasProblems indicates whether any problems were found. + * hasProblems is true if there are any problems. */ hasProblems: boolean; /** - * problemCount is the count of the problems found, if any. + * errorCount is the count of the errors found, if any. */ - problemCount: number; + errorCount: number; /** - * getProblemCount returns the number of errors in the specific item. + * getErrorCount returns the number of errors in the specific item. */ - getProblemCount: (kind: DataStoreItemKind) => number; + getErrorCount: (kind: DataStoreItemKind) => number; + + /** + * warnings are warnings raised by the last call to validation. + */ + warnings: DeveloperWarning[]; } interface ProblemsResult { requestErrors?: DeveloperError[]; + warnings?: DeveloperWarning[]; } /** @@ -71,9 +77,9 @@ export function useProblemService( const invalidRelationships = localParseService.state.relationships.filter( (rel: RelationshipFound) => 'errorMessage' in rel.parsed ); - const problemCount = requestErrors.length + invalidRelationships.length; + const errorCount = requestErrors.length + invalidRelationships.length; - const getProblemCount = (kind: DataStoreItemKind) => { + const getErrorCount = (kind: DataStoreItemKind) => { const allProblems = Array.from(requestErrors); allProblems.push(...(validationService.state.validationErrors ?? [])); let foundCount = allProblems.filter( @@ -89,16 +95,17 @@ export function useProblemService( liveCheckService.state.status === LiveCheckStatus.CHECKING || validationService.isRunning; const validationErrors = validationService.state.validationErrors ?? []; - const stateKey = `${isUpdating}:${problemCount}-${validationErrors.length}-${invalidRelationships.length}`; + const stateKey = `${isUpdating}:${errorCount}-${validationErrors.length}-${invalidRelationships.length}`; return { stateKey: stateKey, isUpdating: isUpdating, - hasProblems: problemCount > 0, - problemCount: problemCount, + errorCount: errorCount, requestErrors: requestErrors, validationErrors: validationErrors, invalidRelationships: invalidRelationships, - getProblemCount: getProblemCount, + getErrorCount: getErrorCount, + warnings: problemsResult.warnings ?? [], + hasProblems: errorCount > 0 || (problemsResult.warnings ?? []).length > 0, }; } diff --git a/playground/src/services/validation.ts b/playground/src/services/validation.ts index 3b3eb18..5b5c789 100644 --- a/playground/src/services/validation.ts +++ b/playground/src/services/validation.ts @@ -1,4 +1,4 @@ -import { DeveloperError } from '@code/spicedb-common/src/protodevdefs/developer/v1/developer'; +import { DeveloperError, DeveloperWarning } from '@code/spicedb-common/src/protodevdefs/developer/v1/developer'; import { DeveloperService } from '@code/spicedb-common/src/services/developerservice'; import { useAlert } from '@code/playground-ui/src/AlertProvider'; import { useGoogleAnalytics } from '@code/playground-ui/src/GoogleAnalyticsHook'; @@ -26,6 +26,7 @@ export interface ValidationState { runError?: string | undefined; validationDatastoreIndex?: string; lastRun?: Date; + warnings?: DeveloperWarning[]; } export interface ValidationResult { @@ -94,6 +95,11 @@ function runValidation( validationDevErrors.push(...result.validationErrors); }); + let warnings: DeveloperWarning[] = []; + request.schemaWarnings((result) => { + warnings = result.warnings; + }) + const response = request.execute(); if (response.internalError) { setValidationState({ @@ -101,6 +107,7 @@ function runValidation( validationDatastoreIndex: datastoreIndex, runError: response.internalError, lastRun: new Date(), + warnings: warnings, }); return; } @@ -119,6 +126,7 @@ function runValidation( validationErrors: validationDevErrors, validationDatastoreIndex: datastoreIndex, lastRun: new Date(), + warnings: warnings, }); callback(validated, { updatedValidationYaml: updatedValidationYaml }); } diff --git a/spicedb-common/package.json b/spicedb-common/package.json index ab32678..2fd37c7 100644 --- a/spicedb-common/package.json +++ b/spicedb-common/package.json @@ -5,7 +5,8 @@ "scripts": { "test": "react-scripts test", "lint": "../node_modules/.bin/eslint src", - "lint-fix": "../node_modules/.bin/eslint --fix src" + "lint-fix": "../node_modules/.bin/eslint --fix src", + "regen-protos": "./buf.dev.gen.yaml" }, "author": "", "eslintConfig": { diff --git a/spicedb-common/src/protodevdefs/core/v1/core.ts b/spicedb-common/src/protodevdefs/core/v1/core.ts index e3c7b1d..8ee0e06 100644 --- a/spicedb-common/src/protodevdefs/core/v1/core.ts +++ b/spicedb-common/src/protodevdefs/core/v1/core.ts @@ -496,6 +496,14 @@ export interface ReachabilityEntrypoint { * @generated from protobuf field: string tupleset_relation = 5; */ tuplesetRelation: string; + /** + * * + * computed_userset_relation is the name of the computed userset relation on the ComputedUserset + * this entrypoint represents, if applicable. + * + * @generated from protobuf field: string computed_userset_relation = 6; + */ + computedUsersetRelation: string; } /** * @generated from protobuf enum core.v1.ReachabilityEntrypoint.ReachabilityEntrypointKind @@ -702,6 +710,12 @@ export interface SetOperation_Child { * @generated from protobuf field: core.v1.UsersetRewrite userset_rewrite = 4; */ usersetRewrite: UsersetRewrite; + } | { + oneofKind: "functionedTupleToUserset"; + /** + * @generated from protobuf field: core.v1.FunctionedTupleToUserset functioned_tuple_to_userset = 8; + */ + functionedTupleToUserset: FunctionedTupleToUserset; } | { oneofKind: "Nil"; /** @@ -762,6 +776,53 @@ export interface TupleToUserset_Tupleset { */ relation: string; } +/** + * @generated from protobuf message core.v1.FunctionedTupleToUserset + */ +export interface FunctionedTupleToUserset { + /** + * @generated from protobuf field: core.v1.FunctionedTupleToUserset.Function function = 1; + */ + function: FunctionedTupleToUserset_Function; + /** + * @generated from protobuf field: core.v1.FunctionedTupleToUserset.Tupleset tupleset = 2; + */ + tupleset?: FunctionedTupleToUserset_Tupleset; + /** + * @generated from protobuf field: core.v1.ComputedUserset computed_userset = 3; + */ + computedUserset?: ComputedUserset; + /** + * @generated from protobuf field: core.v1.SourcePosition source_position = 4; + */ + sourcePosition?: SourcePosition; +} +/** + * @generated from protobuf message core.v1.FunctionedTupleToUserset.Tupleset + */ +export interface FunctionedTupleToUserset_Tupleset { + /** + * @generated from protobuf field: string relation = 1; + */ + relation: string; +} +/** + * @generated from protobuf enum core.v1.FunctionedTupleToUserset.Function + */ +export enum FunctionedTupleToUserset_Function { + /** + * @generated from protobuf enum value: FUNCTION_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + /** + * @generated from protobuf enum value: FUNCTION_ANY = 1; + */ + ANY = 1, + /** + * @generated from protobuf enum value: FUNCTION_ALL = 2; + */ + ALL = 2 +} /** * @generated from protobuf message core.v1.ComputedUserset */ @@ -862,6 +923,75 @@ export enum CaveatOperation_Operation { */ NOT = 3 } +/** + * @generated from protobuf message core.v1.RelationshipFilter + */ +export interface RelationshipFilter { + /** + * resource_type is the *optional* resource type of the relationship. + * NOTE: It is not prefixed with "optional_" for legacy compatibility. + * + * @generated from protobuf field: string resource_type = 1; + */ + resourceType: string; + /** + * optional_resource_id is the *optional* resource ID of the relationship. + * If specified, optional_resource_id_prefix cannot be specified. + * + * @generated from protobuf field: string optional_resource_id = 2; + */ + optionalResourceId: string; + /** + * optional_resource_id_prefix is the *optional* prefix for the resource ID of the relationship. + * If specified, optional_resource_id cannot be specified. + * + * @generated from protobuf field: string optional_resource_id_prefix = 5; + */ + optionalResourceIdPrefix: string; + /** + * relation is the *optional* relation of the relationship. + * + * @generated from protobuf field: string optional_relation = 3; + */ + optionalRelation: string; + /** + * optional_subject_filter is the optional filter for the subjects of the relationships. + * + * @generated from protobuf field: core.v1.SubjectFilter optional_subject_filter = 4; + */ + optionalSubjectFilter?: SubjectFilter; +} +/** + * SubjectFilter specifies a filter on the subject of a relationship. + * + * subject_type is required and all other fields are optional, and will not + * impose any additional requirements if left unspecified. + * + * @generated from protobuf message core.v1.SubjectFilter + */ +export interface SubjectFilter { + /** + * @generated from protobuf field: string subject_type = 1; + */ + subjectType: string; + /** + * @generated from protobuf field: string optional_subject_id = 2; + */ + optionalSubjectId: string; + /** + * @generated from protobuf field: core.v1.SubjectFilter.RelationFilter optional_relation = 3; + */ + optionalRelation?: SubjectFilter_RelationFilter; +} +/** + * @generated from protobuf message core.v1.SubjectFilter.RelationFilter + */ +export interface SubjectFilter_RelationFilter { + /** + * @generated from protobuf field: string relation = 1; + */ + relation: string; +} // @generated message type with reflection information, may provide speed optimized methods class RelationTuple$Type extends MessageType { constructor() { @@ -1937,11 +2067,12 @@ class ReachabilityEntrypoint$Type extends MessageType { { no: 1, name: "kind", kind: "enum", T: () => ["core.v1.ReachabilityEntrypoint.ReachabilityEntrypointKind", ReachabilityEntrypoint_ReachabilityEntrypointKind] }, { no: 2, name: "target_relation", kind: "message", T: () => RelationReference }, { no: 4, name: "result_status", kind: "enum", T: () => ["core.v1.ReachabilityEntrypoint.EntrypointResultStatus", ReachabilityEntrypoint_EntrypointResultStatus] }, - { no: 5, name: "tupleset_relation", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + { no: 5, name: "tupleset_relation", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 6, name: "computed_userset_relation", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } create(value?: PartialMessage): ReachabilityEntrypoint { - const message = { kind: 0, resultStatus: 0, tuplesetRelation: "" }; + const message = { kind: 0, resultStatus: 0, tuplesetRelation: "", computedUsersetRelation: "" }; globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); if (value !== undefined) reflectionMergePartial(this, message, value); @@ -1964,6 +2095,9 @@ class ReachabilityEntrypoint$Type extends MessageType { case /* string tupleset_relation */ 5: message.tuplesetRelation = reader.string(); break; + case /* string computed_userset_relation */ 6: + message.computedUsersetRelation = reader.string(); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -1988,6 +2122,9 @@ class ReachabilityEntrypoint$Type extends MessageType { /* string tupleset_relation = 5; */ if (message.tuplesetRelation !== "") writer.tag(5, WireType.LengthDelimited).string(message.tuplesetRelation); + /* string computed_userset_relation = 6; */ + if (message.computedUsersetRelation !== "") + writer.tag(6, WireType.LengthDelimited).string(message.computedUsersetRelation); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -2331,6 +2468,7 @@ class SetOperation_Child$Type extends MessageType { { no: 2, name: "computed_userset", kind: "message", oneof: "childType", T: () => ComputedUserset, options: { "validate.rules": { message: { required: true } } } }, { no: 3, name: "tuple_to_userset", kind: "message", oneof: "childType", T: () => TupleToUserset, options: { "validate.rules": { message: { required: true } } } }, { no: 4, name: "userset_rewrite", kind: "message", oneof: "childType", T: () => UsersetRewrite, options: { "validate.rules": { message: { required: true } } } }, + { no: 8, name: "functioned_tuple_to_userset", kind: "message", oneof: "childType", T: () => FunctionedTupleToUserset, options: { "validate.rules": { message: { required: true } } } }, { no: 6, name: "_nil", kind: "message", oneof: "childType", T: () => SetOperation_Child_Nil }, { no: 5, name: "source_position", kind: "message", T: () => SourcePosition }, { no: 7, name: "operation_path", kind: "scalar", repeat: 1 /*RepeatType.PACKED*/, T: 13 /*ScalarType.UINT32*/ } @@ -2372,6 +2510,12 @@ class SetOperation_Child$Type extends MessageType { usersetRewrite: UsersetRewrite.internalBinaryRead(reader, reader.uint32(), options, (message.childType as any).usersetRewrite) }; break; + case /* core.v1.FunctionedTupleToUserset functioned_tuple_to_userset */ 8: + message.childType = { + oneofKind: "functionedTupleToUserset", + functionedTupleToUserset: FunctionedTupleToUserset.internalBinaryRead(reader, reader.uint32(), options, (message.childType as any).functionedTupleToUserset) + }; + break; case /* core.v1.SetOperation.Child.Nil _nil */ 6: message.childType = { oneofKind: "Nil", @@ -2412,6 +2556,9 @@ class SetOperation_Child$Type extends MessageType { /* core.v1.UsersetRewrite userset_rewrite = 4; */ if (message.childType.oneofKind === "usersetRewrite") UsersetRewrite.internalBinaryWrite(message.childType.usersetRewrite, writer.tag(4, WireType.LengthDelimited).fork(), options).join(); + /* core.v1.FunctionedTupleToUserset functioned_tuple_to_userset = 8; */ + if (message.childType.oneofKind === "functionedTupleToUserset") + FunctionedTupleToUserset.internalBinaryWrite(message.childType.functionedTupleToUserset, writer.tag(8, WireType.LengthDelimited).fork(), options).join(); /* core.v1.SetOperation.Child.Nil _nil = 6; */ if (message.childType.oneofKind === "Nil") SetOperation_Child_Nil.internalBinaryWrite(message.childType.Nil, writer.tag(6, WireType.LengthDelimited).fork(), options).join(); @@ -2596,6 +2743,121 @@ class TupleToUserset_Tupleset$Type extends MessageType */ export const TupleToUserset_Tupleset = new TupleToUserset_Tupleset$Type(); // @generated message type with reflection information, may provide speed optimized methods +class FunctionedTupleToUserset$Type extends MessageType { + constructor() { + super("core.v1.FunctionedTupleToUserset", [ + { no: 1, name: "function", kind: "enum", T: () => ["core.v1.FunctionedTupleToUserset.Function", FunctionedTupleToUserset_Function, "FUNCTION_"], options: { "validate.rules": { enum: { definedOnly: true, notIn: [0] } } } }, + { no: 2, name: "tupleset", kind: "message", T: () => FunctionedTupleToUserset_Tupleset, options: { "validate.rules": { message: { required: true } } } }, + { no: 3, name: "computed_userset", kind: "message", T: () => ComputedUserset, options: { "validate.rules": { message: { required: true } } } }, + { no: 4, name: "source_position", kind: "message", T: () => SourcePosition } + ]); + } + create(value?: PartialMessage): FunctionedTupleToUserset { + const message = { function: 0 }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: FunctionedTupleToUserset): FunctionedTupleToUserset { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* core.v1.FunctionedTupleToUserset.Function function */ 1: + message.function = reader.int32(); + break; + case /* core.v1.FunctionedTupleToUserset.Tupleset tupleset */ 2: + message.tupleset = FunctionedTupleToUserset_Tupleset.internalBinaryRead(reader, reader.uint32(), options, message.tupleset); + break; + case /* core.v1.ComputedUserset computed_userset */ 3: + message.computedUserset = ComputedUserset.internalBinaryRead(reader, reader.uint32(), options, message.computedUserset); + break; + case /* core.v1.SourcePosition source_position */ 4: + message.sourcePosition = SourcePosition.internalBinaryRead(reader, reader.uint32(), options, message.sourcePosition); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: FunctionedTupleToUserset, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* core.v1.FunctionedTupleToUserset.Function function = 1; */ + if (message.function !== 0) + writer.tag(1, WireType.Varint).int32(message.function); + /* core.v1.FunctionedTupleToUserset.Tupleset tupleset = 2; */ + if (message.tupleset) + FunctionedTupleToUserset_Tupleset.internalBinaryWrite(message.tupleset, writer.tag(2, WireType.LengthDelimited).fork(), options).join(); + /* core.v1.ComputedUserset computed_userset = 3; */ + if (message.computedUserset) + ComputedUserset.internalBinaryWrite(message.computedUserset, writer.tag(3, WireType.LengthDelimited).fork(), options).join(); + /* core.v1.SourcePosition source_position = 4; */ + if (message.sourcePosition) + SourcePosition.internalBinaryWrite(message.sourcePosition, writer.tag(4, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message core.v1.FunctionedTupleToUserset + */ +export const FunctionedTupleToUserset = new FunctionedTupleToUserset$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class FunctionedTupleToUserset_Tupleset$Type extends MessageType { + constructor() { + super("core.v1.FunctionedTupleToUserset.Tupleset", [ + { no: 1, name: "relation", kind: "scalar", T: 9 /*ScalarType.STRING*/, options: { "validate.rules": { string: { maxBytes: "64", pattern: "^[a-z][a-z0-9_]{1,62}[a-z0-9]$" } } } } + ]); + } + create(value?: PartialMessage): FunctionedTupleToUserset_Tupleset { + const message = { relation: "" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: FunctionedTupleToUserset_Tupleset): FunctionedTupleToUserset_Tupleset { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string relation */ 1: + message.relation = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: FunctionedTupleToUserset_Tupleset, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string relation = 1; */ + if (message.relation !== "") + writer.tag(1, WireType.LengthDelimited).string(message.relation); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message core.v1.FunctionedTupleToUserset.Tupleset + */ +export const FunctionedTupleToUserset_Tupleset = new FunctionedTupleToUserset_Tupleset$Type(); +// @generated message type with reflection information, may provide speed optimized methods class ComputedUserset$Type extends MessageType { constructor() { super("core.v1.ComputedUserset", [ @@ -2824,3 +3086,186 @@ class CaveatOperation$Type extends MessageType { * @generated MessageType for protobuf message core.v1.CaveatOperation */ export const CaveatOperation = new CaveatOperation$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class RelationshipFilter$Type extends MessageType { + constructor() { + super("core.v1.RelationshipFilter", [ + { no: 1, name: "resource_type", kind: "scalar", T: 9 /*ScalarType.STRING*/, options: { "validate.rules": { string: { maxBytes: "128", pattern: "^(([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9])?$" } } } }, + { no: 2, name: "optional_resource_id", kind: "scalar", T: 9 /*ScalarType.STRING*/, options: { "validate.rules": { string: { maxBytes: "1024", pattern: "^([a-zA-Z0-9/_|\\-=+]{1,})?$" } } } }, + { no: 5, name: "optional_resource_id_prefix", kind: "scalar", T: 9 /*ScalarType.STRING*/, options: { "validate.rules": { string: { maxBytes: "1024", pattern: "^([a-zA-Z0-9/_|\\-=+]{1,})?$" } } } }, + { no: 3, name: "optional_relation", kind: "scalar", T: 9 /*ScalarType.STRING*/, options: { "validate.rules": { string: { maxBytes: "64", pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" } } } }, + { no: 4, name: "optional_subject_filter", kind: "message", T: () => SubjectFilter } + ]); + } + create(value?: PartialMessage): RelationshipFilter { + const message = { resourceType: "", optionalResourceId: "", optionalResourceIdPrefix: "", optionalRelation: "" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: RelationshipFilter): RelationshipFilter { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string resource_type */ 1: + message.resourceType = reader.string(); + break; + case /* string optional_resource_id */ 2: + message.optionalResourceId = reader.string(); + break; + case /* string optional_resource_id_prefix */ 5: + message.optionalResourceIdPrefix = reader.string(); + break; + case /* string optional_relation */ 3: + message.optionalRelation = reader.string(); + break; + case /* core.v1.SubjectFilter optional_subject_filter */ 4: + message.optionalSubjectFilter = SubjectFilter.internalBinaryRead(reader, reader.uint32(), options, message.optionalSubjectFilter); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: RelationshipFilter, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string resource_type = 1; */ + if (message.resourceType !== "") + writer.tag(1, WireType.LengthDelimited).string(message.resourceType); + /* string optional_resource_id = 2; */ + if (message.optionalResourceId !== "") + writer.tag(2, WireType.LengthDelimited).string(message.optionalResourceId); + /* string optional_resource_id_prefix = 5; */ + if (message.optionalResourceIdPrefix !== "") + writer.tag(5, WireType.LengthDelimited).string(message.optionalResourceIdPrefix); + /* string optional_relation = 3; */ + if (message.optionalRelation !== "") + writer.tag(3, WireType.LengthDelimited).string(message.optionalRelation); + /* core.v1.SubjectFilter optional_subject_filter = 4; */ + if (message.optionalSubjectFilter) + SubjectFilter.internalBinaryWrite(message.optionalSubjectFilter, writer.tag(4, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message core.v1.RelationshipFilter + */ +export const RelationshipFilter = new RelationshipFilter$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class SubjectFilter$Type extends MessageType { + constructor() { + super("core.v1.SubjectFilter", [ + { no: 1, name: "subject_type", kind: "scalar", T: 9 /*ScalarType.STRING*/, options: { "validate.rules": { string: { maxBytes: "128", pattern: "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)*[a-z][a-z0-9_]{1,62}[a-z0-9]$" } } } }, + { no: 2, name: "optional_subject_id", kind: "scalar", T: 9 /*ScalarType.STRING*/, options: { "validate.rules": { string: { maxBytes: "1024", pattern: "^(([a-zA-Z0-9/_|\\-=+]{1,})|\\*)?$" } } } }, + { no: 3, name: "optional_relation", kind: "message", T: () => SubjectFilter_RelationFilter } + ]); + } + create(value?: PartialMessage): SubjectFilter { + const message = { subjectType: "", optionalSubjectId: "" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: SubjectFilter): SubjectFilter { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string subject_type */ 1: + message.subjectType = reader.string(); + break; + case /* string optional_subject_id */ 2: + message.optionalSubjectId = reader.string(); + break; + case /* core.v1.SubjectFilter.RelationFilter optional_relation */ 3: + message.optionalRelation = SubjectFilter_RelationFilter.internalBinaryRead(reader, reader.uint32(), options, message.optionalRelation); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: SubjectFilter, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string subject_type = 1; */ + if (message.subjectType !== "") + writer.tag(1, WireType.LengthDelimited).string(message.subjectType); + /* string optional_subject_id = 2; */ + if (message.optionalSubjectId !== "") + writer.tag(2, WireType.LengthDelimited).string(message.optionalSubjectId); + /* core.v1.SubjectFilter.RelationFilter optional_relation = 3; */ + if (message.optionalRelation) + SubjectFilter_RelationFilter.internalBinaryWrite(message.optionalRelation, writer.tag(3, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message core.v1.SubjectFilter + */ +export const SubjectFilter = new SubjectFilter$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class SubjectFilter_RelationFilter$Type extends MessageType { + constructor() { + super("core.v1.SubjectFilter.RelationFilter", [ + { no: 1, name: "relation", kind: "scalar", T: 9 /*ScalarType.STRING*/, options: { "validate.rules": { string: { maxBytes: "64", pattern: "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$" } } } } + ]); + } + create(value?: PartialMessage): SubjectFilter_RelationFilter { + const message = { relation: "" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: SubjectFilter_RelationFilter): SubjectFilter_RelationFilter { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string relation */ 1: + message.relation = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: SubjectFilter_RelationFilter, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string relation = 1; */ + if (message.relation !== "") + writer.tag(1, WireType.LengthDelimited).string(message.relation); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message core.v1.SubjectFilter.RelationFilter + */ +export const SubjectFilter_RelationFilter = new SubjectFilter_RelationFilter$Type(); diff --git a/spicedb-common/src/protodevdefs/developer/v1/developer.ts b/spicedb-common/src/protodevdefs/developer/v1/developer.ts index 1b9cab7..8bd489f 100644 --- a/spicedb-common/src/protodevdefs/developer/v1/developer.ts +++ b/spicedb-common/src/protodevdefs/developer/v1/developer.ts @@ -103,6 +103,10 @@ export interface Operation { * @generated from protobuf field: developer.v1.FormatSchemaParameters format_schema_parameters = 4; */ formatSchemaParameters?: FormatSchemaParameters; + /** + * @generated from protobuf field: developer.v1.SchemaWarningsParameters schema_warnings_parameters = 5; + */ + schemaWarningsParameters?: SchemaWarningsParameters; } /** * OperationsResults holds the results for the operations, indexed by the operation. @@ -139,6 +143,41 @@ export interface OperationResult { * @generated from protobuf field: developer.v1.FormatSchemaResult format_schema_result = 4; */ formatSchemaResult?: FormatSchemaResult; + /** + * @generated from protobuf field: developer.v1.SchemaWarningsResult schema_warnings_result = 5; + */ + schemaWarningsResult?: SchemaWarningsResult; +} +/** + * DeveloperWarning represents a single warning raised by the development package. + * + * @generated from protobuf message developer.v1.DeveloperWarning + */ +export interface DeveloperWarning { + /** + * message is the message for the developer warning. + * + * @generated from protobuf field: string message = 1; + */ + message: string; + /** + * line is the 1-indexed line for the developer warning. + * + * @generated from protobuf field: uint32 line = 2; + */ + line: number; + /** + * column is the 1-indexed column on the line for the developer warning. + * + * @generated from protobuf field: uint32 column = 3; + */ + column: number; + /** + * source_code is the source code for the developer warning, if any. + * + * @generated from protobuf field: string source_code = 4; + */ + sourceCode: string; } /** * DeveloperError represents a single error raised by the development package. Unlike an internal @@ -473,6 +512,26 @@ export interface FormatSchemaResult { */ formattedSchema: string; } +/** + * SchemaWarningsParameters are the parameters for a `schemaWarnings` operation. + * + * empty + * + * @generated from protobuf message developer.v1.SchemaWarningsParameters + */ +export interface SchemaWarningsParameters { +} +/** + * SchemaWarningsResult is the result of the `schemaWarnings` operation. + * + * @generated from protobuf message developer.v1.SchemaWarningsResult + */ +export interface SchemaWarningsResult { + /** + * @generated from protobuf field: repeated developer.v1.DeveloperWarning warnings = 1; + */ + warnings: DeveloperWarning[]; +} // @generated message type with reflection information, may provide speed optimized methods class DeveloperRequest$Type extends MessageType { constructor() { @@ -649,7 +708,8 @@ class Operation$Type extends MessageType { { no: 1, name: "check_parameters", kind: "message", T: () => CheckOperationParameters }, { no: 2, name: "assertions_parameters", kind: "message", T: () => RunAssertionsParameters }, { no: 3, name: "validation_parameters", kind: "message", T: () => RunValidationParameters }, - { no: 4, name: "format_schema_parameters", kind: "message", T: () => FormatSchemaParameters } + { no: 4, name: "format_schema_parameters", kind: "message", T: () => FormatSchemaParameters }, + { no: 5, name: "schema_warnings_parameters", kind: "message", T: () => SchemaWarningsParameters } ]); } create(value?: PartialMessage): Operation { @@ -676,6 +736,9 @@ class Operation$Type extends MessageType { case /* developer.v1.FormatSchemaParameters format_schema_parameters */ 4: message.formatSchemaParameters = FormatSchemaParameters.internalBinaryRead(reader, reader.uint32(), options, message.formatSchemaParameters); break; + case /* developer.v1.SchemaWarningsParameters schema_warnings_parameters */ 5: + message.schemaWarningsParameters = SchemaWarningsParameters.internalBinaryRead(reader, reader.uint32(), options, message.schemaWarningsParameters); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -700,6 +763,9 @@ class Operation$Type extends MessageType { /* developer.v1.FormatSchemaParameters format_schema_parameters = 4; */ if (message.formatSchemaParameters) FormatSchemaParameters.internalBinaryWrite(message.formatSchemaParameters, writer.tag(4, WireType.LengthDelimited).fork(), options).join(); + /* developer.v1.SchemaWarningsParameters schema_warnings_parameters = 5; */ + if (message.schemaWarningsParameters) + SchemaWarningsParameters.internalBinaryWrite(message.schemaWarningsParameters, writer.tag(5, WireType.LengthDelimited).fork(), options).join(); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -784,7 +850,8 @@ class OperationResult$Type extends MessageType { { no: 1, name: "check_result", kind: "message", T: () => CheckOperationsResult }, { no: 2, name: "assertions_result", kind: "message", T: () => RunAssertionsResult }, { no: 3, name: "validation_result", kind: "message", T: () => RunValidationResult }, - { no: 4, name: "format_schema_result", kind: "message", T: () => FormatSchemaResult } + { no: 4, name: "format_schema_result", kind: "message", T: () => FormatSchemaResult }, + { no: 5, name: "schema_warnings_result", kind: "message", T: () => SchemaWarningsResult } ]); } create(value?: PartialMessage): OperationResult { @@ -811,6 +878,9 @@ class OperationResult$Type extends MessageType { case /* developer.v1.FormatSchemaResult format_schema_result */ 4: message.formatSchemaResult = FormatSchemaResult.internalBinaryRead(reader, reader.uint32(), options, message.formatSchemaResult); break; + case /* developer.v1.SchemaWarningsResult schema_warnings_result */ 5: + message.schemaWarningsResult = SchemaWarningsResult.internalBinaryRead(reader, reader.uint32(), options, message.schemaWarningsResult); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -835,6 +905,9 @@ class OperationResult$Type extends MessageType { /* developer.v1.FormatSchemaResult format_schema_result = 4; */ if (message.formatSchemaResult) FormatSchemaResult.internalBinaryWrite(message.formatSchemaResult, writer.tag(4, WireType.LengthDelimited).fork(), options).join(); + /* developer.v1.SchemaWarningsResult schema_warnings_result = 5; */ + if (message.schemaWarningsResult) + SchemaWarningsResult.internalBinaryWrite(message.schemaWarningsResult, writer.tag(5, WireType.LengthDelimited).fork(), options).join(); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -846,6 +919,74 @@ class OperationResult$Type extends MessageType { */ export const OperationResult = new OperationResult$Type(); // @generated message type with reflection information, may provide speed optimized methods +class DeveloperWarning$Type extends MessageType { + constructor() { + super("developer.v1.DeveloperWarning", [ + { no: 1, name: "message", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "line", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, + { no: 3, name: "column", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, + { no: 4, name: "source_code", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + ]); + } + create(value?: PartialMessage): DeveloperWarning { + const message = { message: "", line: 0, column: 0, sourceCode: "" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: DeveloperWarning): DeveloperWarning { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string message */ 1: + message.message = reader.string(); + break; + case /* uint32 line */ 2: + message.line = reader.uint32(); + break; + case /* uint32 column */ 3: + message.column = reader.uint32(); + break; + case /* string source_code */ 4: + message.sourceCode = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: DeveloperWarning, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string message = 1; */ + if (message.message !== "") + writer.tag(1, WireType.LengthDelimited).string(message.message); + /* uint32 line = 2; */ + if (message.line !== 0) + writer.tag(2, WireType.Varint).uint32(message.line); + /* uint32 column = 3; */ + if (message.column !== 0) + writer.tag(3, WireType.Varint).uint32(message.column); + /* string source_code = 4; */ + if (message.sourceCode !== "") + writer.tag(4, WireType.LengthDelimited).string(message.sourceCode); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message developer.v1.DeveloperWarning + */ +export const DeveloperWarning = new DeveloperWarning$Type(); +// @generated message type with reflection information, may provide speed optimized methods class DeveloperError$Type extends MessageType { constructor() { super("developer.v1.DeveloperError", [ @@ -1460,3 +1601,76 @@ class FormatSchemaResult$Type extends MessageType { * @generated MessageType for protobuf message developer.v1.FormatSchemaResult */ export const FormatSchemaResult = new FormatSchemaResult$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class SchemaWarningsParameters$Type extends MessageType { + constructor() { + super("developer.v1.SchemaWarningsParameters", []); + } + create(value?: PartialMessage): SchemaWarningsParameters { + const message = {}; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: SchemaWarningsParameters): SchemaWarningsParameters { + return target ?? this.create(); + } + internalBinaryWrite(message: SchemaWarningsParameters, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message developer.v1.SchemaWarningsParameters + */ +export const SchemaWarningsParameters = new SchemaWarningsParameters$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class SchemaWarningsResult$Type extends MessageType { + constructor() { + super("developer.v1.SchemaWarningsResult", [ + { no: 1, name: "warnings", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => DeveloperWarning } + ]); + } + create(value?: PartialMessage): SchemaWarningsResult { + const message = { warnings: [] }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: SchemaWarningsResult): SchemaWarningsResult { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* repeated developer.v1.DeveloperWarning warnings */ 1: + message.warnings.push(DeveloperWarning.internalBinaryRead(reader, reader.uint32(), options)); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: SchemaWarningsResult, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* repeated developer.v1.DeveloperWarning warnings = 1; */ + for (let i = 0; i < message.warnings.length; i++) + DeveloperWarning.internalBinaryWrite(message.warnings[i], writer.tag(1, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message developer.v1.SchemaWarningsResult + */ +export const SchemaWarningsResult = new SchemaWarningsResult$Type(); diff --git a/spicedb-common/src/protodevdefs/dispatch/v1/dispatch.client.ts b/spicedb-common/src/protodevdefs/dispatch/v1/dispatch.client.ts index e19edd2..dba0485 100644 --- a/spicedb-common/src/protodevdefs/dispatch/v1/dispatch.client.ts +++ b/spicedb-common/src/protodevdefs/dispatch/v1/dispatch.client.ts @@ -4,6 +4,8 @@ import type { RpcTransport } from "@protobuf-ts/runtime-rpc"; import type { ServiceInfo } from "@protobuf-ts/runtime-rpc"; import { DispatchService } from "./dispatch"; +import type { DispatchLookupResources2Response } from "./dispatch"; +import type { DispatchLookupResources2Request } from "./dispatch"; import type { DispatchLookupSubjectsResponse } from "./dispatch"; import type { DispatchLookupSubjectsRequest } from "./dispatch"; import type { DispatchLookupResourcesResponse } from "./dispatch"; @@ -42,6 +44,10 @@ export interface IDispatchServiceClient { * @generated from protobuf rpc: DispatchLookupSubjects(dispatch.v1.DispatchLookupSubjectsRequest) returns (stream dispatch.v1.DispatchLookupSubjectsResponse); */ dispatchLookupSubjects(input: DispatchLookupSubjectsRequest, options?: RpcOptions): ServerStreamingCall; + /** + * @generated from protobuf rpc: DispatchLookupResources2(dispatch.v1.DispatchLookupResources2Request) returns (stream dispatch.v1.DispatchLookupResources2Response); + */ + dispatchLookupResources2(input: DispatchLookupResources2Request, options?: RpcOptions): ServerStreamingCall; } /** * @generated from protobuf service dispatch.v1.DispatchService @@ -87,4 +93,11 @@ export class DispatchServiceClient implements IDispatchServiceClient, ServiceInf const method = this.methods[4], opt = this._transport.mergeOptions(options); return stackIntercept("serverStreaming", this._transport, method, opt, input); } + /** + * @generated from protobuf rpc: DispatchLookupResources2(dispatch.v1.DispatchLookupResources2Request) returns (stream dispatch.v1.DispatchLookupResources2Response); + */ + dispatchLookupResources2(input: DispatchLookupResources2Request, options?: RpcOptions): ServerStreamingCall { + const method = this.methods[5], opt = this._transport.mergeOptions(options); + return stackIntercept("serverStreaming", this._transport, method, opt, input); + } } diff --git a/spicedb-common/src/protodevdefs/dispatch/v1/dispatch.ts b/spicedb-common/src/protodevdefs/dispatch/v1/dispatch.ts index 702200c..b918f68 100644 --- a/spicedb-common/src/protodevdefs/dispatch/v1/dispatch.ts +++ b/spicedb-common/src/protodevdefs/dispatch/v1/dispatch.ts @@ -46,6 +46,17 @@ export interface DispatchCheckRequest { * @generated from protobuf field: dispatch.v1.DispatchCheckRequest.DebugSetting debug = 6; */ debug: DispatchCheckRequest_DebugSetting; + /** + * * + * check_hints are hints provided to the check call to help the resolver optimize the check + * by skipping calculations for the provided checks. The string key is the fully qualified + * "relationtuple"-string for the problem, e.g. `document:example#relation@user:someuser`. + * It is up to the caller to *ensure* that the hints provided are correct; if incorrect, + * the resolver may return incorrect results which will in turn be cached. + * + * @generated from protobuf field: repeated dispatch.v1.CheckHint check_hints = 7; + */ + checkHints: CheckHint[]; } /** * @generated from protobuf enum dispatch.v1.DispatchCheckRequest.DebugSetting @@ -77,6 +88,27 @@ export enum DispatchCheckRequest_ResultsSetting { */ ALLOW_SINGLE_RESULT = 1 } +/** + * @generated from protobuf message dispatch.v1.CheckHint + */ +export interface CheckHint { + /** + * @generated from protobuf field: core.v1.ObjectAndRelation resource = 1; + */ + resource?: ObjectAndRelation; + /** + * @generated from protobuf field: core.v1.ObjectAndRelation subject = 2; + */ + subject?: ObjectAndRelation; + /** + * @generated from protobuf field: string ttu_computed_userset_relation = 3; + */ + ttuComputedUsersetRelation: string; + /** + * @generated from protobuf field: dispatch.v1.ResourceCheckResult result = 4; + */ + result?: ResourceCheckResult; +} /** * @generated from protobuf message dispatch.v1.DispatchCheckResponse */ @@ -186,6 +218,77 @@ export interface Cursor { */ dispatchVersion: number; } +/** + * @generated from protobuf message dispatch.v1.DispatchLookupResources2Request + */ +export interface DispatchLookupResources2Request { + /** + * @generated from protobuf field: dispatch.v1.ResolverMeta metadata = 1; + */ + metadata?: ResolverMeta; + /** + * @generated from protobuf field: core.v1.RelationReference resource_relation = 2; + */ + resourceRelation?: RelationReference; + /** + * @generated from protobuf field: core.v1.RelationReference subject_relation = 3; + */ + subjectRelation?: RelationReference; + /** + * @generated from protobuf field: repeated string subject_ids = 4; + */ + subjectIds: string[]; + /** + * @generated from protobuf field: core.v1.ObjectAndRelation terminal_subject = 5; + */ + terminalSubject?: ObjectAndRelation; + /** + * @generated from protobuf field: google.protobuf.Struct context = 6; + */ + context?: Struct; + /** + * @generated from protobuf field: dispatch.v1.Cursor optional_cursor = 7; + */ + optionalCursor?: Cursor; + /** + * @generated from protobuf field: uint32 optional_limit = 8; + */ + optionalLimit: number; +} +/** + * @generated from protobuf message dispatch.v1.PossibleResource + */ +export interface PossibleResource { + /** + * @generated from protobuf field: string resource_id = 1; + */ + resourceId: string; + /** + * @generated from protobuf field: repeated string for_subject_ids = 2; + */ + forSubjectIds: string[]; + /** + * @generated from protobuf field: repeated string missing_context_params = 3; + */ + missingContextParams: string[]; +} +/** + * @generated from protobuf message dispatch.v1.DispatchLookupResources2Response + */ +export interface DispatchLookupResources2Response { + /** + * @generated from protobuf field: dispatch.v1.PossibleResource resource = 1; + */ + resource?: PossibleResource; + /** + * @generated from protobuf field: dispatch.v1.ResponseMeta metadata = 2; + */ + metadata?: ResponseMeta; + /** + * @generated from protobuf field: dispatch.v1.Cursor after_response_cursor = 3; + */ + afterResponseCursor?: Cursor; +} /** * @generated from protobuf message dispatch.v1.DispatchReachableResourcesRequest */ @@ -434,6 +537,15 @@ export interface ResolverMeta { * @generated from protobuf field: uint32 depth_remaining = 2; */ depthRemaining: number; + /** + * @deprecated + * @generated from protobuf field: string request_id = 3 [deprecated = true]; + */ + requestId: string; + /** + * @generated from protobuf field: bytes traversal_bloom = 4; + */ + traversalBloom: Uint8Array; } /** * @generated from protobuf message dispatch.v1.ResponseMeta @@ -522,11 +634,12 @@ class DispatchCheckRequest$Type extends MessageType { { no: 3, name: "resource_ids", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ }, { no: 4, name: "subject", kind: "message", T: () => ObjectAndRelation, options: { "validate.rules": { message: { required: true } } } }, { no: 5, name: "results_setting", kind: "enum", T: () => ["dispatch.v1.DispatchCheckRequest.ResultsSetting", DispatchCheckRequest_ResultsSetting] }, - { no: 6, name: "debug", kind: "enum", T: () => ["dispatch.v1.DispatchCheckRequest.DebugSetting", DispatchCheckRequest_DebugSetting] } + { no: 6, name: "debug", kind: "enum", T: () => ["dispatch.v1.DispatchCheckRequest.DebugSetting", DispatchCheckRequest_DebugSetting] }, + { no: 7, name: "check_hints", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => CheckHint } ]); } create(value?: PartialMessage): DispatchCheckRequest { - const message = { resourceIds: [], resultsSetting: 0, debug: 0 }; + const message = { resourceIds: [], resultsSetting: 0, debug: 0, checkHints: [] }; globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); if (value !== undefined) reflectionMergePartial(this, message, value); @@ -555,6 +668,9 @@ class DispatchCheckRequest$Type extends MessageType { case /* dispatch.v1.DispatchCheckRequest.DebugSetting debug */ 6: message.debug = reader.int32(); break; + case /* repeated dispatch.v1.CheckHint check_hints */ 7: + message.checkHints.push(CheckHint.internalBinaryRead(reader, reader.uint32(), options)); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -585,6 +701,9 @@ class DispatchCheckRequest$Type extends MessageType { /* dispatch.v1.DispatchCheckRequest.DebugSetting debug = 6; */ if (message.debug !== 0) writer.tag(6, WireType.Varint).int32(message.debug); + /* repeated dispatch.v1.CheckHint check_hints = 7; */ + for (let i = 0; i < message.checkHints.length; i++) + CheckHint.internalBinaryWrite(message.checkHints[i], writer.tag(7, WireType.LengthDelimited).fork(), options).join(); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -596,6 +715,74 @@ class DispatchCheckRequest$Type extends MessageType { */ export const DispatchCheckRequest = new DispatchCheckRequest$Type(); // @generated message type with reflection information, may provide speed optimized methods +class CheckHint$Type extends MessageType { + constructor() { + super("dispatch.v1.CheckHint", [ + { no: 1, name: "resource", kind: "message", T: () => ObjectAndRelation }, + { no: 2, name: "subject", kind: "message", T: () => ObjectAndRelation }, + { no: 3, name: "ttu_computed_userset_relation", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 4, name: "result", kind: "message", T: () => ResourceCheckResult } + ]); + } + create(value?: PartialMessage): CheckHint { + const message = { ttuComputedUsersetRelation: "" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: CheckHint): CheckHint { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* core.v1.ObjectAndRelation resource */ 1: + message.resource = ObjectAndRelation.internalBinaryRead(reader, reader.uint32(), options, message.resource); + break; + case /* core.v1.ObjectAndRelation subject */ 2: + message.subject = ObjectAndRelation.internalBinaryRead(reader, reader.uint32(), options, message.subject); + break; + case /* string ttu_computed_userset_relation */ 3: + message.ttuComputedUsersetRelation = reader.string(); + break; + case /* dispatch.v1.ResourceCheckResult result */ 4: + message.result = ResourceCheckResult.internalBinaryRead(reader, reader.uint32(), options, message.result); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: CheckHint, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* core.v1.ObjectAndRelation resource = 1; */ + if (message.resource) + ObjectAndRelation.internalBinaryWrite(message.resource, writer.tag(1, WireType.LengthDelimited).fork(), options).join(); + /* core.v1.ObjectAndRelation subject = 2; */ + if (message.subject) + ObjectAndRelation.internalBinaryWrite(message.subject, writer.tag(2, WireType.LengthDelimited).fork(), options).join(); + /* string ttu_computed_userset_relation = 3; */ + if (message.ttuComputedUsersetRelation !== "") + writer.tag(3, WireType.LengthDelimited).string(message.ttuComputedUsersetRelation); + /* dispatch.v1.ResourceCheckResult result = 4; */ + if (message.result) + ResourceCheckResult.internalBinaryWrite(message.result, writer.tag(4, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message dispatch.v1.CheckHint + */ +export const CheckHint = new CheckHint$Type(); +// @generated message type with reflection information, may provide speed optimized methods class DispatchCheckResponse$Type extends MessageType { constructor() { super("dispatch.v1.DispatchCheckResponse", [ @@ -900,6 +1087,224 @@ class Cursor$Type extends MessageType { */ export const Cursor = new Cursor$Type(); // @generated message type with reflection information, may provide speed optimized methods +class DispatchLookupResources2Request$Type extends MessageType { + constructor() { + super("dispatch.v1.DispatchLookupResources2Request", [ + { no: 1, name: "metadata", kind: "message", T: () => ResolverMeta, options: { "validate.rules": { message: { required: true } } } }, + { no: 2, name: "resource_relation", kind: "message", T: () => RelationReference, options: { "validate.rules": { message: { required: true } } } }, + { no: 3, name: "subject_relation", kind: "message", T: () => RelationReference, options: { "validate.rules": { message: { required: true } } } }, + { no: 4, name: "subject_ids", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ }, + { no: 5, name: "terminal_subject", kind: "message", T: () => ObjectAndRelation, options: { "validate.rules": { message: { required: true } } } }, + { no: 6, name: "context", kind: "message", T: () => Struct }, + { no: 7, name: "optional_cursor", kind: "message", T: () => Cursor }, + { no: 8, name: "optional_limit", kind: "scalar", T: 13 /*ScalarType.UINT32*/ } + ]); + } + create(value?: PartialMessage): DispatchLookupResources2Request { + const message = { subjectIds: [], optionalLimit: 0 }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: DispatchLookupResources2Request): DispatchLookupResources2Request { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* dispatch.v1.ResolverMeta metadata */ 1: + message.metadata = ResolverMeta.internalBinaryRead(reader, reader.uint32(), options, message.metadata); + break; + case /* core.v1.RelationReference resource_relation */ 2: + message.resourceRelation = RelationReference.internalBinaryRead(reader, reader.uint32(), options, message.resourceRelation); + break; + case /* core.v1.RelationReference subject_relation */ 3: + message.subjectRelation = RelationReference.internalBinaryRead(reader, reader.uint32(), options, message.subjectRelation); + break; + case /* repeated string subject_ids */ 4: + message.subjectIds.push(reader.string()); + break; + case /* core.v1.ObjectAndRelation terminal_subject */ 5: + message.terminalSubject = ObjectAndRelation.internalBinaryRead(reader, reader.uint32(), options, message.terminalSubject); + break; + case /* google.protobuf.Struct context */ 6: + message.context = Struct.internalBinaryRead(reader, reader.uint32(), options, message.context); + break; + case /* dispatch.v1.Cursor optional_cursor */ 7: + message.optionalCursor = Cursor.internalBinaryRead(reader, reader.uint32(), options, message.optionalCursor); + break; + case /* uint32 optional_limit */ 8: + message.optionalLimit = reader.uint32(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: DispatchLookupResources2Request, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* dispatch.v1.ResolverMeta metadata = 1; */ + if (message.metadata) + ResolverMeta.internalBinaryWrite(message.metadata, writer.tag(1, WireType.LengthDelimited).fork(), options).join(); + /* core.v1.RelationReference resource_relation = 2; */ + if (message.resourceRelation) + RelationReference.internalBinaryWrite(message.resourceRelation, writer.tag(2, WireType.LengthDelimited).fork(), options).join(); + /* core.v1.RelationReference subject_relation = 3; */ + if (message.subjectRelation) + RelationReference.internalBinaryWrite(message.subjectRelation, writer.tag(3, WireType.LengthDelimited).fork(), options).join(); + /* repeated string subject_ids = 4; */ + for (let i = 0; i < message.subjectIds.length; i++) + writer.tag(4, WireType.LengthDelimited).string(message.subjectIds[i]); + /* core.v1.ObjectAndRelation terminal_subject = 5; */ + if (message.terminalSubject) + ObjectAndRelation.internalBinaryWrite(message.terminalSubject, writer.tag(5, WireType.LengthDelimited).fork(), options).join(); + /* google.protobuf.Struct context = 6; */ + if (message.context) + Struct.internalBinaryWrite(message.context, writer.tag(6, WireType.LengthDelimited).fork(), options).join(); + /* dispatch.v1.Cursor optional_cursor = 7; */ + if (message.optionalCursor) + Cursor.internalBinaryWrite(message.optionalCursor, writer.tag(7, WireType.LengthDelimited).fork(), options).join(); + /* uint32 optional_limit = 8; */ + if (message.optionalLimit !== 0) + writer.tag(8, WireType.Varint).uint32(message.optionalLimit); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message dispatch.v1.DispatchLookupResources2Request + */ +export const DispatchLookupResources2Request = new DispatchLookupResources2Request$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class PossibleResource$Type extends MessageType { + constructor() { + super("dispatch.v1.PossibleResource", [ + { no: 1, name: "resource_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "for_subject_ids", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "missing_context_params", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ } + ]); + } + create(value?: PartialMessage): PossibleResource { + const message = { resourceId: "", forSubjectIds: [], missingContextParams: [] }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: PossibleResource): PossibleResource { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string resource_id */ 1: + message.resourceId = reader.string(); + break; + case /* repeated string for_subject_ids */ 2: + message.forSubjectIds.push(reader.string()); + break; + case /* repeated string missing_context_params */ 3: + message.missingContextParams.push(reader.string()); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: PossibleResource, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string resource_id = 1; */ + if (message.resourceId !== "") + writer.tag(1, WireType.LengthDelimited).string(message.resourceId); + /* repeated string for_subject_ids = 2; */ + for (let i = 0; i < message.forSubjectIds.length; i++) + writer.tag(2, WireType.LengthDelimited).string(message.forSubjectIds[i]); + /* repeated string missing_context_params = 3; */ + for (let i = 0; i < message.missingContextParams.length; i++) + writer.tag(3, WireType.LengthDelimited).string(message.missingContextParams[i]); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message dispatch.v1.PossibleResource + */ +export const PossibleResource = new PossibleResource$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class DispatchLookupResources2Response$Type extends MessageType { + constructor() { + super("dispatch.v1.DispatchLookupResources2Response", [ + { no: 1, name: "resource", kind: "message", T: () => PossibleResource }, + { no: 2, name: "metadata", kind: "message", T: () => ResponseMeta }, + { no: 3, name: "after_response_cursor", kind: "message", T: () => Cursor } + ]); + } + create(value?: PartialMessage): DispatchLookupResources2Response { + const message = {}; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: DispatchLookupResources2Response): DispatchLookupResources2Response { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* dispatch.v1.PossibleResource resource */ 1: + message.resource = PossibleResource.internalBinaryRead(reader, reader.uint32(), options, message.resource); + break; + case /* dispatch.v1.ResponseMeta metadata */ 2: + message.metadata = ResponseMeta.internalBinaryRead(reader, reader.uint32(), options, message.metadata); + break; + case /* dispatch.v1.Cursor after_response_cursor */ 3: + message.afterResponseCursor = Cursor.internalBinaryRead(reader, reader.uint32(), options, message.afterResponseCursor); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: DispatchLookupResources2Response, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* dispatch.v1.PossibleResource resource = 1; */ + if (message.resource) + PossibleResource.internalBinaryWrite(message.resource, writer.tag(1, WireType.LengthDelimited).fork(), options).join(); + /* dispatch.v1.ResponseMeta metadata = 2; */ + if (message.metadata) + ResponseMeta.internalBinaryWrite(message.metadata, writer.tag(2, WireType.LengthDelimited).fork(), options).join(); + /* dispatch.v1.Cursor after_response_cursor = 3; */ + if (message.afterResponseCursor) + Cursor.internalBinaryWrite(message.afterResponseCursor, writer.tag(3, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message dispatch.v1.DispatchLookupResources2Response + */ +export const DispatchLookupResources2Response = new DispatchLookupResources2Response$Type(); +// @generated message type with reflection information, may provide speed optimized methods class DispatchReachableResourcesRequest$Type extends MessageType { constructor() { super("dispatch.v1.DispatchReachableResourcesRequest", [ @@ -1562,11 +1967,13 @@ class ResolverMeta$Type extends MessageType { constructor() { super("dispatch.v1.ResolverMeta", [ { no: 1, name: "at_revision", kind: "scalar", T: 9 /*ScalarType.STRING*/, options: { "validate.rules": { string: { maxBytes: "1024" } } } }, - { no: 2, name: "depth_remaining", kind: "scalar", T: 13 /*ScalarType.UINT32*/, options: { "validate.rules": { uint32: { gt: 0 } } } } + { no: 2, name: "depth_remaining", kind: "scalar", T: 13 /*ScalarType.UINT32*/, options: { "validate.rules": { uint32: { gt: 0 } } } }, + { no: 3, name: "request_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 4, name: "traversal_bloom", kind: "scalar", T: 12 /*ScalarType.BYTES*/, options: { "validate.rules": { bytes: { maxLen: "1024" } } } } ]); } create(value?: PartialMessage): ResolverMeta { - const message = { atRevision: "", depthRemaining: 0 }; + const message = { atRevision: "", depthRemaining: 0, requestId: "", traversalBloom: new Uint8Array(0) }; globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); if (value !== undefined) reflectionMergePartial(this, message, value); @@ -1583,6 +1990,12 @@ class ResolverMeta$Type extends MessageType { case /* uint32 depth_remaining */ 2: message.depthRemaining = reader.uint32(); break; + case /* string request_id = 3 [deprecated = true];*/ 3: + message.requestId = reader.string(); + break; + case /* bytes traversal_bloom */ 4: + message.traversalBloom = reader.bytes(); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -1601,6 +2014,12 @@ class ResolverMeta$Type extends MessageType { /* uint32 depth_remaining = 2; */ if (message.depthRemaining !== 0) writer.tag(2, WireType.Varint).uint32(message.depthRemaining); + /* string request_id = 3 [deprecated = true]; */ + if (message.requestId !== "") + writer.tag(3, WireType.LengthDelimited).string(message.requestId); + /* bytes traversal_bloom = 4; */ + if (message.traversalBloom.length) + writer.tag(4, WireType.LengthDelimited).bytes(message.traversalBloom); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1836,5 +2255,6 @@ export const DispatchService = new ServiceType("dispatch.v1.DispatchService", [ { name: "DispatchExpand", options: {}, I: DispatchExpandRequest, O: DispatchExpandResponse }, { name: "DispatchReachableResources", serverStreaming: true, options: {}, I: DispatchReachableResourcesRequest, O: DispatchReachableResourcesResponse }, { name: "DispatchLookupResources", serverStreaming: true, options: {}, I: DispatchLookupResourcesRequest, O: DispatchLookupResourcesResponse }, - { name: "DispatchLookupSubjects", serverStreaming: true, options: {}, I: DispatchLookupSubjectsRequest, O: DispatchLookupSubjectsResponse } + { name: "DispatchLookupSubjects", serverStreaming: true, options: {}, I: DispatchLookupSubjectsRequest, O: DispatchLookupSubjectsResponse }, + { name: "DispatchLookupResources2", serverStreaming: true, options: {}, I: DispatchLookupResources2Request, O: DispatchLookupResources2Response } ]); diff --git a/spicedb-common/src/services/developerservice.ts b/spicedb-common/src/services/developerservice.ts index b1fc146..d849fb4 100644 --- a/spicedb-common/src/services/developerservice.ts +++ b/spicedb-common/src/services/developerservice.ts @@ -14,6 +14,8 @@ import { RunAssertionsResult, RunValidationParameters, RunValidationResult, + SchemaWarningsParameters, + SchemaWarningsResult, } from '../protodevdefs/developer/v1/developer'; import wasmConfig from '../../wasm-config.json'; @@ -91,6 +93,13 @@ type OperationAndCallback = formatSchemaParameters: FormatSchemaParameters; }; callback: ResultExtractor; + } + | { + operation: 'schemaWarnings'; + parameters: { + schemaWarningsParameters: SchemaWarningsParameters; + }; + callback: ResultExtractor; }; /** @@ -106,6 +115,22 @@ class DeveloperServiceRequest { this.relationships = parseRelationships(relationshipsString); } + /** + * schemaWarnings returns the request's schema's warnings, if any. + */ + public schemaWarnings(callback: DevServiceCallback) { + this.operations.push({ + operation: 'schemaWarnings', + parameters: { + schemaWarningsParameters: {}, + }, + callback: (result: OperationResult) => { + callback(result.schemaWarningsResult!); + }, + }); + } + + /** * formatSchema returns the request's schema formatted. */ diff --git a/spicedb-common/wasm-config.json b/spicedb-common/wasm-config.json index da8ad7c..ab7dddb 100644 --- a/spicedb-common/wasm-config.json +++ b/spicedb-common/wasm-config.json @@ -1,4 +1,4 @@ { - "spicedb": "v1.29.5", - "zed": "v0.17.1" + "spicedb": "1779380929f43c9faf048351557fa6a22be56c94", + "zed": "v0.20.0" } diff --git a/wasm/main.wasm b/wasm/main.wasm index 990025b..86c657b 100755 --- a/wasm/main.wasm +++ b/wasm/main.wasm @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae5a38d52353841c6b82c373ab8b1a7ce5f80169d4c682e462b3a2e704d037ec -size 46376012 +oid sha256:f056084c3b0c9d8be58c1d52d23b3179d778876f1b288d49fcbc6a11673e423e +size 56528311 diff --git a/wasm/zed.wasm b/wasm/zed.wasm index d3e99e5..b5a9e6e 100755 --- a/wasm/zed.wasm +++ b/wasm/zed.wasm @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61829b42380be0aefd67068d9ee95b77147002ad843e920d410f9883745f1a05 -size 55126053 +oid sha256:7e504efca86657c6f599a74f8df81b72b0b2ce1cf86eae9e6c626c758ef5d917 +size 62794705 diff --git a/yarn.lock b/yarn.lock index e9f71ec..78d3b07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11847,9 +11847,9 @@ postcss@^7, postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, po supports-color "^6.1.0" postcss@^8.1.0: - version "8.4.37" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.37.tgz#4505f992cd0c20e03d25f13b31901640b2db731a" - integrity sha512-7iB/v/r7Woof0glKLH8b1SPHrsX7uhdO+Geb41QpF/+mWZHU3uxxSlN+UXGVit1PawOYDToO+AbZzhBzWRDwbQ== + version "8.4.38" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" + integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== dependencies: nanoid "^3.3.7" picocolors "^1.0.0"