social query from React

master
Niko PLP 2 weeks ago
parent 3a2f407d0c
commit bee4fb7154
  1. 2
      ng-app/src/apps/ProfileEditor.svelte
  2. 35
      ng-app/src/apps/SocialQueryDemo.svelte
  3. 5
      ng-net/src/app_protocol.rs
  4. 14
      ng-net/src/types.rs
  5. 96
      ng-sdk-js/example-webapp-react-socialquery/package-lock.json
  6. 5
      ng-sdk-js/example-webapp-react-socialquery/package.json
  7. 44
      ng-sdk-js/example-webapp-react-socialquery/src/.ldo/socialquery.context.ts
  8. 63
      ng-sdk-js/example-webapp-react-socialquery/src/.ldo/socialquery.schema.ts
  9. 19
      ng-sdk-js/example-webapp-react-socialquery/src/.ldo/socialquery.shapeTypes.ts
  10. 23
      ng-sdk-js/example-webapp-react-socialquery/src/.ldo/socialquery.typings.ts
  11. 18
      ng-sdk-js/example-webapp-react-socialquery/src/.shapes/socialquery.shex
  12. 2
      ng-sdk-js/example-webapp-react-socialquery/src/App.css
  13. 9
      ng-sdk-js/example-webapp-react-socialquery/src/App.tsx
  14. 21
      ng-sdk-js/example-webapp-react-socialquery/src/Contact.tsx
  15. 7
      ng-sdk-js/example-webapp-react-socialquery/src/Contacts.tsx
  16. 3
      ng-sdk-js/example-webapp-react-socialquery/src/Header.tsx
  17. 5
      ng-sdk-js/example-webapp-react-socialquery/src/MakeContact.tsx
  18. 212
      ng-sdk-js/example-webapp-react-socialquery/src/Query.tsx
  19. 57
      ng-verifier/src/inbox_processor.rs
  20. 30
      ng-verifier/src/request_processor.rs
  21. 15
      ng-verifier/src/verifier.rs

@ -60,7 +60,7 @@
console.log($cur_tab.doc.nuri);
//TODO: more sanitation on the input here!
await ng.sparql_update($active_session.session_id, "PREFIX vcard: <http://www.w3.org/2006/vcard/ns#>"
+"INSERT DATA { <> a vcard:Individual . <> vcard:fn \""+name.replace('"',"\\\"")+"\". <> vcard:hasEmail \""+email+"\" }", "did:ng:"+$cur_tab.doc.nuri );
+"INSERT DATA { <> a vcard:Individual . <> vcard:fn \""+name.trim().replace('"',"\\\"")+"\". <> vcard:hasEmail \""+email.trim()+"\" }", "did:ng:"+$cur_tab.doc.nuri );
toast_success("Your profile was edited successfully!");
set_view_or_edit(true);
} catch (e) {

@ -66,6 +66,7 @@ ORDER BY DESC(?total)`;
$: source = commits.graph.join(" .\r\n") + (commits.graph.length ? " .":"");
let results = [];
let already_started = false;
$: if (commits.graph.length > 4) {
sparql_query(ranking_query, false).then((res) => {
@ -74,6 +75,17 @@ ORDER BY DESC(?total)`;
});
}
$: if (commits) { check_if_started(commits.graph) }
function check_if_started(graph) {
for (const g of graph) {
if (g.substring(57,91) === "did:ng:x:ng#social_query_forwarder") {
already_started = true;
break;
}
}
}
const query = `PREFIX vcard: <http://www.w3.org/2006/vcard/ns#>
PREFIX xskills: <did:ng:x:skills#>
PREFIX ksp: <did:ng:k:skills:programming:>
@ -120,6 +132,7 @@ WHERE {
let session = $active_session;
if (!session) return;
let request_nuri = "did:ng:"+$cur_tab.doc.nuri+":c:"+commit_id+":k:"+commit_key;
console.log(request_nuri)
await ng.social_query_start(
session.session_id,
"did:ng:a",
@ -145,7 +158,7 @@ WHERE {
<Button
on:click={openQuery}
on:keypress={openQuery}
disabled={!$online}
disabled={!$online || already_started}
class="select-none ml-2 mt-2 mb-2 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55"
>
<Lifebuoy tabindex="-1" class="mr-2 focus:outline-none" />
@ -155,15 +168,17 @@ WHERE {
<div class="relative overflow-x-auto">
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 table-auto">
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<th scope="col" class="px-6 py-3">Email</th>
<th scope="col" class="px-6 py-3">Name</th>
<th scope="col" class="px-6 py-3">Rust</th>
<th scope="col" class="px-6 py-3">Svelte</th>
<th scope="col" class="px-6 py-3">Tailwind</th>
<th scope="col" class="px-6 py-3">Rdf</th>
<th scope="col" class="px-6 py-3">Yjs</th>
<th scope="col" class="px-6 py-3">Automerge</th>
<th scope="col" class="px-6 py-3">Total</th>
<tr>
<th scope="col" class="px-6 py-3">Email</th>
<th scope="col" class="px-6 py-3">Name</th>
<th scope="col" class="px-6 py-3">Rust</th>
<th scope="col" class="px-6 py-3">Svelte</th>
<th scope="col" class="px-6 py-3">Tailwind</th>
<th scope="col" class="px-6 py-3">Rdf</th>
<th scope="col" class="px-6 py-3">Yjs</th>
<th scope="col" class="px-6 py-3">Automerge</th>
<th scope="col" class="px-6 py-3">Total</th>
</tr>
</thead>
<tbody>
{#each results as res}

@ -34,6 +34,7 @@ pub enum AppFetchContentV0 {
SignatureRequest,
SignedSnapshotRequest,
Header,
CurrentHeads,
//Invoke,
}
@ -305,6 +306,10 @@ impl NuriV0 {
format!("s:{}:k:{}", obj_ref.id, obj_ref.key)
}
pub fn commit_ref(commit_ref: &ObjectRef) -> String {
format!("c:{}:k:{}", commit_ref.id, commit_ref.key)
}
pub fn token(token: &Digest) -> String {
format!("{DID_PREFIX}:n:{token}")
}

@ -3686,9 +3686,10 @@ impl InboxPost {
from: Option<(OverlayId,PrivKey)>,
query_id: RepoId,
forwarder_id: RepoId,
forwarder_readcap: ReadCap,
content: SocialQueryResponseContent
) -> Result<Self,NgError> {
let content = InboxMsgContent::SocialQuery(SocialQuery::Response(SocialQueryResponse { query_id, forwarder_id, content }));
let content = InboxMsgContent::SocialQuery(SocialQuery::Response(SocialQueryResponse { query_id, forwarder_id, forwarder_readcap, content }));
Self::new(to_overlay, to_inbox, from, &content, vec![], None)
}
@ -3704,7 +3705,8 @@ impl InboxPost {
let from = Some((msg.to_overlay, inbox_privkey));
let query_id = request.query_id;
let forwarder_id = request.forwarder_id;
let content = InboxMsgContent::SocialQuery(SocialQuery::Response(SocialQueryResponse { query_id, forwarder_id, content }));
let forwarder_readcap = request.forwarder_readcap.clone();
let content = InboxMsgContent::SocialQuery(SocialQuery::Response(SocialQueryResponse { query_id, forwarder_id, forwarder_readcap, content }));
Self::new(to_overlay, to_inbox, from, &content, vec![], None)
}
@ -3714,6 +3716,7 @@ impl InboxPost {
from_profile_store_repo: StoreRepo,
from_inbox: PrivKey,
forwarder_id: RepoId,
forwarder_readcap: ReadCap,
to_profile_nuri: String,
to_inbox_nuri: String,
to_broker: Option<Locator>,
@ -3745,6 +3748,7 @@ impl InboxPost {
let content = InboxMsgContent::SocialQuery(SocialQuery::Request(SocialQueryRequest{
query_id,
forwarder_id,
forwarder_readcap,
from_profile_store_repo,
degree,
definition_commit_body_ref,
@ -4178,6 +4182,9 @@ pub struct SocialQueryRequest {
/// Forwarder ID
pub forwarder_id: RepoId,
/// Forwarder read cap
pub forwarder_readcap: ReadCap,
/// Profile ID (must match the from_overlay)
pub from_profile_store_repo: StoreRepo,
@ -4211,6 +4218,9 @@ pub struct SocialQueryResponse {
/// Forwarder ID
pub forwarder_id: RepoId,
/// Forwarder read cap
pub forwarder_readcap: ReadCap,
/// Response content
pub content: SocialQueryResponseContent,
}

@ -12,9 +12,12 @@
"@ldo/connected-nextgraph": "^1.0.0-alpha.15",
"@ldo/ldo": "^1.0.0-alpha.14",
"@ldo/react": "^1.0.0-alpha.15",
"@rdfjs/data-model": "^1.2.0",
"@rdfjs/types": "^1.0.1",
"nextgraphweb": "^0.1.1-alpha.4",
"react": "^19.0.0",
"react-dom": "^19.0.0"
"react-dom": "^19.0.0",
"react-router": "^7.6.0"
},
"devDependencies": {
"@eslint/js": "^9.22.0",
@ -1228,16 +1231,6 @@
"jsonld2graphobject": "^0.0.5"
}
},
"node_modules/@ldo/schema-converter-shex/node_modules/@rdfjs/types": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-1.1.2.tgz",
"integrity": "sha512-wqpOJK1QCbmsGNtyzYnojPU8gRDPid2JO0Q0kMtb4j65xhCK880cnKAfEOwC+dX85VJcCByQx5zOwyyfCjDJsg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@ldo/schema-converter-shex/node_modules/jsonld2graphobject": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/jsonld2graphobject/-/jsonld2graphobject-0.0.5.tgz",
@ -1352,15 +1345,18 @@
}
},
"node_modules/@rdfjs/data-model": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/@rdfjs/data-model/-/data-model-1.3.4.tgz",
"integrity": "sha512-iKzNcKvJotgbFDdti7GTQDCYmL7GsGldkYStiP0K8EYtN7deJu5t7U11rKTz+nR7RtesUggT+lriZ7BakFv8QQ==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@rdfjs/data-model/-/data-model-1.2.0.tgz",
"integrity": "sha512-6ITWcu2sr9zJqXUPDm1XJ8DRpea7PotWBIkTzuO1MCSruLOWH2ICoQOAtlJy30cT+GqH9oAQKPR+CHXejsdizA==",
"license": "MIT",
"dependencies": {
"@rdfjs/types": ">=1.0.1"
"@types/rdf-js": "*"
},
"bin": {
"rdfjs-data-model-test": "bin/test.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@rdfjs/dataset": {
@ -1376,9 +1372,9 @@
}
},
"node_modules/@rdfjs/types": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-2.0.1.tgz",
"integrity": "sha512-uyAzpugX7KekAXAHq26m3JlUIZJOC0uSBhpnefGV5i15bevDyyejoB7I+9MKeUrzXD8OOUI3+4FeV1wwQr5ihA==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-1.1.2.tgz",
"integrity": "sha512-wqpOJK1QCbmsGNtyzYnojPU8gRDPid2JO0Q0kMtb4j65xhCK880cnKAfEOwC+dX85VJcCByQx5zOwyyfCjDJsg==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
@ -1917,6 +1913,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/rdf-js": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/rdf-js/-/rdf-js-4.0.1.tgz",
"integrity": "sha512-S+28+3RoFI+3arls7dS813gYnhb2HiyLX+gs00rgIvCzHU93DaYajhx4tyT+XEO8SjtzZw90OF4OVdYXBwbvkQ==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/react": {
"version": "19.1.2",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz",
@ -2818,6 +2823,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/cookie": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@ -4149,15 +4163,6 @@
"uuid": "^8.3.2"
}
},
"node_modules/jsonld2graphobject/node_modules/@rdfjs/types": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-1.1.2.tgz",
"integrity": "sha512-wqpOJK1QCbmsGNtyzYnojPU8gRDPid2JO0Q0kMtb4j65xhCK880cnKAfEOwC+dX85VJcCByQx5zOwyyfCjDJsg==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/jsonld2graphobject/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
@ -4971,15 +4976,6 @@
"@rdfjs/types": "^1.0.0"
}
},
"node_modules/rdf-data-factory/node_modules/@rdfjs/types": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-1.1.2.tgz",
"integrity": "sha512-wqpOJK1QCbmsGNtyzYnojPU8gRDPid2JO0Q0kMtb4j65xhCK880cnKAfEOwC+dX85VJcCByQx5zOwyyfCjDJsg==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/rdf-string": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/rdf-string/-/rdf-string-1.6.3.tgz",
@ -5021,6 +5017,28 @@
"node": ">=0.10.0"
}
},
"node_modules/react-router": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz",
"integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
"set-cookie-parser": "^2.6.0"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
}
}
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@ -5210,6 +5228,12 @@
"semver": "bin/semver.js"
}
},
"node_modules/set-cookie-parser": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
"license": "MIT"
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",

@ -15,9 +15,12 @@
"@ldo/connected-nextgraph": "^1.0.0-alpha.15",
"@ldo/ldo": "^1.0.0-alpha.14",
"@ldo/react": "^1.0.0-alpha.15",
"@rdfjs/data-model": "^1.2.0",
"@rdfjs/types": "^1.0.1",
"nextgraphweb": "^0.1.1-alpha.4",
"react": "^19.0.0",
"react-dom": "^19.0.0"
"react-dom": "^19.0.0",
"react-router": "^7.6.0"
},
"devDependencies": {
"@eslint/js": "^9.22.0",

@ -0,0 +1,44 @@
import { LdoJsonldContext } from "@ldo/ldo";
/**
* =============================================================================
* socialqueryContext: JSONLD Context for socialquery
* =============================================================================
*/
export const socialqueryContext: LdoJsonldContext = {
type: {
"@id": "@type",
},
SocialQuery: {
"@id": "did:ng:x:class#SocialQuery",
"@context": {
type: {
"@id": "@type",
},
socialQuerySparql: {
"@id": "did:ng:x:ng#social_query_sparql",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
socialQueryForwarder: {
"@id": "did:ng:x:ng#social_query_forwarder",
"@type": "@id",
},
socialQueryEnded: {
"@id": "did:ng:x:ng#social_query_ended",
"@type": "http://www.w3.org/2001/XMLSchema#dateTime",
},
},
},
socialQuerySparql: {
"@id": "did:ng:x:ng#social_query_sparql",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
socialQueryForwarder: {
"@id": "did:ng:x:ng#social_query_forwarder",
"@type": "@id",
},
socialQueryEnded: {
"@id": "did:ng:x:ng#social_query_ended",
"@type": "http://www.w3.org/2001/XMLSchema#dateTime",
},
};

@ -0,0 +1,63 @@
import { Schema } from "shexj";
/**
* =============================================================================
* socialquerySchema: ShexJ Schema for socialquery
* =============================================================================
*/
export const socialquerySchema: Schema = {
type: "Schema",
shapes: [
{
id: "did:ng:x:shape#SocialQuery",
type: "ShapeDecl",
shapeExpr: {
type: "Shape",
expression: {
type: "EachOf",
expressions: [
{
type: "TripleConstraint",
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
valueExpr: {
type: "NodeConstraint",
values: ["did:ng:x:class#SocialQuery"],
},
},
{
type: "TripleConstraint",
predicate: "did:ng:x:ng#social_query_sparql",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
min: 0,
max: 1,
},
{
type: "TripleConstraint",
predicate: "did:ng:x:ng#social_query_forwarder",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
min: 0,
max: 1,
},
{
type: "TripleConstraint",
predicate: "did:ng:x:ng#social_query_ended",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#dateTime",
},
min: 0,
max: 1,
},
],
},
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"],
},
},
],
};

@ -0,0 +1,19 @@
import { ShapeType } from "@ldo/ldo";
import { socialquerySchema } from "./socialquery.schema";
import { socialqueryContext } from "./socialquery.context";
import { SocialQuery } from "./socialquery.typings";
/**
* =============================================================================
* LDO ShapeTypes socialquery
* =============================================================================
*/
/**
* SocialQuery ShapeType
*/
export const SocialQueryShapeType: ShapeType<SocialQuery> = {
schema: socialquerySchema,
shape: "did:ng:x:shape#SocialQuery",
context: socialqueryContext,
};

@ -0,0 +1,23 @@
import { LdoJsonldContext, LdSet } from "@ldo/ldo";
/**
* =============================================================================
* Typescript Typings for socialquery
* =============================================================================
*/
/**
* SocialQuery Type
*/
export interface SocialQuery {
"@id"?: string;
"@context"?: LdoJsonldContext;
type: {
"@id": "SocialQuery";
};
socialQuerySparql?: string;
socialQueryForwarder?: {
"@id": string;
};
socialQueryEnded?: string;
}

@ -0,0 +1,18 @@
# Platform ontologies:
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX dc: <http://purl.org/dc/terms/>
PREFIX ngs: <did:ng:x:shape#>
PREFIX ngc: <did:ng:x:class#>
PREFIX ng: <did:ng:x:ng#>
ngs:SocialQuery EXTRA a {
a [ ngc:SocialQuery ];
ng:social_query_sparql xsd:string ?;
ng:social_query_forwarder IRI ?;
ng:social_query_ended xsd:dateTime ?;
}

@ -39,7 +39,7 @@ input {
margin: 5px;
}
#save {
.button {
background-color:rgb(73, 114, 165);
color:white;
cursor:pointer;

@ -2,7 +2,9 @@
import React, { FunctionComponent } from 'react';
import { Header } from './Header';
import { Contacts } from './Contacts';
import Query from './Query';
import { BrowserNGLdoProvider } from './reactMethods';
import { BrowserRouter, Routes, Route } from "react-router";
import './App.css'
import "../../../common/src/styles.css";
@ -13,7 +15,12 @@ const App: FunctionComponent = () => {
<div className="App">
<BrowserNGLdoProvider>
<Header />
<Contacts />
<BrowserRouter>
<Routes>
<Route path="/" element={<Contacts />} />
<Route path="/query" element={<Query />} />
</Routes>
</BrowserRouter>
</BrowserNGLdoProvider>
</div>
);

@ -1,9 +1,8 @@
import { default as React, FunctionComponent } from "react";
import { useNextGraphAuth } from "./reactMethods";
import { SocialContactShapeType, HasRatingShapeType } from "./.ldo/contact.shapeTypes.ts";
import { useSubscribeToResource, useResource, useSubject, useLdo } from "./reactMethods.ts";
import { SocialContactShapeType } from "./.ldo/contact.shapeTypes.ts";
import { useSubscribeToResource, useResource, useSubject, useLdo, useNextGraphAuth } from "./reactMethods.ts";
import { StarIcon } from '@heroicons/react/24/solid'
import { StarIcon as StarIconOutline, NoSymbolIcon } from '@heroicons/react/24/outline'
import { StarIcon as StarIconOutline } from '@heroicons/react/24/outline'
import {
startTransaction,
transactionChanges,
@ -75,7 +74,7 @@ export const Contact: FunctionComponent = ({nuri}) => {
"@id": ksp + ksp_mapping[skill]
}
})
let res = commitData(editing_contact);
let res = await commitData(editing_contact);
if (res.isError) {
console.error(res.message);
}
@ -111,7 +110,7 @@ export const Contact: FunctionComponent = ({nuri}) => {
// let changes = transactionChanges(editing_contact);
// console.log(changes.added?.toString())
// console.log(changes.removed?.toString())
let res = commitData(editing_contact);
let res = await commitData(editing_contact);
if (res.isError) {
console.error(res.message);
}
@ -120,7 +119,7 @@ export const Contact: FunctionComponent = ({nuri}) => {
// changes = transactionChanges(editing_rating);
// console.log(changes.added?.toString())
// console.log(changes.removed?.toString())
res = commitData(editing_rating);
res = await commitData(editing_rating);
if (res.isError) {
console.error(res.message);
}
@ -153,10 +152,10 @@ export const Contact: FunctionComponent = ({nuri}) => {
if (r.skill["@id"].substring(24) === ksp_mapping[skill]) {
let rating = changeData(r, re);
rating.rated = nextSkills[skill]-1;
const changes = transactionChanges(rating);
console.log(changes.added?.toString())
console.log(changes.removed?.toString())
const res = commitData(rating);
// const changes = transactionChanges(rating);
// console.log(changes.added?.toString())
// console.log(changes.removed?.toString())
const res = await commitData(rating);
if (res.isError) {
console.error(res.message);
}

@ -4,6 +4,8 @@ import { ContainerShapeType } from "./.ldo/container.shapeTypes.ts";
import { useSubscribeToResource, useResource, useSubject } from "./reactMethods.ts";
import { Contact } from "./Contact";
import { MakeContact } from "./MakeContact";
import { Link } from "react-router";
import { LifebuoyIcon } from '@heroicons/react/24/outline'
export const Contacts: FunctionComponent = () => {
const { session } = useNextGraphAuth();
@ -21,9 +23,12 @@ export const Contacts: FunctionComponent = () => {
return <>
<div className="centered">
<div className="flex flex-wrap justify-center gap-5 mt-10 mb-10">
<div className="flex flex-wrap justify-center gap-5 mt-10 mb-5">
<MakeContact/>
</div>
<div className="flex flex-wrap justify-center gap-5 mt-10 mb-10">
<Link to="/query"><button className="button"><LifebuoyIcon className="size-7 inline"/> Query</button> </Link>
</div>
<div className="flex flex-wrap justify-center gap-5 mb-10">
{
myContainer.contains?.map(

@ -26,7 +26,8 @@ export const Header: FunctionComponent = () => {
<div className="text-center max-w-6xl lg:px-8 mx-auto px-4 text-blue-800">
<svg className="mt-10 h-16 w-16 mx-auto" data-slot="icon" fill="none" strokeWidth="1.5" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<svg onClick={login}
onKeyUp={login} className="cursor-pointer mt-10 h-16 w-16 mx-auto" data-slot="icon" fill="none" strokeWidth="1.5" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" d="M8.25 9V5.25A2.25 2.25 0 0 1 10.5 3h6a2.25 2.25 0 0 1 2.25 2.25v13.5A2.25 2.25 0 0 1 16.5 21h-6a2.25 2.25 0 0 1-2.25-2.25V15M12 9l3 3m0 0-3 3m3-3H2.25"></path>
</svg>

@ -1,7 +1,6 @@
import { FormEvent, FunctionComponent, useCallback, useState } from "react";
import { BrowserNGLdoProvider, useLdo, dataset } from './reactMethods';
import { useLdo, dataset } from './reactMethods';
import { SocialContactShapeType } from "./.ldo/contact.shapeTypes.ts";
import { LdSet } from "@ldo/ldo";
export const MakeContact: FunctionComponent = () => {
const [name, setName] = useState("");
@ -54,7 +53,7 @@ export const MakeContact: FunctionComponent = () => {
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input type="submit" id="save" value="Save" />
<input type="submit" className="button" value="Add" />
</form>
);
};

@ -0,0 +1,212 @@
import React, { FunctionComponent, useState } from 'react';
import { LifebuoyIcon } from '@heroicons/react/24/outline'
import { useLdo, dataset, useNextGraphAuth, useResource, useSubject} from './reactMethods';
import { SocialQueryShapeType } from "./.ldo/socialquery.shapeTypes.ts";
import { namedNode } from "@rdfjs/data-model";
import type { Quad } from "@rdfjs/types";
import type { DatasetChanges } from "@ldo/rdf-utils";
import './App.css'
import "../../../common/src/styles.css";
const query_string = `PREFIX vcard: <http://www.w3.org/2006/vcard/ns#>
PREFIX xskills: <did:ng:x:skills#>
PREFIX ksp: <did:ng:k:skills:programming:>
PREFIX ng: <did:ng:x:ng#>
CONSTRUCT { [
vcard:hasEmail ?email;
vcard:fn ?name;
a vcard:Individual;
ng:site ?public_profile;
ng:protected ?protected_profile;
xskills:hasRating [
a xskills:Rating ;
xskills:rated ?level;
xskills:skill ?skill
]
]
}
WHERE {
?contact a vcard:Individual.
?contact vcard:fn ?name.
?contact vcard:hasEmail ?email.
OPTIONAL { ?contact ng:site ?public_profile . ?contact ng:site_inbox ?public_inbox }
OPTIONAL { ?contact ng:protected ?protected_profile . ?contact ng:protected_inbox ?prot_inbox }
?contact xskills:hasRating [
a xskills:Rating ;
xskills:rated ?level;
xskills:skill ?skill
].
?contact xskills:hasRating/xskills:skill ksp:rust.
?contact xskills:hasRating/xskills:skill ksp:svelte.
FILTER ( ?skill IN (
ksp:rust, ksp:svelte, ksp:rdf, ksp:tailwind, ksp:yjs, ksp:automerge
) )
}`;
const ranking_query = `SELECT ?mail (SAMPLE(?n) as?name) (MAX(?rust_) as ?rust) (MAX(?svelte_) as ?svelte) (MAX(?tailwind_) as ?tailwind)
(MAX(?rdf_) as ?rdf) (MAX(?yjs_) as ?yjs) (MAX(?automerge_) as ?automerge) (SUM(?total_) as ?total)
WHERE {
{ SELECT ?mail (SAMPLE(?name) as ?n) ?skill (AVG(?value)+1 AS ?score)
WHERE {
?rating <http://www.w3.org/2006/vcard/ns#hasEmail> ?mail.
?rating <http://www.w3.org/2006/vcard/ns#fn> ?name.
?rating <did:ng:x:skills#hasRating> ?hasrating.
?hasrating <did:ng:x:skills#rated> ?value.
?hasrating <did:ng:x:skills#skill> ?skill.
} GROUP BY ?mail ?skill
}
BIND (IF(sameTerm(?skill, <did:ng:k:skills:programming:rust>), ?score, 0) AS ?rust_)
BIND (IF(sameTerm(?skill, <did:ng:k:skills:programming:svelte>), ?score, 0) AS ?svelte_)
BIND (IF(sameTerm(?skill, <did:ng:k:skills:programming:tailwind>), ?score, 0) AS ?tailwind_)
BIND (IF(sameTerm(?skill, <did:ng:k:skills:programming:rdf>), ?score, 0) AS ?rdf_)
BIND (IF(sameTerm(?skill, <did:ng:k:skills:programming:yjs>), ?score, 0) AS ?yjs_)
BIND (IF(sameTerm(?skill, <did:ng:k:skills:programming:automerge>), ?score, 0) AS ?automerge_)
BIND (?tailwind_+?svelte_+?rust_+?rdf_+?yjs_+?automerge_ AS ?total_)
} GROUP BY ?mail
ORDER BY DESC(?total)`;
const Query: FunctionComponent = () => {
const { createData, commitData, changeData } = useLdo();
const { session } = useNextGraphAuth();
const [resourceUri, setResourceUri] = useState("");
useResource(resourceUri, { subscribe: true });
const [nuri, setNuri] = useState("");
const [querying, setQuerying] = useState(false);
const [results, setResults] = useState([]);
let social_query = useSubject(SocialQueryShapeType, session.sessionId && nuri ? nuri : undefined);
React.useEffect(() => {
async function start() {
let res = await session.ng.app_request_with_nuri_command(nuri, {Fetch:"CurrentHeads"}, session.sessionId);
let request_nuri = res.V0?.Text;
console.log(request_nuri);
// finally start the social query
res = await session.ng.social_query_start(
session.sessionId,
"did:ng:a",
request_nuri,
"did:ng:d:c",
0,
);
}
if (social_query?.socialQuerySparql) {
if (!social_query?.socialQueryForwarder) {
console.log(social_query?.socialQuerySparql);
start();
} else {
console.log("some results arrived")
dataset.on(
[null, null, null, namedNode(resourceUri)],
(changes: DatasetChanges<Quad>) => {
session.ng.sparql_query(session.sessionId, ranking_query, undefined, resourceUri).then((res) => {
setResults(res.results?.bindings);
});
},
);
}
}
}, [resourceUri, nuri, social_query, session])
const openQuery = async () => {
setQuerying(true);
try {
let resource = await dataset.createResource("nextgraph", { primaryClass: "social:query:skills:programming" });
if (!resource.isError) {
console.log("Created resource:", resource.uri);
setResourceUri(resource.uri);
setNuri(resource.uri.substring(0,53));
const query = createData(
SocialQueryShapeType,
nuri,
resource
);
query.type = { "@id": "SocialQuery" };
const result = await commitData(query);
if (result.isError) {
console.error(result.message);
}
// then add the did:ng:x:ng#social_query_sparql
//await session.ng.sparql_update(session.sessionId,`INSERT DATA { <${nuri}> <did:ng:x:ng#social_query_sparql> \"${query_string.replaceAll("\n"," ")}\".}`, resource.uri);
let editing_query = changeData(query, resource);
editing_query.socialQuerySparql = query_string.replaceAll("\n"," ");
// const changes = transactionChanges(editing_query);
// console.log(changes.added?.toString())
// console.log(changes.removed?.toString())
let res = await commitData(editing_query);
if (res.isError) {
console.error(result.message);
}
}
else {
console.error(resource);
}
} catch (e) {
console.error(e)
}
};
if (!session.sessionId) return <></>;
return (
<div className="centered">
<div className="flex flex-col justify-center gap-5 mt-10 mb-5">
{!querying && <p className="p-3">
<button
onClick={openQuery}
onKeyPress={openQuery}
className="button select-none ml-2 mt-2 mb-2 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55"
>
<LifebuoyIcon tabIndex={-1} className="mr-2 focus:outline-none size-6" />
Start query
</button>
</p>
}
<div className="relative overflow-x-auto">
<table className="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 table-auto">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" className="px-6 py-3">Email</th>
<th scope="col" className="px-6 py-3">Name</th>
<th scope="col" className="px-6 py-3">Rust</th>
<th scope="col" className="px-6 py-3">Svelte</th>
<th scope="col" className="px-6 py-3">Tailwind</th>
<th scope="col" className="px-6 py-3">Rdf</th>
<th scope="col" className="px-6 py-3">Yjs</th>
<th scope="col" className="px-6 py-3">Automerge</th>
<th scope="col" className="px-6 py-3">Total</th>
</tr>
</thead>
<tbody>
{
results.map((res) =>
<tr key={res.mail.value} className="bg-white border-b dark:bg-gray-800 dark:border-gray-700 border-gray-200">
<td scope="row" className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">{res.mail.value}</td>
<td scope="row" className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">{res.name.value}</td>
<td scope="row" className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">{Math.round(res.rust.value * 10) / 10 }</td>
<td scope="row" className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">{Math.round(res.svelte.value * 10) / 10 }</td>
<td scope="row" className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">{Math.round(res.tailwind.value * 10) / 10 }</td>
<td scope="row" className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">{Math.round(res.rdf.value * 10) / 10 }</td>
<td scope="row" className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">{Math.round(res.yjs.value * 10) / 10 }</td>
<td scope="row" className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">{Math.round(res.automerge.value * 10) / 10 }</td>
<td scope="row" className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">{Math.round(res.total.value * 10) / 10 }</td>
</tr>
)
}
</tbody>
</table>
</div>
</div>
</div>
);
}
export default Query

@ -9,11 +9,15 @@
//! Processor for each type of InboxMsgContent
use std::sync::Arc;
use base64_url::base64::read;
use ng_net::actor::SoS;
use ng_net::broker::BROKER;
use ng_oxigraph::oxigraph::sparql::QueryResults;
use ng_oxigraph::oxrdf::{NamedNode, Term, Triple};
use ng_oxigraph::oxsdatatypes::DateTime;
use ng_repo::types::{Block, ObjectRef, OverlayId, PrivKey, RepoId, StoreRepo, StoreRepoV0};
use ng_repo::types::{Block, ObjectRef, OverlayId, PrivKey, ReadCap, RepoId, StoreRepo, StoreRepoV0};
use ng_repo::{errors::*, store::Store, types::Commit};
use ng_repo::log::*;
@ -42,9 +46,9 @@ impl Verifier {
from_forwarder_nuri_string: &String,
from_profile_nuri_string: &String,
from_inbox_nuri_string: &String,
) -> Result<(String,NuriV0), VerifierError> {
) -> Result<(String, NuriV0, ReadCap), VerifierError> {
// creating the ForwardedSocialQuery in the private store
let forwarder = self.doc_create_with_store_repo(
let (forwarder, readcap) = self.doc_create_with_store_repo(
"Graph".to_string(), "social:query:forwarded".to_string(),
"store".to_string(), None // meaning in private store
).await?;
@ -66,7 +70,7 @@ impl Verifier {
if let Err(e) = ret {
return Err(VerifierError::SparqlError(e));
}
Ok((forwarder_nuri_string,forwarder_nuri))
Ok((forwarder_nuri_string,forwarder_nuri, readcap))
}
pub(crate) async fn mark_social_query_forwarder(&mut self, forwarder_nuri_string: &String, forwarder_nuri: &NuriV0, predicate: String) -> Result<(), VerifierError> {
@ -131,6 +135,7 @@ impl Verifier {
to_inbox_nuri: &String,
forwarder_nuri: &NuriV0,
forwarder_id: &RepoId,
forwarder_readcap: &ReadCap,
from_profiles: &(
(StoreRepo, PrivKey), // public
(StoreRepo, PrivKey) // protected
@ -166,6 +171,7 @@ impl Verifier {
from_profile.0,
from_profile.1.clone(),
*forwarder_id,
forwarder_readcap.clone(),
to_profile_nuri.clone(),
to_inbox_nuri.clone(),
None,
@ -217,7 +223,7 @@ impl Verifier {
}
// otherwise, create the forwarder
let (forwarder_nuri_string, forwarder_nuri) = self.create_social_query_forwarder(
let (forwarder_nuri_string, forwarder_nuri, readcap) = self.create_social_query_forwarder(
&social_query_doc_nuri_string,
&NuriV0::repo_id(&req.forwarder_id),
&NuriV0::from_store_repo_string(&req.from_profile_store_repo),
@ -341,7 +347,8 @@ impl Verifier {
to_profile_nuri,
to_inbox_nuri,
&forwarder_nuri,
&forwarder_id,
&forwarder_id,
&readcap,
&from_profiles,
&req.query_id,
&req.definition_commit_body_ref,
@ -377,8 +384,38 @@ impl Verifier {
let forwarder_nuri = NuriV0::new_repo_target_from_id(&response.forwarder_id);
// TODO: first we open the response.forwarder_id (because in webapp, it might not be loaded yet)
//self.open_for_target(&forwarder_nuri.target, false).await?;
//first we open the response.forwarder_id (because in webapp, it might not be loaded yet)
{
let broker = BROKER.read().await;
let user = Some(self.user_id().clone());
let remote = (&self.connected_broker).into();
let private_store = self
.repos
.get(self.private_store_id())
.ok_or(NgError::StoreNotFound)?;
if self.repos.get(&response.forwarder_id).is_none() {
// we need to load the forwarder
self.load_repo_from_read_cap(
&response.forwarder_readcap,
&broker,
&user,
&remote,
Arc::clone(&private_store.store),
true,
)
.await?;
self.open_for_target(&forwarder_nuri.target, false).await?;
}
let main_branch_id = {
self.repos.get(&response.forwarder_id).unwrap().main_branch().unwrap().id
};
self.open_branch_(&response.forwarder_id, &main_branch_id,
false, &broker, &user, &self.connected_broker.clone(), true ).await?;
}
let forwarder_nuri_string = NuriV0::repo_id(&response.forwarder_id);
// checking that we do have a running ForwardedSocialQuery, and that it didnt end, otherwise it must be spam.
@ -526,6 +563,7 @@ impl Verifier {
Some(from),
response.query_id,
from_forwarder,
response.forwarder_readcap,
SocialQueryResponseContent::EndOfReplies
)?;
self.post_to_inbox(post).await?;
@ -569,6 +607,7 @@ impl Verifier {
Some(from),
response.query_id,
from_forwarder,
response.forwarder_readcap,
SocialQueryResponseContent::Graph(graph)
)?;
self.post_to_inbox(post).await?;
@ -604,7 +643,7 @@ impl Verifier {
_ => {}
}
let contact = self.doc_create_with_store_repo(
let (contact, _) = self.doc_create_with_store_repo(
"Graph".to_string(), "social:contact".to_string(),
"store".to_string(), None // meaning in private store
).await?;

@ -618,7 +618,7 @@ impl Verifier {
class_name: String,
destination: String,
store_repo: Option<StoreRepo>,
) -> Result<String, NgError> {
) -> Result<(String, ReadCap), NgError> {
let class = BranchCrdt::from(crdt, class_name)?;
@ -656,7 +656,7 @@ impl Verifier {
&mut self,
nuri: NuriV0,
doc_create: DocCreate
) -> Result<String, NgError> {
) -> Result<(String, ReadCap), NgError> {
//TODO: deal with doc_create.destination
let user_id = self.user_id().clone();
@ -672,9 +672,10 @@ impl Verifier {
)
.await?;
let header_branch_id = {
let (read_cap, header_branch_id) = {
let repo = self.get_repo(&repo_id, &store)?;
repo.header_branch().ok_or(NgError::BranchNotFound)?.id
(repo.read_cap.to_owned().unwrap(),
repo.header_branch().ok_or(NgError::BranchNotFound)?.id)
};
// adding an AddRepo commit to the Store branch of store.
@ -710,7 +711,7 @@ impl Verifier {
if let Err(e) = ret {
return Err(NgError::SparqlError(e.to_string()));
}
Ok(nuri_result)
Ok((nuri_result,read_cap))
}
fn get_profile_for_inbox_post(&self, public: bool) -> Result<(StoreRepo, PrivKey),NgError> {
@ -926,7 +927,7 @@ impl Verifier {
}
// creating the ForwardedSocialQuery in the private store
let forwarder = self.doc_create_with_store_repo(
let (forwarder, forwarder_readcap) = self.doc_create_with_store_repo(
"Graph".to_string(), "social:query:forwarded".to_string(),
"store".to_string(), None // meaning in private store
).await?;
@ -955,7 +956,7 @@ impl Verifier {
return Err(NgError::SparqlError(e));
}
let from_profiles = self.get_2_profiles()?;
let from_profiles: ((StoreRepo, PrivKey), (StoreRepo, PrivKey)) = self.get_2_profiles()?;
for (to_profile_nuri, to_inbox_nuri) in contacts {
@ -963,7 +964,8 @@ impl Verifier {
&to_profile_nuri,
&to_inbox_nuri,
&forwarder_nuri,
&forwarder_id,
&forwarder_id,
&forwarder_readcap,
&from_profiles,
query_id,
&definition_commit_body_ref,
@ -1059,7 +1061,7 @@ impl Verifier {
match self.doc_create(nuri, doc_create).await {
Err(NgError::SparqlError(e)) => Ok(AppResponse::error(e)),
Err(e) => Err(e),
Ok(nuri_result) => Ok(AppResponse::V0(AppResponseV0::Nuri(nuri_result)))
Ok((nuri_result,_)) => Ok(AppResponse::V0(AppResponseV0::Nuri(nuri_result)))
}
} else {
Err(NgError::InvalidPayload)
@ -1232,6 +1234,16 @@ impl Verifier {
return Ok(AppResponse::V0(AppResponseV0::Text(vec.join("\n"))));
}
AppFetchContentV0::CurrentHeads => {
if nuri.target.is_repo_id() {
if let Ok(s) = self.get_main_branch_current_heads_nuri(nuri.target.repo_id()) {
return Ok(AppResponse::V0(AppResponseV0::Text(s)));
}
}
return Ok(AppResponse::error(VerifierError::InvalidNuri.to_string()));
}
AppFetchContentV0::History => {
if !nuri.is_valid_for_sparql_update() {
return Err(NgError::InvalidNuri);

@ -1374,7 +1374,7 @@ impl Verifier {
}
}
async fn open_branch_<'a>(
pub(crate) async fn open_branch_<'a>(
&mut self,
repo_id: &RepoId,
branch: &BranchId,
@ -1792,6 +1792,19 @@ impl Verifier {
}
}
pub(crate) fn get_main_branch_current_heads_nuri(&self, repo_id: &RepoId) -> Result<String, VerifierError> {
if let Some(repo) = self.repos.get(repo_id) {
if let Some(info) = repo.main_branch() {
let mut res = NuriV0::repo_id(repo_id);
for head in info.current_heads.iter() {
res = [res,NuriV0::commit_ref(head)].join(":");
}
return Ok(res);
}
}
Err(VerifierError::RepoNotFound)
}
fn update_branch_current_heads(
&mut self,
repo_id: &RepoId,

Loading…
Cancel
Save