Skip to content

Commit efc51bb

Browse files
Merge pull request #69 from solid-contrib/create-new-group
Create new group
2 parents df006b8 + a4a92ae commit efc51bb

23 files changed

Lines changed: 400 additions & 42 deletions

contacts/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this module will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## Unreleased
8+
9+
### Added
10+
11+
- [createNewGroup](https://solid-contrib.github.io/data-modules/contacts-rdflib-api/interfaces/ContactsModule.html#createNewGroup)
12+
713
## 0.2.1
814

915
### Fixed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import {ContactsModuleRdfLib as ContactsModule} from '../dist/index.js';
2+
import { Fetcher, graph, UpdateManager } from "rdflib";
3+
import { faker } from "@faker-js/faker";
4+
5+
const store = graph();
6+
const fetcher = new Fetcher(store);
7+
const updater = new UpdateManager(store);
8+
const contacts = new ContactsModule({ store, fetcher, updater });
9+
10+
let addressBookUri =
11+
"http://localhost:3000/alice/public-write/ab9694d6-120e-415d-a315-90cd84c2e062/index.ttl#this";
12+
13+
const groupName = faker.company.name();
14+
15+
const uri = await contacts.createNewGroup({
16+
addressBookUri,
17+
groupName,
18+
});
19+
console.log("created group:", groupName, "at", uri);
20+
21+
const result = await contacts.readAddressBook(addressBookUri);
22+
console.log("the updated address book:", result);

contacts/jest.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ module.exports = {
44
testEnvironment: "node",
55
rootDir: "src",
66
testPathIgnorePatterns: [".*\\.e2e\\.spec\\.ts"],
7+
detectOpenHandles: true,
8+
forceExit: true,
79
};

contacts/jest.e2e.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ module.exports = {
44
testEnvironment: "node",
55
rootDir: "src/e2e-tests",
66
testTimeout: 60000,
7+
detectOpenHandles: true,
8+
forceExit: true,
79
};

contacts/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
"scripts": {
77
"build": "tsc",
88
"build:doc": "typedoc src/index.ts --out ../gh-pages/contacts-rdflib-api",
9-
"test": "jest --detectOpenHandles --forceExit",
9+
"test": "jest",
1010
"lint": "eslint",
11-
"test:e2e": "jest --config jest.e2e.config.js --detectOpenHandles --forceExit",
11+
"test:e2e": "jest --config jest.e2e.config.js",
1212
"pod": "community-solid-server --config ./dev-server/config/config-mashlib.json --seedConfig ./dev-server/seed.json --rootFilePath ./dev-server/data",
1313
"pod:init": "cp -r ./dev-server/initial-data/* ./dev-server/data/",
1414
"pod:clean": "rm -rf ./dev-server/data"

contacts/src/e2e-tests/contacts.e2e.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,26 @@ describe("contacts module", () => {
9999
],
100100
});
101101
});
102+
103+
it("can create a new group within an existing address book", async () => {
104+
const contacts = setupModule();
105+
106+
const addressBookUri =
107+
"http://localhost:3456/4243dbb6-3126-4bf9-9ea7-45e35c3c8d9d/index.ttl#this";
108+
109+
const groupName = faker.company.name();
110+
const uri = await contacts.createNewGroup({
111+
addressBookUri,
112+
groupName,
113+
});
114+
115+
const result = await contacts.readAddressBook(addressBookUri);
116+
117+
expect(result.groups).toContainEqual({
118+
uri,
119+
name: groupName,
120+
});
121+
});
102122
});
103123

104124
function setupModule() {

contacts/src/index.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ export interface ContactsModule {
3434
* @return Contact name, email addresses and phone numbers
3535
*/
3636
readContact(uri: string): Promise<FullContact>;
37+
38+
/**
39+
* Creates a new group within a given address book
40+
* @param command
41+
* @return The URI of the newly created group
42+
*/
43+
createNewGroup({
44+
addressBookUri,
45+
groupName,
46+
}: CreateNewGroupCommand): Promise<string>;
3747
}
3848

3949
/**
@@ -121,3 +131,17 @@ export interface PhoneNumber {
121131
* Partial group data listed when reading an address book
122132
*/
123133
export interface Group {}
134+
135+
/**
136+
* Data needed to create a new group within an address book
137+
*/
138+
export interface CreateNewGroupCommand {
139+
/**
140+
* The URI of an existing address book the new group should be added to
141+
*/
142+
addressBookUri: string;
143+
/**
144+
* The name of the group to create
145+
*/
146+
groupName: string;
147+
}

contacts/src/rdflib/ContactsModuleRdfLib.spec.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { when } from "jest-when";
2-
import { createAddressBook } from "./createAddressBook";
2+
import { createAddressBook, createNewContact } from "./update-operations";
33
import { ContactsModuleRdfLib } from "./ContactsModuleRdfLib";
44
import { executeUpdate } from "./web-operations/executeUpdate";
55
import { Fetcher, graph, UpdateManager } from "rdflib";
6-
import { createNewContact } from "./createNewContact";
76

8-
jest.mock("./createAddressBook");
9-
jest.mock("./createNewContact");
7+
jest.mock("./update-operations/createAddressBook");
8+
jest.mock("./update-operations/createNewContact");
109
jest.mock("./web-operations/executeUpdate");
1110
jest.mock("./web-operations/fetchNode");
1211

contacts/src/rdflib/ContactsModuleRdfLib.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import {
44
ContactsModule,
55
CreateAddressBookCommand,
66
CreateNewContactCommand,
7+
CreateNewGroupCommand,
78
FullContact,
89
} from "..";
9-
import { AddressBookQuery } from "./AddressBookQuery";
10-
import { createAddressBook } from "./createAddressBook";
10+
import { AddressBookQuery, ContactQuery } from "./queries";
11+
import { createAddressBook, createNewContact } from "./update-operations";
1112
import { executeUpdate } from "./web-operations/executeUpdate";
12-
import { createNewContact } from "./createNewContact";
1313
import { fetchNode } from "./web-operations/fetchNode";
14-
import { ContactQuery } from "./ContactQuery";
14+
import { createNewGroup } from "./update-operations/createNewGroup";
1515

1616
interface ModuleConfig {
1717
store: IndexedFormula;
@@ -89,4 +89,14 @@ export class ContactsModuleRdfLib implements ContactsModule {
8989
phoneNumbers,
9090
};
9191
}
92+
93+
async createNewGroup({ addressBookUri, groupName }: CreateNewGroupCommand) {
94+
const addressBookNode = sym(addressBookUri);
95+
await this.fetchNode(addressBookNode);
96+
97+
const query = new AddressBookQuery(this.store, addressBookNode);
98+
const operation = createNewGroup(query, groupName);
99+
await executeUpdate(this.fetcher, this.updater, operation);
100+
return operation.uri;
101+
}
92102
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { v4 as uuid } from "uuid";
2+
import { Fetcher, graph, UpdateManager } from "rdflib";
3+
import { ContactsModuleRdfLib } from "./ContactsModuleRdfLib";
4+
import {
5+
mockNotFound,
6+
mockTurtleResponse,
7+
} from "../test-support/mockResponses";
8+
import { expectPatchRequest } from "../test-support/expectRequests";
9+
10+
jest.mock("uuid");
11+
12+
describe("create new group", () => {
13+
it("creates group resource", async () => {
14+
const authenticatedFetch = jest.fn();
15+
16+
(uuid as jest.Mock).mockReturnValueOnce(
17+
"b4e9fd85-3b38-4db7-8599-d0eda0b2ac74",
18+
);
19+
20+
const store = graph();
21+
const fetcher = new Fetcher(store, {
22+
fetch: authenticatedFetch,
23+
});
24+
const updater = new UpdateManager(store);
25+
const contacts = new ContactsModuleRdfLib({
26+
store,
27+
fetcher,
28+
updater,
29+
});
30+
31+
mockTurtleResponse(
32+
authenticatedFetch,
33+
"https://pod.test/alice/contacts/index.ttl",
34+
`
35+
@prefix vcard: <http://www.w3.org/2006/vcard/ns#>.
36+
@prefix ab: <http://www.w3.org/ns/pim/ab#>.
37+
@prefix dc: <http://purl.org/dc/elements/1.1/>.
38+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
39+
40+
<#this> a vcard:AddressBook;
41+
dc:title "Alice's contacts";
42+
vcard:nameEmailIndex <people.ttl>;
43+
vcard:groupIndex <groups.ttl>.
44+
`,
45+
);
46+
47+
mockTurtleResponse(
48+
authenticatedFetch,
49+
"https://pod.test/alice/contacts/groups.ttl",
50+
"",
51+
);
52+
53+
mockNotFound(
54+
authenticatedFetch,
55+
"https://pod.test/alice/contacts/Group/b4e9fd85-3b38-4db7-8599-d0eda0b2ac74/index.ttl",
56+
);
57+
58+
const createdUri = await contacts.createNewGroup({
59+
addressBookUri: "https://pod.test/alice/contacts/index.ttl#this",
60+
groupName: "best friends",
61+
});
62+
63+
expect(createdUri).toEqual(
64+
"https://pod.test/alice/contacts/Group/b4e9fd85-3b38-4db7-8599-d0eda0b2ac74/index.ttl#this",
65+
);
66+
expectPatchRequest(
67+
authenticatedFetch,
68+
"https://pod.test/alice/contacts/Group/b4e9fd85-3b38-4db7-8599-d0eda0b2ac74/index.ttl",
69+
`INSERT DATA { <https://pod.test/alice/contacts/Group/b4e9fd85-3b38-4db7-8599-d0eda0b2ac74/index.ttl#this> <http://www.w3.org/2006/vcard/ns#fn> "best friends" .
70+
<https://pod.test/alice/contacts/Group/b4e9fd85-3b38-4db7-8599-d0eda0b2ac74/index.ttl#this> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2006/vcard/ns#Group> .
71+
}`,
72+
);
73+
});
74+
});

0 commit comments

Comments
 (0)