Let's maybe start committing some stuff
This commit is contained in:
commit
c8b0d4e686
13 changed files with 4909 additions and 0 deletions
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
.cpcache
|
||||||
|
.nrepl-port
|
||||||
|
target
|
||||||
|
repl
|
||||||
|
scratch.clj
|
||||||
|
.shadow-cljs
|
||||||
|
target
|
||||||
|
yarn.lock
|
||||||
|
node_modules/
|
||||||
|
.DS_Store
|
||||||
|
resources/public/ui
|
||||||
|
.store
|
||||||
|
deps.local.edn
|
||||||
|
.#*
|
2
bb.edn
Normal file
2
bb.edn
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
{:deps {com.lambdaisland/launchpad #_{:mvn/version "0.12.64-alpha"}
|
||||||
|
{:local/root "/home/arne/github/lambdaisland/launchpad"}}}
|
6
bin/launchpad
Executable file
6
bin/launchpad
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/usr/bin/env bb
|
||||||
|
|
||||||
|
(require '[lambdaisland.launchpad :as launchpad])
|
||||||
|
|
||||||
|
(launchpad/main {:steps (into [(partial launchpad/ensure-java-version 17)]
|
||||||
|
launchpad/default-steps)})
|
6
deps.edn
Normal file
6
deps.edn
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{:deps {hato/hato {:mvn/version "0.9.0"}
|
||||||
|
cheshire/cheshire {:mvn/version "5.11.0"}
|
||||||
|
seancorfield/next.jdbc {:mvn/version "1.2.659"}
|
||||||
|
com.impossibl.pgjdbc-ng/pgjdbc-ng {:mvn/version "0.8.9"}
|
||||||
|
|
||||||
|
}}
|
156
repl_sessions/first_experiments.clj
Normal file
156
repl_sessions/first_experiments.clj
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
(ns first-experiments
|
||||||
|
(:require
|
||||||
|
[clojure.string :as str]
|
||||||
|
[hato.client :as hato])
|
||||||
|
(:import
|
||||||
|
(com.apicatalog.jsonld JsonLd)
|
||||||
|
(com.apicatalog.jsonld.document JsonDocument)))
|
||||||
|
|
||||||
|
(set! *warn-on-reflection* true)
|
||||||
|
#_
|
||||||
|
(:body
|
||||||
|
(hato/get #_"https://toot.cat/.well-known/webfinger?resource=acct:plexus@toot.cat"
|
||||||
|
#_"https://toot.cat/users/plexus"
|
||||||
|
#_"https://www.w3.org/ns/activitystreams"
|
||||||
|
{:headers {"Accept" "application/json"}
|
||||||
|
:as :stream}))
|
||||||
|
|
||||||
|
(defn json-fetch [url]
|
||||||
|
(hato/get url
|
||||||
|
{:headers {"Accept" "application/json"}
|
||||||
|
:http-client {:redirect-policy :normal}
|
||||||
|
:as :json-string-keys}))
|
||||||
|
|
||||||
|
(defn expand-context
|
||||||
|
([new-context]
|
||||||
|
(expand-context {} new-context))
|
||||||
|
([current-context new-context]
|
||||||
|
(cond
|
||||||
|
(string? new-context)
|
||||||
|
(do (println '->> new-context)
|
||||||
|
(expand-context current-context
|
||||||
|
(get (:body (json-fetch new-context)) "@context")))
|
||||||
|
(sequential? new-context)
|
||||||
|
(reduce expand-context current-context new-context)
|
||||||
|
|
||||||
|
(map? new-context)
|
||||||
|
(into current-context
|
||||||
|
(map
|
||||||
|
(fn [[k v]]
|
||||||
|
(let [id (if (map? v) (get v "@id" v) v)
|
||||||
|
[prefix suffix] (str/split id #":")]
|
||||||
|
(if-let [base (get (merge current-context new-context) prefix)]
|
||||||
|
[k (assoc (if (map? v) v {})
|
||||||
|
"@id" (str (if (map? base) (get base "@id") base)
|
||||||
|
suffix))]
|
||||||
|
[k (if (map? v) v {"@id" v})]))))
|
||||||
|
new-context))))
|
||||||
|
|
||||||
|
(defn apply-context [v ctx]
|
||||||
|
(into {}
|
||||||
|
(map (fn [[k v]]
|
||||||
|
(let [attr (get ctx k)
|
||||||
|
k (get-in ctx [k "@id"] k)
|
||||||
|
v (cond
|
||||||
|
(map? v)
|
||||||
|
(apply-context v ctx)
|
||||||
|
|
||||||
|
(= "@type" k)
|
||||||
|
(get-in ctx [v "@id"] v)
|
||||||
|
|
||||||
|
:else
|
||||||
|
v)]
|
||||||
|
[k (if attr
|
||||||
|
(assoc (dissoc attr "@id") "@value" v)
|
||||||
|
v)])))
|
||||||
|
v))
|
||||||
|
|
||||||
|
(let [user (:body (json-fetch "https://toot.cat/users/plexus"))
|
||||||
|
ctx (expand-context {} (get user "@context"))]
|
||||||
|
(apply-context (dissoc user "@context") ctx)
|
||||||
|
)
|
||||||
|
|
||||||
|
(:body (json-fetch "https://www.w3.org/ns/activitystreams"))
|
||||||
|
|
||||||
|
(def clojure-prefixes
|
||||||
|
{"http://purl.org/dc/terms/" "org.purl.dc"
|
||||||
|
"http://www.w3.org/ns/ldp#" "org.w3.ldp"
|
||||||
|
"http://schema.org#" "org.schema"
|
||||||
|
"http://www.w3.org/2006/vcard/ns#" "org.w3.vcard"
|
||||||
|
"http://joinmastodon.org/ns#" "org.joinmastodon"
|
||||||
|
"https://w3id.org/security#" "org.w3id.security"
|
||||||
|
"https://www.w3.org/ns/activitystreams#" "org.w3.activitystreams"
|
||||||
|
"http://www.w3.org/2001/XMLSchema#" "org.w3.xmlns"})
|
||||||
|
|
||||||
|
(json-fetch "https://toot.cat/users/plexus")
|
||||||
|
|
||||||
|
(def context (expand-context "https://toot.cat/users/plexus"))
|
||||||
|
(keep (comp #(get % "@id") val) context)
|
||||||
|
"https://toot.cat/@plexus/109403085288497274"
|
||||||
|
|
||||||
|
(expand-context "https://www.w3.org/ns/activitystreams#Image")
|
||||||
|
|
||||||
|
#_(.get (com.apicatalog.jsonld.JsonLd/expand "https://toot.cat/users/plexus"))
|
||||||
|
|
||||||
|
;; JsonLd.expand("https://w3c.github.io/json-ld-api/tests/expand/0001-in.jsonld")
|
||||||
|
;; .ordered()
|
||||||
|
;; .get();
|
||||||
|
|
||||||
|
(def ^JsonDocument json-doc
|
||||||
|
(JsonDocument/of
|
||||||
|
^java.io.InputStream
|
||||||
|
(:body
|
||||||
|
(hato/get "https://toot.cat/users/plexus"
|
||||||
|
{:headers {"Accept" "application/json"}
|
||||||
|
:as :stream}))))
|
||||||
|
|
||||||
|
(def expanded (.get (JsonLd/expand json-doc)))
|
||||||
|
(def flattened (.get (JsonLd/flatten json-doc)))
|
||||||
|
(def rdf (.get (JsonLd/toRdf json-doc)))
|
||||||
|
|
||||||
|
(defn to-clj [v]
|
||||||
|
(cond
|
||||||
|
(instance? java.util.List v)
|
||||||
|
(into [] (map to-clj) v)
|
||||||
|
|
||||||
|
(instance? java.util.Map v)
|
||||||
|
(update-vals v to-clj)
|
||||||
|
|
||||||
|
(instance? jakarta.json.JsonString v)
|
||||||
|
(.getString ^jakarta.json.JsonString v)
|
||||||
|
|
||||||
|
(instance? jakarta.json.JsonValue v)
|
||||||
|
(let [t (.getValueType ^jakarta.json.JsonValue v)]
|
||||||
|
(cond
|
||||||
|
(= t jakarta.json.JsonValue$ValueType/TRUE) true
|
||||||
|
(= t jakarta.json.JsonValue$ValueType/FALSE) false)
|
||||||
|
)
|
||||||
|
|
||||||
|
:else v
|
||||||
|
))
|
||||||
|
|
||||||
|
(to-clj expanded)
|
||||||
|
|
||||||
|
;; (clojure.reflect/reflect jakarta.json.JsonValue)
|
||||||
|
;; (clojure.reflect/reflect rdf)
|
||||||
|
|
||||||
|
(clojure.walk/postwalk
|
||||||
|
(fn [v]
|
||||||
|
(if (map? v)
|
||||||
|
(update-keys v (fn [k]
|
||||||
|
(case k
|
||||||
|
"@id" :json-ld/id
|
||||||
|
"@type" :json-ld/type
|
||||||
|
"@value" :json-ld/value
|
||||||
|
(if-let [kw (and (string? k)
|
||||||
|
(some (fn [[url ns]]
|
||||||
|
(when (.startsWith k url)
|
||||||
|
(keyword ns
|
||||||
|
(subs k (.length url)))))
|
||||||
|
clojure-prefixes))]
|
||||||
|
kw
|
||||||
|
k))))
|
||||||
|
v))
|
||||||
|
(to-clj expanded))
|
||||||
|
|
||||||
|
(set! *print-namespace-maps* false)
|
239
repl_sessions/first_stream.clj
Normal file
239
repl_sessions/first_stream.clj
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
(ns repl-sessions.first-stream
|
||||||
|
(:require [lambdaisland.souk.json-ld :refer :all]))
|
||||||
|
|
||||||
|
(spit "resources/lambdaisland/souk/json_ld_contexts.edn"
|
||||||
|
(with-out-str (clojure.pprint/pprint @context-cache)))
|
||||||
|
|
||||||
|
(reset!
|
||||||
|
context-cache
|
||||||
|
{"https://www.w3.org/ns/activitystreams"
|
||||||
|
{"Dislike" "as:Dislike",
|
||||||
|
"Leave" "as:Leave",
|
||||||
|
"Application" "as:Application",
|
||||||
|
"Listen" "as:Listen",
|
||||||
|
"followers" {"@id" "as:followers", "@type" "@id"},
|
||||||
|
"startIndex" {"@id" "as:startIndex", "@type" "xsd:nonNegativeInteger"},
|
||||||
|
"View" "as:View",
|
||||||
|
"inbox" {"@id" "ldp:inbox", "@type" "@id"},
|
||||||
|
"object" {"@id" "as:object", "@type" "@id"},
|
||||||
|
"Like" "as:Like",
|
||||||
|
"shares" {"@id" "as:shares", "@type" "@id"},
|
||||||
|
"nameMap" {"@id" "as:name", "@container" "@language"},
|
||||||
|
"width" {"@id" "as:width", "@type" "xsd:nonNegativeInteger"},
|
||||||
|
"Relationship" "as:Relationship",
|
||||||
|
"origin" {"@id" "as:origin", "@type" "@id"},
|
||||||
|
"Link" "as:Link",
|
||||||
|
"url" {"@id" "as:url", "@type" "@id"},
|
||||||
|
"bto" {"@id" "as:bto", "@type" "@id"},
|
||||||
|
"inReplyTo" {"@id" "as:inReplyTo", "@type" "@id"},
|
||||||
|
"next" {"@id" "as:next", "@type" "@id"},
|
||||||
|
"ldp" "http://www.w3.org/ns/ldp#",
|
||||||
|
"signClientKey" {"@id" "as:signClientKey", "@type" "@id"},
|
||||||
|
"CollectionPage" "as:CollectionPage",
|
||||||
|
"describes" {"@id" "as:describes", "@type" "@id"},
|
||||||
|
"anyOf" {"@id" "as:anyOf", "@type" "@id"},
|
||||||
|
"Organization" "as:Organization",
|
||||||
|
"OrderedCollection" "as:OrderedCollection",
|
||||||
|
"orderedItems" {"@id" "as:items", "@type" "@id", "@container" "@list"},
|
||||||
|
"Announce" "as:Announce",
|
||||||
|
"OrderedCollectionPage" "as:OrderedCollectionPage",
|
||||||
|
"height" {"@id" "as:height", "@type" "xsd:nonNegativeInteger"},
|
||||||
|
"Note" "as:Note",
|
||||||
|
"formerType" {"@id" "as:formerType", "@type" "@id"},
|
||||||
|
"Offer" "as:Offer",
|
||||||
|
"Video" "as:Video",
|
||||||
|
"Object" "as:Object",
|
||||||
|
"Travel" "as:Travel",
|
||||||
|
"Mention" "as:Mention",
|
||||||
|
"image" {"@id" "as:image", "@type" "@id"},
|
||||||
|
"Audio" "as:Audio",
|
||||||
|
"IntransitiveActivity" "as:IntransitiveActivity",
|
||||||
|
"endpoints" {"@id" "as:endpoints", "@type" "@id"},
|
||||||
|
"bcc" {"@id" "as:bcc", "@type" "@id"},
|
||||||
|
"Flag" "as:Flag",
|
||||||
|
"longitude" {"@id" "as:longitude", "@type" "xsd:float"},
|
||||||
|
"Question" "as:Question",
|
||||||
|
"radius" {"@id" "as:radius", "@type" "xsd:float"},
|
||||||
|
"Public" {"@id" "as:Public", "@type" "@id"},
|
||||||
|
"Activity" "as:Activity",
|
||||||
|
"IsMember" "as:IsMember",
|
||||||
|
"id" "@id",
|
||||||
|
"proxyUrl" {"@id" "as:proxyUrl", "@type" "@id"},
|
||||||
|
"IsContact" "as:IsContact",
|
||||||
|
"Event" "as:Event",
|
||||||
|
"hreflang" "as:hreflang",
|
||||||
|
"Block" "as:Block",
|
||||||
|
"Person" "as:Person",
|
||||||
|
"altitude" {"@id" "as:altitude", "@type" "xsd:float"},
|
||||||
|
"sharedInbox" {"@id" "as:sharedInbox", "@type" "@id"},
|
||||||
|
"latitude" {"@id" "as:latitude", "@type" "xsd:float"},
|
||||||
|
"liked" {"@id" "as:liked", "@type" "@id"},
|
||||||
|
"Arrive" "as:Arrive",
|
||||||
|
"summary" "as:summary",
|
||||||
|
"Delete" "as:Delete",
|
||||||
|
"attachment" {"@id" "as:attachment", "@type" "@id"},
|
||||||
|
"relationship" {"@id" "as:relationship", "@type" "@id"},
|
||||||
|
"href" {"@id" "as:href", "@type" "@id"},
|
||||||
|
"name" "as:name",
|
||||||
|
"closed" {"@id" "as:closed", "@type" "xsd:dateTime"},
|
||||||
|
"vcard" "http://www.w3.org/2006/vcard/ns#",
|
||||||
|
"Article" "as:Article",
|
||||||
|
"tag" {"@id" "as:tag", "@type" "@id"},
|
||||||
|
"published" {"@id" "as:published", "@type" "xsd:dateTime"},
|
||||||
|
"items" {"@id" "as:items", "@type" "@id"},
|
||||||
|
"startTime" {"@id" "as:startTime", "@type" "xsd:dateTime"},
|
||||||
|
"location" {"@id" "as:location", "@type" "@id"},
|
||||||
|
"Update" "as:Update",
|
||||||
|
"Add" "as:Add",
|
||||||
|
"Read" "as:Read",
|
||||||
|
"context" {"@id" "as:context", "@type" "@id"},
|
||||||
|
"partOf" {"@id" "as:partOf", "@type" "@id"},
|
||||||
|
"Remove" "as:Remove",
|
||||||
|
"preferredUsername" "as:preferredUsername",
|
||||||
|
"Profile" "as:Profile",
|
||||||
|
"totalItems" {"@id" "as:totalItems", "@type" "xsd:nonNegativeInteger"},
|
||||||
|
"prev" {"@id" "as:prev", "@type" "@id"},
|
||||||
|
"Follow" "as:Follow",
|
||||||
|
"IsFollowing" "as:IsFollowing",
|
||||||
|
"Tombstone" "as:Tombstone",
|
||||||
|
"subject" {"@id" "as:subject", "@type" "@id"},
|
||||||
|
"Page" "as:Page",
|
||||||
|
"@vocab" "_:",
|
||||||
|
"current" {"@id" "as:current", "@type" "@id"},
|
||||||
|
"content" "as:content",
|
||||||
|
"units" "as:units",
|
||||||
|
"Place" "as:Place",
|
||||||
|
"instrument" {"@id" "as:instrument", "@type" "@id"},
|
||||||
|
"Undo" "as:Undo",
|
||||||
|
"alsoKnownAs" {"@id" "as:alsoKnownAs", "@type" "@id"},
|
||||||
|
"duration" {"@id" "as:duration", "@type" "xsd:duration"},
|
||||||
|
"last" {"@id" "as:last", "@type" "@id"},
|
||||||
|
"rel" "as:rel",
|
||||||
|
"source" "as:source",
|
||||||
|
"TentativeReject" "as:TentativeReject",
|
||||||
|
"type" "@type",
|
||||||
|
"outbox" {"@id" "as:outbox", "@type" "@id"},
|
||||||
|
"mediaType" "as:mediaType",
|
||||||
|
"oneOf" {"@id" "as:oneOf", "@type" "@id"},
|
||||||
|
"deleted" {"@id" "as:deleted", "@type" "xsd:dateTime"},
|
||||||
|
"target" {"@id" "as:target", "@type" "@id"},
|
||||||
|
"replies" {"@id" "as:replies", "@type" "@id"},
|
||||||
|
"provideClientKey" {"@id" "as:provideClientKey", "@type" "@id"},
|
||||||
|
"Create" "as:Create",
|
||||||
|
"updated" {"@id" "as:updated", "@type" "xsd:dateTime"},
|
||||||
|
"generator" {"@id" "as:generator", "@type" "@id"},
|
||||||
|
"endTime" {"@id" "as:endTime", "@type" "xsd:dateTime"},
|
||||||
|
"TentativeAccept" "as:TentativeAccept",
|
||||||
|
"oauthAuthorizationEndpoint"
|
||||||
|
{"@id" "as:oauthAuthorizationEndpoint", "@type" "@id"},
|
||||||
|
"audience" {"@id" "as:audience", "@type" "@id"},
|
||||||
|
"Service" "as:Service",
|
||||||
|
"Image" "as:Image",
|
||||||
|
"Accept" "as:Accept",
|
||||||
|
"Document" "as:Document",
|
||||||
|
"preview" {"@id" "as:preview", "@type" "@id"},
|
||||||
|
"Invite" "as:Invite",
|
||||||
|
"contentMap" {"@id" "as:content", "@container" "@language"},
|
||||||
|
"Group" "as:Group",
|
||||||
|
"oauthTokenEndpoint" {"@id" "as:oauthTokenEndpoint", "@type" "@id"},
|
||||||
|
"uploadMedia" {"@id" "as:uploadMedia", "@type" "@id"},
|
||||||
|
"to" {"@id" "as:to", "@type" "@id"},
|
||||||
|
"accuracy" {"@id" "as:accuracy", "@type" "xsd:float"},
|
||||||
|
"IsFollowedBy" "as:IsFollowedBy",
|
||||||
|
"Reject" "as:Reject",
|
||||||
|
"summaryMap" {"@id" "as:summary", "@container" "@language"},
|
||||||
|
"Join" "as:Join",
|
||||||
|
"Move" "as:Move",
|
||||||
|
"as" "https://www.w3.org/ns/activitystreams#",
|
||||||
|
"actor" {"@id" "as:actor", "@type" "@id"},
|
||||||
|
"likes" {"@id" "as:likes", "@type" "@id"},
|
||||||
|
"following" {"@id" "as:following", "@type" "@id"},
|
||||||
|
"streams" {"@id" "as:streams", "@type" "@id"},
|
||||||
|
"cc" {"@id" "as:cc", "@type" "@id"},
|
||||||
|
"attributedTo" {"@id" "as:attributedTo", "@type" "@id"},
|
||||||
|
"result" {"@id" "as:result", "@type" "@id"},
|
||||||
|
"xsd" "http://www.w3.org/2001/XMLSchema#",
|
||||||
|
"first" {"@id" "as:first", "@type" "@id"},
|
||||||
|
"Collection" "as:Collection",
|
||||||
|
"icon" {"@id" "as:icon", "@type" "@id"},
|
||||||
|
"Ignore" "as:Ignore"},
|
||||||
|
"https://w3id.org/security/v1"
|
||||||
|
{"EncryptedMessage" "sec:EncryptedMessage",
|
||||||
|
"dc" "http://purl.org/dc/terms/",
|
||||||
|
"canonicalizationAlgorithm" "sec:canonicalizationAlgorithm",
|
||||||
|
"owner" {"@id" "sec:owner", "@type" "@id"},
|
||||||
|
"created" {"@id" "dc:created", "@type" "xsd:dateTime"},
|
||||||
|
"signatureValue" "sec:signatureValue",
|
||||||
|
"CryptographicKey" "sec:Key",
|
||||||
|
"publicKeyPem" "sec:publicKeyPem",
|
||||||
|
"iterationCount" "sec:iterationCount",
|
||||||
|
"id" "@id",
|
||||||
|
"publicKey" {"@id" "sec:publicKey", "@type" "@id"},
|
||||||
|
"Ed25519Signature2018" "sec:Ed25519Signature2018",
|
||||||
|
"publicKeyWif" "sec:publicKeyWif",
|
||||||
|
"GraphSignature2012" "sec:GraphSignature2012",
|
||||||
|
"creator" {"@id" "dc:creator", "@type" "@id"},
|
||||||
|
"publicKeyBase58" "sec:publicKeyBase58",
|
||||||
|
"cipherAlgorithm" "sec:cipherAlgorithm",
|
||||||
|
"digestAlgorithm" "sec:digestAlgorithm",
|
||||||
|
"LinkedDataSignature2015" "sec:LinkedDataSignature2015",
|
||||||
|
"cipherData" "sec:cipherData",
|
||||||
|
"privateKey" {"@id" "sec:privateKey", "@type" "@id"},
|
||||||
|
"EcdsaKoblitzSignature2016" "sec:EcdsaKoblitzSignature2016",
|
||||||
|
"expires" {"@id" "sec:expiration", "@type" "xsd:dateTime"},
|
||||||
|
"signatureAlgorithm" "sec:signingAlgorithm",
|
||||||
|
"signature" "sec:signature",
|
||||||
|
"domain" "sec:domain",
|
||||||
|
"LinkedDataSignature2016" "sec:LinkedDataSignature2016",
|
||||||
|
"revoked" {"@id" "sec:revoked", "@type" "xsd:dateTime"},
|
||||||
|
"encryptionKey" "sec:encryptionKey",
|
||||||
|
"cipherKey" "sec:cipherKey",
|
||||||
|
"salt" "sec:salt",
|
||||||
|
"digestValue" "sec:digestValue",
|
||||||
|
"type" "@type",
|
||||||
|
"password" "sec:password",
|
||||||
|
"expiration" {"@id" "sec:expiration", "@type" "xsd:dateTime"},
|
||||||
|
"publicKeyService" {"@id" "sec:publicKeyService", "@type" "@id"},
|
||||||
|
"nonce" "sec:nonce",
|
||||||
|
"authenticationTag" "sec:authenticationTag",
|
||||||
|
"privateKeyPem" "sec:privateKeyPem",
|
||||||
|
"sec" "https://w3id.org/security#",
|
||||||
|
"normalizationAlgorithm" "sec:normalizationAlgorithm",
|
||||||
|
"initializationVector" "sec:initializationVector",
|
||||||
|
"xsd" "http://www.w3.org/2001/XMLSchema#"}})
|
||||||
|
|
||||||
|
(:body (json-get "https://toot.cat/users/plexus"))
|
||||||
|
|
||||||
|
"@type" "https://www.w3.org/ns/activitystreams#Person"
|
||||||
|
|
||||||
|
(:body (json-get "https://toot.cat/users/plexus/outbox"))
|
||||||
|
(:body (json-get "https://toot.cat/users/plexus/outbox?page=true"))
|
||||||
|
(:body (json-get "https://www.w3.org/ns/activitystreams"))
|
||||||
|
|
||||||
|
(:body (json-get "https://toot.cat/users/plexus/outbox"))
|
||||||
|
|
||||||
|
(expand-context
|
||||||
|
(get (:body (json-get "https://toot.cat/users/plexus"))
|
||||||
|
"@context"))
|
||||||
|
|
||||||
|
{"inbox" "https://www.w3.org/ns/activitystreams#inbox"
|
||||||
|
"as" "https://www.w3.org/ns/activitystreams#"
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON-LD linked data
|
||||||
|
RDF Resource Description Framework
|
||||||
|
|
||||||
|
[entity attribute value]
|
||||||
|
[subject predicate object]
|
||||||
|
|
||||||
|
["http://.../clojure" "http://.../type-of-language" "https://..../functional"]
|
||||||
|
|
||||||
|
Uniform Resource Locator: URL
|
||||||
|
Uniform Resource Identifier: URI
|
||||||
|
Internationalized Resource Identifiers: IRI
|
||||||
|
|
||||||
|
|
||||||
|
"@context"
|
||||||
|
- url
|
||||||
|
- map (object)
|
||||||
|
- list of ...
|
410
repl_sessions/json_ld_stuff.clj
Normal file
410
repl_sessions/json_ld_stuff.clj
Normal file
|
@ -0,0 +1,410 @@
|
||||||
|
(ns repl-sessions.json-ld-stuff
|
||||||
|
(:require
|
||||||
|
[clojure.string :as str]
|
||||||
|
[hato.client :as hato]
|
||||||
|
[cheshire.core :as json])
|
||||||
|
(:import (org.jsoup Jsoup)))
|
||||||
|
|
||||||
|
(set! *print-namespace-maps* false)
|
||||||
|
|
||||||
|
(def json-ld-spec "https://www.w3.org/TR/json-ld11/")
|
||||||
|
|
||||||
|
(def doc (Jsoup/parse ^String (slurp json-ld-spec)))
|
||||||
|
|
||||||
|
(def examples
|
||||||
|
(for [example (.select doc ".example")
|
||||||
|
:let [title (first (.select example ".example-title"))
|
||||||
|
pre (first (.select example "pre"))]
|
||||||
|
:when (and title pre)]
|
||||||
|
(do
|
||||||
|
(doseq [comment (.select pre ".comment")]
|
||||||
|
(.remove comment))
|
||||||
|
[(str/replace (.text title) #"^: " "")
|
||||||
|
(try
|
||||||
|
(json/parse-string (.text (first (.select example "pre")))
|
||||||
|
)
|
||||||
|
(catch Exception e
|
||||||
|
e))])))
|
||||||
|
|
||||||
|
(defn example [title]
|
||||||
|
(get (into {} examples) title))
|
||||||
|
|
||||||
|
(defn json-fetch [url]
|
||||||
|
(hato/get url
|
||||||
|
{:headers {"Accept" "application/json"}
|
||||||
|
:http-client {:redirect-policy :normal}
|
||||||
|
:as :json-string-keys}))
|
||||||
|
|
||||||
|
(def expand-context
|
||||||
|
(memoize
|
||||||
|
(fn
|
||||||
|
([new-context]
|
||||||
|
(expand-context {} new-context))
|
||||||
|
([current-context new-context]
|
||||||
|
(cond
|
||||||
|
(string? new-context)
|
||||||
|
(do (println '->> new-context)
|
||||||
|
(expand-context current-context
|
||||||
|
(get (:body (json-fetch new-context)) "@context")))
|
||||||
|
(sequential? new-context)
|
||||||
|
(reduce expand-context current-context new-context)
|
||||||
|
|
||||||
|
(map? new-context)
|
||||||
|
(into current-context
|
||||||
|
(map
|
||||||
|
(fn [[k v]]
|
||||||
|
(let [id (if (map? v) (get v "@id" v) v)
|
||||||
|
[prefix suffix] (str/split id #":")]
|
||||||
|
(if-let [base (get (merge current-context new-context) prefix)]
|
||||||
|
[k (assoc (if (map? v) v {})
|
||||||
|
"@id" (str (if (map? base) (get base "@id") base)
|
||||||
|
suffix))]
|
||||||
|
[k (if (map? v) v {"@id" v})]))))
|
||||||
|
new-context))))))
|
||||||
|
|
||||||
|
(defn expand-id [id ctx]
|
||||||
|
(if (string? id)
|
||||||
|
(if-let [t (get-in ctx [id "@id"])]
|
||||||
|
t
|
||||||
|
(if (str/includes? id ":")
|
||||||
|
(let [[prefix suffix] (str/split id #":")]
|
||||||
|
(if-let [prefix-url (get-in ctx [prefix "@id"])]
|
||||||
|
(str prefix-url suffix)
|
||||||
|
id))
|
||||||
|
id))
|
||||||
|
id))
|
||||||
|
|
||||||
|
(defn apply-context [v ctx]
|
||||||
|
(into {}
|
||||||
|
(map (fn [[k v]]
|
||||||
|
(let [attr (get ctx k)
|
||||||
|
k (get-in ctx [k "@id"] k)
|
||||||
|
v (cond
|
||||||
|
(map? v)
|
||||||
|
(apply-context v ctx)
|
||||||
|
|
||||||
|
(= "@type" k)
|
||||||
|
(expand-id v ctx)
|
||||||
|
|
||||||
|
:else
|
||||||
|
v)]
|
||||||
|
(prn [k v])
|
||||||
|
[k (if attr
|
||||||
|
(if (= "@id" (get attr "@type"))
|
||||||
|
(assoc attr "@id" (expand-id v ctx))
|
||||||
|
(assoc (dissoc (cond-> attr
|
||||||
|
(contains? attr "@type")
|
||||||
|
(update "@type" expand-id ctx))
|
||||||
|
"@id") "@value" v))
|
||||||
|
v)])))
|
||||||
|
v))
|
||||||
|
|
||||||
|
(defn expand [json-ld]
|
||||||
|
(let [ctx (expand-context {} (get json-ld "@context"))]
|
||||||
|
(apply-context (dissoc json-ld "@context") ctx)))
|
||||||
|
|
||||||
|
(def clojure-prefixes
|
||||||
|
{"dcterms" "http://purl.org/dc/terms/"
|
||||||
|
"ldp" "http://www.w3.org/ns/ldp#"
|
||||||
|
"schema" "http://schema.org#"
|
||||||
|
"vcard" "http://www.w3.org/2006/vcard/ns#"
|
||||||
|
"mastodon" "http://joinmastodon.org/ns#"
|
||||||
|
"security" "https://w3id.org/security#"
|
||||||
|
"activitystreams" "https://www.w3.org/ns/activitystreams#"
|
||||||
|
"xsd" "http://www.w3.org/2001/XMLSchema#"})
|
||||||
|
|
||||||
|
(defn to-clj [json-ld prefixes]
|
||||||
|
(let [shorten #(if (string? %)
|
||||||
|
(or (some (fn [[ns url]]
|
||||||
|
(when (.startsWith % url)
|
||||||
|
(keyword ns
|
||||||
|
(subs % (.length url)))))
|
||||||
|
prefixes)
|
||||||
|
%)
|
||||||
|
%)]
|
||||||
|
(clojure.walk/postwalk
|
||||||
|
(fn [v]
|
||||||
|
(if (map? v)
|
||||||
|
(-> v
|
||||||
|
(update-keys (fn [k]
|
||||||
|
(case k
|
||||||
|
"@id" :rdf/id
|
||||||
|
"@type" :rdf/type
|
||||||
|
"@value" :rdf/value
|
||||||
|
(if-let [kw (shorten k)]
|
||||||
|
kw
|
||||||
|
k))))
|
||||||
|
(update-vals (fn [v]
|
||||||
|
(cond
|
||||||
|
(map? v)
|
||||||
|
(cond
|
||||||
|
(and (= "@id" (:rdf/type v))
|
||||||
|
(contains? v :rdf/id))
|
||||||
|
(:rdf/id v)
|
||||||
|
(contains? v :rdf/value)
|
||||||
|
(case (:rdf/type v)
|
||||||
|
:xsd/dateTime
|
||||||
|
(java.time.ZonedDateTime/parse (:rdf/value v))
|
||||||
|
(:rdf/value v))
|
||||||
|
:else
|
||||||
|
v)
|
||||||
|
(string? v)
|
||||||
|
(shorten v)
|
||||||
|
:else
|
||||||
|
v))))
|
||||||
|
v))
|
||||||
|
(expand json-ld))))
|
||||||
|
|
||||||
|
(defn compact [entity context prefix-map]
|
||||||
|
(let [ctx (into {} (map (juxt (comp #(get % "@id") val) key))
|
||||||
|
(expand-context context))
|
||||||
|
kw->iri (fn [k]
|
||||||
|
(let [iri (str (get prefix-map (namespace k))
|
||||||
|
(name k))]
|
||||||
|
(if-let [n (get ctx iri)]
|
||||||
|
n
|
||||||
|
(if-let [n (some (fn [[k v]]
|
||||||
|
(when (.startsWith iri k)
|
||||||
|
(str v ":" (subs iri (count k)))))
|
||||||
|
ctx)]
|
||||||
|
n
|
||||||
|
k))))
|
||||||
|
expand-kv (fn expand-kv [[k v]]
|
||||||
|
(cond
|
||||||
|
(not (keyword? k))
|
||||||
|
[k v]
|
||||||
|
(= :rdf/id k)
|
||||||
|
["@id" v]
|
||||||
|
(= :rdf/type k)
|
||||||
|
["@type" (kw->iri v)]
|
||||||
|
:else
|
||||||
|
[(kw->iri k)
|
||||||
|
(cond
|
||||||
|
(map? v)
|
||||||
|
(into {} (map expand-kv) v)
|
||||||
|
(sequential? v)
|
||||||
|
(map #(if (map? %)
|
||||||
|
(into {} (map expand-kv) %)
|
||||||
|
%) v)
|
||||||
|
:else v)]))]
|
||||||
|
(into {"@context" context}
|
||||||
|
(map expand-kv)
|
||||||
|
(dissoc entity :rdf/id :rdf/type))))
|
||||||
|
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(println (json/encode (assoc (expand (example "Referencing a JSON-LD context"))
|
||||||
|
"@context" (expand-context {} (get (example "Referencing a JSON-LD context") "@context")))))
|
||||||
|
(println (json/encode (assoc (expand (example "Referencing a JSON-LD context"))
|
||||||
|
"@context" {"foaf" "http://xmlns.com/foaf/0.1/"})))
|
||||||
|
|
||||||
|
|
||||||
|
(println (json/encode(example "Referencing a JSON-LD context")))
|
||||||
|
|
||||||
|
(to-clj (example "Referencing a JSON-LD context")
|
||||||
|
(update-vals (expand-context (get (example "Referencing a JSON-LD context") "@context"))
|
||||||
|
#(get % "@id")))
|
||||||
|
|
||||||
|
(def raw-profile (:body (json-fetch "https://toot.cat/users/plexus")))
|
||||||
|
(def profile-ctx (expand-context (get raw-profile "@context")))
|
||||||
|
(def expanded-profile (expand raw-profile))
|
||||||
|
(def plexus-profile
|
||||||
|
(to-clj expanded-profile clojure-prefixes))
|
||||||
|
|
||||||
|
(get raw-profile "@context")
|
||||||
|
|
||||||
|
(expand-context "https://www.w3.org/ns/activitystreams")
|
||||||
|
|
||||||
|
(compact plexus-profile
|
||||||
|
["https://w3id.org/security/v1"
|
||||||
|
"https://www.w3.org/ns/activitystreams"
|
||||||
|
{"toot" "http://joinmastodon.org/ns#",}]
|
||||||
|
clojure-prefixes)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(update-vals (to-clj (expand )
|
||||||
|
clojure-prefixes)
|
||||||
|
#(or (:rdf/type %)
|
||||||
|
%))
|
||||||
|
(to-clj (expand (:body (json-fetch "https://toot.cat/users/plexus/followers?page=1")))
|
||||||
|
clojure-prefixes)
|
||||||
|
|
||||||
|
(->> "https://www.w3.org/ns/activitystreams#OrderedCollectionPage"
|
||||||
|
json-fetch
|
||||||
|
:body
|
||||||
|
(#(get % "@context"))
|
||||||
|
vals
|
||||||
|
(keep #(get % "@type"))
|
||||||
|
(into #{}))
|
||||||
|
|
||||||
|
(to-clj (expand (:body (json-fetch "https://toot.cat/users/plexus/collections/featured",)))
|
||||||
|
clojure-prefixes)
|
||||||
|
|
||||||
|
|
||||||
|
(example "Loading a relative context")
|
||||||
|
(to-clj (expand (example "In-line context definition"))
|
||||||
|
{"org.schema" "http://schema.org/"}))
|
||||||
|
|
||||||
|
"Values of @id are interpreted as IRI"
|
||||||
|
"IRIs can be relative"
|
||||||
|
"IRI as a key"
|
||||||
|
"Term expansion from context definition"
|
||||||
|
"Type coercion"
|
||||||
|
"Identifying a node"
|
||||||
|
"Specifying the type for a node"
|
||||||
|
"Specifying multiple types for a node"
|
||||||
|
"Using a term to specify the type"
|
||||||
|
"Referencing Objects on the Web"
|
||||||
|
"Embedding Objects"
|
||||||
|
"Using multiple contexts"
|
||||||
|
"Describing disconnected nodes with @graph"
|
||||||
|
"Embedded contexts within node objects"
|
||||||
|
"Combining external and local contexts"
|
||||||
|
"Setting @version in context"
|
||||||
|
"Using a default vocabulary"
|
||||||
|
"Using the null keyword to ignore data"
|
||||||
|
"Using a default vocabulary relative to a previous default vocabulary"
|
||||||
|
"Use a relative IRI reference as node identifier"
|
||||||
|
"Setting the document base in a document"
|
||||||
|
"Using \"#\" as the vocabulary mapping"
|
||||||
|
"Using \"#\" as the vocabulary mapping (expanded)"
|
||||||
|
"Prefix expansion"
|
||||||
|
"Using vocabularies"
|
||||||
|
"Expanded document used to illustrate compact IRI creation"
|
||||||
|
"Compact IRI generation context (1.0)"
|
||||||
|
"Compact IRI generation term selection (1.0)"
|
||||||
|
"Compact IRI generation context (1.1)"
|
||||||
|
"Compact IRI generation term selection (1.1)"
|
||||||
|
"Aliasing keywords"
|
||||||
|
"IRI expansion within a context"
|
||||||
|
"Using a term to define the IRI of another term within a context"
|
||||||
|
"Using a compact IRI as a term"
|
||||||
|
"Illegal Aliasing of a compact IRI to a different IRI"
|
||||||
|
"Associating context definitions with IRIs"
|
||||||
|
"Illegal circular definition of terms within a context"
|
||||||
|
"Defining an @context within a term definition"
|
||||||
|
"Defining an @context within a term definition used on @type"
|
||||||
|
"Expansion using embedded and scoped contexts"
|
||||||
|
"Expansion using embedded and scoped contexts (embedding equivalent)"
|
||||||
|
"Marking a context to not propagate"
|
||||||
|
"A remote context to be imported in a type-scoped context"
|
||||||
|
"Sourcing a context in a type-scoped context and setting it to propagate"
|
||||||
|
"Result of sourcing a context in a type-scoped context and setting it to propagate"
|
||||||
|
"Sourcing a context to modify @vocab and a term definition"
|
||||||
|
"Result of sourcing a context to modify @vocab and a term definition"
|
||||||
|
"A protected term definition can generally not be overridden"
|
||||||
|
"A protected @context with an exception"
|
||||||
|
"Overriding permitted if both definitions are identical"
|
||||||
|
"overriding permitted in property scoped context"
|
||||||
|
"Expanded term definition with type coercion"
|
||||||
|
"Expanded value with type"
|
||||||
|
"Example demonstrating the context-sensitivity for @type"
|
||||||
|
"Example demonstrating the context-sensitivity for @type (statements)"
|
||||||
|
"JSON Literal"
|
||||||
|
"Expanded term definition with types"
|
||||||
|
"Term expansion for values, not identifiers"
|
||||||
|
"Terms not expanded when document-relative"
|
||||||
|
"Term definitions using IRIs and compact IRIs"
|
||||||
|
"Setting the default language of a JSON-LD document"
|
||||||
|
"Clearing default language"
|
||||||
|
"Expanded term definition with language"
|
||||||
|
"Language map expressing a property in three languages"
|
||||||
|
"Overriding default language using an expanded value"
|
||||||
|
"Removing language information using an expanded value"
|
||||||
|
"Setting the default base direction of a JSON-LD document"
|
||||||
|
"Clearing default base direction"
|
||||||
|
"Expanded term definition with language and direction"
|
||||||
|
"Overriding default language and default base direction using an expanded value"
|
||||||
|
"Multiple values with no inherent order"
|
||||||
|
"Using an expanded form to set multiple values"
|
||||||
|
"Multiple array values of different types"
|
||||||
|
"An ordered collection of values in JSON-LD"
|
||||||
|
"Specifying that a collection is ordered in the context"
|
||||||
|
"Coordinates expressed in GeoJSON"
|
||||||
|
"Coordinates expressed in JSON-LD"
|
||||||
|
"An unordered collection of values in JSON-LD"
|
||||||
|
"Specifying that a collection is unordered in the context"
|
||||||
|
"Setting @container: @set on @type"
|
||||||
|
"Nested properties"
|
||||||
|
"Nested properties folded into containing object"
|
||||||
|
"Defining property nesting - Expanded Input"
|
||||||
|
"Defining property nesting - Context"
|
||||||
|
"Defining property nesting"
|
||||||
|
"Referencing node objects"
|
||||||
|
"Embedding a node object as property value of another node object"
|
||||||
|
"Referencing an unidentified node"
|
||||||
|
"Specifying a local blank node identifier"
|
||||||
|
"Indexing data in JSON-LD"
|
||||||
|
"Indexing data using @none"
|
||||||
|
"Property-based data indexing"
|
||||||
|
"Indexing languaged-tagged strings in JSON-LD"
|
||||||
|
"Indexing languaged-tagged strings in JSON-LD with @set representation"
|
||||||
|
"Indexing languaged-tagged strings using @none for no language"
|
||||||
|
"Indexing data in JSON-LD by node identifiers"
|
||||||
|
"Indexing data in JSON-LD by node identifiers with @set representation"
|
||||||
|
"Indexing data in JSON-LD by node identifiers using @none"
|
||||||
|
"Indexing data in JSON-LD by type"
|
||||||
|
"Indexing data in JSON-LD by type with @set representation"
|
||||||
|
"Indexing data in JSON-LD by type using @none"
|
||||||
|
"Included Blocks"
|
||||||
|
"Flattened form for included blocks"
|
||||||
|
"Describing disconnected nodes with @included"
|
||||||
|
"A document with children linking to their parent"
|
||||||
|
"A person and its children using a reverse property"
|
||||||
|
"Using @reverse to define reverse properties"
|
||||||
|
"Identifying and making statements about a graph"
|
||||||
|
"Using @graph to explicitly express the default graph"
|
||||||
|
"Context needs to be duplicated if @graph is not used"
|
||||||
|
"Implicitly named graph"
|
||||||
|
"Indexing graph data in JSON-LD"
|
||||||
|
"Indexing graphs using @none for no index"
|
||||||
|
"Referencing named graphs using an id map"
|
||||||
|
"Referencing named graphs using an id map with @none"
|
||||||
|
"Sample JSON-LD document to be expanded"
|
||||||
|
"Expanded form for the previous example"
|
||||||
|
"Sample expanded JSON-LD document"
|
||||||
|
"Sample context"
|
||||||
|
"Compact form of the sample document once sample context has been applied"
|
||||||
|
"Compacting using a default vocabulary"
|
||||||
|
"Compacting using a base IRI"
|
||||||
|
"Coercing Values to Strings"
|
||||||
|
"Using Arrays for Lists"
|
||||||
|
"Reversing Node Relationships"
|
||||||
|
"Indexing language-tagged strings"
|
||||||
|
"Forcing Object Values"
|
||||||
|
"Indexing language-tagged strings and @set"
|
||||||
|
"Term Selection"
|
||||||
|
"Sample JSON-LD document to be flattened"
|
||||||
|
"Flattened and compacted form for the previous example"
|
||||||
|
"Sample library frame"
|
||||||
|
"Flattened library objects"
|
||||||
|
"Framed library objects"
|
||||||
|
"Referencing a JSON-LD context from a JSON document via an HTTP Link Header"
|
||||||
|
"Specifying an alternate location via an HTTP Link Header"
|
||||||
|
"Embedding JSON-LD in HTML"
|
||||||
|
"Combining multiple JSON-LD script elements into a single dataset"
|
||||||
|
"Using the document base URL to establish the default base IRI"
|
||||||
|
"Embedding JSON-LD containing HTML in HTML"
|
||||||
|
"Targeting a specific script element by id"
|
||||||
|
"Illegal Unconnected Node"
|
||||||
|
"Linked Data Dataset"
|
||||||
|
"Sample JSON-LD document"
|
||||||
|
"Flattened and expanded form for the previous example"
|
||||||
|
"Turtle representation of expanded/flattened document"
|
||||||
|
"A set of statements serialized in Turtle"
|
||||||
|
"The same set of statements serialized in JSON-LD"
|
||||||
|
"Embedding in Turtle"
|
||||||
|
"Same embedding example in JSON-LD"
|
||||||
|
"JSON-LD using native data types for numbers and boolean values"
|
||||||
|
"Same example in Turtle using typed literals"
|
||||||
|
"A list of values in Turtle"
|
||||||
|
"Same example with a list of values in JSON-LD"
|
||||||
|
"RDFa fragment that describes three people"
|
||||||
|
"Same description in JSON-LD (context shared among node objects)"
|
||||||
|
"HTML that describes a book using microdata"
|
||||||
|
"Same book description in JSON-LD (avoiding contexts)"
|
||||||
|
"HTTP Request with profile requesting an expanded document"
|
||||||
|
"HTTP Request with profile requesting a compacted document"
|
||||||
|
"HTTP Request with profile requesting a compacted document with a reference to a compaction context"
|
226
repl_sessions/pg_stuff.clj
Normal file
226
repl_sessions/pg_stuff.clj
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
(ns repl-sessions.pg-stuff
|
||||||
|
(:require [clojure.string :as str]
|
||||||
|
[next.jdbc :as jdbc]
|
||||||
|
[honey.sql :as sql]
|
||||||
|
[next.jdbc.sql :as nsql]
|
||||||
|
[next.jdbc.date-time :as jdbc-date-time]
|
||||||
|
[next.jdbc.result-set :as rs]
|
||||||
|
[next.jdbc.plan]
|
||||||
|
[cheshire.core :as json]
|
||||||
|
))
|
||||||
|
|
||||||
|
(defn pg-url [db-name]
|
||||||
|
(str "jdbc:pgsql://localhost:5432/" db-name "?user=postgres"))
|
||||||
|
|
||||||
|
(defn recreate-db! [name]
|
||||||
|
(let [ds (jdbc/get-datasource (pg-url "postgres"))]
|
||||||
|
(jdbc/execute! ds [(str "DROP DATABASE IF EXISTS " name)])
|
||||||
|
(jdbc/execute! ds [(str "CREATE DATABASE " name)])))
|
||||||
|
|
||||||
|
(recreate-db! "souk")
|
||||||
|
|
||||||
|
(defn sql-ident [v]
|
||||||
|
(if (sequential? v)
|
||||||
|
(str/join "." (map identifier v))
|
||||||
|
(str "\""
|
||||||
|
(if (keyword? v)
|
||||||
|
(subs (str v) 1)
|
||||||
|
v)
|
||||||
|
"\"")))
|
||||||
|
|
||||||
|
(defn sql-kw [k]
|
||||||
|
(str/upper-case
|
||||||
|
(str/replace
|
||||||
|
(if (keyword? k)
|
||||||
|
(name k)
|
||||||
|
k)
|
||||||
|
#"-" " ")))
|
||||||
|
|
||||||
|
(defn sql-str [& ss]
|
||||||
|
(str "'" (str/replace (apply str ss) #"'" "''") "'"))
|
||||||
|
|
||||||
|
(defn sql-list
|
||||||
|
([items]
|
||||||
|
(sql-list "(" ")" ", " items))
|
||||||
|
([before after separator items]
|
||||||
|
(str before (apply str (str/join separator items)) after)))
|
||||||
|
|
||||||
|
(defn strs [& items]
|
||||||
|
(str/join " " items))
|
||||||
|
|
||||||
|
(defn sql [& items]
|
||||||
|
(apply strs
|
||||||
|
(map (fn [x]
|
||||||
|
(cond
|
||||||
|
(vector? x)
|
||||||
|
(case (first x)
|
||||||
|
:ident (sql-ident (second x))
|
||||||
|
:kw (sql-kw (second x))
|
||||||
|
:str (sql-str (second x))
|
||||||
|
:raw (second x)
|
||||||
|
:list (sql-list (map sql (next x)))
|
||||||
|
:commas (sql-list "" "" ", " (map sql (next x)))
|
||||||
|
:fn (str (second x)
|
||||||
|
(sql-list (map sql (nnext x))))
|
||||||
|
(apply sql x))
|
||||||
|
(keyword? x)
|
||||||
|
(sql-ident x)
|
||||||
|
(symbol? x)
|
||||||
|
(sql-kw x)
|
||||||
|
(string? x)
|
||||||
|
(sql-str x)
|
||||||
|
(sequential? x)
|
||||||
|
(sql-list "(" ")" ", " (map sql x))))
|
||||||
|
items)))
|
||||||
|
|
||||||
|
(sql 'create-table :activitystreams/Person
|
||||||
|
(list
|
||||||
|
[:souk/id 'text 'primary-key]
|
||||||
|
[:souk/properties 'jsonb 'default "{}"] ))
|
||||||
|
|
||||||
|
(def set-ts-trigger-def "CREATE OR REPLACE FUNCTION trigger_set_timestamp()\nRETURNS TRIGGER AS $$\nBEGIN\n NEW.\"meta/updated-at\" = NOW();\n RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;")
|
||||||
|
(def default-properties
|
||||||
|
[[:rdf/id 'text 'primary-key]
|
||||||
|
[:rdf/props 'jsonb 'default "{}"]
|
||||||
|
[:meta/created-at 'timestamp-with-time-zone 'default [:fn 'now] 'not-null]
|
||||||
|
[:meta/updated-at 'timestamp-with-time-zone]])
|
||||||
|
|
||||||
|
(let [ds (jdbc/get-datasource (pg-url "souk"))]
|
||||||
|
(jdbc/execute! ds [set-ts-trigger-def])
|
||||||
|
(jdbc/execute! ds [(sql 'drop-table
|
||||||
|
'if-exists
|
||||||
|
:activitystreams/Person)])
|
||||||
|
(jdbc/execute! ds [(sql 'create-table :activitystreams/Person
|
||||||
|
(concat
|
||||||
|
default-properties
|
||||||
|
[[:activitystreams/name 'text 'not-null]
|
||||||
|
[:activitystreams/preferredUsername 'text 'not-null]
|
||||||
|
[:activitystreams/url 'text 'not-null]
|
||||||
|
[:activitystreams/summary 'text]
|
||||||
|
[:ldp/inbox 'text 'not-null]
|
||||||
|
[:activitystreams/outbox 'text 'not-null]
|
||||||
|
[:activitystreams/published 'timestamp-with-time-zone]]))])
|
||||||
|
(jdbc/execute! ds [(sql 'create-trigger :set-timestamp
|
||||||
|
'before-update
|
||||||
|
'on :activitystreams/Person
|
||||||
|
'for-each-row
|
||||||
|
'execute-procedure [:fn 'trigger_set_timestamp])]))
|
||||||
|
(def table-columns
|
||||||
|
(into {}
|
||||||
|
(let [ds (jdbc/get-datasource (pg-url "souk"))
|
||||||
|
opts {}]
|
||||||
|
(with-open [con (jdbc/get-connection ds opts)]
|
||||||
|
(let [md (.getMetaData con)] ; produces java.sql.DatabaseMetaData
|
||||||
|
(doall
|
||||||
|
(for [{:keys [pg_class/TABLE_NAME]} (-> md
|
||||||
|
;; return a java.sql.ResultSet describing all tables and views:
|
||||||
|
(.getTables nil nil nil (into-array ["TABLE" "VIEW"]))
|
||||||
|
(rs/datafiable-result-set ds opts))]
|
||||||
|
[(keyword TABLE_NAME)
|
||||||
|
(map (comp keyword :COLUMN_NAME) (rs/datafiable-result-set (.getColumns md nil nil TABLE_NAME nil) ds opts)
|
||||||
|
)
|
||||||
|
])))))))
|
||||||
|
(to-clj (expand (:body (json-fetch "https://toot.cat/users/plexus")))
|
||||||
|
clojure-prefixes)
|
||||||
|
|
||||||
|
(defn pg-coerce [val]
|
||||||
|
(cond
|
||||||
|
(instance? java.time.ZonedDateTime val)
|
||||||
|
(.toOffsetDateTime val)
|
||||||
|
#_[:raw (strs (sql-kw 'timestamp-with-time-zone)
|
||||||
|
(sql-str
|
||||||
|
|
||||||
|
(.toLocalDate ^java.time.ZonedDateTime val) " "
|
||||||
|
(let [t (.toLocalTime ^java.time.ZonedDateTime val)]
|
||||||
|
(format "%d:%02d:%02d" (.getHour t)
|
||||||
|
(.getMinute t) (.getSecond t)))
|
||||||
|
(.getZone ^java.time.ZonedDateTime val)))]
|
||||||
|
:else
|
||||||
|
val))
|
||||||
|
|
||||||
|
(defn insert-sql [entity]
|
||||||
|
(let [{:rdf/keys [type]} entity
|
||||||
|
cols (get table-columns type)
|
||||||
|
props (seq (select-keys entity cols))]
|
||||||
|
(into [(sql 'insert-into type
|
||||||
|
(cons :rdf/props
|
||||||
|
(map key props))
|
||||||
|
'values
|
||||||
|
(repeat (inc (count props)) '?)
|
||||||
|
'on-conflict [:raw "(\"rdf/id\")"]
|
||||||
|
'do
|
||||||
|
'update-set
|
||||||
|
(into [:commas]
|
||||||
|
(map (fn [[k]]
|
||||||
|
[k '= '?]))
|
||||||
|
props))]
|
||||||
|
(cons
|
||||||
|
(json/encode (apply dissoc entity :rdf/type (map key props)))
|
||||||
|
(concat
|
||||||
|
(map (comp pg-coerce val) props)
|
||||||
|
(map (comp pg-coerce val) props))))))
|
||||||
|
|
||||||
|
(let [ds (jdbc/get-datasource (pg-url "souk"))]
|
||||||
|
(jdbc/execute! ds (insert-sql (assoc repl-sessions.json-ld-stuff/plexus-profile
|
||||||
|
:activitystreams/name "John Doe")))
|
||||||
|
)
|
||||||
|
|
||||||
|
(defrecord MyMapResultSetBuilder [^java.sql.ResultSet rs rsmeta cols]
|
||||||
|
rs/RowBuilder
|
||||||
|
(->row [this] (transient {}))
|
||||||
|
(column-count [this] (count cols))
|
||||||
|
(with-column [this row i]
|
||||||
|
(rs/with-column-value this row (nth cols (dec i))
|
||||||
|
(if (= java.sql.Types/TIMESTAMP_WITH_TIMEZONE (.getColumnType rsmeta i))
|
||||||
|
(.getObject rs ^Integer i ^Class java.time.OffsetDateTime)
|
||||||
|
(rs/read-column-by-index (.getObject rs ^Integer i) rsmeta i))))
|
||||||
|
(with-column-value [this row col v]
|
||||||
|
(assoc! row col v))
|
||||||
|
(row! [this row] (persistent! row))
|
||||||
|
rs/ResultSetBuilder
|
||||||
|
(->rs [this] (transient []))
|
||||||
|
(with-row [this mrs row]
|
||||||
|
(conj! mrs row))
|
||||||
|
(rs! [this mrs] (persistent! mrs)))
|
||||||
|
|
||||||
|
(defn my-builder
|
||||||
|
[rs opts]
|
||||||
|
(let [rsmeta (.getMetaData rs)
|
||||||
|
cols (rs/get-unqualified-column-names rsmeta opts)]
|
||||||
|
(def rs rs)
|
||||||
|
(def meta rsmeta)
|
||||||
|
(def cols cols)
|
||||||
|
(->MyMapResultSetBuilder rs rsmeta cols)))
|
||||||
|
(.getColumnType meta 3)
|
||||||
|
|
||||||
|
(let [ds (jdbc/get-datasource (pg-url "souk"))]
|
||||||
|
(jdbc/execute-one! ds [(sql 'select '* 'from :activitystreams/Person)]
|
||||||
|
|
||||||
|
{:builder-fn my-builder})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(pg-coerce (java.time.ZonedDateTime/parse "2017-04-11T00:00Z"))
|
||||||
|
|
||||||
|
{:activitystreams/followers "@id",
|
||||||
|
:activitystreams/published "xsd:dateTime",
|
||||||
|
:mastodon/devices "@id",
|
||||||
|
:json-ld/type nil,
|
||||||
|
:activitystreams/outbox "@id",
|
||||||
|
:activitystreams/following "@id",
|
||||||
|
:activitystreams/endpoints "@id",
|
||||||
|
:activitystreams/name nil,
|
||||||
|
:activitystreams/icon "@id",
|
||||||
|
:security/publicKey "@id",
|
||||||
|
:mastodon/featured "@id",
|
||||||
|
:activitystreams/manuallyApprovesFollowers nil,
|
||||||
|
:activitystreams/summary nil,
|
||||||
|
:activitystreams/image "@id",
|
||||||
|
:activitystreams/tag "@id",
|
||||||
|
:mastodon/discoverable nil,
|
||||||
|
:json-ld/id nil,
|
||||||
|
:activitystreams/preferredUsername nil,
|
||||||
|
:activitystreams/url "@id",
|
||||||
|
:ldp/inbox "@id",
|
||||||
|
:activitystreams/attachment "@id",
|
||||||
|
:mastodon/featuredTags "@id"}
|
47
repl_sessions/rsa_keys.clj
Normal file
47
repl_sessions/rsa_keys.clj
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
(ns repl-sessions.rsa-keys
|
||||||
|
(:require
|
||||||
|
[clojure.string :as str])
|
||||||
|
(:import
|
||||||
|
(java.security KeyPairGenerator Signature)
|
||||||
|
(java.security.spec X509EncodedKeySpec)
|
||||||
|
(java.util Base64)))
|
||||||
|
|
||||||
|
(def kpg (KeyPairGenerator/getInstance "RSA"))
|
||||||
|
|
||||||
|
(.initialize kpg 2048)
|
||||||
|
(def kp (.generateKeyPair kpg))
|
||||||
|
|
||||||
|
(.getEncoded (.getPublic kp))
|
||||||
|
(.getEncoded (.getPrivate kp))
|
||||||
|
|
||||||
|
(let [s (.encodeToString (Base64/getEncoder) (.getEncoded (.getPublic kp)))
|
||||||
|
parts (map (partial apply str) (partition-all 64 s))]
|
||||||
|
(str/join
|
||||||
|
(map #(str % "\r\n")
|
||||||
|
`["-----BEGIN PUBLIC KEY-----"
|
||||||
|
~@parts
|
||||||
|
"-----END PUBLIC KEY-----"])))
|
||||||
|
|
||||||
|
(def pem "-----BEGIN PUBLIC KEY-----\r\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy3WsUuEyZLsy/2XxJ+ou\r\nnNr14R1x9laQh4EitjT4e1OPJwHHIBqEPUWk4MQzU13Jga4uua28Ecl3BxC9lSnf\r\nDp96Z0NAdkYjuCgC9xo9EjKaK8ijIbm58d4uifIl/XKZE6tYTGXXzmnx4nCfcWfF\r\n67tut/4k+/wVMjjHMLl9VhzHsBz3Wr+h7v+4SLFftq9NorMknWQuIh3IzQUNZBps\r\nCw8JRDUx8Of/I44mJMc2N12f41TLK65VCvkXF3K5qIS9jTEdhhOA8dsB92DEyaTu\r\ns+jhqXM4ivFfxDyOasQRZ0bEO+OEcJua7nnvNsFzGLkIb3/eJ1HlCQ+AKVSUGcBZ\r\nbwIDAQAB\r\n-----END PUBLIC KEY-----\r\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(X509EncodedKeySpec.
|
||||||
|
(.decode (Base64/getDecoder)
|
||||||
|
(str/replace pem #"(-+(BEGIN|END) PUBLIC KEY-+|\R)" ""))
|
||||||
|
)
|
||||||
|
|
||||||
|
;; sign
|
||||||
|
(def sign (Signature/getInstance "SHA256withRSA"))
|
||||||
|
(.initSign sign (.getPrivate kp))
|
||||||
|
(.update sign (.getBytes "hello"))
|
||||||
|
(def signature (.sign sign))
|
||||||
|
|
||||||
|
(.encodeToString (Base64/getEncoder) signature)
|
||||||
|
|
||||||
|
;; verify
|
||||||
|
(def sign (Signature/getInstance "SHA256withRSA"))
|
||||||
|
(.initVerify sign (.getPublic kp))
|
||||||
|
|
||||||
|
(.update sign (.getBytes "hello"))
|
||||||
|
(.verify sign signature)
|
3436
repl_sessions/vocabulary.clj
Normal file
3436
repl_sessions/vocabulary.clj
Normal file
File diff suppressed because it is too large
Load diff
198
resources/lambdaisland/souk/json_ld_contexts.edn
Normal file
198
resources/lambdaisland/souk/json_ld_contexts.edn
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
{"https://www.w3.org/ns/activitystreams"
|
||||||
|
{"Dislike" "as:Dislike",
|
||||||
|
"Leave" "as:Leave",
|
||||||
|
"Application" "as:Application",
|
||||||
|
"Listen" "as:Listen",
|
||||||
|
"followers" {"@id" "as:followers", "@type" "@id"},
|
||||||
|
"startIndex"
|
||||||
|
{"@id" "as:startIndex", "@type" "xsd:nonNegativeInteger"},
|
||||||
|
"View" "as:View",
|
||||||
|
"inbox" {"@id" "ldp:inbox", "@type" "@id"},
|
||||||
|
"object" {"@id" "as:object", "@type" "@id"},
|
||||||
|
"Like" "as:Like",
|
||||||
|
"shares" {"@id" "as:shares", "@type" "@id"},
|
||||||
|
"nameMap" {"@id" "as:name", "@container" "@language"},
|
||||||
|
"width" {"@id" "as:width", "@type" "xsd:nonNegativeInteger"},
|
||||||
|
"Relationship" "as:Relationship",
|
||||||
|
"origin" {"@id" "as:origin", "@type" "@id"},
|
||||||
|
"Link" "as:Link",
|
||||||
|
"url" {"@id" "as:url", "@type" "@id"},
|
||||||
|
"bto" {"@id" "as:bto", "@type" "@id"},
|
||||||
|
"inReplyTo" {"@id" "as:inReplyTo", "@type" "@id"},
|
||||||
|
"next" {"@id" "as:next", "@type" "@id"},
|
||||||
|
"ldp" "http://www.w3.org/ns/ldp#",
|
||||||
|
"signClientKey" {"@id" "as:signClientKey", "@type" "@id"},
|
||||||
|
"CollectionPage" "as:CollectionPage",
|
||||||
|
"describes" {"@id" "as:describes", "@type" "@id"},
|
||||||
|
"anyOf" {"@id" "as:anyOf", "@type" "@id"},
|
||||||
|
"Organization" "as:Organization",
|
||||||
|
"OrderedCollection" "as:OrderedCollection",
|
||||||
|
"orderedItems"
|
||||||
|
{"@id" "as:items", "@type" "@id", "@container" "@list"},
|
||||||
|
"Announce" "as:Announce",
|
||||||
|
"OrderedCollectionPage" "as:OrderedCollectionPage",
|
||||||
|
"height" {"@id" "as:height", "@type" "xsd:nonNegativeInteger"},
|
||||||
|
"Note" "as:Note",
|
||||||
|
"formerType" {"@id" "as:formerType", "@type" "@id"},
|
||||||
|
"Offer" "as:Offer",
|
||||||
|
"Video" "as:Video",
|
||||||
|
"Object" "as:Object",
|
||||||
|
"Travel" "as:Travel",
|
||||||
|
"Mention" "as:Mention",
|
||||||
|
"image" {"@id" "as:image", "@type" "@id"},
|
||||||
|
"Audio" "as:Audio",
|
||||||
|
"IntransitiveActivity" "as:IntransitiveActivity",
|
||||||
|
"endpoints" {"@id" "as:endpoints", "@type" "@id"},
|
||||||
|
"bcc" {"@id" "as:bcc", "@type" "@id"},
|
||||||
|
"Flag" "as:Flag",
|
||||||
|
"longitude" {"@id" "as:longitude", "@type" "xsd:float"},
|
||||||
|
"Question" "as:Question",
|
||||||
|
"radius" {"@id" "as:radius", "@type" "xsd:float"},
|
||||||
|
"Public" {"@id" "as:Public", "@type" "@id"},
|
||||||
|
"Activity" "as:Activity",
|
||||||
|
"IsMember" "as:IsMember",
|
||||||
|
"id" "@id",
|
||||||
|
"proxyUrl" {"@id" "as:proxyUrl", "@type" "@id"},
|
||||||
|
"IsContact" "as:IsContact",
|
||||||
|
"Event" "as:Event",
|
||||||
|
"hreflang" "as:hreflang",
|
||||||
|
"Block" "as:Block",
|
||||||
|
"Person" "as:Person",
|
||||||
|
"altitude" {"@id" "as:altitude", "@type" "xsd:float"},
|
||||||
|
"sharedInbox" {"@id" "as:sharedInbox", "@type" "@id"},
|
||||||
|
"latitude" {"@id" "as:latitude", "@type" "xsd:float"},
|
||||||
|
"liked" {"@id" "as:liked", "@type" "@id"},
|
||||||
|
"Arrive" "as:Arrive",
|
||||||
|
"summary" "as:summary",
|
||||||
|
"Delete" "as:Delete",
|
||||||
|
"attachment" {"@id" "as:attachment", "@type" "@id"},
|
||||||
|
"relationship" {"@id" "as:relationship", "@type" "@id"},
|
||||||
|
"href" {"@id" "as:href", "@type" "@id"},
|
||||||
|
"name" "as:name",
|
||||||
|
"closed" {"@id" "as:closed", "@type" "xsd:dateTime"},
|
||||||
|
"vcard" "http://www.w3.org/2006/vcard/ns#",
|
||||||
|
"Article" "as:Article",
|
||||||
|
"tag" {"@id" "as:tag", "@type" "@id"},
|
||||||
|
"published" {"@id" "as:published", "@type" "xsd:dateTime"},
|
||||||
|
"items" {"@id" "as:items", "@type" "@id"},
|
||||||
|
"startTime" {"@id" "as:startTime", "@type" "xsd:dateTime"},
|
||||||
|
"location" {"@id" "as:location", "@type" "@id"},
|
||||||
|
"Update" "as:Update",
|
||||||
|
"Add" "as:Add",
|
||||||
|
"Read" "as:Read",
|
||||||
|
"context" {"@id" "as:context", "@type" "@id"},
|
||||||
|
"partOf" {"@id" "as:partOf", "@type" "@id"},
|
||||||
|
"Remove" "as:Remove",
|
||||||
|
"preferredUsername" "as:preferredUsername",
|
||||||
|
"Profile" "as:Profile",
|
||||||
|
"totalItems"
|
||||||
|
{"@id" "as:totalItems", "@type" "xsd:nonNegativeInteger"},
|
||||||
|
"prev" {"@id" "as:prev", "@type" "@id"},
|
||||||
|
"Follow" "as:Follow",
|
||||||
|
"IsFollowing" "as:IsFollowing",
|
||||||
|
"Tombstone" "as:Tombstone",
|
||||||
|
"subject" {"@id" "as:subject", "@type" "@id"},
|
||||||
|
"Page" "as:Page",
|
||||||
|
"@vocab" "_:",
|
||||||
|
"current" {"@id" "as:current", "@type" "@id"},
|
||||||
|
"content" "as:content",
|
||||||
|
"units" "as:units",
|
||||||
|
"Place" "as:Place",
|
||||||
|
"instrument" {"@id" "as:instrument", "@type" "@id"},
|
||||||
|
"Undo" "as:Undo",
|
||||||
|
"alsoKnownAs" {"@id" "as:alsoKnownAs", "@type" "@id"},
|
||||||
|
"duration" {"@id" "as:duration", "@type" "xsd:duration"},
|
||||||
|
"last" {"@id" "as:last", "@type" "@id"},
|
||||||
|
"rel" "as:rel",
|
||||||
|
"source" "as:source",
|
||||||
|
"TentativeReject" "as:TentativeReject",
|
||||||
|
"type" "@type",
|
||||||
|
"outbox" {"@id" "as:outbox", "@type" "@id"},
|
||||||
|
"mediaType" "as:mediaType",
|
||||||
|
"oneOf" {"@id" "as:oneOf", "@type" "@id"},
|
||||||
|
"deleted" {"@id" "as:deleted", "@type" "xsd:dateTime"},
|
||||||
|
"target" {"@id" "as:target", "@type" "@id"},
|
||||||
|
"replies" {"@id" "as:replies", "@type" "@id"},
|
||||||
|
"provideClientKey" {"@id" "as:provideClientKey", "@type" "@id"},
|
||||||
|
"Create" "as:Create",
|
||||||
|
"updated" {"@id" "as:updated", "@type" "xsd:dateTime"},
|
||||||
|
"generator" {"@id" "as:generator", "@type" "@id"},
|
||||||
|
"endTime" {"@id" "as:endTime", "@type" "xsd:dateTime"},
|
||||||
|
"TentativeAccept" "as:TentativeAccept",
|
||||||
|
"oauthAuthorizationEndpoint"
|
||||||
|
{"@id" "as:oauthAuthorizationEndpoint", "@type" "@id"},
|
||||||
|
"audience" {"@id" "as:audience", "@type" "@id"},
|
||||||
|
"Service" "as:Service",
|
||||||
|
"Image" "as:Image",
|
||||||
|
"Accept" "as:Accept",
|
||||||
|
"Document" "as:Document",
|
||||||
|
"preview" {"@id" "as:preview", "@type" "@id"},
|
||||||
|
"Invite" "as:Invite",
|
||||||
|
"contentMap" {"@id" "as:content", "@container" "@language"},
|
||||||
|
"Group" "as:Group",
|
||||||
|
"oauthTokenEndpoint" {"@id" "as:oauthTokenEndpoint", "@type" "@id"},
|
||||||
|
"uploadMedia" {"@id" "as:uploadMedia", "@type" "@id"},
|
||||||
|
"to" {"@id" "as:to", "@type" "@id"},
|
||||||
|
"accuracy" {"@id" "as:accuracy", "@type" "xsd:float"},
|
||||||
|
"IsFollowedBy" "as:IsFollowedBy",
|
||||||
|
"Reject" "as:Reject",
|
||||||
|
"summaryMap" {"@id" "as:summary", "@container" "@language"},
|
||||||
|
"Join" "as:Join",
|
||||||
|
"Move" "as:Move",
|
||||||
|
"as" "https://www.w3.org/ns/activitystreams#",
|
||||||
|
"actor" {"@id" "as:actor", "@type" "@id"},
|
||||||
|
"likes" {"@id" "as:likes", "@type" "@id"},
|
||||||
|
"following" {"@id" "as:following", "@type" "@id"},
|
||||||
|
"streams" {"@id" "as:streams", "@type" "@id"},
|
||||||
|
"cc" {"@id" "as:cc", "@type" "@id"},
|
||||||
|
"attributedTo" {"@id" "as:attributedTo", "@type" "@id"},
|
||||||
|
"result" {"@id" "as:result", "@type" "@id"},
|
||||||
|
"xsd" "http://www.w3.org/2001/XMLSchema#",
|
||||||
|
"first" {"@id" "as:first", "@type" "@id"},
|
||||||
|
"Collection" "as:Collection",
|
||||||
|
"icon" {"@id" "as:icon", "@type" "@id"},
|
||||||
|
"Ignore" "as:Ignore"},
|
||||||
|
"https://w3id.org/security/v1"
|
||||||
|
{"EncryptedMessage" "sec:EncryptedMessage",
|
||||||
|
"dc" "http://purl.org/dc/terms/",
|
||||||
|
"canonicalizationAlgorithm" "sec:canonicalizationAlgorithm",
|
||||||
|
"owner" {"@id" "sec:owner", "@type" "@id"},
|
||||||
|
"created" {"@id" "dc:created", "@type" "xsd:dateTime"},
|
||||||
|
"signatureValue" "sec:signatureValue",
|
||||||
|
"CryptographicKey" "sec:Key",
|
||||||
|
"publicKeyPem" "sec:publicKeyPem",
|
||||||
|
"iterationCount" "sec:iterationCount",
|
||||||
|
"id" "@id",
|
||||||
|
"publicKey" {"@id" "sec:publicKey", "@type" "@id"},
|
||||||
|
"Ed25519Signature2018" "sec:Ed25519Signature2018",
|
||||||
|
"publicKeyWif" "sec:publicKeyWif",
|
||||||
|
"GraphSignature2012" "sec:GraphSignature2012",
|
||||||
|
"creator" {"@id" "dc:creator", "@type" "@id"},
|
||||||
|
"publicKeyBase58" "sec:publicKeyBase58",
|
||||||
|
"cipherAlgorithm" "sec:cipherAlgorithm",
|
||||||
|
"digestAlgorithm" "sec:digestAlgorithm",
|
||||||
|
"LinkedDataSignature2015" "sec:LinkedDataSignature2015",
|
||||||
|
"cipherData" "sec:cipherData",
|
||||||
|
"privateKey" {"@id" "sec:privateKey", "@type" "@id"},
|
||||||
|
"EcdsaKoblitzSignature2016" "sec:EcdsaKoblitzSignature2016",
|
||||||
|
"expires" {"@id" "sec:expiration", "@type" "xsd:dateTime"},
|
||||||
|
"signatureAlgorithm" "sec:signingAlgorithm",
|
||||||
|
"signature" "sec:signature",
|
||||||
|
"domain" "sec:domain",
|
||||||
|
"LinkedDataSignature2016" "sec:LinkedDataSignature2016",
|
||||||
|
"revoked" {"@id" "sec:revoked", "@type" "xsd:dateTime"},
|
||||||
|
"encryptionKey" "sec:encryptionKey",
|
||||||
|
"cipherKey" "sec:cipherKey",
|
||||||
|
"salt" "sec:salt",
|
||||||
|
"digestValue" "sec:digestValue",
|
||||||
|
"type" "@type",
|
||||||
|
"password" "sec:password",
|
||||||
|
"expiration" {"@id" "sec:expiration", "@type" "xsd:dateTime"},
|
||||||
|
"publicKeyService" {"@id" "sec:publicKeyService", "@type" "@id"},
|
||||||
|
"nonce" "sec:nonce",
|
||||||
|
"authenticationTag" "sec:authenticationTag",
|
||||||
|
"privateKeyPem" "sec:privateKeyPem",
|
||||||
|
"sec" "https://w3id.org/security#",
|
||||||
|
"normalizationAlgorithm" "sec:normalizationAlgorithm",
|
||||||
|
"initializationVector" "sec:initializationVector",
|
||||||
|
"xsd" "http://www.w3.org/2001/XMLSchema#"}}
|
19
src/lambdaisland/souk/activitypub.clj
Normal file
19
src/lambdaisland/souk/activitypub.clj
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
(ns lambdaisland.souk.activitypub
|
||||||
|
(:require [lambdaisland.souk.json-ld :as ld]))
|
||||||
|
|
||||||
|
(set! *print-namespace-maps* false)
|
||||||
|
|
||||||
|
(def common-prefixes
|
||||||
|
{"dcterms" "http://purl.org/dc/terms/"
|
||||||
|
"ldp" "http://www.w3.org/ns/ldp#"
|
||||||
|
"schema" "http://schema.org/"
|
||||||
|
"vcard" "http://www.w3.org/2006/vcard/ns#"
|
||||||
|
"mastodon" "http://joinmastodon.org/ns#"
|
||||||
|
"security" "https://w3id.org/security#"
|
||||||
|
"activitystreams" "https://www.w3.org/ns/activitystreams#"
|
||||||
|
"xsd" "http://www.w3.org/2001/XMLSchema#"
|
||||||
|
"owl" "http://www.w3.org/2002/07/owl#"
|
||||||
|
"rdfs" "http://www.w3.org/2000/01/rdf-schema#"})
|
||||||
|
|
||||||
|
(defn GET [url]
|
||||||
|
(ld/internalize (ld/expand (:body (ld/json-get url))) common-prefixes))
|
150
src/lambdaisland/souk/json_ld.clj
Normal file
150
src/lambdaisland/souk/json_ld.clj
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
(ns lambdaisland.souk.json-ld
|
||||||
|
(:require [hato.client :as hato]
|
||||||
|
[clojure.string :as str]
|
||||||
|
[clojure.walk :as walk]))
|
||||||
|
|
||||||
|
(defn json-get [url]
|
||||||
|
(hato/get url
|
||||||
|
{:headers {"Accept" "application/json"}
|
||||||
|
:http-client {:redirect-policy :normal}
|
||||||
|
:as :json-string-keys}))
|
||||||
|
|
||||||
|
(def context-cache (atom {})) ;; url -> context
|
||||||
|
|
||||||
|
(defn fetch-context [url]
|
||||||
|
(if-let [ctx (get @context-cache url)]
|
||||||
|
ctx
|
||||||
|
(get
|
||||||
|
(swap! context-cache
|
||||||
|
(fn [cache]
|
||||||
|
(assoc cache url (get (:body (json-get url)) "@context"))))
|
||||||
|
url)))
|
||||||
|
|
||||||
|
(defn expand-context
|
||||||
|
([new-context]
|
||||||
|
(expand-context {} new-context))
|
||||||
|
([current-context new-context]
|
||||||
|
(cond
|
||||||
|
(string? new-context)
|
||||||
|
(expand-context current-context (fetch-context new-context))
|
||||||
|
|
||||||
|
(sequential? new-context)
|
||||||
|
(reduce expand-context current-context new-context)
|
||||||
|
|
||||||
|
(map? new-context)
|
||||||
|
(into current-context
|
||||||
|
(map
|
||||||
|
(fn [[k v]]
|
||||||
|
(let [id (if (map? v) (get v "@id" v) v)
|
||||||
|
[prefix suffix] (str/split id #":")]
|
||||||
|
(if-let [base (get (merge current-context new-context) prefix)]
|
||||||
|
[k (assoc (if (map? v) v {})
|
||||||
|
"@id" (str (if (map? base) (get base "@id") base)
|
||||||
|
suffix))]
|
||||||
|
[k (if (map? v) v {"@id" v})]))))
|
||||||
|
new-context))))
|
||||||
|
|
||||||
|
(defn expand-id [id ctx]
|
||||||
|
(if (string? id)
|
||||||
|
(if-let [t (get-in ctx [id "@id"])]
|
||||||
|
t
|
||||||
|
(if (str/includes? id ":")
|
||||||
|
(let [[prefix suffix] (str/split id #":")]
|
||||||
|
(if-let [prefix-url (get-in ctx [prefix "@id"])]
|
||||||
|
(str prefix-url suffix)
|
||||||
|
id))
|
||||||
|
id))
|
||||||
|
id))
|
||||||
|
|
||||||
|
(defn apply-context [v ctx]
|
||||||
|
(cond
|
||||||
|
(map? v)
|
||||||
|
(into {}
|
||||||
|
(map (fn [[k v]]
|
||||||
|
(let [attr (get ctx k)
|
||||||
|
k (get-in ctx [k "@id"] k)
|
||||||
|
v (apply-context v ctx)]
|
||||||
|
[k (if attr
|
||||||
|
(cond
|
||||||
|
(and (#{"@id" "@type"} k) (string? v))
|
||||||
|
(expand-id v ctx)
|
||||||
|
(and (= "@id" (get attr "@type")) (string? v))
|
||||||
|
(assoc attr "@id" (expand-id v ctx))
|
||||||
|
:else
|
||||||
|
(assoc (dissoc (cond-> attr
|
||||||
|
(contains? attr "@type")
|
||||||
|
(update "@type" expand-id ctx))
|
||||||
|
"@id")
|
||||||
|
"@value" v))
|
||||||
|
v)])))
|
||||||
|
v)
|
||||||
|
|
||||||
|
(sequential? v)
|
||||||
|
(into (empty v) (map #(apply-context % ctx)) v)
|
||||||
|
|
||||||
|
(and (string? v) (str/includes? v ":"))
|
||||||
|
(expand-id v ctx)
|
||||||
|
|
||||||
|
:else
|
||||||
|
v))
|
||||||
|
|
||||||
|
(defn expand [json-ld]
|
||||||
|
(let [ctx (expand-context {} (get json-ld "@context"))]
|
||||||
|
(apply-context (dissoc json-ld "@context") ctx)))
|
||||||
|
|
||||||
|
(defn internalize [v prefixes]
|
||||||
|
(let [shorten #(if (and (string? %) (str/includes? % "://"))
|
||||||
|
(or (some (fn [[ns url]]
|
||||||
|
(when (.startsWith % url)
|
||||||
|
(keyword ns
|
||||||
|
(subs % (.length url)))))
|
||||||
|
prefixes)
|
||||||
|
%)
|
||||||
|
%)]
|
||||||
|
(cond
|
||||||
|
(sequential? v)
|
||||||
|
(into (empty v) (map #(internalize % prefixes)) v)
|
||||||
|
|
||||||
|
(map? v)
|
||||||
|
(-> v
|
||||||
|
(cond-> (contains? v "@type")
|
||||||
|
(doto prn)
|
||||||
|
(contains? v "@type")
|
||||||
|
(update "@type" shorten))
|
||||||
|
(update-keys (fn [k]
|
||||||
|
(case k
|
||||||
|
"@id" :rdf/id
|
||||||
|
"@type" :rdf/type
|
||||||
|
"@value" :rdf/value
|
||||||
|
(if-let [kw (shorten k)]
|
||||||
|
kw
|
||||||
|
k))))
|
||||||
|
(update-vals (fn [v]
|
||||||
|
(cond
|
||||||
|
(map? v)
|
||||||
|
(let [{id "@id" type "@type" value "@value"} v]
|
||||||
|
(cond
|
||||||
|
(and (= "@id" type) (contains? v "@id"))
|
||||||
|
id
|
||||||
|
(contains? v "@value")
|
||||||
|
(case type
|
||||||
|
"http://www.w3.org/2001/XMLSchema#dateTime"
|
||||||
|
(java.time.ZonedDateTime/parse value)
|
||||||
|
(internalize value prefixes))
|
||||||
|
:else
|
||||||
|
(internalize v prefixes)))
|
||||||
|
|
||||||
|
(string? v)
|
||||||
|
(shorten v)
|
||||||
|
|
||||||
|
:else
|
||||||
|
(internalize v prefixes)))))
|
||||||
|
:else
|
||||||
|
v)))
|
||||||
|
|
||||||
|
;; (compact
|
||||||
|
;; (expand (:body (json-get "https://toot.cat/users/plexus")))
|
||||||
|
;; common-prefixes)
|
||||||
|
|
||||||
|
;; "name"
|
||||||
|
;; "profileName"
|
Loading…
Reference in a new issue