diff --git a/contacts/CHANGELOG.md b/contacts/CHANGELOG.md index bd7d81d0..bb8abb85 100644 --- a/contacts/CHANGELOG.md +++ b/contacts/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this module will be documented in this file. 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). +## Unreleased + +### Added + +- [createNewGroup](https://solid-contrib.github.io/data-modules/contacts-rdflib-api/interfaces/ContactsModule.html#createNewGroup) + ## 0.2.1 ### Fixed diff --git a/contacts/examples/create-new-group.mjs b/contacts/examples/create-new-group.mjs new file mode 100644 index 00000000..71df91a1 --- /dev/null +++ b/contacts/examples/create-new-group.mjs @@ -0,0 +1,22 @@ +import {ContactsModuleRdfLib as ContactsModule} from '../dist/index.js'; +import { Fetcher, graph, UpdateManager } from "rdflib"; +import { faker } from "@faker-js/faker"; + +const store = graph(); +const fetcher = new Fetcher(store); +const updater = new UpdateManager(store); +const contacts = new ContactsModule({ store, fetcher, updater }); + +let addressBookUri = + "http://localhost:3000/alice/public-write/ab9694d6-120e-415d-a315-90cd84c2e062/index.ttl#this"; + +const groupName = faker.company.name(); + +const uri = await contacts.createNewGroup({ + addressBookUri, + groupName, +}); +console.log("created group:", groupName, "at", uri); + +const result = await contacts.readAddressBook(addressBookUri); +console.log("the updated address book:", result); diff --git a/contacts/jest.config.js b/contacts/jest.config.js index 1faadd82..e8fb285d 100644 --- a/contacts/jest.config.js +++ b/contacts/jest.config.js @@ -4,4 +4,6 @@ module.exports = { testEnvironment: "node", rootDir: "src", testPathIgnorePatterns: [".*\\.e2e\\.spec\\.ts"], + detectOpenHandles: true, + forceExit: true, }; diff --git a/contacts/jest.e2e.config.js b/contacts/jest.e2e.config.js index 9e521ccb..f91f4289 100644 --- a/contacts/jest.e2e.config.js +++ b/contacts/jest.e2e.config.js @@ -4,4 +4,6 @@ module.exports = { testEnvironment: "node", rootDir: "src/e2e-tests", testTimeout: 60000, + detectOpenHandles: true, + forceExit: true, }; diff --git a/contacts/package.json b/contacts/package.json index f0544bb2..87912c7c 100644 --- a/contacts/package.json +++ b/contacts/package.json @@ -6,9 +6,9 @@ "scripts": { "build": "tsc", "build:doc": "typedoc src/index.ts --out ../gh-pages/contacts-rdflib-api", - "test": "jest --detectOpenHandles --forceExit", + "test": "jest", "lint": "eslint", - "test:e2e": "jest --config jest.e2e.config.js --detectOpenHandles --forceExit", + "test:e2e": "jest --config jest.e2e.config.js", "pod": "community-solid-server --config ./dev-server/config/config-mashlib.json --seedConfig ./dev-server/seed.json --rootFilePath ./dev-server/data", "pod:init": "cp -r ./dev-server/initial-data/* ./dev-server/data/", "pod:clean": "rm -rf ./dev-server/data" diff --git a/contacts/src/e2e-tests/contacts.e2e.spec.ts b/contacts/src/e2e-tests/contacts.e2e.spec.ts index f10acd5b..95d64576 100644 --- a/contacts/src/e2e-tests/contacts.e2e.spec.ts +++ b/contacts/src/e2e-tests/contacts.e2e.spec.ts @@ -99,6 +99,26 @@ describe("contacts module", () => { ], }); }); + + it("can create a new group within an existing address book", async () => { + const contacts = setupModule(); + + const addressBookUri = + "http://localhost:3456/4243dbb6-3126-4bf9-9ea7-45e35c3c8d9d/index.ttl#this"; + + const groupName = faker.company.name(); + const uri = await contacts.createNewGroup({ + addressBookUri, + groupName, + }); + + const result = await contacts.readAddressBook(addressBookUri); + + expect(result.groups).toContainEqual({ + uri, + name: groupName, + }); + }); }); function setupModule() { diff --git a/contacts/src/index.ts b/contacts/src/index.ts index 4b832343..41f47f26 100644 --- a/contacts/src/index.ts +++ b/contacts/src/index.ts @@ -34,6 +34,16 @@ export interface ContactsModule { * @return Contact name, email addresses and phone numbers */ readContact(uri: string): Promise; + + /** + * Creates a new group within a given address book + * @param command + * @return The URI of the newly created group + */ + createNewGroup({ + addressBookUri, + groupName, + }: CreateNewGroupCommand): Promise; } /** @@ -121,3 +131,17 @@ export interface PhoneNumber { * Partial group data listed when reading an address book */ export interface Group {} + +/** + * Data needed to create a new group within an address book + */ +export interface CreateNewGroupCommand { + /** + * The URI of an existing address book the new group should be added to + */ + addressBookUri: string; + /** + * The name of the group to create + */ + groupName: string; +} diff --git a/contacts/src/rdflib/ContactsModuleRdfLib.spec.ts b/contacts/src/rdflib/ContactsModuleRdfLib.spec.ts index 642dc4f6..5bdfff61 100644 --- a/contacts/src/rdflib/ContactsModuleRdfLib.spec.ts +++ b/contacts/src/rdflib/ContactsModuleRdfLib.spec.ts @@ -1,12 +1,11 @@ import { when } from "jest-when"; -import { createAddressBook } from "./createAddressBook"; +import { createAddressBook, createNewContact } from "./update-operations"; import { ContactsModuleRdfLib } from "./ContactsModuleRdfLib"; import { executeUpdate } from "./web-operations/executeUpdate"; import { Fetcher, graph, UpdateManager } from "rdflib"; -import { createNewContact } from "./createNewContact"; -jest.mock("./createAddressBook"); -jest.mock("./createNewContact"); +jest.mock("./update-operations/createAddressBook"); +jest.mock("./update-operations/createNewContact"); jest.mock("./web-operations/executeUpdate"); jest.mock("./web-operations/fetchNode"); diff --git a/contacts/src/rdflib/ContactsModuleRdfLib.ts b/contacts/src/rdflib/ContactsModuleRdfLib.ts index b86564ca..0eef3155 100644 --- a/contacts/src/rdflib/ContactsModuleRdfLib.ts +++ b/contacts/src/rdflib/ContactsModuleRdfLib.ts @@ -4,14 +4,14 @@ import { ContactsModule, CreateAddressBookCommand, CreateNewContactCommand, + CreateNewGroupCommand, FullContact, } from ".."; -import { AddressBookQuery } from "./AddressBookQuery"; -import { createAddressBook } from "./createAddressBook"; +import { AddressBookQuery, ContactQuery } from "./queries"; +import { createAddressBook, createNewContact } from "./update-operations"; import { executeUpdate } from "./web-operations/executeUpdate"; -import { createNewContact } from "./createNewContact"; import { fetchNode } from "./web-operations/fetchNode"; -import { ContactQuery } from "./ContactQuery"; +import { createNewGroup } from "./update-operations/createNewGroup"; interface ModuleConfig { store: IndexedFormula; @@ -89,4 +89,14 @@ export class ContactsModuleRdfLib implements ContactsModule { phoneNumbers, }; } + + async createNewGroup({ addressBookUri, groupName }: CreateNewGroupCommand) { + const addressBookNode = sym(addressBookUri); + await this.fetchNode(addressBookNode); + + const query = new AddressBookQuery(this.store, addressBookNode); + const operation = createNewGroup(query, groupName); + await executeUpdate(this.fetcher, this.updater, operation); + return operation.uri; + } } diff --git a/contacts/src/rdflib/create-new-group.integration.spec.ts b/contacts/src/rdflib/create-new-group.integration.spec.ts new file mode 100644 index 00000000..5eb1258b --- /dev/null +++ b/contacts/src/rdflib/create-new-group.integration.spec.ts @@ -0,0 +1,74 @@ +import { v4 as uuid } from "uuid"; +import { Fetcher, graph, UpdateManager } from "rdflib"; +import { ContactsModuleRdfLib } from "./ContactsModuleRdfLib"; +import { + mockNotFound, + mockTurtleResponse, +} from "../test-support/mockResponses"; +import { expectPatchRequest } from "../test-support/expectRequests"; + +jest.mock("uuid"); + +describe("create new group", () => { + it("creates group resource", async () => { + const authenticatedFetch = jest.fn(); + + (uuid as jest.Mock).mockReturnValueOnce( + "b4e9fd85-3b38-4db7-8599-d0eda0b2ac74", + ); + + const store = graph(); + const fetcher = new Fetcher(store, { + fetch: authenticatedFetch, + }); + const updater = new UpdateManager(store); + const contacts = new ContactsModuleRdfLib({ + store, + fetcher, + updater, + }); + + mockTurtleResponse( + authenticatedFetch, + "https://pod.test/alice/contacts/index.ttl", + ` + @prefix vcard: . + @prefix ab: . + @prefix dc: . + @prefix xsd: . + + <#this> a vcard:AddressBook; + dc:title "Alice's contacts"; + vcard:nameEmailIndex ; + vcard:groupIndex . +`, + ); + + mockTurtleResponse( + authenticatedFetch, + "https://pod.test/alice/contacts/groups.ttl", + "", + ); + + mockNotFound( + authenticatedFetch, + "https://pod.test/alice/contacts/Group/b4e9fd85-3b38-4db7-8599-d0eda0b2ac74/index.ttl", + ); + + const createdUri = await contacts.createNewGroup({ + addressBookUri: "https://pod.test/alice/contacts/index.ttl#this", + groupName: "best friends", + }); + + expect(createdUri).toEqual( + "https://pod.test/alice/contacts/Group/b4e9fd85-3b38-4db7-8599-d0eda0b2ac74/index.ttl#this", + ); + expectPatchRequest( + authenticatedFetch, + "https://pod.test/alice/contacts/Group/b4e9fd85-3b38-4db7-8599-d0eda0b2ac74/index.ttl", + `INSERT DATA { "best friends" . + . + }`, + ); + }); +}); diff --git a/contacts/src/rdflib/AddressBookQuery.spec.ts b/contacts/src/rdflib/queries/AddressBookQuery.spec.ts similarity index 93% rename from contacts/src/rdflib/AddressBookQuery.spec.ts rename to contacts/src/rdflib/queries/AddressBookQuery.spec.ts index 4dda7f2a..cedffe9c 100644 --- a/contacts/src/rdflib/AddressBookQuery.spec.ts +++ b/contacts/src/rdflib/queries/AddressBookQuery.spec.ts @@ -1,6 +1,6 @@ import { AddressBookQuery } from "./AddressBookQuery"; import { graph, lit, sym } from "rdflib"; -import { dc, vcard } from "./namespaces"; +import { dc, vcard } from "../namespaces"; import { v4 as uuid } from "uuid"; @@ -366,6 +366,25 @@ describe("AddressBookQuery", () => { expect(result).toBe(null); }); + it("returns null if index is not a named node", () => { + const store = graph(); + const addressBookNode = sym( + "http://pod.test/alice/contacts/index.ttl#this", + ); + store.add( + addressBookNode, + vcard("groupIndex"), + lit("invalid index"), + addressBookNode.doc(), + ); + const query = new AddressBookQuery( + store, + sym("http://pod.test/alice/contacts/index.ttl#this"), + ); + const result = query.queryGroupIndex(); + expect(result).toBe(null); + }); + it("returns the node found in store", () => { const store = graph(); const addressBookNode = sym( @@ -648,4 +667,23 @@ describe("AddressBookQuery", () => { ); }); }); + + describe("propose new group node", () => { + it("mints a new URI based on the address book container", () => { + (uuid as jest.Mock).mockReturnValueOnce( + "367da26e-460c-4ab8-b6ca-a32edc88df51", + ); + const store = graph(); + const addressBookNode = sym( + "http://pod.test/alice/contacts/index.ttl#this", + ); + const query = new AddressBookQuery(store, addressBookNode); + const contactNode = query.proposeNewGroupNode(); + expect(contactNode).toEqual( + sym( + "http://pod.test/alice/contacts/Group/367da26e-460c-4ab8-b6ca-a32edc88df51/index.ttl#this", + ), + ); + }); + }); }); diff --git a/contacts/src/rdflib/AddressBookQuery.ts b/contacts/src/rdflib/queries/AddressBookQuery.ts similarity index 80% rename from contacts/src/rdflib/AddressBookQuery.ts rename to contacts/src/rdflib/queries/AddressBookQuery.ts index a5639e83..a9ed669e 100644 --- a/contacts/src/rdflib/AddressBookQuery.ts +++ b/contacts/src/rdflib/queries/AddressBookQuery.ts @@ -1,6 +1,6 @@ import { IndexedFormula, isNamedNode, NamedNode, sym } from "rdflib"; -import { dc, vcard } from "./namespaces"; -import { Contact, Group } from "../index"; +import { dc, vcard } from "../namespaces"; +import { Contact, Group } from "../../index"; import { v4 as uuid } from "uuid"; export class AddressBookQuery { @@ -14,10 +14,17 @@ export class AddressBookQuery { } proposeNewContactNode(): NamedNode { + return this.proposeNewNode("Person"); + } + + proposeNewGroupNode(): NamedNode { + return this.proposeNewNode("Group"); + } + + private proposeNewNode(containerPath: string) { const id = uuid(); const baseUri = this.addressBookNode.dir()?.uri; - const personDir = "Person"; - return sym(`${baseUri}${personDir}/${id}/index.ttl#this`); + return sym(`${baseUri}${containerPath}/${id}/index.ttl#this`); } queryTitle() { @@ -65,13 +72,18 @@ export class AddressBookQuery { : []; } - queryGroupIndex() { - return this.store.any( + queryGroupIndex(): NamedNode | null { + const index = this.store.any( this.addressBookNode, vcard("groupIndex"), undefined, this.addressBookDoc, ); + if (isNamedNode(index)) { + return index as NamedNode; + } else { + return null; + } } queryGroups(): Group[] { diff --git a/contacts/src/rdflib/ContactQuery.spec.ts b/contacts/src/rdflib/queries/ContactQuery.spec.ts similarity index 99% rename from contacts/src/rdflib/ContactQuery.spec.ts rename to contacts/src/rdflib/queries/ContactQuery.spec.ts index 732ba276..935d961e 100644 --- a/contacts/src/rdflib/ContactQuery.spec.ts +++ b/contacts/src/rdflib/queries/ContactQuery.spec.ts @@ -1,6 +1,6 @@ import { ContactQuery } from "./ContactQuery"; import { graph, lit, sym } from "rdflib"; -import { rdf, vcard } from "./namespaces"; +import { rdf, vcard } from "../namespaces"; describe("ContactQuery", () => { describe("query name", () => { diff --git a/contacts/src/rdflib/ContactQuery.ts b/contacts/src/rdflib/queries/ContactQuery.ts similarity index 96% rename from contacts/src/rdflib/ContactQuery.ts rename to contacts/src/rdflib/queries/ContactQuery.ts index 25d6263c..acce6fa6 100644 --- a/contacts/src/rdflib/ContactQuery.ts +++ b/contacts/src/rdflib/queries/ContactQuery.ts @@ -1,6 +1,6 @@ import { IndexedFormula, isNamedNode, NamedNode, Node, sym } from "rdflib"; -import { vcard } from "./namespaces"; -import { Email, PhoneNumber } from "../index"; +import { vcard } from "../namespaces"; +import { Email, PhoneNumber } from "../../index"; import { Namespace } from "rdflib/lib/factories/factory-types"; const MAILTO_URI_SCHEME = "mailto:"; diff --git a/contacts/src/rdflib/queries/index.ts b/contacts/src/rdflib/queries/index.ts new file mode 100644 index 00000000..16173812 --- /dev/null +++ b/contacts/src/rdflib/queries/index.ts @@ -0,0 +1,2 @@ +export { AddressBookQuery } from "./AddressBookQuery"; +export { ContactQuery } from "./ContactQuery"; diff --git a/contacts/src/rdflib/createAddressBook.spec.ts b/contacts/src/rdflib/update-operations/createAddressBook.spec.ts similarity index 96% rename from contacts/src/rdflib/createAddressBook.spec.ts rename to contacts/src/rdflib/update-operations/createAddressBook.spec.ts index 53e7b776..dec8f0a5 100644 --- a/contacts/src/rdflib/createAddressBook.spec.ts +++ b/contacts/src/rdflib/update-operations/createAddressBook.spec.ts @@ -1,8 +1,8 @@ import { createAddressBook } from "./createAddressBook"; import { v4 as uuid } from "uuid"; import { lit, st, sym } from "rdflib"; -import { dc, vcard } from "./namespaces"; -import { UpdateOperation } from "./web-operations/executeUpdate"; +import { dc, vcard } from "../namespaces"; +import { UpdateOperation } from "./index"; jest.mock("uuid"); diff --git a/contacts/src/rdflib/createAddressBook.ts b/contacts/src/rdflib/update-operations/createAddressBook.ts similarity index 89% rename from contacts/src/rdflib/createAddressBook.ts rename to contacts/src/rdflib/update-operations/createAddressBook.ts index 9160e609..8fbe8e41 100644 --- a/contacts/src/rdflib/createAddressBook.ts +++ b/contacts/src/rdflib/update-operations/createAddressBook.ts @@ -1,7 +1,8 @@ import { lit, st, sym } from "rdflib"; import { v4 as uuid } from "uuid"; -import { dc, vcard } from "./namespaces"; -import { UpdateOperation } from "./web-operations/executeUpdate"; +import { dc, vcard } from "../namespaces"; + +import { UpdateOperation } from "./index"; export function createAddressBook( container: string, diff --git a/contacts/src/rdflib/createNewContact.spec.ts b/contacts/src/rdflib/update-operations/createNewContact.spec.ts similarity index 98% rename from contacts/src/rdflib/createNewContact.spec.ts rename to contacts/src/rdflib/update-operations/createNewContact.spec.ts index dce292f3..841ffb61 100644 --- a/contacts/src/rdflib/createNewContact.spec.ts +++ b/contacts/src/rdflib/update-operations/createNewContact.spec.ts @@ -1,7 +1,7 @@ import { createNewContact } from "./createNewContact"; -import { AddressBookQuery } from "./AddressBookQuery"; +import { AddressBookQuery } from "../queries"; import { lit, st, sym } from "rdflib"; -import { rdf, vcard } from "./namespaces"; +import { rdf, vcard } from "../namespaces"; describe("createNewContact", () => { it("returns the uri of the new contact", () => { diff --git a/contacts/src/rdflib/createNewContact.ts b/contacts/src/rdflib/update-operations/createNewContact.ts similarity index 88% rename from contacts/src/rdflib/createNewContact.ts rename to contacts/src/rdflib/update-operations/createNewContact.ts index ba4a3727..75c09e16 100644 --- a/contacts/src/rdflib/createNewContact.ts +++ b/contacts/src/rdflib/update-operations/createNewContact.ts @@ -1,9 +1,9 @@ -import { UpdateOperation } from "./web-operations/executeUpdate"; -import { AddressBookQuery } from "./AddressBookQuery"; +import { AddressBookQuery } from "../queries/AddressBookQuery"; import { lit, st, sym } from "rdflib"; -import { rdf, vcard } from "./namespaces"; +import { rdf, vcard } from "../namespaces"; -import { NewContact } from "../index"; +import { NewContact } from "../../index"; +import { UpdateOperation } from "./index"; export function createNewContact( addressBook: AddressBookQuery, diff --git a/contacts/src/rdflib/update-operations/createNewGroup.spec.ts b/contacts/src/rdflib/update-operations/createNewGroup.spec.ts new file mode 100644 index 00000000..7d30f151 --- /dev/null +++ b/contacts/src/rdflib/update-operations/createNewGroup.spec.ts @@ -0,0 +1,109 @@ +import { lit, st, sym } from "rdflib"; +import { AddressBookQuery } from "../queries"; +import { createNewGroup } from "./createNewGroup"; +import { rdf, vcard } from "../namespaces"; + +describe("createNewGroup", () => { + it("returns the uri of the new group", () => { + const addressBookQuery = { + proposeNewGroupNode: () => + sym( + "https://pod.test/contacts/Group/a426de26-b51c-4540-8068-10d0ac175cd7/index.ttl#this", + ), + queryGroupIndex: () => sym("https://pod.test/contacts/groups.ttl"), + } as unknown as AddressBookQuery; + const result = createNewGroup(addressBookQuery, "anything"); + expect(result.uri).toEqual( + "https://pod.test/contacts/Group/a426de26-b51c-4540-8068-10d0ac175cd7/index.ttl#this", + ); + }); + + it("returns no deletions", () => { + const addressBookQuery = { + proposeNewGroupNode: () => + sym( + "https://pod.test/contacts/Group/a426de26-b51c-4540-8068-10d0ac175cd7/index.ttl#this", + ), + queryGroupIndex: () => sym("https://pod.test/contacts/groups.ttl"), + } as unknown as AddressBookQuery; + const result = createNewGroup(addressBookQuery, "anything"); + expect(result.deletions).toEqual([]); + }); + + it("does not create any plain files", () => { + const addressBookQuery = { + proposeNewGroupNode: () => + sym( + "https://pod.test/contacts/Group/a426de26-b51c-4540-8068-10d0ac175cd7/index.ttl#this", + ), + queryGroupIndex: () => sym("https://pod.test/contacts/groups.ttl"), + } as unknown as AddressBookQuery; + const result = createNewGroup(addressBookQuery, "anything"); + expect(result.filesToCreate).toEqual([]); + }); + + it("throws an error if group index is missing", () => { + const addressBookQuery = { + proposeNewGroupNode: () => + sym( + "https://pod.test/contacts/Group/a426de26-b51c-4540-8068-10d0ac175cd7/index.ttl#this", + ), + queryGroupIndex: () => null, + } as unknown as AddressBookQuery; + expect(() => createNewGroup(addressBookQuery, "anything")).toThrow( + new Error("group index is missing or invalid"), + ); + }); + + describe("insertions", () => { + let addressBookQuery: AddressBookQuery; + const newGroupNode = sym( + "https://pod.test/contacts/Group/a426de26-b51c-4540-8068-10d0ac175cd7/index.ttl#this", + ); + beforeEach(() => { + addressBookQuery = { + addressBookNode: sym("https://pod.test/contacts/index.ttl#this"), + proposeNewGroupNode: () => newGroupNode, + queryGroupIndex: () => sym("https://pod.test/contacts/groups.ttl"), + } as unknown as AddressBookQuery; + }); + + it("inserts the group name to the groupIndex", () => { + const result = createNewGroup(addressBookQuery, "best friends"); + expect(result.insertions).toContainEqual( + st( + newGroupNode, + vcard("fn"), + lit("best friends"), + sym("https://pod.test/contacts/groups.ttl"), + ), + ); + }); + + it("inserts the group name to the group document", () => { + const result = createNewGroup(addressBookQuery, "best friends"); + expect(result.insertions).toContainEqual( + st(newGroupNode, vcard("fn"), lit("best friends"), newGroupNode.doc()), + ); + }); + + it("adds a type to the new group", () => { + const result = createNewGroup(addressBookQuery, "anything"); + expect(result.insertions).toContainEqual( + st(newGroupNode, rdf("type"), vcard("Group"), newGroupNode.doc()), + ); + }); + + it("adds the group to the address book", () => { + const result = createNewGroup(addressBookQuery, "anything"); + expect(result.insertions).toContainEqual( + st( + addressBookQuery.addressBookNode, + vcard("includesGroup"), + newGroupNode, + sym("https://pod.test/contacts/groups.ttl"), + ), + ); + }); + }); +}); diff --git a/contacts/src/rdflib/update-operations/createNewGroup.ts b/contacts/src/rdflib/update-operations/createNewGroup.ts new file mode 100644 index 00000000..980c8818 --- /dev/null +++ b/contacts/src/rdflib/update-operations/createNewGroup.ts @@ -0,0 +1,32 @@ +import { AddressBookQuery } from "../queries"; +import { UpdateOperation } from "./index"; +import { lit, st } from "rdflib"; +import { rdf, vcard } from "../namespaces"; + +export function createNewGroup( + addressBook: AddressBookQuery, + groupName: string, +): UpdateOperation { + const groupIndex = addressBook.queryGroupIndex(); + if (!groupIndex) { + throw new Error("group index is missing or invalid"); + } + const groupNode = addressBook.proposeNewGroupNode(); + const groupDoc = groupNode.doc(); + return { + uri: groupNode.uri, + insertions: [ + st(groupNode, vcard("fn"), lit(groupName), groupIndex), + st(groupNode, vcard("fn"), lit(groupName), groupDoc), + st(groupNode, rdf("type"), vcard("Group"), groupDoc), + st( + addressBook.addressBookNode, + vcard("includesGroup"), + groupNode, + groupIndex, + ), + ], + deletions: [], + filesToCreate: [], + }; +} diff --git a/contacts/src/rdflib/update-operations/index.ts b/contacts/src/rdflib/update-operations/index.ts new file mode 100644 index 00000000..4cf41690 --- /dev/null +++ b/contacts/src/rdflib/update-operations/index.ts @@ -0,0 +1,15 @@ +import { Statement } from "rdflib"; + +export { createAddressBook } from "./createAddressBook"; +export { createNewContact } from "./createNewContact"; + +export interface FileToCreate { + uri: string; +} + +export interface UpdateOperation { + uri: string; + insertions: Statement[]; + deletions: Statement[]; + filesToCreate: FileToCreate[]; +} diff --git a/contacts/src/rdflib/web-operations/executeUpdate.ts b/contacts/src/rdflib/web-operations/executeUpdate.ts index 4d6028cf..780f734d 100644 --- a/contacts/src/rdflib/web-operations/executeUpdate.ts +++ b/contacts/src/rdflib/web-operations/executeUpdate.ts @@ -1,15 +1,5 @@ -import { Fetcher, Statement, UpdateManager } from "rdflib"; - -export interface UpdateOperation { - uri: string; - insertions: Statement[]; - deletions: Statement[]; - filesToCreate: FileToCreate[]; -} - -export interface FileToCreate { - uri: string; -} +import { Fetcher, UpdateManager } from "rdflib"; +import { UpdateOperation } from "../update-operations"; export async function executeUpdate( fetcher: Fetcher,