+
+ dog_and_duck/quack/fault_messages.clj
+
+
+
+ 001 (ns dog-and-duck.quack.fault-messages)
+
+
+ 002
+
+
+ 003 (def messages
+
+
+ 004 "Actual fault messages to which fault codes resolve."
+
+
+ 005 {:id-not-https "Publicly facing content SHOULD use HTTPS URIs"
+
+
+ 006 :id-not-uri "identifiers must be publicly dereferencable URIs"
+
+
+ 007 :no-context "Section 3 of the ActivityPub specification states Implementers SHOULD include the ActivityPub context in their object definitions`."
+
+
+ 008 :no-id-persistent "Persistent objects MUST have unique global identifiers."
+
+
+ 009 :no-id-transient "The ActivityPub specification allows objects without `id` fields only if they are intentionally transient; even so it is preferred that the object should have an explicit null id."
+
+
+ 010 :null-id-persistent "Persistent objects MUST have non-null identifiers."
+
+
+ 011 :no-type "The ActivityPub specification states that the `type` field is optional, but it is hard to process objects with no known type."
+
+
+ 012 :not-an-object "ActivityStreams object must be JSON objects."})
+
+
+
diff --git a/docs/cloverage/dog_and_duck/quack/picky.clj.html b/docs/cloverage/dog_and_duck/quack/picky.clj.html
index 82aa1dd..ba4a0b1 100644
--- a/docs/cloverage/dog_and_duck/quack/picky.clj.html
+++ b/docs/cloverage/dog_and_duck/quack/picky.clj.html
@@ -14,424 +14,481 @@
003 Generally, each `-faults` function will return:
- 004 1. `nil` if no faults were found;
+ 004
- 005 2. a sequence of fault objects if faults were found.
+ 005 1. `nil` if no faults were found;
- 006
+ 006 2. a sequence of fault objects if faults were found.
- 007 Each fault object shall have the properties:
+ 007
- 008 1. `:@context` whose value shall be the URL of a
+ 008 Each fault object shall have the properties:
- 009 document specifying this vocabulary;
+ 009
- 010 2. `:type` whose value shall be `Fault`;
+ 010 1. `:@context` whose value shall be the URL of a
- 011 3. `:severity` whose value shall be one of
+ 011 document specifying this vocabulary;
- 012 `minor`, `should`, `must` or `critical`;
+ 012 2. `:type` whose value shall be `Fault`;
- 013 4. `:fault` whose value shall be a unique token
+ 013 3. `:severity` whose value shall be one of
- 014 representing the particular fault type;
+ 014 `minor`, `should`, `must` or `critical`;
- 015 5. `:narrative` whose value shall be a natural
+ 015 4. `:fault` whose value shall be a unique token
- 016 language description of the fault type.
+ 016 representing the particular fault type;
- 017
+ 017 5. `:narrative` whose value shall be a natural
- 018 Note that the reason for the `:fault` property is
+ 018 language description of the fault type.
- 019 to be able to have a well known place, linked to
+ 019
- 020 from the @context URL, which allows narratives
+ 020 Note that the reason for the `:fault` property is
- 021 for each fault type to be served in as many
+ 021 to be able to have a well known place, linked to
- 022 natural languages as possible.
+ 022 from the @context URL, which allows narratives
- 023
+ 023 for each fault type to be served in as many
- 024 The idea further is that it should ultimately be
+ 024 natural languages as possible.
- 025 possible to serialise a fault report as a
+ 025
- 026 document which in its own right conforms to the
+ 026 The idea further is that it should ultimately be
- 027 ActivityStreams spec."
+ 027 possible to serialise a fault report as a
- 028 (:require [dog-and-duck.utils.process :refer [pid]]))
+ 028 document which in its own right conforms to the
+
+
+ 029 ActivityStreams spec."
+
+
+ 030 (:require [dog-and-duck.quack.fault-messages :refer [messages]]
+
+
+ 031 [dog-and-duck.utils.process :refer [pid]])
+
+
+ 032 (:import [java.net URI URISyntaxException]))
- 029
+ 033
- 030 (def ^:const severity
+ 034 (def ^:const severity
- 031 "Severity of faults found, as follows:
+ 035 "Severity of faults found, as follows:
- 032
+ 036
- 033 1. `:minor` things which I consider to be faults, but which
+ 037 1. `:minor` things which I consider to be faults, but which
- 034 don't actually breach the spec;
+ 038 don't actually breach the spec;
- 035 2. `:should` instances where the spec says something SHOULD
+ 039 2. `:should` instances where the spec says something SHOULD
- 036 be done, which isn't;
+ 040 be done, which isn't;
- 037 3. `:must` instances where the spec says something MUST
+ 041 3. `:must` instances where the spec says something MUST
- 038 be done, which isn't;
+ 042 be done, which isn't;
- 039 4. `:critical` instances where I believe the fault means that
+ 043 4. `:critical` instances where I believe the fault means that
- 040 the object cannot be meaningfully processed."
+ 044 the object cannot be meaningfully processed."
- 041 #{:minor :should :must :critical})
+ 045 #{:minor :should :must :critical})
- 042
+ 046
- 043 (def ^:const severity-filters
+ 047 (def ^:const severity-filters
- 044 "Hack for implementing a severity hierarchy"
+ 048 "Hack for implementing a severity hierarchy"
- 045 {:all #{}
+ 049 {:all #{}
- 046 :minor #{:minor}
+ 050 :minor #{:minor}
- 047 :should #{:minor :should}
+ 051 :should #{:minor :should}
- 048 :must #{:minor :should :must}
+ 052 :must #{:minor :should :must}
- 049 :critical severity})
+ 053 :critical severity})
- 050
+ 054
- 051 (defn filter-severity
+ 055 (defn filter-severity
- 052 "Return a list of reports taken from these `reports` where the severity
+ 056 "Return a list of reports taken from these `reports` where the severity
- 053 of the report is greater than this `severity`."
+ 057 of the report is greater than this `severity`."
- 054 [reports severity]
+ 058 [reports severity]
-
- 055 (assert
+
+ 059 (assert
-
- 056 (and
+
+ 060 (and
-
- 057 (coll? reports)
+
+ 061 (coll? reports)
-
- 058 (every? map? reports)
-
-
- 059 (every? :severity reports)))
-
-
- 060 (remove
-
-
- 061 #((severity-filters severity) (:severity %))
-
-
- 062 reports))
-
-
- 063
+
+ 062 (every? map? reports)
- 064 (def ^:const activitystreams-context-uri
+ 063 (every? :severity reports)))
-
- 065 "The URI of the context of an ActivityStreams object is expected to be this
+
+ 064 (remove
-
- 066 literal string."
-
-
- 067 "https://www.w3.org/ns/activitystreams")
-
-
- 068
-
-
- 069 (def ^:const validation-fault-context-uri
-
-
- 070 "The URI of the context of a validation fault report object shall be this
-
-
- 071 literal string."
-
-
- 072 "https://simon-brooke.github.io/dog-and-duck/codox/Validation_Faults.html")
-
-
- 073
+
+ 065 #((severity-filters severity) (:severity %))
- 074 (defn context?
+ 066 reports))
+
+
+ 067
+
+
+ 068 (def ^:const activitystreams-context-uri
- 075 "Returns `true` iff `x` quacks like an ActivityStreams context, else false.
+ 069 "The URI of the context of an ActivityStreams object is expected to be this
- 076
+ 070 literal string."
- 077 A context is either
+ 071 "https://www.w3.org/ns/activitystreams")
+
+
+ 072
+
+
+ 073 (def ^:const validation-fault-context-uri
- 078 1. the URI (actually an IRI) `activitystreams-context-uri`, or
+ 074 "The URI of the context of a validation fault report object shall be this
- 079 2. a collection comprising that URI and a map."
+ 075 literal string."
- 080 [x]
+ 076 "https://simon-brooke.github.io/dog-and-duck/codox/Validation_Faults.html")
+
+
+ 077
+
+
+ 078 (defn context?
+
+
+ 079 "Returns `true` iff `x` quacks like an ActivityStreams context, else false.
+
+
+ 080
+
+
+ 081 A context is either
+
+
+ 082 1. the URI (actually an IRI) `activitystreams-context-uri`, or
+
+
+ 083 2. a collection comprising that URI and a map."
+
+
+ 084 [x]
- 081 (cond
+ 085 (cond
- 082 (nil? x) false
+ 086 (nil? x) false
- 083 (string? x) (and (= x activitystreams-context-uri) true)
+ 087 (string? x) (and (= x activitystreams-context-uri) true)
- 084 (coll? x) (and (context? (first (remove map? x)))
+ 088 (coll? x) (and (context? (first (remove map? x)))
- 085 (= (count x) 2)
+ 089 (= (count x) 2)
- 086 true)
+ 090 true)
- 087 :else false))
+ 091 :else false))
- 088
+ 092
-
- 089 (defmacro has-context?
+
+ 093 (defmacro has-context?
- 090 "True if `x` is an ActivityStreams object with a valid context, else `false`."
+ 094 "True if `x` is an ActivityStreams object with a valid context, else `false`."
- 091 [x]
+ 095 [x]
- 092 `(context? ((keyword "@context") ~x)))
+ 096 `(context? ((keyword "@context") ~x)))
- 093
-
-
- 094
-
-
- 095
+ 097
- 096 (defn make-fault-object
+ 098 (defn make-fault-object
- 097 "Return a fault object with these `severity`, `fault` and `narrative` values.
+ 099 "Return a fault object with these `severity`, `fault` and `narrative` values.
- 098
+ 100
- 099 An ActivityPub object MUST have a globally unique ID. Whether this is
+ 101 An ActivityPub object MUST have a globally unique ID. Whether this is
- 100 meaningful depends on whether we persist fault report objects and serve
+ 102 meaningful depends on whether we persist fault report objects and serve
- 101 them, which at present I have no plans to do."
+ 103 them, which at present I have no plans to do."
- 102 [severity fault narrative]
+ 104 ;; TODO: should not pass in the narrative; instead should use the :fault value
-
- 103 (assoc {}
+
+ 105 ;; to look up the narrative in a resource file.
+
+
+ 106 [severity fault]
+
+
+ 107 (assoc {}
+
+
+ 108 (keyword "@context") validation-fault-context-uri
+
+
+ 109 :id (str "https://"
+
+
+ 110 (.. java.net.InetAddress getLocalHost getHostName)
+
+
+ 111 "/fault/"
+
+
+ 112 pid
+
+
+ 113 ":"
+
+
+ 114 (inst-ms (java.util.Date.)))
+
+
+ 115 :type "Fault"
+
+
+ 116 :severity severity
+
+
+ 117 :fault fault
+
+
+ 118 :narrative (messages fault)))
+
+
+ 119
+
+
+ 120 (defn object-faults
+
+
+ 121 "Return a list of faults found in object `x`, or `nil` if none are."
+
+
+ 122 [x]
+
+
+ 123 (let [faults (remove
+
+
+ 124 empty?
+
+
+ 125 (list
+
+
+ 126 (when-not (map? x)
+
+
+ 127 (make-fault-object
+
+
+ 128 :critical
+
+
+ 129 :not-an-object))
+
+
+ 130 (when-not
+
+
+ 131 (has-context? x)
+
+
+ 132 (make-fault-object
+
+
+ 133 :should
+
+
+ 134 :no-context))
+
+
+ 135 (when-not (:type x)
+
+
+ 136 (make-fault-object
+
+
+ 137 :minor
+
+
+ 138 :no-type))
+
+
+ 139 (when-not (and (map? x) (contains? x :id))
+
+
+ 140 (make-fault-object
+
+
+ 141 :minor
+
+
+ 142 :no-id-transient))))]
+
+
+ 143 (if (empty? faults) nil faults)))
+
+
+ 144
+
+
+ 145 (defn persistent-object-faults
+
+
+ 146 "Return a list of faults found in persistent object `x`, or `nil` if none are."
+
+
+ 147 [x]
+
+
+ 148 (let [faults (concat
+
+
+ 149 (object-faults x)
+
+
+ 150 (remove empty?
+
+
+ 151 (list
+
+
+ 152 (if (contains? x :id)
+
+
+ 153 (try (let [id (URI. (:id x))]
+
+
+ 154 (when-not (= (.getScheme id) "https")
+
+
+ 155 (make-fault-object :should :id-not-https)))
+
+
+ 156 (catch URISyntaxException _
+
+
+ 157 (make-fault-object :must :id-not-uri))
+
+
+ 158 (catch NullPointerException _
- 104 (keyword "@context") validation-fault-context-uri
+ 159 (make-fault-object :must :null-id-persistent)))
-
- 105 :id (str "https://"
+
+ 160 (make-fault-object :must :no-id-persistent)))))]
-
- 106 (.. java.net.InetAddress getLocalHost getHostName)
-
-
- 107 "/fault/"
-
-
- 108 pid
-
-
- 109 ":"
-
-
- 110 (inst-ms (java.util.Date.)))
-
-
- 111 :type "Fault"
-
-
- 112 :severity severity
-
-
- 113 :fault fault
-
-
- 114 :narrative narrative))
+
+ 161 (if (empty? faults) nil faults)))
- 115
-
-
- 116 (defn object-faults
-
-
- 117 [x]
-
-
- 118 (remove
-
-
- 119 empty?
-
-
- 120 (list
-
-
- 121 (when-not
-
-
- 122 (has-context? x)
-
-
- 123 (make-fault-object
-
-
- 124 :should
-
-
- 125 :no-context
-
-
- 126 "Section 3 of the ActivityPub specification states
-
-
- 127 `Implementers SHOULD include the ActivityPub context in
-
-
- 128 their object definitions`.")
-
-
- 129 (when-not (:type x)
-
-
- 130 (make-fault-object
-
-
- 131 :minor
-
-
- 132 :no-type
-
-
- 133 "The ActivityPub specification states that the `type` field is
-
-
- 134 optional, but it is hard to process objects with no known type."))
-
-
- 135 (when-not (contains? x :id)
-
-
- 136 (make-fault-object
-
-
- 137 :minor
-
-
- 138 :no-id-transient
-
-
- 139 "The ActivityPub specification allows objects without `id` fields
-
-
- 140 only if they are intentionally transient; even so it is preferred
-
-
- 141 that the object should have an explicit null id."
-
-
- 142 ))
-
-
- 143 ))))
+ 162