Skip to content

Commit

Permalink
Merge pull request #134 from perspect3vism/link-mutation-method
Browse files Browse the repository at this point in the history
Link mutation methods
  • Loading branch information
jdeepee authored Dec 8, 2022
2 parents 21aa33a + 1e36546 commit f6dcc9f
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 7 deletions.
49 changes: 49 additions & 0 deletions core/src/Ad4mClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,55 @@ describe('Ad4mClient', () => {
expect(link.data.target).toBe('lang://Qm123')
})

it('addLinks() smoke test', async () => {
const links = await ad4mClient.perspective.addLinks('00001', [
{source: 'root', target: 'lang://Qm123', predicate: 'p'},
{source: 'root', target: 'lang://Qm123', predicate: 'p'}
])
expect(links.length).toBe(2)
expect(links[0].author).toBe('did:ad4m:test')
expect(links[0].data.source).toBe('root')
expect(links[0].data.predicate).toBe('p')
expect(links[0].data.target)
})

it('removeLinks() smoke test', async () => {
const links = await ad4mClient.perspective.removeLinks('00001', [
{author: '', timestamp: '', proof: {signature: '', key: ''}, data: {source: 'root', target: 'lang://Qm123', predicate: 'p'}},
{author: '', timestamp: '', proof: {signature: '', key: ''}, data: {source: 'root', target: 'lang://Qm123', predicate: 'p'}}
])
expect(links.length).toBe(2)
expect(links[0].author).toBe('did:ad4m:test')
expect(links[0].data.source).toBe('root')
expect(links[0].data.predicate).toBe('p')
expect(links[0].data.target)
})

it('linkMutations() smoke test', async () => {
const mutations = await ad4mClient.perspective.linkMutations('00001', {
additions: [
{source: 'root', target: 'lang://Qm123', predicate: 'p'},
{source: 'root', target: 'lang://Qm123', predicate: 'p'}
],
removals: [
{author: '', timestamp: '', proof: {signature: '', key: ''}, data: {source: 'root', target: 'lang://Qm123', predicate: 'p'}},
{author: '', timestamp: '', proof: {signature: '', key: ''}, data: {source: 'root', target: 'lang://Qm123', predicate: 'p'}}
]
});
expect(mutations.additions.length).toBe(2)
expect(mutations.removals.length).toBe(2)

expect(mutations.additions[0].author).toBe('did:ad4m:test')
expect(mutations.additions[0].data.source).toBe('root')
expect(mutations.additions[0].data.predicate).toBe('p')
expect(mutations.additions[0].data.target).toBe('lang://Qm123')

expect(mutations.removals[0].author).toBe('did:ad4m:test')
expect(mutations.removals[0].data.source).toBe('root')
expect(mutations.removals[0].data.predicate).toBe('p')
expect(mutations.removals[0].data.target).toBe('lang://Qm123')
})

it('addLinkExpression() smoke test', async () => {
const testLink = new LinkExpression()
testLink.author = "did:ad4m:test"
Expand Down
13 changes: 13 additions & 0 deletions core/src/links/Links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,21 @@ export class LinkMutations {
@Field(type => [LinkInput])
additions: LinkInput[];

@Field(type => [LinkExpressionInput])
removals: LinkExpressionInput[];
}
@ObjectType()
export class LinkExpressionMutations {
@Field(type => [LinkExpression])
additions: LinkExpression[];

@Field(type => [LinkExpression])
removals: LinkExpression[];

constructor(additions: LinkExpression[], removals: LinkExpression[]) {
this.additions = additions
this.removals = removals
}
}

@InputType()
Expand Down
43 changes: 42 additions & 1 deletion core/src/perspectives/PerspectiveClient.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApolloClient, gql } from "@apollo/client/core";
import { Link, LinkExpressionInput, LinkExpression, LinkInput } from "../links/Links";
import { Link, LinkExpressionInput, LinkExpression, LinkInput, LinkMutations, LinkExpressionMutations } from "../links/Links";
import unwrapApolloResult from "../unwrapApolloResult";
import { LinkQuery } from "./LinkQuery";
import { Perspective } from "./Perspective";
Expand Down Expand Up @@ -167,6 +167,47 @@ export class PerspectiveClient {
return perspectiveAddLink
}

async addLinks(uuid: string, links: Link[]): Promise<LinkExpression[]> {
const { perspectiveAddLinks } = unwrapApolloResult(await this.#apolloClient.mutate({
mutation: gql`mutation perspectiveAddLinks($uuid: String!, $links: [LinkInput!]!){
perspectiveAddLinks(links: $links, uuid: $uuid) {
${LINK_EXPRESSION_FIELDS}
}
}`,
variables: { uuid, links }
}))
return perspectiveAddLinks
}

async removeLinks(uuid: string, links: LinkExpressionInput[]): Promise<LinkExpression[]> {
const { perspectiveRemoveLinks } = unwrapApolloResult(await this.#apolloClient.mutate({
mutation: gql`mutation perspectiveRemoveLinks($uuid: String!, $links: [LinkExpressionInput!]!){
perspectiveRemoveLinks(links: $links, uuid: $uuid) {
${LINK_EXPRESSION_FIELDS}
}
}`,
variables: { uuid, links }
}))
return perspectiveRemoveLinks
}

async linkMutations(uuid: string, mutations: LinkMutations): Promise<LinkExpressionMutations> {
const { perspectiveLinkMutations } = unwrapApolloResult(await this.#apolloClient.mutate({
mutation: gql`mutation perspectiveLinkMutations($uuid: String!, $mutations: LinkMutations!){
perspectiveLinkMutations(mutations: $mutations, uuid: $uuid) {
additions {
${LINK_EXPRESSION_FIELDS}
}
removals {
${LINK_EXPRESSION_FIELDS}
}
}
}`,
variables: { uuid, mutations }
}))
return perspectiveLinkMutations
}

async addLinkExpression(uuid: string, link: LinkExpressionInput): Promise<LinkExpression> {
const { perspectiveAddLinkExpression } = unwrapApolloResult(await this.#apolloClient.mutate({
mutation: gql`mutation perspectiveAddLinkExpression($uuid: String!, $link: LinkExpressionInput!){
Expand Down
23 changes: 19 additions & 4 deletions core/src/perspectives/PerspectiveProxy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { LinkCallback, PerspectiveClient } from "./PerspectiveClient";
import { Link, LinkExpression, LinkExpressionInput } from "../links/Links";
import { Link, LinkExpression, LinkExpressionInput, LinkExpressionMutations, LinkInput, LinkMutations } from "../links/Links";
import { LinkQuery } from "./LinkQuery";
import { Neighbourhood } from "../neighbourhood/Neighbourhood";
import { PerspectiveHandle } from './PerspectiveHandle'
Expand Down Expand Up @@ -96,16 +96,31 @@ export class PerspectiveProxy {
return await this.#client.addLink(this.#handle.uuid, link)
}

/** Adds multiple links to this perspective **/
async addLinks(links: Link[]): Promise<LinkExpression[]> {
return await this.#client.addLinks(this.#handle.uuid, links)
}

/** Removes multiple links from this perspective **/
async removeLinks(links: LinkExpressionInput[]): Promise<LinkExpression[]> {
return await this.#client.removeLinks(this.#handle.uuid, links)
}

/** Adds and removes multiple links from this perspective **/
async linkMutations(mutations: LinkMutations): Promise<LinkExpressionMutations> {
return await this.#client.linkMutations(this.#handle.uuid, mutations)
}

/** Adds a linkExpression to this perspective */
async addLinkExpression(link: LinkExpression): Promise<LinkExpression> {
async addLinkExpression(link: LinkExpressionInput): Promise<LinkExpression> {
return await this.#client.addLinkExpression(this.#handle.uuid, link)
}

async update(oldLink: LinkExpression, newLink: Link) {
async update(oldLink: LinkExpressionInput, newLink: Link) {
return await this.#client.updateLink(this.#handle.uuid, oldLink, newLink)
}

async remove(link: LinkExpression) {
async remove(link: LinkExpressionInput) {
return await this.#client.removeLink(this.#handle.uuid, link)
}

Expand Down
47 changes: 46 additions & 1 deletion core/src/perspectives/PerspectiveResolver.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Arg, Mutation, PubSub, PubSubEngine, Query, Resolver, Subscription } from "type-graphql";
import { LinkExpression, LinkExpressionInput, LinkInput } from "../links/Links";
import { LinkExpression, LinkExpressionInput, LinkExpressionMutations, LinkInput, LinkMutations } from "../links/Links";
import { Neighbourhood } from "../neighbourhood/Neighbourhood";
import { LinkQuery } from "./LinkQuery";
import { Perspective } from "./Perspective";
Expand Down Expand Up @@ -98,6 +98,51 @@ export default class PerspectiveResolver {
return l
}

@Mutation(returns => [LinkExpression])
perspectiveAddLinks(@Arg('uuid') uuid: string, @Arg('links', type => [LinkInput]) links: LinkInput[], @PubSub() pubSub: any): LinkExpression[] {
const l = new LinkExpression()
l.author = 'did:ad4m:test'
l.timestamp = Date.now()
l.proof = testLink.proof
l.data = links[0]

const l2 = new LinkExpression()
l2.author = 'did:ad4m:test'
l2.timestamp = Date.now()
l2.proof = testLink.proof
l2.data = links[0]

pubSub.publish(LINK_ADDED_TOPIC, { link: l })
pubSub.publish(LINK_ADDED_TOPIC, { link: l2 })
return [l, l2]
}

@Mutation(returns => [LinkExpression])
perspectiveRemoveLinks(@Arg('uuid') uuid: string, @Arg('links', type => [LinkExpressionInput]) links: LinkExpressionInput[], @PubSub() pubSub: any): LinkExpression[] {
const l = new LinkExpression()
l.author = 'did:ad4m:test'
l.timestamp = Date.now()
l.proof = testLink.proof
l.data = links[0].data

const l2 = new LinkExpression()
l2.author = 'did:ad4m:test'
l2.timestamp = Date.now()
l2.proof = testLink.proof
l2.data = links[0].data

pubSub.publish(LINK_REMOVED_TOPIC, { link: l })
pubSub.publish(LINK_ADDED_TOPIC, { link: l2 })
return [l, l2]
}

@Mutation(returns => LinkExpressionMutations)
perspectiveLinkMutations(@Arg('uuid') uuid: string, @Arg('mutations') mutations: LinkMutations, @PubSub() pubSub: any): LinkExpressionMutations {
const perspectiveAddLinks = this.perspectiveAddLinks(uuid, mutations.additions, pubSub);
const perspectiveRemoveLinks = this.perspectiveRemoveLinks(uuid, mutations.removals, pubSub);
return new LinkExpressionMutations(perspectiveAddLinks, perspectiveRemoveLinks)
}

@Mutation(returns => LinkExpression)
perspectiveAddLinkExpression(@Arg('uuid') uuid: string, @Arg('link') link: LinkExpressionInput, @PubSub() pubSub: any): LinkExpression {
pubSub.publish(LINK_ADDED_TOPIC, { link })
Expand Down
80 changes: 79 additions & 1 deletion executor/src/core/Perspective.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Agent, Expression, Neighbourhood, LinkExpression, LinkExpressionInput, LinkInput, LanguageRef, PerspectiveHandle, Literal, PerspectiveDiff, parseExprUrl, Perspective as Ad4mPerspective } from "@perspect3vism/ad4m"
import { Agent, Expression, Neighbourhood, LinkExpression, LinkExpressionInput, LinkInput, LanguageRef, PerspectiveHandle, Literal, PerspectiveDiff, parseExprUrl, Perspective as Ad4mPerspective, LinkMutations, LinkExpressionMutations } from "@perspect3vism/ad4m"
import { Link, linkEqual, LinkQuery } from "@perspect3vism/ad4m";
import { SHA3 } from "sha3";
import type AgentService from "./agent/AgentService";
Expand Down Expand Up @@ -360,6 +360,84 @@ export default class Perspective {
return linkExpression
}

async addLinks(links: (LinkInput | LinkExpressionInput)[]): Promise<LinkExpression[]> {
const linkExpressions = links.map(l => this.ensureLinkExpression(l));
const diff = {
additions: linkExpressions,
removals: []
} as PerspectiveDiff
const addLinks = await this.commit(diff);

if (!addLinks) {
this.#db.addPendingDiff(this.uuid, diff);
}

linkExpressions.forEach(l => this.addLocalLink(l))
this.#prologNeedsRebuild = true;
for (const link of linkExpressions) {
this.#pubsub.publish(PubSub.LINK_ADDED_TOPIC, {
perspective: this.plain(),
link: link
})
};

return linkExpressions
}

async removeLinks(links: LinkInput[]): Promise<LinkExpression[]> {
const linkExpressions = links.map(l => this.ensureLinkExpression(l));
const diff = {
additions: [],
removals: linkExpressions
} as PerspectiveDiff
const removeLinks = await this.commit(diff);

if (!removeLinks) {
this.#db.addPendingDiff(this.uuid, diff);
}

linkExpressions.forEach(l => this.removeLocalLink(l))
this.#prologNeedsRebuild = true;
for (const link of linkExpressions) {
this.#pubsub.publish(PubSub.LINK_ADDED_TOPIC, {
perspective: this.plain(),
link: link
})
};

return linkExpressions
}

async linkMutations(mutations: LinkMutations): Promise<LinkExpressionMutations> {
const diff = {
additions: mutations.additions.map(l => this.ensureLinkExpression(l)),
removals: mutations.removals.map(l => this.ensureLinkExpression(l))
};
const mutation = await this.commit(diff);

if (!mutation) {
this.#db.addPendingDiff(this.uuid, diff);
};

diff.additions.forEach(l => this.addLocalLink(l))
diff.removals.forEach(l => this.removeLocalLink(l))
this.#prologNeedsRebuild = true;
for (const link of diff.additions) {
this.#pubsub.publish(PubSub.LINK_ADDED_TOPIC, {
perspective: this.plain(),
link: link
});
};
for (const link of diff.removals) {
this.#pubsub.publish(PubSub.LINK_REMOVED_TOPIC, {
perspective: this.plain(),
link: link
});
};

return diff;
}

private findLink(linkToFind: LinkExpressionInput): string | undefined {
const allLinks = this.#db.getAllLinks(this.uuid)
for(const {name, link} of allLinks) {
Expand Down
21 changes: 21 additions & 0 deletions executor/src/core/graphQL-interface/GraphQL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,13 @@ function createResolvers(core: PerspectivismCore, config: OuterConfig) {
return await perspective.addLink(link)
},
//@ts-ignore
perspectiveAddLinks: async (parent, args, context, info) => {
const { uuid, links } = args
checkCapability(context.capabilities, Auth.perspectiveUpdateCapability([uuid]))
const perspective = core.perspectivesController.perspective(uuid)
return await perspective.addLinks(links)
},
//@ts-ignore
perspectiveAddLinkExpression: async (parent, args, context, info) => {
const { uuid, link } = args
checkCapability(context.capabilities, Auth.perspectiveUpdateCapability([uuid]))
Expand All @@ -547,6 +554,20 @@ function createResolvers(core: PerspectivismCore, config: OuterConfig) {
return true
},
//@ts-ignore
perspectiveRemoveLinks: async (parent, args, context, info) => {
const { uuid, links } = args
checkCapability(context.capabilities, Auth.perspectiveUpdateCapability([uuid]))
const perspective = core.perspectivesController.perspective(uuid)
return await perspective.removeLinks(links)
},
//@ts-ignore
perspectiveLinkMutations: async (parent, args, context, info) => {
const { uuid, mutations } = args
checkCapability(context.capabilities, Auth.perspectiveUpdateCapability([uuid]))
const perspective = core.perspectivesController.perspective(uuid)
return await perspective.linkMutations(mutations)
},
//@ts-ignore
perspectiveUpdate: (parent, args, context, info) => {
const { uuid, name } = args
checkCapability(context.capabilities, Auth.perspectiveUpdateCapability([uuid]))
Expand Down
Loading

0 comments on commit f6dcc9f

Please sign in to comment.