Skip to content

Commit d0bb25c

Browse files
committed
feat(keto-cli): create DeleteRelationCommand
1 parent aa37f81 commit d0bb25c

File tree

3 files changed

+166
-1
lines changed

3 files changed

+166
-1
lines changed

packages/keto-cli/src/app/create-relation.command.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,6 @@ export class CreateRelationCommand extends CommandRunner {
6565
required: false,
6666
})
6767
parseAccessToken(val: string): string | undefined {
68-
return val ?? process.env['ORY_KETO_API_KEY'];
68+
return val;
6969
}
7070
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { OryRelationshipsService } from '@getlarge/keto-client-wrapper';
2+
import {
3+
createRelationQuery,
4+
RelationTuple,
5+
} from '@getlarge/keto-relations-parser';
6+
import { Configuration } from '@ory/client';
7+
import { CommandTestFactory } from 'nest-commander-testing';
8+
9+
import { DeleteRelationCommand } from './delete-relation.command';
10+
11+
class MockOryRelationshipsService {
12+
deleteRelationships = jest.fn();
13+
configuration = new Configuration({
14+
basePath: 'http://localhost',
15+
accessToken: '',
16+
});
17+
get config(): Configuration {
18+
return this.configuration;
19+
}
20+
set config(config: Configuration) {
21+
this.configuration = config;
22+
}
23+
}
24+
25+
describe('DeleteRelationCommand', () => {
26+
let service: DeleteRelationCommand;
27+
let oryRelationshipsService: OryRelationshipsService;
28+
29+
beforeAll(async () => {
30+
const app = await CommandTestFactory.createTestingCommand({
31+
imports: [],
32+
providers: [
33+
{
34+
provide: OryRelationshipsService,
35+
useClass: MockOryRelationshipsService,
36+
},
37+
DeleteRelationCommand,
38+
],
39+
}).compile();
40+
41+
service = app.get(DeleteRelationCommand);
42+
oryRelationshipsService = app.get(OryRelationshipsService);
43+
});
44+
45+
describe('run', () => {
46+
it('should process tuple and delete relationship', async () => {
47+
const tuple: RelationTuple = {
48+
namespace: 'Group',
49+
object: 'admin',
50+
relation: 'members',
51+
subjectIdOrSet: {
52+
namespace: 'User',
53+
object: '1',
54+
},
55+
};
56+
const expectedQuery = createRelationQuery(tuple).unwrapOrThrow();
57+
oryRelationshipsService.deleteRelationships = jest
58+
.fn()
59+
.mockResolvedValue({
60+
data: {
61+
namespace: 'Group',
62+
object: 'admin',
63+
relation: 'members',
64+
subject_set: {
65+
relation: '',
66+
namespace: 'User',
67+
object: '1',
68+
},
69+
},
70+
});
71+
72+
expect(oryRelationshipsService.config.basePath).toBe('http://localhost');
73+
await expect(
74+
service.run(
75+
[
76+
'--tuple',
77+
'Group:admin#members@User:1',
78+
'--basePath',
79+
'http://localhost:4467',
80+
],
81+
{
82+
tuple: expectedQuery,
83+
basePath: 'http://localhost:4467',
84+
}
85+
)
86+
).resolves.toBeUndefined();
87+
88+
expect(oryRelationshipsService.config.basePath).toBe(
89+
'http://localhost:4467'
90+
);
91+
expect(oryRelationshipsService.deleteRelationships).toHaveBeenCalledWith(
92+
expectedQuery
93+
);
94+
});
95+
});
96+
});
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { OryRelationshipsService } from '@getlarge/keto-client-wrapper';
2+
import {
3+
createRelationQuery,
4+
parseRelationTuple,
5+
} from '@getlarge/keto-relations-parser';
6+
import { Logger } from '@nestjs/common';
7+
import { Configuration, RelationQuery } from '@ory/client';
8+
import { Command, CommandRunner, Option } from 'nest-commander';
9+
10+
interface CommandOptions
11+
extends Pick<Configuration, 'basePath' | 'accessToken'> {
12+
tuple: RelationQuery;
13+
}
14+
15+
@Command({ name: 'delete', description: 'Delete relationship on Ory Keto' })
16+
export class DeleteRelationCommand extends CommandRunner {
17+
readonly logger = new Logger(DeleteRelationCommand.name);
18+
19+
constructor(
20+
private readonly oryRelationshipsService: OryRelationshipsService
21+
) {
22+
super();
23+
}
24+
25+
async run(passedParams: string[], options: CommandOptions): Promise<void> {
26+
const { tuple } = options;
27+
if (options.accessToken || options.basePath) {
28+
this.oryRelationshipsService.config = new Configuration({
29+
...this.oryRelationshipsService.config,
30+
...options,
31+
});
32+
}
33+
await this.oryRelationshipsService.deleteRelationships(tuple);
34+
this.logger.debug('Deleted relation');
35+
this.logger.log(tuple);
36+
}
37+
38+
@Option({
39+
flags: '-t, --tuple [string]',
40+
description: 'Relationship tuple to delete, using Zanzibar notation',
41+
required: true,
42+
})
43+
parseRelationTuple(val: string): RelationQuery {
44+
const res = parseRelationTuple(val);
45+
if (res.hasError()) {
46+
throw res.error;
47+
}
48+
const relationQuery = createRelationQuery(res.value);
49+
return relationQuery.unwrapOrThrow();
50+
}
51+
52+
@Option({
53+
flags: '-b, --basePath [string]',
54+
description: 'Ory Keto Admin URL',
55+
required: false,
56+
})
57+
parseBasePath(val: string): string | undefined {
58+
return val;
59+
}
60+
61+
@Option({
62+
flags: '-a, --accessToken [string]',
63+
description: 'Ory Keto Access Token',
64+
required: false,
65+
})
66+
parseAccessToken(val: string): string | undefined {
67+
return val;
68+
}
69+
}

0 commit comments

Comments
 (0)