Skip to content

Fix bulk check typing issue #189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 117 additions & 7 deletions src/__utils__/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,119 @@
import {
ClientSecurity,
NewClient,
ZedClientInterface,
RelationshipUpdate_Operation,
} from '../v1.js'

/**
* Generates a random token with a prefix to support
* unique, idempotent local testing.
* @param prefix
* @returns
* Generates a random token to support unique, idempotent local testing.
*/
export function generateTestToken(prefix: string): string {
return `${prefix}-${Date.now().toString()}`
}
export function generateTestToken(): string {
return Math.random().toString()
}

/*
* Asserts that a value or expression is non-falsey.
* It's mostly useful to use a statement to narrow a type
* (as opposed to using an if statement, which is annoying
* in tests)
*/
export function assert(val: unknown, msg = "Assertion failed"): asserts val {
if(!val) throw new Error(msg);
}

export const testClient = () => NewClient(
generateTestToken(),
'localhost:50051',
ClientSecurity.INSECURE_LOCALHOST_ALLOWED
)

const testSchema = `
caveat likes_harry_potter(likes bool) {
likes == true
}

definition post {
relation writer: user
relation reader: user
relation caveated_reader: user with likes_harry_potter

permission write = writer
permission view = reader + writer
permission view_as_fan = caveated_reader + writer
}
definition user {}
`

export const writeTestSchema = async (client: ZedClientInterface) => {
await client.promises.writeSchema({
schema: testSchema
})
}

export const writeTestTuples = async (client: ZedClientInterface) => {
const emilia = {
object: {
objectType: "user",
objectId: "emilia"
},
optionalRelation: "",
}
const beatrice = {
object: {
objectType: "user",
objectId: "beatrice"
},
optionalRelation: "",
}
const postOne = {
objectType: "post",
objectId: "post-one"
}
const postTwo = {
objectType: "post",
objectId: "post-two"
}
await client.promises.writeRelationships({
// NOTE: optionalPreconditions seems like it should be omittable,
// but the way that the typescript plugin handles repeated fields
// is to treat them as required.
optionalPreconditions: [],
updates: [
{
operation: RelationshipUpdate_Operation.CREATE,
relationship: {
subject: emilia,
relation: "writer",
resource: postOne,
}
},
{
operation: RelationshipUpdate_Operation.CREATE,
relationship: {
subject: emilia,
relation: "writer",
resource: postTwo,
}
},
{
operation: RelationshipUpdate_Operation.CREATE,
relationship: {
subject: beatrice,
relation: "reader",
resource: postOne,
}
},
{
operation: RelationshipUpdate_Operation.CREATE,
relationship: {
subject: beatrice,
relation: "caveated_reader",
resource: postOne,
optionalCaveat: { caveatName: "likes_harry_potter" }
}
},
]
})
return { emilia, beatrice, postOne, postTwo };
}
78 changes: 71 additions & 7 deletions src/v1-promise.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import * as grpc from '@grpc/grpc-js';
import { generateTestToken } from './__utils__/helpers.js';
import { getOneofValue } from '@protobuf-ts/runtime';
import {
generateTestToken,
testClient,
writeTestSchema,
writeTestTuples,
assert,
} from './__utils__/helpers.js';
import { Struct } from './authzedapi/google/protobuf/struct.js';
import {
BulkExportRelationshipsRequest,
Expand All @@ -22,6 +29,8 @@
} from './v1.js';
import { describe, it, expect, beforeEach } from 'vitest'

const fullyConsistent: Consistency = { requirement: { oneofKind: "fullyConsistent", fullyConsistent: true }};

describe('a check with an unknown namespace', () => {
it('should raise a failed precondition', async () => {
const resource = ObjectReference.create({
Expand All @@ -43,7 +52,7 @@
});

const { promises: client } = NewClient(
generateTestToken('v1-promise-test-unknown'),
generateTestToken(),
'localhost:50051',
ClientSecurity.INSECURE_LOCALHOST_ALLOWED
);
Expand Down Expand Up @@ -111,7 +120,7 @@

it('should succeed', async () => {
const { promises: client } = NewClient(
generateTestToken('v1-promise-namespace'),
generateTestToken(),
'localhost:50051',
ClientSecurity.INSECURE_LOCALHOST_ALLOWED
);
Expand All @@ -132,7 +141,7 @@

it('should succeed with full signatures', async () => {
const { promises: client } = NewClient(
generateTestToken('v1-promise-namespace'),
generateTestToken(),
'localhost:50051',
ClientSecurity.INSECURE_LOCALHOST_ALLOWED
);
Expand Down Expand Up @@ -167,7 +176,7 @@
it('should succeed', async () => {
// Write some schema.
const { promises: client } = NewClient(
generateTestToken('v1-promise-caveats'),
generateTestToken(),
'localhost:50051',
ClientSecurity.INSECURE_LOCALHOST_ALLOWED
);
Expand Down Expand Up @@ -310,6 +319,61 @@
});
});

describe("checkBulkPermissions", () => {
it("can check bulk permissions", async () => {
const client = testClient();
await writeTestSchema(client);
const { emilia, postOne } = await writeTestTuples(client);
const response = await client.promises.checkBulkPermissions({
consistency: fullyConsistent,
items: [
{
resource: postOne,
permission: "view",
subject: emilia,
}
]
})
expect(response.pairs.length).to.equal(2)
// These assertions are annoying but necessary to keep typescript happy.
assert(!(response.pairs[0].response.oneofKind === "error"))
assert(response.pairs[0].response.oneofKind)
expect(response.pairs[0].response.item.permissionship === CheckPermissionResponse_Permissionship.HAS_PERMISSION)

assert(!(response.pairs[1].response.oneofKind === "error"))
assert(response.pairs[1].response.oneofKind)
expect(response.pairs[1].response.item.permissionship === CheckPermissionResponse_Permissionship.HAS_PERMISSION)
})

it("can check bulk permissions with a deadline", async () => {
const client = testClient();
await writeTestSchema(client);
const { emilia, postOne } = await writeTestTuples(client);
const response = await client.promises.checkBulkPermissions(
{
consistency: fullyConsistent,
items: [
{
resource: postOne,
permission: "view",
subject: emilia,
}
]
},
{deadline: Date.now() + 5000},

Check failure on line 363 in src/v1-promise.test.ts

View workflow job for this annotation

GitHub Actions / Build and Test JS client (18)

Object literal may only specify known properties, and 'deadline' does not exist in type 'Metadata'.

Check failure on line 363 in src/v1-promise.test.ts

View workflow job for this annotation

GitHub Actions / Lint and Test (18)

Object literal may only specify known properties, and 'deadline' does not exist in type 'Metadata'.

Check failure on line 363 in src/v1-promise.test.ts

View workflow job for this annotation

GitHub Actions / Build and Test JS client (20)

Object literal may only specify known properties, and 'deadline' does not exist in type 'Metadata'.

Check failure on line 363 in src/v1-promise.test.ts

View workflow job for this annotation

GitHub Actions / Lint and Test (20)

Object literal may only specify known properties, and 'deadline' does not exist in type 'Metadata'.

Check failure on line 363 in src/v1-promise.test.ts

View workflow job for this annotation

GitHub Actions / Build and Test JS client (21)

Object literal may only specify known properties, and 'deadline' does not exist in type 'Metadata'.
)
expect(response.pairs.length).to.equal(2)
// These assertions are annoying but necessary to keep typescript happy.
assert(!(response.pairs[0].response.oneofKind === "error"))
assert(response.pairs[0].response.oneofKind)
expect(response.pairs[0].response.item.permissionship === CheckPermissionResponse_Permissionship.HAS_PERMISSION)

assert(!(response.pairs[1].response.oneofKind === "error"))
assert(response.pairs[1].response.oneofKind)
expect(response.pairs[1].response.item.permissionship === CheckPermissionResponse_Permissionship.HAS_PERMISSION)
})
})

describe('Lookup APIs', () => {
let token: string;

Expand Down Expand Up @@ -346,7 +410,7 @@
});

beforeEach(async () => {
token = generateTestToken('v1-promise-lookup');
token = generateTestToken();
const { promises: client } = NewClient(
token,
'localhost:50051',
Expand Down Expand Up @@ -459,7 +523,7 @@
let token: string;

beforeEach(async () => {
token = generateTestToken('v1-experimental-service');
token = generateTestToken();
const { promises: client } = NewClient(
token,
'localhost:50051',
Expand Down
12 changes: 6 additions & 6 deletions src/v1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe("a check with an unknown namespace", () => {
});

const client = NewClient(
generateTestToken("v1-test-unknown"),
generateTestToken(),
"localhost:50051",
ClientSecurity.INSECURE_LOCALHOST_ALLOWED
);
Expand All @@ -66,7 +66,7 @@ describe("a check with an known namespace", () => {
it("should succeed", () => new Promise<void>((done) => {
// Write some schema.
const client = NewClient(
generateTestToken("v1-namespace"),
generateTestToken(),
"localhost:50051",
ClientSecurity.INSECURE_LOCALHOST_ALLOWED,
PreconnectServices.PERMISSIONS_SERVICE | PreconnectServices.SCHEMA_SERVICE
Expand Down Expand Up @@ -172,7 +172,7 @@ describe("a check with an known namespace", () => {
it("should succeed", () => new Promise<void>((done) => {
// Write some schema.
const client = NewClient(
generateTestToken("v1-namespace-caveats"),
generateTestToken(),
"localhost:50051",
ClientSecurity.INSECURE_LOCALHOST_ALLOWED
);
Expand Down Expand Up @@ -288,7 +288,7 @@ describe("Lookup APIs", () => {
let token: string;

beforeEach(() => new Promise<void>((done) => {
token = generateTestToken("v1-lookup");
token = generateTestToken();
const client = NewClient(
token,
"localhost:50051",
Expand Down Expand Up @@ -461,7 +461,7 @@ describe("a check with a negative timeout", () => {
});

const client = NewClient(
generateTestToken("v1-test-unknown"),
generateTestToken(),
"localhost:50051",
ClientSecurity.INSECURE_LOCALHOST_ALLOWED,
PreconnectServices.NONE,
Expand All @@ -482,7 +482,7 @@ describe("Experimental Service", () => {
let token: string;

beforeEach(() => new Promise<void>((done) => {
token = generateTestToken("v1-experimental-service");
token = generateTestToken();
const client = NewClient(
token,
"localhost:50051",
Expand Down
Loading