You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wanijo/src/wanijo/instance/domain.clj

302 lines
9.6 KiB

(ns wanijo.instance.domain
(:require [clojure.spec.alpha :as spec]
[wanijo.specs :as specs]
[wanijo.framework.neo4j :as neo4j]
[wanijo.schema.domain :as domain-schema]
[wanijo.attribute.domain :as domain-attr]
[wanijo.tag.domain :as domain-tag]))
(spec/def ::instance
(spec/keys :req-un [::neo4j/uuid
::specs/created_at
::specs/updated_at
::specs/name]))
(spec/def ::properties
(spec/coll-of
(spec/keys :req-un [::neo4j/uuid
::specs/created_at
::specs/updated_at
::domain-attr/attribute])))
(spec/def ::link
(spec/keys :req-un [::neo4j/uuid
::specs/created_at
::name]))
(spec/def ::target ::instance)
(spec/def ::source ::instance)
(spec/def ::links-out
(spec/coll-of
(spec/keys :req-un [::link
::target
::domain-schema/schema])))
(spec/def ::links-in
(spec/coll-of
(spec/keys :req-un [::link
::source
::domain-schema/schema])))
(spec/def ::tags
(spec/coll-of ::domain-tag/tag))
(spec/def ::contains-schema
(spec/keys :req-un [::domain-schema/schema]))
(neo4j/defquery findy-by-schema
"MATCH (i:instance)-[:of]->(s:schema)
WHERE s.uuid = {uuid}
RETURN i
ORDER BY i.name")
(defn find-by-schema! [schema-uuid]
(map :i
(neo4j/exec-query!
findy-by-schema
{:uuid schema-uuid})))
(neo4j/defquery create-instance
"MATCH (s:schema {uuid:{schema_uuid}}),
(u:user {uuid:{user_uuid}})
CREATE (i:instance {uuid:{uuid}})-[:of]->(s),
(i)-[:created_by]->(u)
SET i.name = {name},
i.created_at = {created_at},
i.updated_at = {created_at}")
(neo4j/defquery create-property
"MATCH (i:instance {uuid:{uuid}}),
(a:attribute {uuid:{attr_uuid}})
CREATE (p:property {uuid:{prop_uuid}})-[:of]->(i),
(p)-[:of]->(a)
SET p.value = {value},
p.created_at = {created_at},
p.updated_at = {updated_at}")
(defn create! [user-uuid
schema-uuid
{:keys [name properties]}]
(let [instance-uuid (neo4j/uuid)
now (neo4j/now-str)
instance-tuple [create-instance
{:schema_uuid schema-uuid
:name name
:uuid instance-uuid
:created_at now
:user_uuid user-uuid}]
prop-tuples (for [{:keys [attribute value]} properties]
[create-property
{:uuid instance-uuid
:attr_uuid (:uuid attribute)
:prop_uuid (neo4j/uuid)
:value value
:created_at now
:updated_at now}])]
(apply neo4j/exec-queries!
(concat [instance-tuple]
prop-tuples))))
(spec/def ::instance-with-schema
(spec/merge ::instance ::contains-schema))
(neo4j/defquery find-by-uuid
"MATCH (i:instance {uuid:{uuid}})
-[:of]->(s:schema)
RETURN i, s")
(defn find-by-uuid! [uuid]
{:post [(spec/assert ::instance-with-schema %)]}
(->> (neo4j/exec-query! find-by-uuid
{:uuid uuid})
(map #(assoc (:i %)
:schema
(:s %)))
first))
(neo4j/defquery find-properties
"MATCH (i:instance {uuid:{uuid}}),
(p:property)-[:of]->(i),
(p)-[:of]->(a:attribute)
RETURN p, a
ORDER BY a.name")
(defn find-properties! [uuid]
(map #(assoc (:p %)
:attribute
(:a %))
(neo4j/exec-query! find-properties
{:uuid uuid})))
(spec/def ::prop-tuple-values
(spec/keys :req-un [::neo4j/uuid
::specs/now
::value
::specs/instance_uuid
::specs/attribute_uuid]))
(spec/def ::prop-tuple
(spec/coll-of
(spec/tuple fn? ::prop-tuple-values)))
(neo4j/defquery edit-instance
"MATCH (i:instance {uuid:{uuid}})
SET i.name = {name},
i.updated_at = {updated_at}")
(neo4j/defquery edit-property
"MATCH (i:instance {uuid:{instance_uuid}}),
(a:attribute {uuid:{attribute_uuid}})
MERGE (p:property {uuid:{uuid}})-[:of]->(i)
MERGE (p)-[:of]->(a)
ON CREATE SET p.created_at = {now},
p.updated_at = {now},
p.value = {value}
ON MATCH SET p.updated_at = {now},
p.value = {value}")
(defn instance->prop-tuples [instance]
{:post [(spec/assert ::prop-tuple %)]}
(map (fn [prop]
(let [prop-uuid (:uuid prop)
uuid (if (empty? prop-uuid)
(neo4j/uuid)
prop-uuid)]
[edit-property
{:uuid uuid
:now (neo4j/now-str)
:value (:value prop)
:instance_uuid (:uuid instance)
:attribute_uuid (-> prop :attribute :uuid)}]))
(:properties instance)))
(defn edit! [instance]
(let [prop-tuples (instance->prop-tuples instance)]
(apply neo4j/exec-queries!
(concat [[edit-instance
{:uuid (:uuid instance)
:name (:name instance)
:updated_at (neo4j/now-str)}]]
prop-tuples))))
(neo4j/defquery delete
"MATCH (i:instance {uuid:{uuid}}),
(i)-[ic:of]->(s:schema),
(i)-[cb:created_by]->(:user)
OPTIONAL MATCH
(p:property)-[pc:of]->(i),
(p)-[pac:of]->(a:attribute),
(i)-[lt:link_to]->(),
(i)<-[lf:link_from]-()
DELETE pac, pc, cb, ic, p, lt, lf i")
(defn delete! [uuid]
(neo4j/exec-query! delete {:uuid uuid}))
(neo4j/defquery create-link
"MATCH (i:instance {uuid:{from}}),
(u:user {uuid:{by}}),
(t:instance {uuid:{target}})
CREATE (l:link {uuid:{uuid}})-[:created_by]->(u)
SET l.created_at = {created_at},
l.name = {name}
CREATE (i)<-[:link_from]-(l)-[:link_to]->(t)")
(defn create-link! [link]
(let [tuples (map (fn [target-uuid]
[create-link
{:from (:from link)
:by (:by link)
:target target-uuid
:uuid (neo4j/uuid)
:created_at (neo4j/now-str)
:name (:name link)}])
(:to link))]
(apply neo4j/exec-queries! tuples)))
(neo4j/defquery outgoing-links
"MATCH (i:instance {uuid:{uuid}}),
(i)<-[:link_from]-(l:link),
(l)-[:link_to]->(t:instance),
(t)-[:of]->(s:schema)
RETURN i, l, t, s
ORDER BY s.name, t.name, t.created_at")
(defn outgoing-links! [uuid]
{:post [(spec/assert ::links-out %)]}
(map (fn [row]
{:link (:l row)
:target (:t row)
:schema (:s row)})
(neo4j/exec-query! outgoing-links
{:uuid uuid})))
(neo4j/defquery incoming-links
"MATCH (i:instance {uuid:{uuid}}),
(i)<-[:link_to]-(l:link),
(l)-[:link_from]->(source:instance),
(source)-[:of]->(schema:schema)
RETURN i, l, source, schema
ORDER BY schema.name, source.name, source.created_at")
(defn incoming-links! [uuid]
{:post [(spec/assert ::links-in %)]}
(map (fn [row]
{:link (:l row)
:source (:source row)
:schema (:schema row)})
(neo4j/exec-query! incoming-links
{:uuid uuid})))
(neo4j/defquery delete-link
"MATCH (l:link {uuid:{uuid}}),
(l)-[r]-()
DELETE r, l")
(defn delete-link! [uuid]
(neo4j/exec-query! delete-link
{:uuid uuid}))
(spec/def ::contains-full-information
(spec/keys :req-un [::properties
::links-out
::links-in
::tags]))
(spec/def ::full-instance
(spec/merge ::instance-with-schema
::contains-full-information))
(defn full-instance-by-uuid! [uuid]
{:post [(spec/assert ::full-instance %)]}
(assoc (find-by-uuid! uuid)
:properties
(find-properties! uuid)
:links-out
(outgoing-links! uuid)
:links-in
(incoming-links! uuid)
:tags
(domain-tag/tags-by-instance! uuid)))
(neo4j/defquery is-starred
"MATCH (u:user {uuid:{user_uuid}}),
(i:instance {uuid:{uuid}})
RETURN EXISTS((i)-[:starred_by]->(u)) AS starred")
(defn is-starred! [uuid user-uuid]
(-> (neo4j/exec-query! is-starred
{:user_uuid user-uuid
:uuid uuid})
first
:starred))
(neo4j/defquery mark-starred
"MATCH (i:instance {uuid:{uuid}}),
(u:user {uuid:{user_uuid}})
MERGE (i)-[s:starred_by]->(u)
ON CREATE
SET s.created_at = {now}")
(defn mark-starred! [uuid user-uuid]
(neo4j/exec-query! mark-starred
{:uuid uuid
:user_uuid user-uuid
:now (neo4j/now-str)}))
(neo4j/defquery remove-starred
"MATCH (:instance {uuid:{uuid}})
-[s:starred_by]->
(:user {uuid:{user_uuid}})
DELETE s")
(defn remove-starred! [uuid user-uuid]
(neo4j/exec-query! remove-starred
{:uuid uuid
:user_uuid user-uuid}))
(neo4j/defquery starred-by-user
"MATCH (u:user {uuid:{user_uuid}})
<-[s:starred_by]-
(i:instance)
RETURN i, s
ORDER BY s.created_at DESC")
(defn starred-by-user! [user-uuid]
(map #(assoc (:i %)
:starred_at (-> % :s :created_at))
(neo4j/exec-query! starred-by-user
{:user_uuid user-uuid})))