From 0ae34c8ac1b9121a54ada3023e62614de3f72bc2 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Tue, 10 Jan 2023 02:15:29 +0000 Subject: [PATCH] Brought documentation up to date --- README.md | 2 +- docs/cloverage/codecov.json | 207 ++- docs/cloverage/coverage.xml | 2 +- .../cloverage/dog_and_duck/quack/cli.clj.html | 425 +++++ .../dog_and_duck/quack/picky.clj.html | 131 +- .../quack/picky/collections.clj.html | 111 +- .../quack/picky/constants.clj.html | 337 ++-- .../quack/picky/distribution.clj.html | 98 + .../dog_and_duck/quack/picky/objects.clj.html | 1574 +++++++++++++++++ .../dog_and_duck/quack/picky/scratch.clj.html | 167 ++ .../dog_and_duck/quack/picky/time.clj.html | 206 +++ .../dog_and_duck/quack/picky/utils.clj.html | 1056 +++++------ .../dog_and_duck/quack/quack.clj.html | 311 ++-- .../dog_and_duck/scratch/scratch.clj.html | 209 ++- docs/cloverage/index.html | 293 ++- docs/codox/Desiderata.html | 2 +- docs/codox/Using_ActivityPub.html | 2 +- docs/codox/Validation_Faults.html | 2 +- docs/codox/dog-and-duck.quack.cli.html | 3 + .../dog-and-duck.quack.picky.collections.html | 2 +- .../dog-and-duck.quack.picky.constants.html | 10 +- ...nd-duck.quack.picky.control-variables.html | 2 +- ...dog-and-duck.quack.picky.distribution.html | 4 + docs/codox/dog-and-duck.quack.picky.html | 2 +- .../dog-and-duck.quack.picky.objects.html | 27 + .../dog-and-duck.quack.picky.scratch.html | 3 + docs/codox/dog-and-duck.quack.picky.time.html | 4 + .../codox/dog-and-duck.quack.picky.utils.html | 22 +- docs/codox/dog-and-duck.quack.quack.html | 14 +- docs/codox/dog-and-duck.scratch.parser.html | 2 +- docs/codox/dog-and-duck.scratch.scratch.html | 2 +- docs/codox/dog-and-duck.utils.process.html | 2 +- docs/codox/index.html | 2 +- docs/codox/intro.html | 53 +- src/dog_and_duck/scratch/scratch.clj | 14 +- 35 files changed, 3894 insertions(+), 1409 deletions(-) create mode 100644 docs/cloverage/dog_and_duck/quack/cli.clj.html create mode 100644 docs/cloverage/dog_and_duck/quack/picky/distribution.clj.html create mode 100644 docs/cloverage/dog_and_duck/quack/picky/objects.clj.html create mode 100644 docs/cloverage/dog_and_duck/quack/picky/scratch.clj.html create mode 100644 docs/cloverage/dog_and_duck/quack/picky/time.clj.html create mode 100644 docs/codox/dog-and-duck.quack.cli.html create mode 100644 docs/codox/dog-and-duck.quack.picky.distribution.html create mode 100644 docs/codox/dog-and-duck.quack.picky.objects.html create mode 100644 docs/codox/dog-and-duck.quack.picky.scratch.html create mode 100644 docs/codox/dog-and-duck.quack.picky.time.html diff --git a/README.md b/README.md index 3db50b7..5b7d3bb 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Note, though, that internationalisation files for languages other than British E The following severity levels are understood: - 0. `info` things which are not actuallys fault, but issues noted during + 0. `info` things which are not actually faults, but issues noted during validation; 1. `minor` things which I consider to be faults, but which don't actually breach the spec; diff --git a/docs/cloverage/codecov.json b/docs/cloverage/codecov.json index def2f8e..bb5d8b2 100644 --- a/docs/cloverage/codecov.json +++ b/docs/cloverage/codecov.json @@ -2,95 +2,90 @@ {"dog_and_duck/quack/picky/constants.clj": [null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 1, null, null, null, - null, 1, null, null, null, null, 1, null, null, null, null, null, 1, - null, null, null, 1, null, 1, null, null, null, null, null, null, - null, null, null, null, null, 1, null, 1, null, 1, 1, 1, 1, 1, 1, - null, 1, null, null, null, null, 1, null, null, null, null, 1, null, - null, null, null], + null, null, null, null, null, 1, null, null, null, null, 1, null, + null, null, null, null, 1, null, null, null, 1, null, 1, null, null, + null, null, 1, null, null, null, null, null, null, null, null, null, + null, null, 1, null, 1, null, 1, 1, 1, 1, 1, 1, null, 1, null, null, + null, null, 1, null, null, null, null, 1, null, null, null, null, 1, + null, null, null, null, null, 1, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, 1, null, null, + null, 1, null], "dog_and_duck/quack/picky/utils.clj": [null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, 1, - null, null, 7, null, 1, null, null, null, 0, null, 1, null, null, - null, null, null, 0, 0, 0, 0, 0, null, 1, null, null, 0, 0, 0, null, - null, null, null, true, null, null, 0, null, null, 1, null, null, - null, 4, null, 1, null, null, null, 0, 0, 0, 0, null, 1, null, null, - null, 3, 3, true, 3, null, 1, null, null, null, true, true, 45, 45, - 45, 70, 45, null, 0, 0, null, 0, 0, null, 1, null, null, null, null, - null, null, true, 162, 66, true, 6, null, null, null, 53, null, - null, 3, null, 1, null, null, null, null, null, null, null, null, - 220, 220, 220, 220, null, 220, null, 220, null, 220, 220, true, - null, 0, 0, null, 53, null, null, 2, 2, null, 1, null, null, null, - true, null, 1, null, null, null, null, null, null, 0, 0, 0, 0, 0, 0, - 0, 0, 0, null, 0, 0, 0, 0, 0, 0, null, 1, null, null, null, null, - null, null, null, null, null, 0, 0, null, 0, null, 3, null, null, - null, 3, null, 1, null, null, null, null, null, 0, null, 0, 0, null, - null, 1, null, null, null, null, null, null, true, 53, 53, 53, 3, - 53, 53, 41, 53, 9, 53, 16, null, 0, 0, 0, 0, 0, null, null, 1, null, + null, null, null, null, null, null, null, null, null, 1, null, null, + 7, null, 1, null, null, null, 1023, null, 1, null, null, null, true, + null, 1, null, null, null, null, null, null, null, null, null, true, + 1023, 1023, true, true, true, 1023, 140, null, 1, null, null, true, + 408, 352, null, null, null, null, 125, 122, 70, null, null, null, + true, null, null, 0, null, null, 1, null, null, null, 74, null, 1, + null, null, null, 70, 70, true, 70, null, 1, null, null, null, 3, 3, + true, 3, null, 1, null, null, null, true, 0, null, 0, 0, 16, true, + 11, 11, 11, true, 21, null, 11, null, 0, 0, null, 0, 0, null, 1, + null, null, null, null, null, null, true, 56, 47, true, 8, null, + null, null, 1, null, null, 1, null, 1, null, null, null, null, null, + null, null, null, 99, 99, 99, 99, null, 99, null, 99, null, 99, 99, + true, null, 0, 0, null, 115, null, null, 2, 2, null, 1, null, null, + null, true, null, 1, null, null, null, null, null, null, 12, 12, 12, + true, true, 0, 0, 0, 0, 0, 0, 0, 0, null, 0, 0, 0, 0, 1, null, 1, + null, null, null, null, null, null, null, null, null, 1, 1, null, 1, + null, 1, null, null, null, 298, null, 1, null, null, null, null, + null, 0, null, 0, 0], + "dog_and_duck/quack/picky/time.clj": + [null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, 0, 0, 0, 0, 0, 0, null, 0, 0, 0, null, - null, 0, 0, 0, null, 0, 0, 0, 0, 0, null, 0, 0, 0, 0, 0, null, 1, - null, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, null, 0, 0, 0, 0], + null, null, null, null, null, 1, null, null, null, true, true, null, + 0, null, null, 0, null, null, 1, null, null, null, 0, 0, 0, null, 0, + null, null, 0, null, null, 1, null, null, null, null, null, null, 0, + 0, 0, 0, 0], "dog_and_duck/quack/quack.clj": [null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, 1, null, null, null, null, null, + null, null, null, null, null, null, null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, 34, null, 34, null, 1, null, null, null, null, - null, 4, null, 4, null, 1, null, 3, null, 3, null, 1, null, null, - null, null, null, null, null, 0, 0, 0, 0, null, null, null, null, 1, - null, 0, null, 0, null, 1, null, 0, null, 0, null, 1, null, null, - null, null, null, 0, 0, 0, null, null, 1, null, null, null, null, - null, true, true, true, true, 2, 1, null, 1, 1, true, true, 1, null, - null, null, null, null, null, null, null, 0, 0, 0, 0, null, null, 1, - null, null, 0, null, 1, null, null, 0, null, 1, null, null, 0, null, - 1, null, null, 2, null, null], - "dog_and_duck/quack/picky/fault_messages.clj": - [null, 1, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, 1, null, 1, - null, null, null, null, null, null, null, null, null, null, null, - null], + null, null, null, null, 14, null, 14, null, 1, null, null, null, + null, null, 1, null, true, null, 1, null, 3, null, 3, null, 1, null, + null, null, null, null, null, null, 0, 0, 0, 0, null, null, null, + null, 1, null, 0, null, 0, null, 1, null, 0, null, 0, null, 1, null, + null, null, null, null, 0, 0, 0, null, null, 1, null, null, null, + null, null, true, true, true, true, 2, 1, null, 1, 1, true, true, 1, + null, null, null, null, null, null, null, null, 0, 0, 0, 0, null, + null, 1, null, null, 0, null, 1, null, null, 0, null, 1, null, null, + 0, null, 1, null, null, 2, null, null], "dog_and_duck/utils/process.clj": [null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 1, null, null, null, null, null, null, 1, 1, 1, 1, 1, null, true, null, null, null, null, null, null, null, null, null, 1, null, 1, null, null, null, null, 1], - "clj_activitypub/internal/http_util.clj": - [null, 1, null, null, null, null, null, null, null, null, 1, 1, 1, 1, - 2, 2, 1, 1, 1, null, 1, 0, 0, null, 1, null, null, null, 0], - "clj_activitypub/internal/thread_cache.clj": - [null, 1, null, null, null, null, 1, null, null, 8, null, 1, 4, 4, 4, - null, 1, null, 2, null, 2, 2, 2, 2, 2, 2, 2, 2, null, 4, 4, 4, null, - 4, 4, 2, 2, true, 2, 2, null, 0, 0, 0, 2, 2, 2, 2], "dog_and_duck/scratch/parser.clj": [null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, 1, null, null, null, 9, 9, 9, 9, 9, true, 0, null, null, null, - null, null, null, null, null, null, null, null, null, null], - "dog_and_duck/quack/picky/required_properties.clj":[null, 1], + null, 1, null, null, null, 11, 11, 11, 11, 11, true, 0, null, null, + null, null, null, null, null, null, null, null, null, null, null], + "dog_and_duck/quack/picky/scratch.clj": + [null, 1, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null], "dog_and_duck/quack/picky/control_variables.clj": [null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 1, null, null, null, null, null, null, null, null], - "clj_activitypub/core.clj": - [null, 1, null, null, null, null, null, null, null, null, null, 1, - null, null, null, null, null, 0, 0, 0, 0, 0, 0, 0, 0, null, 1, null, - null, null, 6, 2, 2, null, 1, 1, null, null, null, 1, 1, 1, 1, null, - null, 1, null, 1, null, null, null, 0, null, 0, null, 0, 0, 0, 0, 0, - 0, null, 1, null, 1, 0, 0, 0, 0, 0, 0, 0, 0, null, 1, null, null, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, null, 1, null, null, null, 0, 0, 0, 0, - 0, 0, 0, null, 1, null, null, null, true, null, 1, null, null, null, - null, null, 0, null, 0, 0, 0, 0, 0, null, 1, null, null, null, true, - null, 1, 0, null, null, 0, 0, null, 1, 0, null, null, 0, 0, null, 1, - null, null, null, 0, 0, 0, 0, 0, 0], - "clj_activitypub/internal/crypto.clj": - [null, 1, null, null, null, null, null, null, null, 1, 1, null, 1, 0, - 0, null, null, 1, 0, 0, null, 1, 0, 0, null, null, 1, 0, null, 1, 0, - 0, null, 1, 0, 0, 0, 0, 0, null], + "dog_and_duck/quack/cli.clj": + [null, 1, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, 1, null, null, null, 1, null, 1, null, 1, + null, 1, null, 0, true, 1, 1, 1, null, 0, true, 1, null, 1, 1, null, + 1, null, 0, 0, 0, 0, 0, 0, 0, null, 1, null, 0, 0, 0, 0, 0, 0, null, + 1, null, 0, null, 1, null, 0, 0, null, 1, 0, null, 0, 0, 0, 0, 0, 0, + 0, null, null, 1, null, 0, 0, 0, 0, 0, null, 0, null, null, 0, 0, 0, + null, 0, 0, 0, 0, 0, null, 0, 0, 0, null, 0, 0, 0, 0, 0, null, 1, + null, 0, 0, 0, 0, 0, 0, null, 1, 0, 0, 0, 0, 0, 0, 0, 0, null, 0, 0, + 0, 0, 0, 0, 0, 0, 0], "dog_and_duck/quack/picky.clj": [null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, @@ -98,34 +93,68 @@ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 1, null, null, null, null, 0, - null, 6, 6, null, 0, null, 0, null, 4, null, 1, null, null, 12, 12, - 12, 12, 9, 8, 4, null, 1, null, 0, 3, null, 0, 0, 0, 0, null, 1, - null, null, 3, 3, 3, 3, 1, 3, 3, 3, 3, null, 1, null, null, null, - null, 0, 0, 0, 0, 0, 0, null, null, null, 1, null, null, null, true, - 0, true, true, null, 1, null, null, null, 1, null, 1, null, null, 1, - null, 1, 0, null, null, null, 1, null, null, 1, 1, 1, 1, null, 1, 1, - 1, 1, 1, 1, null, 1, 1, 1, 0, null, null, null, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, null, 1, null, null, null, null, null, - null, null, 0, 0, 0, null, 0, 0, 0, 0, null, 1, null, 0, 0, 0, 0, 0, - 0, 0, null, 1, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, 0, 0, 0, 0, 0, 0, null, null, - null, null, null, 0, 0, 0, 0, null, null, 0, 0], - "dog_and_duck/scratch/core.clj": + null, 6, 6, null, 0, null, 0, null, 4, null, 1, null, null, 9, 9, 8, + 8, 7, 6, 3, null, 1, null, 0, 1, null, 0, 0, 0, 0, null, 1, null, + null, 3, 3, 3, 3, 1, 3, 3, 3, 3, null, 1, null, null, null, null, 0, + 0, 0, 0, 0, 0, null, null, null, 1, null, null, null, true, 0, true, + true, null, 1, null, null, null, 1, null, 1, null, null, 1, null, 1, + 0, null, null, null, 1, null, null, 1, 1, 1, 1, null, 1, 1, 1, 1, 1, + 1, null, 1, 1, 1, 0, null, null, null, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, null, 1, null, null, null, null, null, null, null, + 0, 0, 0, null, 0, 0, 0, 0, null, 1, null, 0, 0, 0, 0, 0, 0, 0, 0, + null, 1, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, 3, 3, 3, 3, 12, 3, null, null, null, + null, null, 3, true, 1, 1, null, null, true, 0], + "dog_and_duck/quack/picky/objects.clj": [null, 1, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, 1, null, null, 0], - "clj_activitypub/webfinger.clj": - [null, 1, null, null, null, null, null, null, null, 1, null, 1, 1, - null, 1, null, null, 1, 1, 1, null, 1, 1, null, 1, null, null, null, - 3, 3, 3, true, 1, true, 2, null], + null, null, null, null, null, null, null, null, null, null, null, + null, null, null, 1, null, 0, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, 1, + null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, 1, 1, true, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, null, null, null, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, true, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, null, null, null, null, null, 85, 85, 73, 33, true, null, 1, 1, + 1, 78, 1, 1, 76, 1, null, null, null, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, null, null, null, null, null, 70, 70, 58, 20, 8, null, 1, 1, 1, + 70, null, null, 1, 1, 1, 1, 1, 1, 1, 1, 1, true, null, true, 1, 1, + null, null, true, 1, 1, 547, 51, null, 1, 1, true, 1, 1, true, 1, 1, + 1, 1, null, 1, 1, true, 70, null, null, 12, 12, 12, true, 1, 1, 1, + true, 70, 0, null, null, null, null, null, null, null, null, 70, 70, + null, 20, 8, null, 1, 1, null, null, null, 1, 1, 1, true, 1, 1, 1, + 1, 1, true, 1, 1, 1, 1, 1, 12, null, 1, 1, 1, 1, null, null, null, + 1, 1, null, 1, null, 1, 1, true, 66, null, null, 14, 14, 0, true, 1, + 1, 1, 1, 1, 1, 70, null, 12, null, 1, 1, 12, null, 1, 1, null, 1, 1, + 1, 1, 1, 1, true, null, 1, 1, true, 1, 1, null, true, 1, null, null, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 70, 1, 1, 1, null, null, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, null, 1, 1, 1, null, null, + true, 1, 1, 1, 1, 1, true, 1, 1, 1, null, 1, 1104, 1104, 1104, 807, + 807, 21, null, 1, null, null, 1104, 1104, 1104, 1104, 283, 283, 283, + null, 1, 549, true, 547, true, 547, 547, 547, 547, null, 1, null, + null, null, 42, 42, 42, 41, 41, 2542, 41, 41, 549, 41, null, 1, + null, null, null, null, null, null, null, null, null, null, null, + 42, 42, 42, 42, 3, 42, 42, 20, 42, 3, 42, 17, 42, null, 16, 16, 16, + 12, 12, null, 1, null, null, null, null, null, null, null, null, 1, + 1, true, true, 0, null, 0, null, null, 0, null, null, 1, null, null, + null, null, 10, 10, 0, true, null, 1, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, + null, 18, 18, 8, 0, null, null, 0, 0, 0, null, 0, 0, 7, 1, 1, null, + 1, 1, 1, 1, 17, null, 1, null, null, null, 0, 0, 0, 0, 0, 0, 0, 0, + 0, null, 0, 0, 0, 0], "dog_and_duck/scratch/scratch.clj": [null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, 1, null, 1, 1, 1, null, null, null, null, 1, null, 1, 1, - 1, 1, null, null, 1, null, null, null, null, null, null, null, 1, 1, - 1, 1, 1, 1, null, null], + null, null, null, null, null, 1, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null], "dog_and_duck/quack/picky/collections.clj": [null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, 1, null, null, null, 0, 0, 0, 0, 0, 0, null, 1, null, null, - null, 0, 0, 0, 0, 0, 0, 0, null, 1, null, 0, 0, 0, 0, 0, null, null, - 0, 0]}} + null, 1, null, null, null, 3, 3, 3, 3, 3, 2, null, 1, null, null, + null, 6, 6, 6, 6, 6, 6, 6, 6, null, 1, null, null, null, 2, 2, 2, 2, + 2, null, null, 2, 2], + "dog_and_duck/quack/picky/distribution.clj": + [null, 1, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, 1, null, null, null, null, + null, 0, 0, 0, 0, null, null]}} diff --git a/docs/cloverage/coverage.xml b/docs/cloverage/coverage.xml index a91e783..d2621c3 100644 --- a/docs/cloverage/coverage.xml +++ b/docs/cloverage/coverage.xml @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/cloverage/dog_and_duck/quack/cli.clj.html b/docs/cloverage/dog_and_duck/quack/cli.clj.html new file mode 100644 index 0000000..6611554 --- /dev/null +++ b/docs/cloverage/dog_and_duck/quack/cli.clj.html @@ -0,0 +1,425 @@ + + + + dog_and_duck/quack/cli.clj + + + + 001  (ns dog-and-duck.quack.cli +
+ + 002    (:require [clojure.data.json :refer [read-str]] +
+ + 003              [clojure.java.io :refer [resource]] +
+ + 004              [clojure.pprint :refer [pprint]] +
+ + 005              [clojure.string :refer [join]] +
+ + 006              [clojure.tools.cli :refer [parse-opts]] +
+ + 007              [clojure.walk :refer [keywordize-keys]] +
+ + 008              [dog-and-duck.quack.picky.constants :refer [severity]] +
+ + 009              [dog-and-duck.quack.picky.objects :refer [object-faults]] +
+ + 010              [dog-and-duck.quack.picky.utils :refer [filter-severity]] +
+ + 011              [hiccup.core :refer [html]] +
+ + 012              [scot.weft.i18n.core :refer [get-message *config*]] +
+ + 013              [trptr.java-wrapper.locale :as locale]) +
+ + 014    (:gen-class)) +
+ + 015   +
+ + 016  (def ^:const stylesheet-url +
+ + 017    ;; TODO: fix this to github pages before go live +
+ + 018    "https://simon-brooke.github.io/dog-and-duck/style.css") +
+ + 019   +
+ + 020  (def cli-options +
+ + 021    ;; An option with a required argument +
+ + 022    [["-i" "--input SOURCE" "The file or URL to validate" +
+ + 023      :default "standard input"] +
+ + 024     ["-o" "--output DEST" "The file to write to, defaults to standard out" +
+ + 025      :default "standard output"] +
+ + 026     ["-f" "--format FORMAT" "The format to output, one of `edn` `csv` `html`" +
+ + 027      :default :edn +
+ + 028      :parse-fn #(keyword %) +
+ + 029      :validate [#(#{:csv :edn :html} %) "Expect one of `edn` `csv` `html`"]] +
+ + 030     ["-l" "--language LANG" "The ISO 639-1 code for the language to output" +
+ + 031      :default (-> (locale/get-default) locale/to-language-tag)] +
+ + 032     ["-s" "--severity LEVEL" "The minimum severity of faults to report" +
+ + 033      :default :info +
+ + 034      :parse-fn #(keyword %) +
+ + 035      :validate [#(severity %) (join " " +
+ + 036                                     (cons +
+ + 037                                      "Expected one of" +
+ + 038                                      (map name severity)))]] +
+ + 039     ["-h" "--help"]]) +
+ + 040   +
+ + 041  (defn validate +
+ + 042    [source] +
+ + 043    (println (str "Reading " source)) +
+ + 044    (let [input (read-str (slurp source))] +
+ + 045      (cond (map? input) (object-faults (keywordize-keys input)) +
+ + 046            (and (coll? input) +
+ + 047                 (every? map? input)) (map #(object-faults +
+ + 048                                             (keywordize-keys %) +
+ + 049                                             input))))) +
+ + 050   +
+ + 051  (defn output-csv +
+ + 052    [faults] +
+ + 053    (let [cols (set (reduce concat (map keys faults)))] +
+ + 054      (with-out-str +
+ + 055        (println (join ", " (map name cols))) +
+ + 056        (map +
+ + 057         #(println (join ", " (map (fn [p] (p %)) cols))) +
+ + 058         faults)))) +
+ + 059   +
+ + 060  (defn html-header-row +
+ + 061    [cols] +
+ + 062    (apply vector (cons :tr (map #(vector :th (name %)) cols)))) +
+ + 063   +
+ + 064  (defn html-fault-row +
+ + 065    [fault cols] +
+ + 066    (apply +
+ + 067     vector (cons :tr (map (fn [col] (vector :td (col fault))) cols)))) +
+ + 068   +
+ + 069  (defn- version-string [] +
+ + 070    (join +
+ + 071     " " +
+ + 072     ["dog-and-duck/quack" +
+ + 073      (try +
+ + 074        (some->> +
+ + 075         (resource "META-INF/maven/dog-and-duck/dog-and-duck/pom.properties") +
+ + 076         slurp +
+ + 077         (re-find #"version=(.*)") +
+ + 078         second) +
+ + 079        (catch Exception _ nil))])) +
+ + 080   +
+ + 081  (defn output-html +
+ + 082    [faults opts] +
+ + 083    (let [source-name (if (= (:input opts) *in*) "Standard input" (str (:input opts))) +
+ + 084          title (join " " [(get-message :validation-report-for) source-name]) +
+ + 085          cols (set (reduce concat (map keys faults))) +
+ + 086          version (version-string)] +
+ + 087      (str +
+ + 088       "<!DOCTYPE html>" +
+ + 089       (html +
+ + 090        [:html +
+ + 091         [:head +
+ + 092          [:title title] +
+ + 093          [:meta {:name "generator" :content version}] +
+ + 094          [:link {:rel "stylesheet" :media "screen" :href stylesheet-url :type "text/css"}]] +
+ + 095         [:body +
+ + 096          [:h1 title] +
+ + 097          [:p (join " " (remove nil? [(get-message :generated-on) +
+ + 098                                      (java.time.LocalDateTime/now) +
+ + 099                                      (get-message :by) +
+ + 100                                      version]))] +
+ + 101          (if-not +
+ + 102           (empty? faults) +
+ + 103            (apply +
+ + 104             vector +
+ + 105             :table +
+ + 106             (html-header-row cols) +
+ + 107             (map +
+ + 108              #(html-fault-row % cols) +
+ + 109              faults)) +
+ + 110            [:p (get-message :no-faults-found)])]])))) +
+ + 111   +
+ + 112  (defn output +
+ + 113    [content options] +
+ + 114    (let [faults (filter-severity content (:severity options))] +
+ + 115      (spit (:output options) +
+ + 116            (case (:format options) +
+ + 117              :html (output-html faults options) +
+ + 118              :csv (output-csv faults) +
+ + 119              (with-out-str (pprint faults)))))) +
+ + 120   +
+ + 121  (defn -main [& args] +
+ + 122    (let [opts (parse-opts args cli-options) +
+ + 123          options (assoc (:options opts) +
+ + 124                         :input (if (= (:input (:options opts)) "standard input") +
+ + 125                                  *in* +
+ + 126                                  (:input (:options opts))) +
+ + 127                         :output (if (= (:output (:options opts)) "standard output") +
+ + 128                                   *out* +
+ + 129                                   (:output (:options opts))))] +
+ + 130      ;;(println options) +
+ + 131      (when (:help options) +
+ + 132        (println (:summary opts))) +
+ + 133      (when (:errors opts) +
+ + 134        (println (:errors opts))) +
+ + 135      (when-not (or (:help options) (:errors options)) +
+ + 136        (binding [*config* (assoc *config* :default-language (:language options))] +
+ + 137          (output +
+ + 138           (validate (:input options)) +
+ + 139           options))))) +
+ + diff --git a/docs/cloverage/dog_and_duck/quack/picky.clj.html b/docs/cloverage/dog_and_duck/quack/picky.clj.html index 9364e58..201ba62 100644 --- a/docs/cloverage/dog_and_duck/quack/picky.clj.html +++ b/docs/cloverage/dog_and_duck/quack/picky.clj.html @@ -104,31 +104,31 @@ 033                [dog-and-duck.quack.picky.constants :refer [actor-types]]
- 034                [dog-and-duck.quack.picky.utils :refer [any-or-faults + 034                [dog-and-duck.quack.picky.objects :refer [coll-object-reference-or-fault
- 035                                                        coll-object-reference-or-fault + 035                                                          object-faults
- 036                                                        concat-non-empty + 036                                                          object-reference-or-faults]]
- 037                                                        has-activity-type? + 037                [dog-and-duck.quack.picky.utils :refer [any-or-faults
- 038                                                        has-actor-type? has-type? + 038                                                        concat-non-empty
- 039                                                        has-type-or-fault + 039                                                        has-activity-type?
- 040                                                        make-fault-object + 040                                                        has-actor-type? has-type?
- 041                                                        object-faults + 041                                                        has-type-or-fault
- 042                                                        object-reference-or-faults + 042                                                        make-fault-object
043                                                        string-or-fault]]) @@ -628,134 +628,137 @@ 208    [x]
- - 209    (concat-non-empty (persistent-object-faults x) + + 209    (concat-non-empty
- 210                      (activity-type-faults x) + 210     (persistent-object-faults x) +
+ + 211     (activity-type-faults x)
- 211                      (list + 212     (list
- 212                       (when-not + 213      (when-not
- 213                        (has-activity-type? x) + 214       (has-activity-type? x)
- 214                         (make-fault-object :must :not-activity-type)) + 215        (make-fault-object :must :not-activity-type))
- 215                       (when-not (string? (:summary x)) (make-fault-object :should :no-summary))))) + 216      (when-not (string? (:summary x)) (make-fault-object :should :no-summary)))))
- 216   + 217  
- 217  (defn collection-faults + 218  (defn collection-faults
- 218    "Return a list of faults found in the collection `x`; if `type` is also  + 219    "Return a list of faults found in the collection `x`; if `type` is also 
- 219     specified, it should be a string naming a specific collection type for + 220     specified, it should be a string naming a specific collection type for
- 220     which checks should be performed.  + 221     which checks should be performed. 
- 221      + 222     
- 222     Every collection *should*(?) have a `totalItems` field (an integer). + 223     Every collection *should*(?) have a `totalItems` field (an integer).
- 223      + 224     
- 224     Beyond that, collections are either 'just collections' (in which case + 225     Beyond that, collections are either 'just collections' (in which case
- 225     they *should* have an `items` field (a sequence)), or else they're paged + 226     they *should* have an `items` field (a sequence)), or else they're paged
- 226     collections, in which case they *must*(?) have a `first` field which is  + 227     collections, in which case they *must*(?) have a `first` field which is 
- 227     a collection page or a URI pointing to a collection page, and *should*  + 228     a collection page or a URI pointing to a collection page, and *should* 
- 228     have a `last` field which is similar. + 229     have a `last` field which is similar.
- 229      + 230     
- 230     The pages of collections *should* be collection pages; the pages of  + 231     The pages of collections *should* be collection pages; the pages of 
- 231     ordered collections *should* be ordered collection pages." + 232     ordered collections *should* be ordered collection pages."
- 232    ([x] + 233    ([x]
- - 233     (collection-faults + + 234     (collection-faults
- - 234      x + + 235      x
- - 235      (first + + 236      (first
- - 236       (remove nil? + + 237       (remove nil?
- - 237               (map #(when (has-type? x %) %) + + 238               (map #(when (has-type? x %) %)
- - 238                    ["Collection" + + 239                    ["Collection"
- 239                     "OrderedCollection" + 240                     "OrderedCollection"
- 240                     "CollectionPage" + 241                     "CollectionPage"
- 241                     "OrderedCollectionPage"]))))) + 242                     "OrderedCollectionPage"])))))
- 242    ([x type] + 243    ([x type]
- 243     ;; (log/info "collection-faults called with argumens " x ", " type) + 244     ;; (log/info "collection-faults called with argumens " x ", " type)
- - 244     (case type + + 245     (case type
- - 245       ("Collection" "OrderedCollection") (any-or-faults + + 246       ("Collection" "OrderedCollection") (any-or-faults
- - 246                                           (list (simple-collection-faults x type) + + 247                                           (list (simple-collection-faults x type)
- - 247                                                 (paged-collection-faults x type)) + + 248                                                 (paged-collection-faults x type))
- 248                                           :must + 249                                           :must
- 249                                           :no-items) + 250                                           :no-items)
- - 250       ("CollectionPage" "OrderedCollectionPage") (collection-page-faults x type) + + 251       ("CollectionPage" "OrderedCollectionPage") (collection-page-faults x type)
- 251       (list (make-fault-object :critical :expected-collection))))) + 252       (list (make-fault-object :critical :expected-collection)))))
diff --git a/docs/cloverage/dog_and_duck/quack/picky/collections.clj.html b/docs/cloverage/dog_and_duck/quack/picky/collections.clj.html index f6149ce..7c3b846 100644 --- a/docs/cloverage/dog_and_duck/quack/picky/collections.clj.html +++ b/docs/cloverage/dog_and_duck/quack/picky/collections.clj.html @@ -8,16 +8,16 @@ 001  (ns dog-and-duck.quack.picky.collections

- 002    (:require [dog-and-duck.quack.picky.utils :refer [concat-non-empty + 002    (:require [dog-and-duck.quack.picky.objects :refer [object-faults
- 003                                                      cond-make-fault-object + 003                                                        object-reference-or-faults]]
- 004                                                      object-faults + 004              [dog-and-duck.quack.picky.utils :refer [concat-non-empty
- 005                                                      object-reference-or-faults]])) + 005                                                      cond-make-fault-object]]))
006   @@ -85,23 +85,23 @@ 027    [x type]
- + 028    (concat-non-empty
- + 029     (object-faults x type)
- + 030     (list (object-reference-or-faults x type :critical :expected-collection)
- + 031           (cond-make-fault-object (integer? (:totalItems x)) :should :no-total-items)
- - 032           (object-reference-or-faults (:first x) nil :must :no-first-page) + + 032           (object-reference-or-faults (:first x) (str type "Page") :must :no-first-page)
- - 033           (object-reference-or-faults (:last x) nil :should :no-last-page)))) + + 033           (object-reference-or-faults (:last x) (str type "Page") :should :no-last-page))))
034   @@ -118,62 +118,71 @@ 038    [x type]
- + 039    (concat-non-empty
- + 040     (object-faults x type)
- - 041     (cons + + 041     (concat
- - 042      (list (object-reference-or-faults x type :critical :expected-collection) + + 042      (list (cond-make-fault-object (integer? (:totalItems x)) :should :no-total-items)
- - 043            (cond-make-fault-object (integer? (:totalItems x)) :should :no-total-items) + + 043            (cond-make-fault-object (coll? (:items x)) :must :no-items-collection))
- - 044            (cond-make-fault-object (coll? (:items x)) :must :no-items-collection)) -
- - 045      (map #(object-reference-or-faults % nil :must :not-object-reference) (:items x))))) -
- - 046   + + 044      (reduce
- 047  (defn collection-page-faults + 045       concat +
+ + 046       (map #(object-reference-or-faults % nil :must :not-object-reference) (:items x)))))) +
+ + 047   +
+ + 048  (defn collection-page-faults
- 048    [x type] -
- - 049    (concat-non-empty -
- - 050     (simple-collection-faults x type) -
- - 051     (list -
- - 052      (object-reference-or-faults (:partOf x) -
- - 053                                  (apply str (drop-last 4 type)) + 049    "Return a list of faults found in `x` considered as a collection page
- 054                                  :should + 050     object of this sub-`type`, or `nil` if none are found."
- 055                                  :n-part-of) + 051    [x type]
- - 056      (object-reference-or-faults (:next x) type :minor :no-next-page) + + 052    (concat-non-empty
- - 057      (object-reference-or-faults (:prev x) type :minor :no-prev-page)))) + + 053     (simple-collection-faults x type) +
+ + 054     (list +
+ + 055      (object-reference-or-faults (:partOf x) +
+ + 056                                  (apply str (drop-last 4 type)) +
+ + 057                                  :should +
+ + 058                                  :n-part-of) +
+ + 059      (object-reference-or-faults (:next x) type :minor :no-next-page) +
+ + 060      (object-reference-or-faults (:prev x) type :minor :no-prev-page))))
diff --git a/docs/cloverage/dog_and_duck/quack/picky/constants.clj.html b/docs/cloverage/dog_and_duck/quack/picky/constants.clj.html index e174b06..86f1761 100644 --- a/docs/cloverage/dog_and_duck/quack/picky/constants.clj.html +++ b/docs/cloverage/dog_and_duck/quack/picky/constants.clj.html @@ -68,178 +68,289 @@ 021    "The URI of the context of an ActivityStreams object is expected to be this

- 022     literal string." + 022     literal string.
- 023    "https://www.w3.org/ns/activitystreams") + 023      +
+ + 024     **NOTE THAT** the URI actually used in the published suite of  +
+ + 025     activitystreams-test-documents use this URI with 'http' rather than +
+ + 026     'https' as the property part, but the spec itself specifies 'https'." +
+ + 027    "https://www.w3.org/ns/activitystreams")
- 024   + 028  
- 025  (def ^:const actor-types + 029  (def ^:const actor-types
- 026    "The set of types we will accept as actors. + 030    "The set of types we will accept as actors.
- 027      + 031     
- 028     There's an [explicit set of allowed actor types] + 032     There's an [explicit set of allowed actor types]
- 029     (https://www.w3.org/TR/activitystreams-vocabulary/#actor-types)." + 033     (https://www.w3.org/TR/activitystreams-vocabulary/#actor-types)."
- 030    #{"Application" + 034    #{"Application"
- 031      "Group" + 035      "Group"
- 032      "Organization" + 036      "Organization"
- 033      "Person" + 037      "Person"
- 034      "Service"}) + 038      "Service"})
- 035   + 039  
- 036  (def ^:const context-key + 040  (def ^:const context-key
- 037    "The Clojure reader barfs on `:@context`, although it is in principle a valid  + 041    "The Clojure reader barfs on `:@context`, although it is in principle a valid 
- 038     keyword. So we'll make it once, here, to make the code more performant and + 042     keyword. So we'll make it once, here, to make the code more performant and
- 039     easier to read." + 043     easier to read."
- 040    (keyword "@context")) + 044    (keyword "@context"))
- 041   -
- - 042  (def ^:const severity -
- - 043    "Severity of faults found, as follows: -
- - 044      -
- - 045     0. `:info` not actually a fault, but an issue noted during validation; -
- - 046     1. `:minor` things which I consider to be faults, but which  -
- - 047        don't actually breach the spec; -
- - 048     2. `:should` instances where the spec says something SHOULD -
- - 049        be done, which isn't; -
- - 050     3. `:must` instances where the spec says something MUST -
- - 051        be done, which isn't; -
- - 052     4. `:critical` instances where I believe the fault means that -
- - 053        the object cannot be meaningfully processed." -
- - 054    #{:info :minor :should :must :critical}) -
- - 055   -
- - 056  (def ^:const severity-filters -
- - 057    "Hack for implementing a severity hierarchy" -
- - 058    {:all #{} -
- - 059     :info #{} + 045  
- 060     :minor #{:info} + 046  (def ^:const re-rfc5646 
- - 061     :should #{:info :minor} + + 047    "A regex which tests conformity to RFC 5646. Cribbed from
- - 062     :must #{:info :minor :should} + + 048     https://newbedev.com/regex-to-detect-locales" +
+ + 049    #"^[a-z]{2,4}(-[A-Z][a-z]{3})?(-([A-Z]{2}|[0-9]{3}))?$") +
+ + 050  
- 063     :critical severity}) + 051  (def ^:const severity +
+ + 052    "Severity of faults found, as follows: +
+ + 053      +
+ + 054     0. `:info` not actually a fault, but an issue noted during validation; +
+ + 055     1. `:minor` things which I consider to be faults, but which  +
+ + 056        don't actually breach the spec; +
+ + 057     2. `:should` instances where the spec says something SHOULD +
+ + 058        be done, which isn't; +
+ + 059     3. `:must` instances where the spec says something MUST +
+ + 060        be done, which isn't; +
+ + 061     4. `:critical` instances where I believe the fault means that +
+ + 062        the object cannot be meaningfully processed." +
+ + 063    #{:info :minor :should :must :critical})
064  
- - 065  (def ^:const validation-fault-context-uri + + 065  (def ^:const severity-filters
- 066    "The URI of the context of a validation fault report object shall be this + 066    "Hack for implementing a severity hierarchy"
- - 067     literal string." -
- - 068    "https://simon-brooke.github.io/dog-and-duck/codox/Validation_Faults.html") -
- - 069   + + 067    {:all #{}
- 070  (def ^:const verb-types + 068     :info #{}
- - 071    "The set of types we will accept as verbs. + + 069     :minor #{:info}
- - 072      + + 070     :should #{:info :minor}
- - 073     There's an [explicit set of allowed verb types] + + 071     :must #{:info :minor :should}
- - 074     (https://www.w3.org/TR/activitystreams-vocabulary/#activity-types)." -
- - 075    #{"Accept" "Add" "Announce" "Arrive" "Block" "Create" "Delete" "Dislike" -
- - 076      "Flag" "Follow" "Ignore" "Invite" "Join" "Leave" "Like" "Listen" "Move" -
- - 077      "Offer" "Question" "Reject" "Read" "Remove" "TentativeAccept" -
- - 078      "TentativeReject" "Travel" "Undo" "Update" "View"}) + + 072     :critical #{:info :minor :should :must}})
- 079   + 073   +
+ + 074  (def ^:const validation-fault-context-uri +
+ + 075    "The URI of the context of a validation fault report object shall be this +
+ + 076     literal string." +
+ + 077    "https://simon-brooke.github.io/dog-and-duck/codox/Validation_Faults.html") +
+ + 078   +
+ + 079  (def ^:const activity-types +
+ + 080    "The set of types we will accept as activities. +
+ + 081      +
+ + 082     There's an [explicit set of allowed activity types] +
+ + 083     (https://www.w3.org/TR/activitystreams-vocabulary/#activity-types)." +
+ + 084    #{"Accept" "Add" "Announce" "Arrive" "Block" "Create" "Delete" "Dislike" +
+ + 085      "Flag" "Follow" "Ignore" "Invite" "Join" "Leave" "Like" "Listen" "Move" +
+ + 086      "Offer" "Question" "Reject" "Read" "Remove" "TentativeAccept" +
+ + 087      "TentativeReject" "Travel" "Undo" "Update" "View"}) +
+ + 088   +
+ + 089  (def ^:const noun-types +
+ + 090    "The set of object types we will accept as nouns. +
+ + 091      +
+ + 092     There's an [explicit set of allowed 'object types'] +
+ + 093     (https://www.w3.org/TR/activitystreams-vocabulary/#object-types), but by  +
+ + 094     implication it is not exhaustive." +
+ + 095    #{"Article"  +
+ + 096      "Audio"  +
+ + 097      "Document" +
+ + 098      "Event" +
+ + 099      "Image" +
+ + 100      "Link" +
+ + 101      "Mention" +
+ + 102      "Note" +
+ + 103      "Object" +
+ + 104      "Page" +
+ + 105      "Place" +
+ + 106      "Profile" +
+ + 107      "Relationsip" +
+ + 108      "Tombstone" +
+ + 109      "Video"}) +
+ + 110   +
+ + 111  (def ^:const implicit-noun-types +
+ + 112    "These types are not explicitly listed in [Section 3.3 of the spec] +
+ + 113     (https://www.w3.org/TR/activitystreams-vocabulary/#object-types), but are  +
+ + 114     mentioned in narrative" +
+ + 115    #{"Link"}) +
+ + 116  
diff --git a/docs/cloverage/dog_and_duck/quack/picky/distribution.clj.html b/docs/cloverage/dog_and_duck/quack/picky/distribution.clj.html new file mode 100644 index 0000000..31f4f81 --- /dev/null +++ b/docs/cloverage/dog_and_duck/quack/picky/distribution.clj.html @@ -0,0 +1,98 @@ + + + + dog_and_duck/quack/picky/distribution.clj + + + + 001  (ns dog-and-duck.quack.picky.distribution) +
+ + 002   +
+ + 003  ;;;     Copyright (C) Simon Brooke, 2022 +
+ + 004   +
+ + 005  ;;;     This program is free software; you can redistribute it and/or +
+ + 006  ;;;     modify it under the terms of the GNU General Public License +
+ + 007  ;;;     as published by the Free Software Foundation; either version 2 +
+ + 008  ;;;     of the License, or (at your option) any later version. +
+ + 009   +
+ + 010  ;;;     This program is distributed in the hope that it will be useful, +
+ + 011  ;;;     but WITHOUT ANY WARRANTY; without even the implied warranty of +
+ + 012  ;;;     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +
+ + 013  ;;;     GNU General Public License for more details. +
+ + 014   +
+ + 015  ;;;     You should have received a copy of the GNU General Public License +
+ + 016  ;;;     along with this program; if not, write to the Free Software +
+ + 017  ;;;     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +
+ + 018   +
+ + 019  (defn distribution +
+ + 020    "Distribution of values of function `f` when applied to `vals`. +
+ + 021      +
+ + 022     I *know* there's a library function that does this, probably better, but I +
+ + 023     don't remember what it's called!" +
+ + 024    [f vals] +
+ + 025    (loop [result {} values vals] +
+ + 026      (if (empty? values) result  +
+ + 027          (let [r (apply f (list (first values)))] +
+ + 028                    (recur  +
+ + 029                     (assoc result r (if (result r) (inc (result r)) 1))  +
+ + 030                     (rest values)))))) +
+ + diff --git a/docs/cloverage/dog_and_duck/quack/picky/objects.clj.html b/docs/cloverage/dog_and_duck/quack/picky/objects.clj.html new file mode 100644 index 0000000..952a4a3 --- /dev/null +++ b/docs/cloverage/dog_and_duck/quack/picky/objects.clj.html @@ -0,0 +1,1574 @@ + + + + dog_and_duck/quack/picky/objects.clj + + + + 001  (ns dog-and-duck.quack.picky.objects +
+ + 002    (:require [clojure.data.json :as json] +
+ + 003              [clojure.set :refer [union]] +
+ + 004              [dog-and-duck.quack.picky.constants :refer [actor-types +
+ + 005                                                          noun-types +
+ + 006                                                          re-rfc5646]] +
+ + 007              [dog-and-duck.quack.picky.control-variables :refer [*reify-refs*]] +
+ + 008              [dog-and-duck.quack.picky.time :refer [date-time-property-or-fault +
+ + 009                                                     xsd-date-time? +
+ + 010                                                     xsd-duration?]] +
+ + 011              [dog-and-duck.quack.picky.utils :refer [concat-non-empty +
+ + 012                                                      cond-make-fault-object +
+ + 013                                                      has-activity-type? +
+ + 014                                                      has-context? +
+ + 015                                                      has-type? +
+ + 016                                                      has-type-or-fault +
+ + 017                                                      make-fault-object +
+ + 018                                                      nil-if-empty +
+ + 019                                                      object-or-uri? +
+ + 020                                                      truthy? +
+ + 021                                                      xsd-non-negative-integer?]] +
+ + 022              [taoensso.timbre :refer [info warn]]) +
+ + 023    (:import [java.io FileNotFoundException] +
+ + 024             [java.net URI URISyntaxException])) +
+ + 025   +
+ + 026  (defn- xsd-float? +
+ + 027    [pv] +
+ + 028    (or (integer? pv) (float? pv))) +
+ + 029   +
+ + 030  ;;;     Copyright (C) Simon Brooke, 2022 +
+ + 031   +
+ + 032  ;;;     This program is free software; you can redistribute it and/or +
+ + 033  ;;;     modify it under the terms of the GNU General Public License +
+ + 034  ;;;     as published by the Free Software Foundation; either version 2 +
+ + 035  ;;;     of the License, or (at your option) any later version. +
+ + 036   +
+ + 037  ;;;     This program is distributed in the hope that it will be useful, +
+ + 038  ;;;     but WITHOUT ANY WARRANTY; without even the implied warranty of +
+ + 039  ;;;     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +
+ + 040  ;;;     GNU General Public License for more details. +
+ + 041   +
+ + 042  ;;;     You should have received a copy of the GNU General Public License +
+ + 043  ;;;     along with this program; if not, write to the Free Software +
+ + 044  ;;;     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +
+ + 045   +
+ + 046  (def object-expected-properties +
+ + 047    "Requirements of properties of object, cribbed from +
+ + 048     https://www.w3.org/TR/activitystreams-vocabulary/#properties +
+ + 049      +
+ + 050     Note the following sub-key value types: +
+ + 051      +
+ + 052     * `:collection` opposite of `:functional`: if true, value should be a +
+ + 053        collection (in the Clojure sense), not a single object; +
+ + 054     * `:functional` if true, value should be a single object; if false, may +
+ + 055        be a single object or a sequence of objects, but each must pass  +
+ + 056        validation checks; +
+ + 057     * `:if-invalid` a sequence of two keywords, first indicating severity, +
+ + 058        second being a message key; +
+ + 059     * `:if-missing` a sequence of two keywords, first indicating severity, +
+ + 060        second being a message key; +
+ + 061     * `:required` a boolean, or a function of one argument returning a  +
+ + 062        boolean, in which case the function will be applied to the object +
+ + 063        having the property; +
+ + 064     * `:validator` a function of one argument returning a boolean, which will  +
+ + 065        be applied to the value or values of the identified property." +
+ + 066    {:accuracy {:functional false +
+ + 067                :if-invalid [:must :invalid-number] +
+ + 068                :validator (fn [pv] (and (xsd-float? pv) +
+ + 069                                         (>= pv 0) +
+ + 070                                         (<= pv 100)))} +
+ + 071     :actor {:functional false +
+ + 072             :if-invalid [:must :invalid-actor] +
+ + 073             :if-missing [:must :no-actor] +
+ + 074             :required has-activity-type? +
+ + 075             :validator object-or-uri?} +
+ + 076     :altitude {:functional false +
+ + 077                :if-invalid [:must :invalid-number] +
+ + 078                :validator xsd-float?} +
+ + 079     :anyOf {:collection true +
+ + 080             :functional false +
+ + 081             ;; a Question should have a `:oneOf` or `:anyOf`, but at this layer +
+ + 082             ;; that's hard to check. +
+ + 083             :if-invalid [:must :invalid-option] +
+ + 084             :validator object-or-uri?} +
+ + 085     :attachment {:functional false +
+ + 086                  :if-invalid [:must :invalid-attachment] +
+ + 087                  :validator object-or-uri?} +
+ + 088     :attributedTo {:functional false +
+ + 089                    :if-invalid [:must :invalid-attribution] +
+ + 090                    :validator object-or-uri?} +
+ + 091     :audience {:functional false +
+ + 092                :if-invalid [:must :invalid-audience] +
+ + 093                :validator object-or-uri?} +
+ + 094     :bcc {:functional false +
+ + 095           :if-invalid [:must :invalid-audience] ;; do we need a separate message for bcc, cc, etc? +
+ + 096           :validator object-or-uri?} +
+ + 097     :cc {:functional false +
+ + 098          :if-invalid [:must :invalid-audience] ;; do we need a separate message for bcc, cc, etc? +
+ + 099          :validator object-or-uri?} +
+ + 100     :closed {:functional false +
+ + 101              :if-invalid [:must :invalid-closed] +
+ + 102              :validator (fn [pv] (truthy? (or (object-or-uri? pv) +
+ + 103                                               (xsd-date-time? pv) +
+ + 104                                               (#{"true" "false"} pv))))} +
+ + 105     :content {:functional false +
+ + 106               :if-invalid [:must :invalid-content] +
+ + 107               :validator string?} +
+ + 108     :context {:functional false +
+ + 109               :if-invalid [:must :invalid-context] +
+ + 110               :validator object-or-uri?} +
+ + 111     :current {:functional true +
+ + 112               :if-missing [:minor :paged-collection-no-current] +
+ + 113               :if-invalid [:must :paged-collection-invalid-current] +
+ + 114               :required (fn [x] ;; if an object is a collection which has pages, +
+ + 115                                   ;; it ought to have a `:current` page. But  +
+ + 116                                   ;; 1. it isn't required to, and +
+ + 117                                   ;; 2. there's no certain way of telling that it +
+ + 118                                   ;;    does have pages - although if it has a +
+ + 119                                   ;;    `:first`, then it is. +
+ + 120                           (and +
+ + 121                            (or (has-type? x "Collection") +
+ + 122                                (has-type? x "OrderedCollection")) +
+ + 123                            (:first x))) +
+ + 124               :validator (fn [pv] (object-or-uri? pv #{"CollectionPage" +
+ + 125                                                        "OrderedCollectionPage"}))} +
+ + 126     :deleted {:functional true +
+ + 127               :if-missing [:minor :tombstone-missing-deleted] +
+ + 128               :if-invalid [:must :invalid-deleted] +
+ + 129               :required (fn [x] (has-type? x "Tombstone")) +
+ + 130               :validator xsd-date-time?} +
+ + 131     :describes {:functional true +
+ + 132                 :required (fn [x] (has-type? x "Profile")) +
+ + 133                 :if-invalid [:must :invalid-describes] +
+ + 134                 ;; TODO: actually the spec says this MUST be an object and +
+ + 135                 ;; not a URI, which it doesn't say anywhere else, but this seems +
+ + 136                 ;; to make no sense? +
+ + 137                 :validator object-or-uri?} +
+ + 138     :duration {:functional false +
+ + 139                :if-invalid [:must :invalid-duration] +
+ + 140                :validator xsd-duration?} +
+ + 141     :endTime {:functional true +
+ + 142               :if-invalid [:must :invalid-date-time] +
+ + 143               :validator xsd-date-time?} +
+ + 144     :first {:functional true +
+ + 145             :if-missing [:minor :paged-collection-no-first] +
+ + 146             :if-invalid [:must :paged-collection-invalid-first] +
+ + 147             :required (fn [x] ;; if an object is a collection which has pages, +
+ + 148                                   ;; it ought to have a `:first` page. But  +
+ + 149                                   ;; 1. it isn't required to, and +
+ + 150                                   ;; 2. there's no certain way of telling that it +
+ + 151                                   ;;    does have pages - although if it has a +
+ + 152                                   ;;    `:last`, then it is. +
+ + 153                         (and +
+ + 154                          (or (has-type? x "Collection") +
+ + 155                              (has-type? x "OrderedCollection")) +
+ + 156                          (:last x))) +
+ + 157             :validator (fn [pv] (object-or-uri? pv #{"CollectionPage" +
+ + 158                                                      "OrderedCollectionPage"}))} +
+ + 159     :formerType {:functional false +
+ + 160                  :if-missing [:minor :tombstone-missing-former-type] +
+ + 161                  :if-invalid [:must :invalid-former-type] +
+ + 162                  :required (fn [x] (has-type? x "Tombstone")) +
+ + 163                  ;; The narrative of the spec says this should be an `Object`, +
+ + 164                  ;; but in all the provided examples it's a string. +
+ + 165                  :validator string?} +
+ + 166     :generator {:functional false +
+ + 167                 :if-invalid [:must :invalid-generator] +
+ + 168                 :validator object-or-uri?} +
+ + 169     :height {:functional false +
+ + 170              :if-invalid [:must :invalid-non-negative] +
+ + 171              :validator xsd-non-negative-integer?} +
+ + 172     :href {:functional false +
+ + 173            :if-invalid [:must :invalid-href] +
+ + 174            :validator (fn [pv] (try (uri? (URI. pv)) +
+ + 175                                     (catch URISyntaxException _ false)))} +
+ + 176     :hreflang {:validator (fn [pv] (truthy? (re-matches re-rfc5646 pv)))} +
+ + 177     :icon {:functional false +
+ + 178            :if-invalid [:must :invalid-icon] +
+ + 179            ;; an icon is also expected to have a 1:1 aspect ratio, but that's +
+ + 180            ;; too much detail at this level of verification +
+ + 181            :validator (fn [pv] (object-or-uri? pv "Image"))} +
+ + 182     :id {:functional true +
+ + 183          :if-missing [:minor :no-id-transient] +
+ + 184          :if-invalid [:must :invalid-id] +
+ + 185          :validator (fn [pv] (try (uri? (URI. pv)) +
+ + 186                                   (catch URISyntaxException _ false)))} +
+ + 187     :image {:functional false +
+ + 188             :if-invalid [:must :invalid-image] +
+ + 189             :validator (fn [pv] (object-or-uri? pv "Image"))} +
+ + 190     :inReplyTo {:functional false +
+ + 191                 :if-invalid [:must :invalid-in-reply-to] +
+ + 192                 :validator (fn [pv] (object-or-uri? pv noun-types))} +
+ + 193     :instrument {:functional false +
+ + 194                  :if-invalid [:must :invalid-instrument] +
+ + 195                  :validator object-or-uri?} +
+ + 196     :items {:collection true +
+ + 197             :functional false +
+ + 198             :if-invalid [:must :invalid-items] +
+ + 199             :if-missing [:must :no-items-or-pages] +
+ + 200             :required (fn [x] (or (has-type? x "CollectionPage") +
+ + 201                                   (and (has-type? x "Collection") +
+ + 202                                        ;; if it's a collection and has pages, +
+ + 203                                        ;; it doesn't need items. +
+ + 204                                        (not (:current x)) +
+ + 205                                        (not (:first x)) +
+ + 206                                        (not (:last x))))) +
+ + 207             :validator (fn [pv] (and (coll? pv) (every? object-or-uri? pv)))} +
+ + 208     :last {:functional true +
+ + 209            :if-missing [:minor :paged-collection-no-last] +
+ + 210            :if-invalid [:must :paged-collection-invalid-last] +
+ + 211            :required (fn [x] (if (and +
+ + 212                                   (string? x) +
+ + 213                                   (try (uri? (URI. x)) +
+ + 214                                        (catch URISyntaxException _ false))) +
+ + 215                                true +
+ + 216                                   ;; if an object is a collection which has pages, +
+ + 217                                   ;; it ought to have a `:last` page. But  +
+ + 218                                   ;; 1. it isn't required to, and +
+ + 219                                   ;; 2. there's no certain way of telling that it +
+ + 220                                   ;;    does have pages - although if it has a +
+ + 221                                   ;;    `:first`, then it is. +
+ + 222                                (and +
+ + 223                                 (has-type? x #{"Collection" +
+ + 224                                                "OrderedCollection"}) +
+ + 225                                 (:first x)))) +
+ + 226            :validator (fn [pv] (object-or-uri? pv #{"CollectionPage" +
+ + 227                                                     "OrderedCollectionPage"}))} +
+ + 228     :latitude {:functional true +
+ + 229                :if-invalid [:must :invalid-latitude] +
+ + 230                ;; The XSD spec says this is an IEEE 754-2008, and the IEEE +
+ + 231                ;; wants US$104 for me to find out what that is. So I don't +
+ + 232                ;; strictly know that an integer is valid here. +
+ + 233                :validator xsd-float?} +
+ + 234     :location {:functional false +
+ + 235                :if-invalid [:must :invalid-location] +
+ + 236                :validator (fn [pv] (object-or-uri? pv #{"Place"}))} +
+ + 237     :longitude {:functional true +
+ + 238                 :if-invalid [:must :invalid-longitude] +
+ + 239                 :validator xsd-float?} +
+ + 240     :mediaType {:functional true +
+ + 241                 :if-invalid [:must :invalid-mime-type] +
+ + 242                 :validator (fn [pv] (truthy? (re-matches #"\w+/[-.\w]+(?:\+[-.\w]+)?" pv)))} +
+ + 243     :name {:functional false +
+ + 244            :if-invalid [:must :invalid-name] +
+ + 245            :validator string?} +
+ + 246     :next {:functional true +
+ + 247            :if-invalid [:must :invalid-next-page] +
+ + 248            :validator (fn [pv] (object-or-uri? pv #{"CollectionPage" +
+ + 249                                                     "OrderedCollectionPage"}))} +
+ + 250     :object {:functional false +
+ + 251              :if-invalid [:must :invalid-direct-object] +
+ + 252              :validator object-or-uri?} +
+ + 253     :oneOf {:collection true +
+ + 254             :functional false +
+ + 255             ;; a Question should have a `:oneOf` ot `:anyOf`, but at this layer +
+ + 256             ;; that's hard to check. +
+ + 257             :if-invalid [:must :invalid-option] +
+ + 258             :validator object-or-uri?} +
+ + 259      +
+ + 260     :orderedItems {:collection true +
+ + 261             :functional false +
+ + 262             :if-invalid [:must :invalid-items] +
+ + 263             :if-missing [:must :no-items-or-pages] +
+ + 264             :required (fn [x] (or (has-type? x "OrderedCollectionPage") +
+ + 265                                   (and (has-type? x "OrderedCollection") +
+ + 266                                        ;; if it's a collection and has pages, +
+ + 267                                        ;; it doesn't need items. +
+ + 268                                        (not (:current x)) +
+ + 269                                        (not (:first x)) +
+ + 270                                        (not (:last x))))) +
+ + 271             :validator (fn [pv] (and (coll? pv) (every? object-or-uri? pv)))} +
+ + 272     :origin {:functional false +
+ + 273              :if-invalid [:must :invalid-origin] +
+ + 274              :validator object-or-uri?} +
+ + 275     :partOf {:functional true +
+ + 276              :if-missing [:must :missing-part-of] +
+ + 277              :if-invalid [:must :invalid-part-of] +
+ + 278              :required (fn [x] (object-or-uri? x #{"CollectionPage" +
+ + 279                                                    "OrderedCollectionPage"})) +
+ + 280              :validator (fn [pv] (object-or-uri? pv #{"Collection" +
+ + 281                                                       "OrderedCollection"}))} +
+ + 282     :prev {:functional true +
+ + 283            :if-invalid [:must :invalid-prior-page] +
+ + 284            :validator (fn [pv] (object-or-uri? pv #{"CollectionPage" +
+ + 285                                                     "OrderedCollectionPage"}))} +
+ + 286     :preview {:functional false +
+ + 287               :if-invalid [:must :invalid-preview] +
+ + 288               ;; probably likely to be an Image or Video, but that isn't stated. +
+ + 289               :validator object-or-uri?} +
+ + 290     :published {:functional true +
+ + 291                 :if-invalid [:must :invalid-date-time] +
+ + 292                 :validator xsd-date-time?} +
+ + 293     :replies {:functional true +
+ + 294               :if-invalid [:must :invalid-replies] +
+ + 295               :validator (fn [pv] (object-or-uri? pv #{"Collection" +
+ + 296                                                        "OrderedCollection"}))} +
+ + 297     :radius {:functional true +
+ + 298              :if-invalid [:must :invalid-positive-number] +
+ + 299              :validator (fn [pv] (and (xsd-float? pv) (> pv 0)))} +
+ + 300     :rel {:functional false +
+ + 301           :if-invalid [:must :invalid-link-relation] +
+ + 302           ;; TODO: this is not really good enough. +
+ + 303           :validator (fn [pv] (truthy? (re-matches #"[a-zA-A0-9_\-\.\:\?/\\]*" pv)))} +
+ + 304     :relationship {;; this exists in the spec, but it doesn't seem to be required and it's +
+ + 305                    ;; extremely hazily specified.  +
+ + 306                    } +
+ + 307     :result {:functional false +
+ + 308              :if-invalid [:must :invalid-result] +
+ + 309              :validator object-or-uri?} +
+ + 310     :startIndex {:functional true +
+ + 311                  :if-invalid [:must :invalid-start-index] +
+ + 312                  :validator xsd-non-negative-integer?} +
+ + 313     :start-time {:functional true +
+ + 314                  :if-invalid [:must :invalid-date-time] +
+ + 315                  :validator xsd-date-time?} +
+ + 316     :subject {:functional true +
+ + 317               :if-invalid [:must :invalid-subject] +
+ + 318               :if-missing [:minor :no-relationship-subject] +
+ + 319               :required (fn [x] (has-type? x "Relationship")) +
+ + 320               :validator object-or-uri?} +
+ + 321     :summary {:functional false +
+ + 322               :if-invalid [:must :invalid-summary] +
+ + 323               ;; TODO: HTML formatting is allowed, but other forms of formatting +
+ + 324               ;; are not. Can this be validated? +
+ + 325               :validator string?} +
+ + 326     :tag {:functional false +
+ + 327           :if-invalid [:must :invalid-tag] +
+ + 328           :validator object-or-uri?} +
+ + 329     :target {:functional false +
+ + 330              :if-invalid [:must :invalid-target] +
+ + 331              :validator object-or-uri?} +
+ + 332     :to {:functional false +
+ + 333          :if-invalid [:must :invalid-to] +
+ + 334          :validator (fn [pv] (object-or-uri? pv actor-types))} +
+ + 335     :totalItems {:functional true +
+ + 336                  :if-invalid [:must :invalid-total-items] +
+ + 337                  :validator xsd-non-negative-integer?} +
+ + 338     :type {:functional false +
+ + 339            :if-missing [:minor :no-type] +
+ + 340            :if-invalid [:must :invalid-type] +
+ + 341            ;; strictly, it's an `anyURI`, but realistically these are not checkable. +
+ + 342            :validator string?} +
+ + 343     :units {:functional true +
+ + 344             :if-invalid [:must :invalid-units] +
+ + 345             ;; the narrative says that `anyURI`, but actually unless it's a recognised +
+ + 346             ;; unit the property is useless. These are the units explicitly specified. +
+ + 347             :validator (fn [pv] (#{"cm" "feet" "inches" "km" "m" "miles"} pv))} +
+ + 348     :updated {:functional true +
+ + 349               :if-invalid [:must :invalid-updated] +
+ + 350               :validator xsd-date-time?} +
+ + 351     :url {:functional false +
+ + 352           :if-invalid [:must :invalid-url-property] +
+ + 353           :validator (fn [pv] (object-or-uri? pv "Link"))} +
+ + 354     :width {:functional true +
+ + 355             :if-invalid [:must :invalid-width] +
+ + 356             :validator xsd-non-negative-integer?}}) +
+ + 357   +
+ + 358  (defn check-property-required [obj prop clause] +
+ + 359    (let [required (:required clause) +
+ + 360          [severity token] (:if-missing clause)] +
+ + 361      (when required +
+ + 362        (when +
+ + 363         (and (apply required (list obj)) (not (obj prop))) +
+ + 364          (make-fault-object severity token))))) +
+ + 365   +
+ + 366  (defn check-property-valid +
+ + 367    [obj prop clause] +
+ + 368    ;; (info "obj" obj "prop" prop "clause" clause) +
+ + 369    (let [val (obj prop) +
+ + 370          validator (:validator clause) +
+ + 371          [severity token] (:if-invalid clause)] +
+ + 372      (when (and val validator) +
+ + 373        (cond-make-fault-object +
+ + 374         (apply validator (list val)) +
+ + 375         severity token)))) +
+ + 376   +
+ + 377  (defn check-property [obj prop] +
+ + 378    (assert (map? obj)) +
+ + 379    (assert (keyword? prop)) +
+ + 380    (let [clause (object-expected-properties prop)] +
+ + 381      (nil-if-empty +
+ + 382       (remove nil? +
+ + 383               (list +
+ + 384                (check-property-required obj prop clause) +
+ + 385                (check-property-valid obj prop clause)))))) +
+ + 386   +
+ + 387  (defn properties-faults +
+ + 388    "Return a lost of faults found on properties of the object `x`, or +
+ + 389     `nil` if none are." +
+ + 390    [x] +
+ + 391    (apply  +
+ + 392     concat-non-empty +
+ + 393     (let [props (set (keys x)) +
+ + 394           required (set +
+ + 395                     (filter +
+ + 396                      #((object-expected-properties %) :required) +
+ + 397                      (keys object-expected-properties)))] +
+ + 398       (map +
+ + 399        (fn [p] (check-property x p)) +
+ + 400        (union props required))))) +
+ + 401   +
+ + 402  (defn object-faults +
+ + 403    "Return a list of faults found in object `x`, or `nil` if none are. +
+ + 404      +
+ + 405     If `expected-type` is also passed, verify that `x` has `expected-type`. +
+ + 406     `expected-type` may be passed as a string or as a set of strings. Detailed +
+ + 407     verification of the particular features of types is not done here." +
+ + 408   +
+ + 409    ;; TODO: many more properties which are nor required, nevertheless have required +
+ + 410    ;; property TYPES as detailed in +
+ + 411    ;; https://www.w3.org/TR/activitystreams-vocabulary/#properties +
+ + 412    ;; if these properties are present, these types should be checked. +
+ + 413    ([x] +
+ + 414     (concat-non-empty +
+ + 415      (remove empty? +
+ + 416              (list +
+ + 417               (when-not (map? x) +
+ + 418                 (make-fault-object :critical :not-an-object)) +
+ + 419               (when-not +
+ + 420                (has-context? x) +
+ + 421                 (make-fault-object :should :no-context)) +
+ + 422               (when-not (:type x) +
+ + 423                 (make-fault-object :minor :no-type)) +
+ + 424               (when-not (and (map? x) (contains? x :id)) +
+ + 425                 (make-fault-object :minor :no-id-transient)))) +
+ + 426      (properties-faults x))) +
+ + 427    ([x expected-type] +
+ + 428     (concat-non-empty +
+ + 429      (object-faults x) +
+ + 430      (when expected-type +
+ + 431        (list +
+ + 432         (has-type-or-fault x expected-type :critical :unexpected-type)))))) +
+ + 433   +
+ + 434  (def maybe-reify +
+ + 435    "If `*reify-refs*` is `true`, return the object at this `target` URI. +
+ + 436     Returns `nil` if +
+ + 437      +
+ + 438     1. `*reify-refs*` is false; +
+ + 439     2. the object was not found; +
+ + 440     3. access to the object was not permitted. +
+ + 441      +
+ + 442     Consequently, use with care." +
+ + 443    (memoize +
+ + 444     (fn [target] +
+ + 445       (try (let [uri (URI. target)] +
+ + 446              (when *reify-refs* +
+ + 447                (json/read-str (slurp uri)))) +
+ + 448            (catch URISyntaxException _ +
+ + 449              (warn "Reification target" target "was not a valid URI.") +
+ + 450              nil) +
+ + 451            (catch FileNotFoundException _ +
+ + 452              (warn "Reification target" target "was not found.") +
+ + 453              nil))))) +
+ + 454   +
+ + 455  (defn maybe-reify-or-faults +
+ + 456    "If `*reify-refs*` is `true`, runs basic checks on the object at this  +
+ + 457     `target` URI, if it is found, or a list containing a fault object with +
+ + 458     this `severity` and `token` if it is not." +
+ + 459    [value expected-type severity token] +
+ + 460    (let [object (maybe-reify value)] +
+ + 461      (cond object +
+ + 462            (object-faults object expected-type) +
+ + 463            *reify-refs* (list (make-fault-object severity token))))) +
+ + 464   +
+ + 465  (defn object-reference-or-faults +
+ + 466    "If this `value` is either  +
+ + 467      +
+ + 468     1. an object of `expected-type`; +
+ + 469     2. a URI referencing an object of  `expected-type`; or +
+ + 470     3. a link object referencing an object of  `expected-type` +
+ + 471      +
+ + 472     and no faults are returned from validating the linked object, then return +
+ + 473     `nil`; else return a sequence comprising a fault object with this `severity` +
+ + 474     and `token`, prepended to the faults returned. +
+ + 475      +
+ + 476     As with `has-type-or-fault` (q.v.), `expected-type` may be passed as a +
+ + 477     string, as a set of strings, or `nil` (indicating the type of the  +
+ + 478     referenced object should not be checked). +
+ + 479      +
+ + 480     **NOTE THAT** if `*reify-refs*` is `false`, referenced objects will not +
+ + 481     actually be checked." +
+ + 482    [value expected-type severity token] +
+ + 483    (let [faults (cond +
+ + 484                   (string? value) (maybe-reify-or-faults value severity token expected-type) +
+ + 485                   (map? value) (if (has-type? value "Link") +
+ + 486                                  (cond +
+ + 487                                    ;; if we were looking for a link and we've  +
+ + 488                                    ;; found a link, that's OK. +
+ + 489                                    (= expected-type "Link") nil +
+ + 490                                    (and (set? expected-type) (expected-type "Link")) nil +
+ + 491                                    (nil? expected-type) nil +
+ + 492                                    :else +
+ + 493                                    (object-reference-or-faults +
+ + 494                                     (:href value) expected-type severity token)) +
+ + 495                                  (object-faults value expected-type)) +
+ + 496                   :else (throw +
+ + 497                          (ex-info +
+ + 498                           "Argument `value` was not an object or a link to an object" +
+ + 499                           {:arguments {:value value} +
+ + 500                            :expected-type expected-type +
+ + 501                            :severity severity +
+ + 502                            :token token})))] +
+ + 503      (when faults (cons (make-fault-object severity token) faults)))) +
+ + 504   +
+ + 505  (defn coll-object-reference-or-fault +
+ + 506    "As object-reference-or-fault, except `value` argument may also be a list of +
+ + 507     objects and/or object references." +
+ + 508    [value expected-type severity token] +
+ + 509    (cond +
+ + 510      (map? value) (object-reference-or-faults value expected-type severity token) +
+ + 511      (coll? value) (concat-non-empty +
+ + 512                     (map +
+ + 513                      #(object-reference-or-faults +
+ + 514                        % expected-type severity token) +
+ + 515                      value)) +
+ + 516      :else (throw +
+ + 517             (ex-info +
+ + 518              "Argument `value` was not an object, a link to an object, nor a list of these." +
+ + 519              {:arguments {:value value} +
+ + 520               :expected-type expected-type +
+ + 521               :severity severity +
+ + 522               :token token})))) +
+ + diff --git a/docs/cloverage/dog_and_duck/quack/picky/scratch.clj.html b/docs/cloverage/dog_and_duck/quack/picky/scratch.clj.html new file mode 100644 index 0000000..3a5ad6b --- /dev/null +++ b/docs/cloverage/dog_and_duck/quack/picky/scratch.clj.html @@ -0,0 +1,167 @@ + + + + dog_and_duck/quack/picky/scratch.clj + + + + 001  (ns dog-and-duck.quack.picky.scratch +
+ + 002    "Development scratchpad" +
+ + 003    (:require [clojure.data.json :refer [read-str]] +
+ + 004              [clojure.java.io :refer [file]] +
+ + 005              [clojure.walk :refer [keywordize-keys]] +
+ + 006              [dog-and-duck.quack.picky.distribution :refer [distribution]] +
+ + 007              [dog-and-duck.quack.picky.objects :refer +
+ + 008               [object-faults properties-faults]] +
+ + 009              [dog-and-duck.quack.picky.utils :refer [concat-non-empty +
+ + 010                                                      filter-severity]])) +
+ + 011   +
+ + 012  ;; (def files (filter +
+ + 013  ;;             #(and (.isFile %) (.endsWith (.getName %) ".json")) +
+ + 014  ;;             (file-seq (file "resources/activitystreams-test-documents")))) +
+ + 015   +
+ + 016  ;; (def r +
+ + 017  ;;   (reduce +
+ + 018  ;;    concat-non-empty +
+ + 019  ;;    (map +
+ + 020  ;;     #(try +
+ + 021  ;;        (let [contents (read-str (slurp %)) +
+ + 022  ;;              faults (cond (map? contents) (filter-severity +
+ + 023  ;;                                            (object-faults +
+ + 024  ;;                                             (keywordize-keys contents)) +
+ + 025  ;;                                            :should) +
+ + 026  ;;                         ;;   (coll? contents) (apply +
+ + 027  ;;                         ;;                     concat-non-empty +
+ + 028  ;;                         ;;                     (map (fn [obj] +
+ + 029  ;;                         ;;                            (object-faults +
+ + 030  ;;                         ;;                             (keywordize-keys obj))) +
+ + 031  ;;                         ;;                          contents)) +
+ + 032  ;;                           )] +
+ + 033  ;;          (map (fn [f] (assoc f :document (.getName %))) faults)) +
+ + 034  ;;        (catch Exception any +
+ + 035  ;;          [(.getName %) (str "Exception " +
+ + 036  ;;                             (.getName (.getClass any)) +
+ + 037  ;;                             ": " +
+ + 038  ;;                             (.getMessage any))])) +
+ + 039  ;;     (filter +
+ + 040  ;;      #(and (.isFile %) (.endsWith (.getName %) ".json")) +
+ + 041  ;;      (file-seq (file "resources/activitystreams-test-documents")))))) +
+ + 042   +
+ + 043  ;; (count (filter-severity (object-faults (keywordize-keys (read-str (slurp "resources/activitystreams-test-documents/vocabulary-ex189-jsonld.json")))) :critical)) +
+ + 044   +
+ + 045  ;; (count (filter +
+ + 046  ;;         #(and (.isFile %) (.endsWith (.getName %) ".json")) +
+ + 047  ;;         (file-seq (file "resources/activitystreams-test-documents")))) +
+ + 048   +
+ + 049  ;; (count r) +
+ + 050  ;; (last r) +
+ + 051  ;; (clojure.pprint/pprint (last r)) +
+ + 052   +
+ + 053  ;; (distribution :fault r) +
+ + diff --git a/docs/cloverage/dog_and_duck/quack/picky/time.clj.html b/docs/cloverage/dog_and_duck/quack/picky/time.clj.html new file mode 100644 index 0000000..914070a --- /dev/null +++ b/docs/cloverage/dog_and_duck/quack/picky/time.clj.html @@ -0,0 +1,206 @@ + + + + dog_and_duck/quack/picky/time.clj + + + + 001  (ns dog-and-duck.quack.picky.time +
+ + 002    "Time, gentleman, please! Recognising and validating date time values." +
+ + 003    (:require [dog-and-duck.quack.picky.utils :refer [cond-make-fault-object +
+ + 004                                                      make-fault-object +
+ + 005                                                      truthy?]] +
+ + 006              [scot.weft.i18n.core :refer [get-message]] +
+ + 007              [taoensso.timbre :refer [warn error]]) +
+ + 008    (:import [java.time LocalDateTime] +
+ + 009             [java.time.format DateTimeFormatter DateTimeParseException] +
+ + 010             [javax.xml.datatype DatatypeFactory])) +
+ + 011   +
+ + 012  ;;;     Copyright (C) Simon Brooke, 2023 +
+ + 013   +
+ + 014  ;;;     This program is free software; you can redistribute it and/or +
+ + 015  ;;;     modify it under the terms of the GNU General Public License +
+ + 016  ;;;     as published by the Free Software Foundation; either version 2 +
+ + 017  ;;;     of the License, or (at your option) any later version. +
+ + 018   +
+ + 019  ;;;     This program is distributed in the hope that it will be useful, +
+ + 020  ;;;     but WITHOUT ANY WARRANTY; without even the implied warranty of +
+ + 021  ;;;     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +
+ + 022  ;;;     GNU General Public License for more details. +
+ + 023   +
+ + 024  ;;;     You should have received a copy of the GNU General Public License +
+ + 025  ;;;     along with this program; if not, write to the Free Software +
+ + 026  ;;;     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +
+ + 027   +
+ + 028  (defn xsd-date-time? +
+ + 029    "Return `true` if `value` matches the pattern for an  +
+ + 030     [xsd:dateTime](https://www.w3.org/TR/xmlschema11-2/#dateTime), else `false`" +
+ + 031    [^String value] +
+ + 032    (try +
+ + 033      (if (LocalDateTime/from (.parse DateTimeFormatter/ISO_DATE_TIME value)) true false) +
+ + 034      (catch DateTimeParseException _ +
+ + 035        (warn (get-message :bad-date-time) ":" value) +
+ + 036        false) +
+ + 037      (catch Exception e +
+ + 038        (error "Exception thrown while parsing date" value e) +
+ + 039        false))) +
+ + 040   +
+ + 041  (defn xsd-duration? +
+ + 042    "Return `true` if `value` matches the pattern for an  +
+ + 043     [xsd:duration](https://www.w3.org/TR/xmlschema11-2/#duration), else `false`" +
+ + 044    [value] +
+ + 045    (truthy? +
+ + 046     (and (string? value) +
+ + 047          (try (.newDuration (DatatypeFactory/newInstance) value) +
+ + 048               (catch IllegalArgumentException _ +
+ + 049                 (warn (get-message :bad-duration) ":" value) +
+ + 050                 false) +
+ + 051               (catch Exception e +
+ + 052                 (error "Exception thrown while parsing duration" value e) +
+ + 053                 false))))) +
+ + 054   +
+ + 055  (defn date-time-property-or-fault +
+ + 056    "If the value of this `property` of object `x` is a valid xsd:dateTime  +
+ + 057     value, return a fault object with this `token` and `severity`.  +
+ + 058      +
+ + 059     If `required?` is false and there is no such property, no fault will be +
+ + 060     returned." +
+ + 061    [x property severity token required?] +
+ + 062    (let [value (property x)] +
+ + 063      (if (and required? (not (x property))) +
+ + 064        (make-fault-object severity token) +
+ + 065        (cond-make-fault-object +
+ + 066         (and value (xsd-date-time? value)) severity token)))) +
+ + diff --git a/docs/cloverage/dog_and_duck/quack/picky/utils.clj.html b/docs/cloverage/dog_and_duck/quack/picky/utils.clj.html index 51c6d14..2180db1 100644 --- a/docs/cloverage/dog_and_duck/quack/picky/utils.clj.html +++ b/docs/cloverage/dog_and_duck/quack/picky/utils.clj.html @@ -11,955 +11,757 @@ 002    "Utility functions supporting the picky validator"

- 003    (:require [clojure.data.json :as json] + 003    (:require [clojure.set :refer [intersection]]
- 004              [clojure.set :refer [intersection]] + 004              [dog-and-duck.quack.picky.constants :refer [activitystreams-context-uri
- 005              [dog-and-duck.quack.picky.constants :refer [activitystreams-context-uri + 005                                                          actor-types
- 006                                                          actor-types + 006                                                          context-key severity-filters
- 007                                                          context-key severity-filters + 007                                                          validation-fault-context-uri
- 008                                                          validation-fault-context-uri + 008                                                          activity-types]]
- 009                                                          verb-types]] + 009              [dog-and-duck.utils.process :refer [get-hostname get-pid]]
- 010              [dog-and-duck.quack.picky.control-variables :refer [*reify-refs*]] + 010              [scot.weft.i18n.core :refer [get-message]]
- 011              [dog-and-duck.quack.picky.fault-messages :refer [messages]] + 011              [taoensso.timbre :as log :refer [warn]]) +
+ + 012  
- 012              [dog-and-duck.utils.process :refer [get-hostname get-pid]] -
- - 013              [taoensso.timbre :as log :refer [warn]]) + 013    (:import [java.net URI URISyntaxException]))
014  
- 015    (:import [java.net URI URISyntaxException])) + 015  ;;;     Copyright (C) Simon Brooke, 2022
016  
- 017  ;;;     Copyright (C) Simon Brooke, 2022 + 017  ;;;     This program is free software; you can redistribute it and/or +
+ + 018  ;;;     modify it under the terms of the GNU General Public License +
+ + 019  ;;;     as published by the Free Software Foundation; either version 2 +
+ + 020  ;;;     of the License, or (at your option) any later version.
- 018   + 021  
- 019  ;;;     This program is free software; you can redistribute it and/or + 022  ;;;     This program is distributed in the hope that it will be useful,
- 020  ;;;     modify it under the terms of the GNU General Public License + 023  ;;;     but WITHOUT ANY WARRANTY; without even the implied warranty of
- 021  ;;;     as published by the Free Software Foundation; either version 2 + 024  ;;;     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- 022  ;;;     of the License, or (at your option) any later version. + 025  ;;;     GNU General Public License for more details.
- 023   + 026  
- 024  ;;;     This program is distributed in the hope that it will be useful, + 027  ;;;     You should have received a copy of the GNU General Public License
- 025  ;;;     but WITHOUT ANY WARRANTY; without even the implied warranty of + 028  ;;;     along with this program; if not, write to the Free Software
- 026  ;;;     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -
- - 027  ;;;     GNU General Public License for more details. + 029  ;;;     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- 028   -
- - 029  ;;;     You should have received a copy of the GNU General Public License -
- - 030  ;;;     along with this program; if not, write to the Free Software -
- - 031  ;;;     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. + 030  
- 032   -
- - 033   + 031  
- 034  (defn actor-type? + 032  (defn actor-type?
- 035    "Return `true` if the `x` is a recognised actor type, else `false`." + 033    "Return `true` if the `x` is a recognised actor type, else `false`."
- 036    [^String x] + 034    [^String x]
- 037    (if (actor-types x) true false)) + 035    (if (actor-types x) true false))
- 038   + 036  
- 039  (defn truthy? + 037  (defn truthy?
- 040    "Return `true` if `x` is truthy, else `false`. There must be some more  + 038    "Return `true` if `x` is truthy, else `false`. There must be some more 
- 041     idiomatic way to do this?" + 039     idiomatic way to do this?"
- 042    [x] + 040    [x]
- - 043    (if x true false)) + + 041    (if x true false))
- 044   + 042  
- 045  (defn has-type? + 043  (defn xsd-non-negative-integer?
- 046    "Return `true` if object `x` has type `type`, else `false`. + 044    "Return `true` if `value` matches the pattern for an 
- 047      + 045     [xsd:nonNegativeInteger](https://www.w3.org/TR/xmlschema11-2/#nonNegativeInteger), else `false`"
- 048     The values of `type` fields of ActivityStreams objects may be lists; they + 046    [x]
- - 049     are considered to have a type if the type token is a member of the list." -
- - 050    [x type] -
- - 051    (assert (map? x) (string? type)) -
- - 052    (let [tv (:type x)] -
- - 053      (cond -
- - 054        (coll? tv) (truthy? (not-empty (filter #(= % type) tv))) -
- - 055        :else (= tv type)))) + + 047    (and (integer? x)(>= x 0)))
- 056   + 048  
- 057  (defn object-or-uri? + 049  (defn has-type?
- 058    "Very basic check that `x` is either an object or a URI." + 050    "Return `true` if object `x` has a type in `acceptable`, else `false`.
- 059    [x] -
- - 060    (try -
- - 061      (cond (string? x) (uri? (URI. x)) -
- - 062            (map? x) (if (and (:type x) (:id x)) true false) + 051     
- 063            :else false) + 052     The values of `:type` fields of ActivityStreams objects may be lists; they
- 064      (catch URISyntaxException _ false) + 053     are considered to have a type if a member of the list is in `acceptable`.
- 065      (catch NullPointerException _ false))) + 054      +
+ + 055     `acceptable` may be passed as a string, in which case there is only one +
+ + 056     acceptable value, or as a set of strings, in which case any member of the +
+ + 057     set is acceptable." +
+ + 058    [x acceptable] +
+ + 059    (assert (map? x) (or (string? acceptable) (set? acceptable))) +
+ + 060    (let [tv (:type x)] +
+ + 061      (truthy? +
+ + 062       (cond +
+ + 063         (and (string? acceptable) (coll? tv)) (not-empty (filter #(= % acceptable) tv)) +
+ + 064         (and (set? acceptable) (coll? tv)) (not-empty (filter #(acceptable %) tv)) +
+ + 065         (string? acceptable) (= tv acceptable) +
+ + 066         (set? acceptable) (acceptable tv)))))
- 066   + 067   +
+ + 068  (defn object-or-uri? +
+ + 069    "Very basic check that `x` is either an object or a URI." +
+ + 070    ([x] +
+ + 071     (try +
+ + 072       (cond (string? x) (uri? (URI. x)) +
+ + 073             (map? x) true +
+ + 074             :else false) +
+ + 075       (catch URISyntaxException _ false) +
+ + 076       (catch NullPointerException _ false))) +
+ + 077    ([x type] +
+ + 078     (if (object-or-uri? x) +
+ + 079       (if (map? x) +
+ + 080         (has-type? x type) +
+ + 081         true) +
+ + 082       false))) +
+ + 083  
- 067  (defmacro link-or-uri? + 084  (defmacro link-or-uri?
- 068    "Very basic check that `x` is either a link object or a URI." + 085    "Very basic check that `x` is either a link object or a URI."
- 069    [x] + 086    [x]
- 070    `(if (object-or-uri? ~x) (has-type? ~x "Link") false)) + 087    `(if (object-or-uri? ~x) (has-type? ~x "Link") false))
- 071   + 088  
- 072   + 089  
- 073  (defn verb-type? + 090  (defn activity-type?
- 074    "`true` if `x`, a string, represents a recognised ActivityStreams activity + 091    "`true` if `x`, a string, represents a recognised ActivityStreams activity
- 075     type." + 092     type."
- 076    [^String x] + 093    [^String x]
- 077    (if (verb-types x) true false)) + 094    (if (activity-types x) true false))
- 078   + 095  
- 079  (defn has-activity-type? + 096  (defn has-activity-type?
- 080    "Return `true` if the object `x` has a type which is an activity type, else  + 097    "Return `true` if the object `x` has a type which is an activity type, else 
- 081     `false`." + 098     `false`."
- 082    [x] -
- - 083    (let [tv (:type x)] -
- - 084      (cond -
- - 085        (coll? tv) (truthy? (not-empty (filter verb-type? tv))) -
- - 086        :else (verb-type? tv)))) -
- - 087   -
- - 088  (defn has-actor-type? -
- - 089    "Return `true` if the object `x` has a type which is an actor type, else  -
- - 090     `false`." -
- - 091    [x] + 099    [x]
- 092    (let [tv (:type x)] + 100    (let [tv (:type x)]
- 093      (cond + 101      (cond
- 094        (coll? tv) (truthy? (not-empty (filter actor-type? tv))) + 102        (coll? tv) (truthy? (not-empty (filter activity-type? tv)))
- 095        :else (actor-type? tv)))) + 103        :else (activity-type? tv))))
- 096   + 104  
- 097  (defn filter-severity + 105  (defn has-actor-type?
- 098    "Return a list of reports taken from these `reports` where the severity + 106    "Return `true` if the object `x` has a type which is an actor type, else 
- 099     of the report is greater than this or equal to this `severity`." + 107     `false`."
- 100    [reports severity] + 108    [x]
- - 101    (cond (nil? reports) nil + + 109    (let [tv (:type x)] +
+ + 110      (cond +
+ + 111        (coll? tv) (truthy? (not-empty (filter actor-type? tv))) +
+ + 112        :else (actor-type? tv)))) +
+ + 113   +
+ + 114  (defn filter-severity +
+ + 115    "Return a list of reports taken from these `reports` where the severity +
+ + 116     of the report is greater than this or equal to this `severity`." +
+ + 117    [reports severity] +
+ + 118    (cond (nil? (severity-filters severity)) (throw +
+ + 119                                               (ex-info +
+ + 120                                                "Argument `severity` was not a valid severity key" +
+ + 121                                                {:arguments {:reports reports +
+ + 122                                                             :severity severity}})) +
+ + 123      (empty? reports) nil
- 102          (and + 124          (and
- 103           (coll? reports) + 125           (coll? reports)
- 104           (every? map? reports) + 126           (every? map? reports)
- 105           (every? :severity reports)) (remove + 127           (every? :severity reports))(remove +
+ + 128                                       #(if  (:severity %)
- 106                                        #((severity-filters severity) (:severity %)) -
- - 107                                        reports) + 129                                          ((severity-filters severity) (:severity %))
- 108          :else + 130                                          false) +
+ + 131                                       reports) +
+ + 132          :else
- 109          (throw + 133          (throw
- 110           (ex-info + 134           (ex-info
- 111            "Argument `reports` was not a collection of fault reports" + 135            "Argument `reports` was not a collection of fault reports"
- 112            {:arguments {:reports reports + 136            {:arguments {:reports reports
- 113                         :severity severity}})))) + 137                         :severity severity}}))))
- 114   + 138  
- 115  (defn context? + 139  (defn context?
- 116    "Returns `true` iff `x` quacks like an ActivityStreams context, else false. + 140    "Returns `true` iff `x` quacks like an ActivityStreams context, else false.
- 117      + 141     
- 118     A context is either + 142     A context is either
- 119     1. the URI (actually an IRI) `activitystreams-context-uri`, or + 143     1. the URI (actually an IRI) `activitystreams-context-uri`, or
- 120     2. a collection comprising that URI and a map." + 144     2. a collection comprising that URI and a map."
- 121    [x] + 145    [x]
- 122    (cond + 146    (cond
- 123      (nil? x) false + 147      (nil? x) false
- 124      (string? x) (and (= x activitystreams-context-uri) true) + 148      (string? x) (and (= x activitystreams-context-uri) true)
- 125      (coll? x) (and (context? (first (remove map? x))) + 149      (coll? x) (and (context? (first (remove map? x)))
- 126                     (= (count x) 2) + 150                     (= (count x) 2)
- 127                     true) + 151                     true)
- 128      :else false)) + 152      :else false))
- 129   + 153  
- - 130  (defmacro has-context? + + 154  (defmacro has-context?
- 131    "True if `x` is an ActivityStreams object with a valid context, else `false`." + 155    "True if `x` is an ActivityStreams object with a valid context, else `false`."
- 132    [x] + 156    [x]
- 133    `(context? (context-key ~x))) + 157    `(context? (context-key ~x)))
- 134   + 158  
- 135  (defn make-fault-object + 159  (defn make-fault-object
- 136    "Return a fault object with these `severity`, `fault` and `narrative` values. + 160    "Return a fault object with these `severity`, `fault` and `narrative` values.
- 137      + 161     
- 138     An ActivityPub object MUST have a globally unique ID. Whether this is  + 162     An ActivityPub object MUST have a globally unique ID. Whether this is 
- 139     meaningful depends on whether we persist fault report objects and serve + 163     meaningful depends on whether we persist fault report objects and serve
- 140     them, which at present I have no plans to do." + 164     them, which at present I have no plans to do."
- 141    ;; TODO: should not pass in the narrative; instead should use the :fault value + 165    ;; TODO: should not pass in the narrative; instead should use the :fault value
- 142    ;; to look up the narrative in a resource file. + 166    ;; to look up the narrative in a resource file.
- 143    [severity fault] + 167    [severity fault]
- 144    (assoc {} + 168    (assoc {}
- 145           context-key validation-fault-context-uri + 169           context-key validation-fault-context-uri
- 146           :id (str "https://" + 170           :id (str "https://"
- 147                    (get-hostname) + 171                    (get-hostname)
- 148                    "/fault/" + 172                    "/fault/"
- 149                    (get-pid) + 173                    (get-pid)
- 150                    ":" + 174                    ":"
- 151                    (inst-ms (java.util.Date.))) + 175                    (inst-ms (java.util.Date.)))
- 152           :type "Fault" + 176           :type "Fault"
- 153           :severity severity + 177           :severity severity
- 154           :fault fault + 178           :fault fault
- 155           :narrative (or (messages fault) + 179           :narrative (or (get-message fault)
- 156                          (do + 180                          (do
- 157                            (warn "No narrative provided for fault token " fault) + 181                            (warn "No narrative provided for fault token " fault)
- 158                            (str fault))))) + 182                            (str fault)))))
- 159   + 183  
- - 160  (defmacro nil-if-empty + + 184  (defmacro nil-if-empty
- 161    "if `x` is an empty collection, return `nil`; else return `x`." + 185    "if `x` is an empty collection, return `nil`; else return `x`."
- 162    [x] + 186    [x]
- 163    `(if (and (coll? ~x) (empty? ~x)) nil + 187    `(if (and (coll? ~x) (empty? ~x)) nil
- 164         ~x)) + 188         ~x))
- 165   + 189  
- 166  (defn concat-non-empty + 190  (defn concat-non-empty
- 167    "Quick function to replace the pattern (nil-if-empty (remove nil? (concat ...))) + 191    "Quick function to replace the pattern (nil-if-empty (remove nil? (concat ...)))
- 168     which I'm using a lot!" + 192     which I'm using a lot!"
- 169    [& lists] + 193    [& lists]
- 170    (nil-if-empty (remove nil? (apply concat lists)))) -
- - 171   -
- - 172  (defn has-type-or-fault -
- - 173    "If object `x` has a `:type` value which is `acceptable`, return `nil`; -
- - 174     else return a fault object with this `severity` and `token`. -
- - 175      -
- - 176     `acceptable` may be passed as either nil, a string, or a set of strings. -
- - 177     If `acceptable` is `nil`, no type specific tests will be performed." -
- - 178    [x acceptable severity token] -
- - 179    (when acceptable -
- - 180      (let [tv (:type x)] -
- - 181        (when-not -
- - 182         (cond -
- - 183           (and (string? tv) (string? acceptable)) (= tv acceptable) -
- - 184           (and (string? tv) (set? acceptable)) (acceptable tv) -
- - 185           (and (coll? tv) (string? acceptable)) ((set tv) acceptable) -
- - 186           (and (coll? tv) (set? acceptable)) (not-empty -
- - 187                                               (intersection (set tv) acceptable)) -
- - 188           :else -
- - 189           (throw (ex-info "Type value or `acceptable` argument not as expected." -
- - 190                           {:arguments {:x x -
- - 191                                        :acceptable acceptable -
- - 192                                        :severity severity -
- - 193                                        :token token}}))) -
- - 194          (make-fault-object severity token))))) + 194    (nil-if-empty (remove nil? (apply concat lists))))
195  
- 196  (defn any-or-faults + 196  (defn has-type-or-fault
- 197    "Return `nil` if validating one of these options returns `nil`; otherwise  + 197    "If object `x` has a `:type` value which is `acceptable`, return `nil`;
- 198     return a list comprising a fault report object with this `severity-if-none` + 198     else return a fault object with this `severity` and `token`.
- 199     and this token followed by all the fault reports from validating each + 199     
- 200     option. + 200     `acceptable` may be passed as either nil, a string, or a set of strings.
- 201      + 201     If `acceptable` is `nil`, no type specific tests will be performed."
- 202     There are several places - but especially in validating collections - where -
- - 203     there are several different valid configurations, but few or no properties -
- - 204     are always required." -
- - 205    [options severity-if-none token] -
- - 206    (let [faults (filter empty? options)] -
- - 207      (when (empty? faults)  -
- - 208        ;; i.e. there was at least one option that returned no faults... -
- - 209        (cons (make-fault-object severity-if-none token) faults)))) -
- - 210   -
- - 211  (defmacro cond-make-fault-object -
- - 212    "If `v` is `false` or `nil`, return a fault object with this `severity` and `token`, -
- - 213     else return nil." -
- - 214    [v severity token] + 202    [x acceptable severity token]
- 215    `(when-not ~v (make-fault-object ~severity ~token))) + 203    (when acceptable +
+ + 204      (let [tv (:type x)] +
+ + 205        (when-not +
+ + 206         (cond +
+ + 207           (and (string? tv) (string? acceptable)) (= tv acceptable) +
+ + 208           (and (string? tv) (set? acceptable)) (acceptable tv) +
+ + 209           (and (coll? tv) (string? acceptable)) ((set tv) acceptable) +
+ + 210           (and (coll? tv) (set? acceptable)) (not-empty +
+ + 211                                               (intersection (set tv) acceptable)) +
+ + 212           (not +
+ + 213            (or (string? acceptable) +
+ + 214                (set? acceptable))) (throw +
+ + 215                                     (ex-info +
+ + 216                                      "`acceptable` argument not as expected." +
+ + 217                                      {:arguments {:x x +
+ + 218                                                   :acceptable acceptable +
+ + 219                                                   :severity severity +
+ + 220                                                   :token token}}))) +
+ + 221          (make-fault-object severity token)))))
- 216   + 222  
- 217  (defn string-or-fault + 223  (defn any-or-faults
- 218    "If this `value` is not a string, return a fault object with this `severity`  + 224    "Return `nil` if validating one of these options returns `nil`; otherwise 
- 219     and `token`, else `nil`. If `pattern` is also passed, it is expected to be + 225     return a list comprising a fault report object with this `severity-if-none`
- 220     a Regex, and the fault object will be returned unless `value` matches the  + 226     and this token followed by all the fault reports from validating each
- 221     `pattern`." + 227     option.
- 222    ([value severity token] + 228      +
+ + 229     There are several places - but especially in validating collections - where +
+ + 230     there are several different valid configurations, but few or no properties +
+ + 231     are always required." +
+ + 232    [options severity-if-none token] +
+ + 233    (let [faults (filter empty? options)] +
+ + 234      (when (empty? faults) +
+ + 235        ;; i.e. there was at least one option that returned no faults... +
+ + 236        (cons (make-fault-object severity-if-none token) faults)))) +
+ + 237   +
+ + 238  (defn cond-make-fault-object +
+ + 239    "If `v` is `false` or `nil`, return a fault object with this `severity` and `token`, +
+ + 240     else return nil." +
+ + 241    [v severity token] +
+ + 242    (when-not v (make-fault-object severity token))) +
+ + 243   +
+ + 244  (defn string-or-fault +
+ + 245    "If this `value` is not a string, return a fault object with this `severity`  +
+ + 246     and `token`, else `nil`. If `pattern` is also passed, it is expected to be +
+ + 247     a Regex, and the fault object will be returned unless `value` matches the  +
+ + 248     `pattern`." +
+ + 249    ([value severity token]
- 223     (when-not (string? value) (make-fault-object severity token))) + 250     (when-not (string? value) (make-fault-object severity token)))
- 224    ([value severity token pattern] + 251    ([value severity token pattern]
- 225     (when not (and (string? value) (re-matches pattern value)) + 252     (when not (and (string? value) (re-matches pattern value))
- 226           (make-fault-object severity token)))) -
- - 227   -
- - 228   -
- - 229  (defn object-faults -
- - 230    "Return a list of faults found in object `x`, or `nil` if none are. -
- - 231      -
- - 232     If `expected-type` is also passed, verify that `x` has `expected-type`. -
- - 233     `expected-type` may be passed as a string or as a set of strings. Detailed -
- - 234     verification of the particular features of types is not done here." -
- - 235    ([x] -
- - 236     (nil-if-empty -
- - 237      (remove empty? -
- - 238              (list -
- - 239               (when-not (map? x) -
- - 240                 (make-fault-object :critical :not-an-object)) -
- - 241               (when-not -
- - 242                (has-context? x) -
- - 243                 (make-fault-object :should :no-context)) -
- - 244               (when-not (:type x) -
- - 245                 (make-fault-object :minor :no-type)) -
- - 246               (when-not (and (map? x) (contains? x :id)) -
- - 247                 (make-fault-object :minor :no-id-transient)))))) -
- - 248    ([x expected-type] -
- - 249     (concat-non-empty -
- - 250      (object-faults x) -
- - 251      (when expected-type -
- - 252        (list -
- - 253         (has-type-or-fault x expected-type :critical :unexpected-type)))))) -
- - 254   -
- - 255   -
- - 256  (defn object-reference-or-faults -
- - 257    "If this `value` is either  -
- - 258      -
- - 259     1. an object of `expected-type`; -
- - 260     2. a URI referencing an object of  `expected-type`; or -
- - 261     3. a link object referencing an object of  `expected-type` -
- - 262      -
- - 263     and no faults are returned from validating the linked object, then return -
- - 264     `nil`; else return a sequence comprising a fault object with this `severity` -
- - 265     and `token`, prepended to the faults returned. -
- - 266      -
- - 267     As with `has-type-or-fault` (q.v.), `expected-type` may be passed as a -
- - 268     string, as a set of strings, or `nil` (indicating the type of the  -
- - 269     referenced object should not be checked). -
- - 270      -
- - 271     **NOTE THAT** if `*reify-refs*` is `false`, referenced objects will not -
- - 272     actually be checked." -
- - 273    [value expected-type severity token] -
- - 274    (let [faults (cond -
- - 275                   (string? value) (try (let [uri (URI. value) -
- - 276                                              object (when *reify-refs* -
- - 277                                                       (json/read-str (slurp uri)))] -
- - 278                                          (when object -
- - 279                                            (object-faults object expected-type))) -
- - 280                                        (catch URISyntaxException _ -
- - 281                                          (make-fault-object severity token))) -
- - 282                   (map? value) (if (has-type? value "Link") -
- - 283                                  (cond -
- - 284                                    ;; if we were looking for a link and we've  -
- - 285                                    ;; found a link, that's OK. -
- - 286                                    (= expected-type "Link") nil -
- - 287                                    (and (set? expected-type) (expected-type "Link")) nil -
- - 288                                    (nil? expected-type) nil -
- - 289                                    :else -
- - 290                                    (object-reference-or-faults -
- - 291                                     (:href value) expected-type severity token)) -
- - 292                                  (object-faults value expected-type)) -
- - 293                   :else (throw -
- - 294                          (ex-info -
- - 295                           "Argument `value` was not an object or a link to an object" -
- - 296                           {:arguments {:value value} -
- - 297                            :expected-type expected-type -
- - 298                            :severity severity -
- - 299                            :token token})))] -
- - 300      (when faults (cons (make-fault-object severity token) faults)))) -
- - 301   -
- - 302  (defn coll-object-reference-or-fault -
- - 303    "As object-reference-or-fault, except `value` argument may also be a list of -
- - 304     objects and/or object references." -
- - 305    [value expected-type severity token] -
- - 306    (cond -
- - 307      (map? value) (object-reference-or-faults value expected-type severity token) -
- - 308      (coll? value) (concat-non-empty -
- - 309                     (map -
- - 310                      #(object-reference-or-faults -
- - 311                        % expected-type severity token) -
- - 312                      value)) -
- - 313      :else (throw -
- - 314             (ex-info -
- - 315              "Argument `value` was not an object, a link to an object, nor a list of these." -
- - 316              {:arguments {:value value} -
- - 317               :expected-type expected-type -
- - 318               :severity severity -
- - 319               :token token})))) + 253           (make-fault-object severity token))))
diff --git a/docs/cloverage/dog_and_duck/quack/quack.clj.html b/docs/cloverage/dog_and_duck/quack/quack.clj.html index 00d1cff..ebd588c 100644 --- a/docs/cloverage/dog_and_duck/quack/quack.clj.html +++ b/docs/cloverage/dog_and_duck/quack/quack.clj.html @@ -62,460 +62,463 @@ 019              [dog-and-duck.quack.picky.control-variables :refer [*reject-severity*]]

- 020              [dog-and-duck.quack.picky.utils :refer [filter-severity object-faults]]) + 020              [dog-and-duck.quack.picky.objects :refer [object-faults]] +
+ + 021              [dog-and-duck.quack.picky.utils :refer [filter-severity]])
- 021   + 022  
- 022    (:import [java.net URI URISyntaxException])) + 023    (:import [java.net URI URISyntaxException]))
- 023   + 024  
- 024  ;;;     Copyright (C) Simon Brooke, 2022 + 025  ;;;     Copyright (C) Simon Brooke, 2022
- 025   + 026  
- 026  ;;;     This program is free software; you can redistribute it and/or + 027  ;;;     This program is free software; you can redistribute it and/or
- 027  ;;;     modify it under the terms of the GNU General Public License + 028  ;;;     modify it under the terms of the GNU General Public License
- 028  ;;;     as published by the Free Software Foundation; either version 2 + 029  ;;;     as published by the Free Software Foundation; either version 2
- 029  ;;;     of the License, or (at your option) any later version. + 030  ;;;     of the License, or (at your option) any later version.
- 030   + 031  
- 031  ;;;     This program is distributed in the hope that it will be useful, + 032  ;;;     This program is distributed in the hope that it will be useful,
- 032  ;;;     but WITHOUT ANY WARRANTY; without even the implied warranty of + 033  ;;;     but WITHOUT ANY WARRANTY; without even the implied warranty of
- 033  ;;;     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + 034  ;;;     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- 034  ;;;     GNU General Public License for more details. + 035  ;;;     GNU General Public License for more details.
- 035   + 036  
- 036  ;;;     You should have received a copy of the GNU General Public License + 037  ;;;     You should have received a copy of the GNU General Public License
- 037  ;;;     along with this program; if not, write to the Free Software + 038  ;;;     along with this program; if not, write to the Free Software
- 038  ;;;     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. + 039  ;;;     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- 039   + 040  
- 040  (defn object? + 041  (defn object?
- 041    "Returns `true` iff `x` is recognisably an ActivityStreams object. + 042    "Returns `true` iff `x` is recognisably an ActivityStreams object.
- 042      + 043     
- 043     **NOTE THAT** The ActivityStreams spec  + 044     **NOTE THAT** The ActivityStreams spec 
- 044     [says](https://www.w3.org/TR/activitystreams-core/#object): + 045     [says](https://www.w3.org/TR/activitystreams-core/#object):
- 045      + 046     
- 046     > All properties are optional (including the id and type) + 047     > All properties are optional (including the id and type)
- 047      + 048     
- 048     But we are *just not having that*, because otherwise we're flying blind. + 049     But we are *just not having that*, because otherwise we're flying blind.
- 049     We *shall* reject objects lacking at least `:type`. Missing `:id` keys are + 050     We *shall* reject objects lacking at least `:type`. Missing `:id` keys are
- 050     tolerable because they represent transient objects, which we expect to  + 051     tolerable because they represent transient objects, which we expect to 
- 051     handle. + 052     handle.
- 052      + 053     
- 053     **NOTE THAT** The ActivityPub spec [says](https://www.w3.org/TR/activitypub/#obj) + 054     **NOTE THAT** The ActivityPub spec [says](https://www.w3.org/TR/activitypub/#obj)
- 054      + 055     
- 055     > Implementers SHOULD include the ActivityPub context in their object  + 056     > Implementers SHOULD include the ActivityPub context in their object 
- 056     > definitions + 057     > definitions
- 057      + 058     
- 058     but in samples found in the wild they typically don't." + 059     but in samples found in the wild they typically don't."
- 059    ([x] + 060    ([x]
- 060     (object? x *reject-severity*)) + 061     (object? x *reject-severity*))
- 061    ([x severity] + 062    ([x severity]
- 062     (empty? (filter-severity (object-faults x) severity)))) + 063     (empty? (filter-severity (object-faults x) severity))))
- 063   + 064  
- 064  (defn persistent-object? + 065  (defn persistent-object?
- 065    "`true` iff `x` is a persistent object. + 066    "`true` iff `x` is a persistent object.
- 066   + 067  
- 067     Transient objects in ActivityPub are not required to have an `id` key, but persistent + 068     Transient objects in ActivityPub are not required to have an `id` key, but persistent
- 068     ones must have a key, and it must be an IRI (but normally a URI)." + 069     ones must have a key, and it must be an IRI (but normally a URI)."
- 069    ([x] + 070    ([x]
- 070     (persistent-object? x *reject-severity*)) + 071     (persistent-object? x *reject-severity*))
- 071    ([x severity] + 072    ([x severity]
- - 072     (empty? (filter-severity (persistent-object-faults x) severity)))) + + 073     (empty? (filter-severity (persistent-object-faults x) severity))))
- 073   + 074  
- 074  (defn actor? + 075  (defn actor?
- 075    "Returns `true` if `x` quacks like an actor, else false." + 076    "Returns `true` if `x` quacks like an actor, else false."
- 076    ([x] (actor? x *reject-severity*)) + 077    ([x] (actor? x *reject-severity*))
- 077    ([x severity] + 078    ([x severity]
- 078     (empty? (filter-severity (actor-faults x) severity)))) + 079     (empty? (filter-severity (actor-faults x) severity))))
- 079   + 080  
- 080  (defn actor-or-uri? + 081  (defn actor-or-uri?
- 081    "`true` if `x` is either a URI or an actor. + 082    "`true` if `x` is either a URI or an actor.
- 082      + 083     
- 083     **TODO**: I need to decide about whether to reify referenced objects + 084     **TODO**: I need to decide about whether to reify referenced objects
- 084     before validation or after. After reification, every reference to an actor + 085     before validation or after. After reification, every reference to an actor
- 085     *must be* to an actor object, but before, may only be to a URI pointing to  + 086     *must be* to an actor object, but before, may only be to a URI pointing to 
- 086     one." + 087     one."
- 087    [x] + 088    [x]
- 088    (try + 089    (try
- 089      (and + 090      (and
- 090       (cond (string? x) (uri? (URI. x)) + 091       (cond (string? x) (uri? (URI. x))
- 091             :else (actor? x)) + 092             :else (actor? x))
- 092       true) + 093       true)
- 093      (catch URISyntaxException _ false) + 094      (catch URISyntaxException _ false)
- 094      (catch NullPointerException _ false))) + 095      (catch NullPointerException _ false)))
- 095   + 096  
- 096  (defn activity? + 097  (defn activity?
- 097    "`true` iff `x` quacks like an activity, else false." + 098    "`true` iff `x` quacks like an activity, else false."
- 098    ([x] (activity? x *reject-severity*)) + 099    ([x] (activity? x *reject-severity*))
- 099    ([x severity] + 100    ([x severity]
- 100     (empty? (filter-severity (activity-faults x) severity)))) + 101     (empty? (filter-severity (activity-faults x) severity))))
- 101   + 102  
- 102  (defn link? + 103  (defn link?
- 103    "`true` iff `x` quacks like a link, else false." + 104    "`true` iff `x` quacks like a link, else false."
- 104    ([x] (link? x *reject-severity*)) + 105    ([x] (link? x *reject-severity*))
- 105    ([x severity] + 106    ([x severity]
- 106     (empty? (filter-severity (link-faults x) severity)))) + 107     (empty? (filter-severity (link-faults x) severity))))
- 107   + 108  
- 108  (defn link-or-uri? + 109  (defn link-or-uri?
- 109    "`true` iff `x` is either a URI or a link, else false. + 110    "`true` iff `x` is either a URI or a link, else false.
- 110      + 111     
- 111     There are several points in the specification where e.g. the `:image` + 112     There are several points in the specification where e.g. the `:image`
- 112     property (if present) may be either a link or a URI." + 113     property (if present) may be either a link or a URI."
- 113    [x] + 114    [x]
- 114    (and + 115    (and
- 115     (cond (string? x) (uri? (URI. x)) + 116     (cond (string? x) (uri? (URI. x))
- 116           :else (link? x)) + 117           :else (link? x))
- 117     true)) + 118     true))
- 118   + 119  
- 119  (defn collection? + 120  (defn collection?
- 120    "`true` iff `x` quacks like a collection of type `object-type`, else `false`. + 121    "`true` iff `x` quacks like a collection of type `object-type`, else `false`.
- 121      + 122     
- 122     With one argument, will recognise plain collections and ordered collections, + 123     With one argument, will recognise plain collections and ordered collections,
- 123     but (currently) not collection pages." + 124     but (currently) not collection pages."
- 124    ([x ^String object-type] + 125    ([x ^String object-type]
- 125     (let [items (or (:items x) (:orderedItems x))] + 126     (let [items (or (:items x) (:orderedItems x))]
- 126       (and + 127       (and
- 127        (cond + 128        (cond
- 128          (:items x) (nil? (:orderedItems x)) + 129          (:items x) (nil? (:orderedItems x))
- 129          (:orderedItems x) (nil? (:items x)) ;; can't have both properties + 130          (:orderedItems x) (nil? (:items x)) ;; can't have both properties
- 130          (integer? (:totalItems x)) true ;; can have neither, provided it has totalItems. + 131          (integer? (:totalItems x)) true ;; can have neither, provided it has totalItems.
- 131          :else false) + 132          :else false)
- 132        (object? x) + 133        (object? x)
- 133        (= (:type x) object-type) + 134        (= (:type x) object-type)
- 134        (if items + 135        (if items
- 135          (and (coll? items) + 136          (and (coll? items)
- 136               (every? object? items) ;; if there are items, they must form a + 137               (every? object? items) ;; if there are items, they must form a
- 137                                      ;; collection of objects. + 138                                      ;; collection of objects.
- 138               true) + 139               true)
- 139          true) ;; but it's OK if there aren't. + 140          true) ;; but it's OK if there aren't.
- 140        true) + 141        true)
- 141       ;; test for totalItems not done here, because collection pages don't + 142       ;; test for totalItems not done here, because collection pages don't
- 142       ;; have it. + 143       ;; have it.
- 143       )) + 144       ))
- 144    ([x] + 145    ([x]
- 145     (and + 146     (and
- 146      (or (collection? x "Collection") + 147      (or (collection? x "Collection")
- 147          (collection? x "OrderedCollection")) + 148          (collection? x "OrderedCollection"))
- 148      (integer? (:totalItems x)) + 149      (integer? (:totalItems x))
- 149      true))) + 150      true)))
- 150   + 151  
- 151  (defn unordered-collection? + 152  (defn unordered-collection?
- 152    "`true` iff `x` quacks like an unordered collection, else `false`." + 153    "`true` iff `x` quacks like an unordered collection, else `false`."
- 153    [x] + 154    [x]
- 154    (and (collection? x "Collection") (integer? (:totalItems x)) true)) + 155    (and (collection? x "Collection") (integer? (:totalItems x)) true))
- 155   + 156  
- 156  (defn ordered-collection? + 157  (defn ordered-collection?
- 157    "`true` iff `x` quacks like an ordered collection, else `false`." + 158    "`true` iff `x` quacks like an ordered collection, else `false`."
- 158    [x] + 159    [x]
- 159    (and (collection? x "OrderedCollection") (integer? (:totalItems x)) true)) + 160    (and (collection? x "OrderedCollection") (integer? (:totalItems x)) true))
- 160   + 161  
- 161  (defn collection-page? + 162  (defn collection-page?
- 162    "`true` iff `x` quacks like a page in a paged collection, else `false`." + 163    "`true` iff `x` quacks like a page in a paged collection, else `false`."
- 163    [x] + 164    [x]
- 164    (collection? x "CollectionPage")) + 165    (collection? x "CollectionPage"))
- 165   + 166  
- 166  (defn ordered-collection-page? + 167  (defn ordered-collection-page?
- 167    "`true` iff `x` quacks like a page in an ordered paged collection, else `false`." + 168    "`true` iff `x` quacks like a page in an ordered paged collection, else `false`."
- 168    [x] + 169    [x]
- 169    (collection? x "OrderedCollectionPage")) -
- - 170   + 170    (collection? x "OrderedCollectionPage"))
171  
+ + 172   +
diff --git a/docs/cloverage/dog_and_duck/scratch/scratch.clj.html b/docs/cloverage/dog_and_duck/scratch/scratch.clj.html index 4452f7b..56f94ec 100644 --- a/docs/cloverage/dog_and_duck/scratch/scratch.clj.html +++ b/docs/cloverage/dog_and_duck/scratch/scratch.clj.html @@ -17,163 +17,190 @@ 004              [clj-activitypub.webfinger :as webfinger]

- 005              [clj-pgp.generate :as pgp-gen] + 005              [clj-activitypub.net :as activitypub-net]
- 006              [clojure.walk :refer [keywordize-keys]])) + 006              [clj-pgp.generate :as pgp-gen] +
+ + 007              [clojure.walk :refer [keywordize-keys]] +
+ + 008              [clojure.pprint :refer [pprint]] +
+ + 009              [clojure.data.json :as json]))
- 007   + 010  
- 008  ;;;     Copyright (C) Simon Brooke, 2022 + 011  ;;;     Copyright (C) Simon Brooke, 2022
- 009   + 012  
- 010  ;;;     This program is free software; you can redistribute it and/or + 013  ;;;     This program is free software; you can redistribute it and/or
- 011  ;;;     modify it under the terms of the GNU General Public License + 014  ;;;     modify it under the terms of the GNU General Public License
- 012  ;;;     as published by the Free Software Foundation; either version 2 + 015  ;;;     as published by the Free Software Foundation; either version 2
- 013  ;;;     of the License, or (at your option) any later version. + 016  ;;;     of the License, or (at your option) any later version.
- 014       + 017      
- 015  ;;;     This program is distributed in the hope that it will be useful, + 018  ;;;     This program is distributed in the hope that it will be useful,
- 016  ;;;     but WITHOUT ANY WARRANTY; without even the implied warranty of + 019  ;;;     but WITHOUT ANY WARRANTY; without even the implied warranty of
- 017  ;;;     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + 020  ;;;     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- 018  ;;;     GNU General Public License for more details. + 021  ;;;     GNU General Public License for more details.
- 019       + 022      
- 020  ;;;     You should have received a copy of the GNU General Public License + 023  ;;;     You should have received a copy of the GNU General Public License
- 021  ;;;     along with this program; if not, write to the Free Software + 024  ;;;     along with this program; if not, write to the Free Software
- 022  ;;;     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. -
- - 023   -
- - 024  ;;; Use any ActivityPub account handle you like - for example, your own -
- - 025  (def account-handle "@simon_brooke@mastodon.scot") + 025  ;;;     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
026  
- - 027  (def handle (activitypub/parse-account account-handle)) -
- - 028  (webfinger/fetch-user-id "mastodon.scot" "simon_brooke") -
- - 029  (apply webfinger/fetch-user-id (map handle [:domain :username])) -
- - 030   -
- 031  ;;; Retrieve the account details from its home server -
- - 032  ;;; (`keywordize-keys` is not necessary here but produces a more idiomatic clojure -
- - 033  ;;; data structure) -
- - 034  (def account -
- - 035    "Fetch my account to mess with" -
- - 036    (let [handle (activitypub/parse-account account-handle)] + 027  ;;; Use any ActivityPub account handle you like - for example, your own
- 037      (keywordize-keys -
- - 038       (activitypub/fetch-user -
- - 039        (apply webfinger/fetch-user-id (map handle [:domain :username])))))) + 028  (def account-handle "@simon_brooke@mastodon.scot")
- 040   + 029  
- 041  ;;; examine what you got back! + 030  ;;(def handle (activitypub/parse-account account-handle))
- - 042  (:inbox account) + + 031  ;;(webfinger/fetch-user-id "mastodon.scot" "simon_brooke") +
+ + 032  ;;(apply webfinger/fetch-user-id (map handle [:domain :username])) +
+ + 033   +
+ + 034  ;;; Retrieve the account details from its home server +
+ + 035  ;;; (`keywordize-keys` is not necessary here but produces a more idiomatic clojure +
+ + 036  ;;; data structure) +
+ + 037  ;; (def account +
+ + 038  ;;   (-> account-handle +
+ + 039  ;;       (webfinger/parse-handle) +
+ + 040  ;;       (webfinger/fetch-user-id!) +
+ + 041  ;;       (activitypub-net/fetch-user!) +
+ + 042  ;;       (select-keys [:name :preferredUsername :inbox :summary])))
043  
- 044  ;; (def rsa (pgp-gen/rsa-keypair-generator 2048)) + 044  ;; ;;; examine what you got back!
- 045  ;; (def kp (pgp-gen/generate-keypair rsa :rsa-general)) + 045  ;; (:inbox account)
046  
- 047  ;; how we make a public/private key pair. But this key pair is not the one  + 047  ;; (-> account
- 048  ;; known to mastodon.scot as my key pair, so that doesn't get us very far... + 048  ;;     :inbox
- 049  ;; I think. -
- - 050  (let [rsa (pgp-gen/rsa-keypair-generator 2048) -
- - 051        kp (pgp-gen/generate-keypair rsa :rsa-general) -
- - 052        public (-> kp .getPublicKey .getEncoded) -
- - 053        private (-> kp .getPrivateKey .getPrivateKeyDataPacket .getEncoded)] -
- - 054    (println (str "Public key:  " public)) -
- - 055    (println (str "Private key: " private)) + 049  ;;     slurp
- 056    ) + 050  ;;     json/read-str +
+ + 051  ;;     pprint) ;; => 80
- 057   + 052   +
+ + 053  ;; (def rsa (pgp-gen/rsa-keypair-generator 2048)) +
+ + 054  ;; (def kp (pgp-gen/generate-keypair rsa :rsa-general)) +
+ + 055   +
+ + 056  ;; how we make a public/private key pair. But this key pair is not the one  +
+ + 057  ;; known to mastodon.scot as my key pair, so that doesn't get us very far... +
+ + 058  ;; I think. +
+ + 059  ;; (let [rsa (pgp-gen/rsa-keypair-generator 2048) +
+ + 060  ;;       kp (pgp-gen/generate-keypair rsa :rsa-general) +
+ + 061  ;;       public (-> kp .getPublicKey .getEncoded) +
+ + 062  ;;       private (-> kp .getPrivateKey .getPrivateKeyDataPacket .getEncoded)] +
+ + 063  ;;   (println (str "Public key:  " public)) +
+ + 064  ;;   (println (str "Private key: " private)) +
+ + 065  ;;   ) +
+ + 066  
diff --git a/docs/cloverage/index.html b/docs/cloverage/index.html index b0c5087..0b55056 100644 --- a/docs/cloverage/index.html +++ b/docs/cloverage/index.html @@ -15,126 +15,60 @@ TotalBlankInstrumented - clj-activitypub.core
71
230
-23.59 % + dog-and-duck.quack.cli
70
514
+11.99 %
24
20
2
60
-30.23 % -1501486 - - - clj-activitypub.internal.crypto
9
39
-18.75 % -
9
14
-39.13 % -39823 - - - clj-activitypub.internal.http-util
29
10
-74.36 % -
12
3
-80.00 % -28315 - - - clj-activitypub.internal.thread-cache
105
21
-83.33 % -
30
1
3
-91.18 % -47334 - - - clj-activitypub.webfinger
107
5
-95.54 % -
16
2
-100.00 % -35518 + style="width:76.34408602150538%; + float:left;"> 71 +23.66 % +1391093 dog-and-duck.quack.picky
169
237
-41.63 % + style="width:55.41871921182266%; + float:left;"> 225
181
+55.42 %
65
3
43
-61.26 % -25115111 + style="width:66.07142857142857%; + float:left;"> 74
5
33
+70.54 % +25215112 dog-and-duck.quack.picky.collections
4
129
-3.01 % + style="width:100.0%; + float:left;"> 130 +100.00 %
4
20
-16.67 % -57824 + style="width:100.0%; + float:left;"> 25 +100.00 % +60825 dog-and-duck.quack.picky.constants
73
+ float:left;"> 99 100.00 %
18
+ float:left;"> 23 100.00 % -791218 +1161523 dog-and-duck.quack.picky.control-variables
4963 - dog-and-duck.quack.picky.fault-messages
29
-100.00 % -
3
-100.00 % -3553 - - - dog-and-duck.quack.picky.required-properties
1
-100.00 % -
1
-100.00 % -101 - - - dog-and-duck.quack.picky.utils
479
480
-49.95 % -
57
9
81
-44.90 % -31930147 - - - dog-and-duck.quack.quack
122
140
-46.56 % -
25
6
dog-and-duck.quack.picky.distribution
2
18
-63.27 % -1712149 - - - dog-and-duck.scratch.core
10.00 % +
2
4
33.33 % +3056 + + + dog-and-duck.quack.picky.objects
1220
279
+81.39 %
2
278
25
30
+90.99 % +52216333 + + + dog-and-duck.quack.picky.scratch
1
-66.67 % -2253 +100.00 % +
1
+100.00 % +5361 + + + dog-and-duck.quack.picky.time
10
125
+7.41 % +
4
2
12
+33.33 % +66718 + + + dog-and-duck.quack.picky.utils
434
259
+62.63 % +
65
18
25
+76.85 % +25326108 + + + dog-and-duck.quack.quack
121
141
+46.18 % +
24
7
18
+63.27 % +1722149 dog-and-duck.scratch.parser
dog-and-duck.scratch.scratch
66
+ float:left;"> 3
100.00 %
17
+ float:left;"> 2
100.00 % -57917 +66102 dog-and-duck.utils.process
Totals: -50.34 % +60.82 % -57.34 % +75.54 % diff --git a/docs/codox/Desiderata.html b/docs/codox/Desiderata.html index 810a57a..fd544c4 100644 --- a/docs/codox/Desiderata.html +++ b/docs/codox/Desiderata.html @@ -1,6 +1,6 @@ -Desiderata

Desiderata

+Desiderata

Desiderata

Social media features which users want which [Mastodon](https://en.wikipedia.org/wiki/Mastodon_(social_network)) does not provide, or provides poorly.

  1. User-specified inbox-ordering algorithms;
  2. diff --git a/docs/codox/Using_ActivityPub.html b/docs/codox/Using_ActivityPub.html index 2d7035c..ff9b583 100644 --- a/docs/codox/Using_ActivityPub.html +++ b/docs/codox/Using_ActivityPub.html @@ -1,6 +1,6 @@ -Using ActivityPub

    Using ActivityPub

    +Using ActivityPub

    Using ActivityPub

    Introduction

    I do not know what I am doing; I am learning, and playing. Nothing in this document should be treated as good advice; it simply relates to the current state of my knowledge.

    What happens when you post a new item to an ActivityPub server

    diff --git a/docs/codox/Validation_Faults.html b/docs/codox/Validation_Faults.html index f6ae4bd..a792602 100644 --- a/docs/codox/Validation_Faults.html +++ b/docs/codox/Validation_Faults.html @@ -1,6 +1,6 @@ -Validation Faults in ActivityPub documents

    Validation Faults in ActivityPub documents

    +Validation Faults in ActivityPub documents

    Validation Faults in ActivityPub documents

    Motivation

    This document is intended to provide an extension vocabulary for ActivityStreams documents, which provides vocabulary for categorising and describing faults in ActivityPub documents.

    The motivation is to be able to serialise a validation report on an ActivityPub document as an ActivityStreams document.

    diff --git a/docs/codox/dog-and-duck.quack.cli.html b/docs/codox/dog-and-duck.quack.cli.html new file mode 100644 index 0000000..ea99992 --- /dev/null +++ b/docs/codox/dog-and-duck.quack.cli.html @@ -0,0 +1,3 @@ + +dog-and-duck.quack.cli documentation

    dog-and-duck.quack.cli

    TODO: write docs

    -main

    (-main & args)

    TODO: write docs

    cli-options

    TODO: write docs

    html-fault-row

    (html-fault-row fault cols)

    TODO: write docs

    html-header-row

    (html-header-row cols)

    TODO: write docs

    output

    (output content options)

    TODO: write docs

    output-csv

    (output-csv faults)

    TODO: write docs

    output-html

    (output-html faults opts)

    TODO: write docs

    stylesheet-url

    TODO: write docs

    validate

    (validate source)

    TODO: write docs

    \ No newline at end of file diff --git a/docs/codox/dog-and-duck.quack.picky.collections.html b/docs/codox/dog-and-duck.quack.picky.collections.html index 9b3f142..d89cb27 100644 --- a/docs/codox/dog-and-duck.quack.picky.collections.html +++ b/docs/codox/dog-and-duck.quack.picky.collections.html @@ -1,3 +1,3 @@ -dog-and-duck.quack.picky.collections documentation

    dog-and-duck.quack.picky.collections

    TODO: write docs

    collection-page-faults

    (collection-page-faults x type)

    TODO: write docs

    paged-collection-faults

    (paged-collection-faults x type)

    Return a list of faults found in x considered as a paged collection object of this sub-type, or nil if none are found.

    simple-collection-faults

    (simple-collection-faults x type)

    Return a list of faults found in x considered as a non-paged collection object of this sub-type, or nil if none are found.

    \ No newline at end of file +dog-and-duck.quack.picky.collections documentation

    dog-and-duck.quack.picky.collections

    TODO: write docs

    collection-page-faults

    (collection-page-faults x type)

    Return a list of faults found in x considered as a collection page object of this sub-type, or nil if none are found.

    paged-collection-faults

    (paged-collection-faults x type)

    Return a list of faults found in x considered as a paged collection object of this sub-type, or nil if none are found.

    simple-collection-faults

    (simple-collection-faults x type)

    Return a list of faults found in x considered as a non-paged collection object of this sub-type, or nil if none are found.

    \ No newline at end of file diff --git a/docs/codox/dog-and-duck.quack.picky.constants.html b/docs/codox/dog-and-duck.quack.picky.constants.html index 7dd8bad..78543bd 100644 --- a/docs/codox/dog-and-duck.quack.picky.constants.html +++ b/docs/codox/dog-and-duck.quack.picky.constants.html @@ -1,12 +1,14 @@ -dog-and-duck.quack.picky.constants documentation

    dog-and-duck.quack.picky.constants

    Constants supporting the picky validator.

    activitystreams-context-uri

    The URI of the context of an ActivityStreams object is expected to be this literal string.

    actor-types

    The set of types we will accept as actors.

    -

    There’s an explicit set of allowed actor types.

    context-key

    The Clojure reader barfs on :@context, although it is in principle a valid keyword. So we’ll make it once, here, to make the code more performant and easier to read.

    severity

    Severity of faults found, as follows:

    +dog-and-duck.quack.picky.constants documentation

    dog-and-duck.quack.picky.constants

    Constants supporting the picky validator.

    activity-types

    The set of types we will accept as activities.

    +

    There’s an explicit set of allowed activity types.

    activitystreams-context-uri

    The URI of the context of an ActivityStreams object is expected to be this literal string.

    +

    NOTE THAT the URI actually used in the published suite of activitystreams-test-documents use this URI with ‘http’ rather than ‘https’ as the property part, but the spec itself specifies ‘https’.

    actor-types

    The set of types we will accept as actors.

    +

    There’s an explicit set of allowed actor types.

    context-key

    The Clojure reader barfs on :@context, although it is in principle a valid keyword. So we’ll make it once, here, to make the code more performant and easier to read.

    implicit-noun-types

    These types are not explicitly listed in Section 3.3 of the spec, but are mentioned in narrative

    noun-types

    The set of object types we will accept as nouns.

    +

    There’s an explicit set of allowed ‘object types’, but by implication it is not exhaustive.

    re-rfc5646

    A regex which tests conformity to RFC 5646. Cribbed from https://newbedev.com/regex-to-detect-locales

    severity

    Severity of faults found, as follows:

    1. :info not actually a fault, but an issue noted during validation;
    2. :minor things which I consider to be faults, but which don’t actually breach the spec;
    3. :should instances where the spec says something SHOULD be done, which isn’t;
    4. :must instances where the spec says something MUST be done, which isn’t;
    5. :critical instances where I believe the fault means that the object cannot be meaningfully processed.
    6. -

    severity-filters

    Hack for implementing a severity hierarchy

    validation-fault-context-uri

    The URI of the context of a validation fault report object shall be this literal string.

    verb-types

    The set of types we will accept as verbs.

    -

    There’s an explicit set of allowed verb types.

    \ No newline at end of file +

severity-filters

Hack for implementing a severity hierarchy

validation-fault-context-uri

The URI of the context of a validation fault report object shall be this literal string.

\ No newline at end of file diff --git a/docs/codox/dog-and-duck.quack.picky.control-variables.html b/docs/codox/dog-and-duck.quack.picky.control-variables.html index 93114ad..9c3579d 100644 --- a/docs/codox/dog-and-duck.quack.picky.control-variables.html +++ b/docs/codox/dog-and-duck.quack.picky.control-variables.html @@ -1,6 +1,6 @@ -dog-and-duck.quack.picky.control-variables documentation

dog-and-duck.quack.picky.control-variables

Control variables for the picky validator.

*reify-refs*

dynamic

If true, references to objects in fields will be reified and validated. If false, they won’t, but an :info level fault report will be generated.

+dog-and-duck.quack.picky.control-variables documentation

dog-and-duck.quack.picky.control-variables

Control variables for the picky validator.

*reify-refs*

dynamic

If true, references to objects in fields will be reified and validated. If false, they won’t, but an :info level fault report will be generated.

There are several things in the spec which, in a document, may correctly be either

  1. a fully fleshed out object, or
  2. diff --git a/docs/codox/dog-and-duck.quack.picky.distribution.html b/docs/codox/dog-and-duck.quack.picky.distribution.html new file mode 100644 index 0000000..6cd5e59 --- /dev/null +++ b/docs/codox/dog-and-duck.quack.picky.distribution.html @@ -0,0 +1,4 @@ + +dog-and-duck.quack.picky.distribution documentation

    dog-and-duck.quack.picky.distribution

    TODO: write docs

    distribution

    (distribution f vals)

    Distribution of values of function f when applied to vals.

    +

    I know there’s a library function that does this, probably better, but I don’t remember what it’s called!

    \ No newline at end of file diff --git a/docs/codox/dog-and-duck.quack.picky.html b/docs/codox/dog-and-duck.quack.picky.html index 654beba..f598648 100644 --- a/docs/codox/dog-and-duck.quack.picky.html +++ b/docs/codox/dog-and-duck.quack.picky.html @@ -1,6 +1,6 @@ -dog-and-duck.quack.picky documentation

    dog-and-duck.quack.picky

    Fault-finder for ActivityPub documents.

    +dog-and-duck.quack.picky documentation

    dog-and-duck.quack.picky

    Fault-finder for ActivityPub documents.

    Generally, each -faults function will return:

    1. nil if no faults were found;
    2. diff --git a/docs/codox/dog-and-duck.quack.picky.objects.html b/docs/codox/dog-and-duck.quack.picky.objects.html new file mode 100644 index 0000000..67d7b5b --- /dev/null +++ b/docs/codox/dog-and-duck.quack.picky.objects.html @@ -0,0 +1,27 @@ + +dog-and-duck.quack.picky.objects documentation

      dog-and-duck.quack.picky.objects

      TODO: write docs

      check-property

      (check-property obj prop)

      TODO: write docs

      check-property-required

      (check-property-required obj prop clause)

      TODO: write docs

      check-property-valid

      (check-property-valid obj prop clause)

      TODO: write docs

      coll-object-reference-or-fault

      (coll-object-reference-or-fault value expected-type severity token)

      As object-reference-or-fault, except value argument may also be a list of objects and/or object references.

      maybe-reify

      If *reify-refs* is true, return the object at this target URI. Returns nil if

      +
        +
      1. *reify-refs* is false;
      2. +
      3. the object was not found;
      4. +
      5. access to the object was not permitted.
      6. +
      +

      Consequently, use with care.

      maybe-reify-or-faults

      (maybe-reify-or-faults value expected-type severity token)

      If *reify-refs* is true, runs basic checks on the object at this target URI, if it is found, or a list containing a fault object with this severity and token if it is not.

      object-expected-properties

      Requirements of properties of object, cribbed from https://www.w3.org/TR/activitystreams-vocabulary/#properties

      +

      Note the following sub-key value types:

      +
        +
      • :collection opposite of :functional: if true, value should be a collection (in the Clojure sense), not a single object;
      • +
      • :functional if true, value should be a single object; if false, may be a single object or a sequence of objects, but each must pass validation checks;
      • +
      • :if-invalid a sequence of two keywords, first indicating severity, second being a message key;
      • +
      • :if-missing a sequence of two keywords, first indicating severity, second being a message key;
      • +
      • :required a boolean, or a function of one argument returning a boolean, in which case the function will be applied to the object having the property;
      • +
      • :validator a function of one argument returning a boolean, which will be applied to the value or values of the identified property.
      • +

      object-faults

      (object-faults x)(object-faults x expected-type)

      Return a list of faults found in object x, or nil if none are.

      +

      If expected-type is also passed, verify that x has expected-type. expected-type may be passed as a string or as a set of strings. Detailed verification of the particular features of types is not done here.

      object-reference-or-faults

      (object-reference-or-faults value expected-type severity token)

      If this value is either

      +
        +
      1. an object of expected-type;
      2. +
      3. a URI referencing an object of expected-type; or
      4. +
      5. a link object referencing an object of expected-type
      6. +
      +

      and no faults are returned from validating the linked object, then return nil; else return a sequence comprising a fault object with this severity and token, prepended to the faults returned.

      +

      As with has-type-or-fault (q.v.), expected-type may be passed as a string, as a set of strings, or nil (indicating the type of the referenced object should not be checked).

      +

      NOTE THAT if *reify-refs* is false, referenced objects will not actually be checked.

      properties-faults

      (properties-faults x)

      Return a lost of faults found on properties of the object x, or nil if none are.

      \ No newline at end of file diff --git a/docs/codox/dog-and-duck.quack.picky.scratch.html b/docs/codox/dog-and-duck.quack.picky.scratch.html new file mode 100644 index 0000000..ac8693c --- /dev/null +++ b/docs/codox/dog-and-duck.quack.picky.scratch.html @@ -0,0 +1,3 @@ + +dog-and-duck.quack.picky.scratch documentation

      dog-and-duck.quack.picky.scratch

      Development scratchpad

      \ No newline at end of file diff --git a/docs/codox/dog-and-duck.quack.picky.time.html b/docs/codox/dog-and-duck.quack.picky.time.html new file mode 100644 index 0000000..196063d --- /dev/null +++ b/docs/codox/dog-and-duck.quack.picky.time.html @@ -0,0 +1,4 @@ + +dog-and-duck.quack.picky.time documentation

      dog-and-duck.quack.picky.time

      Time, gentleman, please! Recognising and validating date time values.

      date-time-property-or-fault

      (date-time-property-or-fault x property severity token required?)

      If the value of this property of object x is a valid xsd:dateTime value, return a fault object with this token and severity.

      +

      If required? is false and there is no such property, no fault will be returned.

      xsd-date-time?

      (xsd-date-time? value)

      Return true if value matches the pattern for an xsd:dateTime, else false

      xsd-duration?

      (xsd-duration? value)

      Return true if value matches the pattern for an xsd:duration, else false

      \ No newline at end of file diff --git a/docs/codox/dog-and-duck.quack.picky.utils.html b/docs/codox/dog-and-duck.quack.picky.utils.html index 41b6075..ed78aad 100644 --- a/docs/codox/dog-and-duck.quack.picky.utils.html +++ b/docs/codox/dog-and-duck.quack.picky.utils.html @@ -1,17 +1,9 @@ -dog-and-duck.quack.picky.utils documentation

      dog-and-duck.quack.picky.utils

      Utility functions supporting the picky validator

      actor-type?

      (actor-type? x)

      Return true if the x is a recognised actor type, else false.

      any-or-faults

      (any-or-faults options severity-if-none token)

      Return nil if validating one of these options returns nil; otherwise return a list comprising a fault report object with this severity-if-none and this token followed by all the fault reports from validating each option.

      -

      There are several places - but especially in validating collections - where there are several different valid configurations, but few or no properties are always required.

      coll-object-reference-or-fault

      (coll-object-reference-or-fault value expected-type severity token)

      As object-reference-or-fault, except value argument may also be a list of objects and/or object references.

      concat-non-empty

      (concat-non-empty & lists)

      Quick function to replace the pattern (nil-if-empty (remove nil? (concat …))) which I’m using a lot!

      cond-make-fault-object

      macro

      (cond-make-fault-object v severity token)

      If v is false or nil, return a fault object with this severity and token, else return nil.

      context?

      (context? x)

      Returns true iff x quacks like an ActivityStreams context, else false.

      -

      A context is either 1. the URI (actually an IRI) activitystreams-context-uri, or 2. a collection comprising that URI and a map.

      filter-severity

      (filter-severity reports severity)

      Return a list of reports taken from these reports where the severity of the report is greater than this or equal to this severity.

      has-activity-type?

      (has-activity-type? x)

      Return true if the object x has a type which is an activity type, else false.

      has-actor-type?

      (has-actor-type? x)

      Return true if the object x has a type which is an actor type, else false.

      has-context?

      macro

      (has-context? x)

      True if x is an ActivityStreams object with a valid context, else false.

      has-type-or-fault

      (has-type-or-fault x acceptable severity token)

      If object x has a :type value which is acceptable, return nil; else return a fault object with this severity and token.

      -

      acceptable may be passed as either nil, a string, or a set of strings. If acceptable is nil, no type specific tests will be performed.

      has-type?

      (has-type? x type)

      Return true if object x has type type, else false.

      -

      The values of type fields of ActivityStreams objects may be lists; they are considered to have a type if the type token is a member of the list.

      make-fault-object

      (make-fault-object severity fault)

      Return a fault object with these severity, fault and narrative values.

      -

      An ActivityPub object MUST have a globally unique ID. Whether this is meaningful depends on whether we persist fault report objects and serve them, which at present I have no plans to do.

      nil-if-empty

      macro

      (nil-if-empty x)

      if x is an empty collection, return nil; else return x.

      object-faults

      (object-faults x)(object-faults x expected-type)

      Return a list of faults found in object x, or nil if none are.

      -

      If expected-type is also passed, verify that x has expected-type. expected-type may be passed as a string or as a set of strings. Detailed verification of the particular features of types is not done here.

      object-or-uri?

      (object-or-uri? x)

      Very basic check that x is either an object or a URI.

      object-reference-or-faults

      (object-reference-or-faults value expected-type severity token)

      If this value is either

      -
        -
      1. an object of expected-type;
      2. -
      3. a URI referencing an object of expected-type; or
      4. -
      5. a link object referencing an object of expected-type
      6. -
      -

      and no faults are returned from validating the linked object, then return nil; else return a sequence comprising a fault object with this severity and token, prepended to the faults returned.

      -

      As with has-type-or-fault (q.v.), expected-type may be passed as a string, as a set of strings, or nil (indicating the type of the referenced object should not be checked).

      -

      NOTE THAT if *reify-refs* is false, referenced objects will not actually be checked.

      string-or-fault

      (string-or-fault value severity token)(string-or-fault value severity token pattern)

      If this value is not a string, return a fault object with this severity and token, else nil. If pattern is also passed, it is expected to be a Regex, and the fault object will be returned unless value matches the pattern.

      truthy?

      (truthy? x)

      Return true if x is truthy, else false. There must be some more idiomatic way to do this?

      verb-type?

      (verb-type? x)

      true if x, a string, represents a recognised ActivityStreams activity type.

      \ No newline at end of file +dog-and-duck.quack.picky.utils documentation

      dog-and-duck.quack.picky.utils

      Utility functions supporting the picky validator

      activity-type?

      (activity-type? x)

      true if x, a string, represents a recognised ActivityStreams activity type.

      actor-type?

      (actor-type? x)

      Return true if the x is a recognised actor type, else false.

      any-or-faults

      (any-or-faults options severity-if-none token)

      Return nil if validating one of these options returns nil; otherwise return a list comprising a fault report object with this severity-if-none and this token followed by all the fault reports from validating each option.

      +

      There are several places - but especially in validating collections - where there are several different valid configurations, but few or no properties are always required.

      concat-non-empty

      (concat-non-empty & lists)

      Quick function to replace the pattern (nil-if-empty (remove nil? (concat …))) which I’m using a lot!

      cond-make-fault-object

      (cond-make-fault-object v severity token)

      If v is false or nil, return a fault object with this severity and token, else return nil.

      context?

      (context? x)

      Returns true iff x quacks like an ActivityStreams context, else false.

      +

      A context is either 1. the URI (actually an IRI) activitystreams-context-uri, or 2. a collection comprising that URI and a map.

      filter-severity

      (filter-severity reports severity)

      Return a list of reports taken from these reports where the severity of the report is greater than this or equal to this severity.

      has-activity-type?

      (has-activity-type? x)

      Return true if the object x has a type which is an activity type, else false.

      has-actor-type?

      (has-actor-type? x)

      Return true if the object x has a type which is an actor type, else false.

      has-context?

      macro

      (has-context? x)

      True if x is an ActivityStreams object with a valid context, else false.

      has-type-or-fault

      (has-type-or-fault x acceptable severity token)

      If object x has a :type value which is acceptable, return nil; else return a fault object with this severity and token.

      +

      acceptable may be passed as either nil, a string, or a set of strings. If acceptable is nil, no type specific tests will be performed.

      has-type?

      (has-type? x acceptable)

      Return true if object x has a type in acceptable, else false.

      +

      The values of :type fields of ActivityStreams objects may be lists; they are considered to have a type if a member of the list is in acceptable.

      +

      acceptable may be passed as a string, in which case there is only one acceptable value, or as a set of strings, in which case any member of the set is acceptable.

      make-fault-object

      (make-fault-object severity fault)

      Return a fault object with these severity, fault and narrative values.

      +

      An ActivityPub object MUST have a globally unique ID. Whether this is meaningful depends on whether we persist fault report objects and serve them, which at present I have no plans to do.

      nil-if-empty

      macro

      (nil-if-empty x)

      if x is an empty collection, return nil; else return x.

      object-or-uri?

      (object-or-uri? x)(object-or-uri? x type)

      Very basic check that x is either an object or a URI.

      string-or-fault

      (string-or-fault value severity token)(string-or-fault value severity token pattern)

      If this value is not a string, return a fault object with this severity and token, else nil. If pattern is also passed, it is expected to be a Regex, and the fault object will be returned unless value matches the pattern.

      truthy?

      (truthy? x)

      Return true if x is truthy, else false. There must be some more idiomatic way to do this?

      xsd-non-negative-integer?

      (xsd-non-negative-integer? x)

      Return true if value matches the pattern for an xsd:nonNegativeInteger, else false

      \ No newline at end of file diff --git a/docs/codox/dog-and-duck.quack.quack.html b/docs/codox/dog-and-duck.quack.quack.html index cfba417..3d3a69d 100644 --- a/docs/codox/dog-and-duck.quack.quack.html +++ b/docs/codox/dog-and-duck.quack.quack.html @@ -1,14 +1,14 @@ -dog-and-duck.quack.quack documentation

      dog-and-duck.quack.quack

      Validator for ActivityPub objects: if it walks like a duck, and it quacks like a duck…

      +dog-and-duck.quack.quack documentation

      dog-and-duck.quack.quack

      Validator for ActivityPub objects: if it walks like a duck, and it quacks like a duck…

      **NOTE THAT the ActivityPub spec says

      Servers SHOULD validate the content they receive to avoid content spoofing attacks

      -

      but in practice ActivityPub content collected in the wild bears only a hazy relationship to the spec, so this is difficult. I suspect that I may have to implement a *strict* dynamic variable, so that users can toggle some checks off.

      activity?

      (activity? x)(activity? x severity)

      true iff x quacks like an activity, else false.

      actor-or-uri?

      (actor-or-uri? x)

      true if x is either a URI or an actor.

      -

      TODO: I need to decide about whether to reify referenced objects before validation or after. After reification, every reference to an actor must be to an actor object, but before, may only be to a URI pointing to one.

      actor?

      (actor? x)(actor? x severity)

      Returns true if x quacks like an actor, else false.

      collection-page?

      (collection-page? x)

      true iff x quacks like a page in a paged collection, else false.

      collection?

      (collection? x object-type)(collection? x)

      true iff x quacks like a collection of type object-type, else false.

      -

      With one argument, will recognise plain collections and ordered collections, but (currently) not collection pages.

      link?

      (link? x)(link? x severity)

      true iff x quacks like a link, else false.

      object?

      (object? x)(object? x severity)

      Returns true iff x is recognisably an ActivityStreams object.

      +

      but in practice ActivityPub content collected in the wild bears only a hazy relationship to the spec, so this is difficult. I suspect that I may have to implement a *strict* dynamic variable, so that users can toggle some checks off.

      activity?

      (activity? x)(activity? x severity)

      true iff x quacks like an activity, else false.

      actor-or-uri?

      (actor-or-uri? x)

      true if x is either a URI or an actor.

      +

      TODO: I need to decide about whether to reify referenced objects before validation or after. After reification, every reference to an actor must be to an actor object, but before, may only be to a URI pointing to one.

      actor?

      (actor? x)(actor? x severity)

      Returns true if x quacks like an actor, else false.

      collection-page?

      (collection-page? x)

      true iff x quacks like a page in a paged collection, else false.

      collection?

      (collection? x object-type)(collection? x)

      true iff x quacks like a collection of type object-type, else false.

      +

      With one argument, will recognise plain collections and ordered collections, but (currently) not collection pages.

      link?

      (link? x)(link? x severity)

      true iff x quacks like a link, else false.

      object?

      (object? x)(object? x severity)

      Returns true iff x is recognisably an ActivityStreams object.

      NOTE THAT The ActivityStreams spec says:

      All properties are optional (including the id and type)

      @@ -18,5 +18,5 @@

      Implementers SHOULD include the ActivityPub context in their object definitions

      -

      but in samples found in the wild they typically don’t.

      ordered-collection-page?

      (ordered-collection-page? x)

      true iff x quacks like a page in an ordered paged collection, else false.

      ordered-collection?

      (ordered-collection? x)

      true iff x quacks like an ordered collection, else false.

      persistent-object?

      (persistent-object? x)(persistent-object? x severity)

      true iff x is a persistent object.

      -

      Transient objects in ActivityPub are not required to have an id key, but persistent ones must have a key, and it must be an IRI (but normally a URI).

      unordered-collection?

      (unordered-collection? x)

      true iff x quacks like an unordered collection, else false.

      \ No newline at end of file +

      but in samples found in the wild they typically don’t.

      ordered-collection-page?

      (ordered-collection-page? x)

      true iff x quacks like a page in an ordered paged collection, else false.

      ordered-collection?

      (ordered-collection? x)

      true iff x quacks like an ordered collection, else false.

      persistent-object?

      (persistent-object? x)(persistent-object? x severity)

      true iff x is a persistent object.

      +

      Transient objects in ActivityPub are not required to have an id key, but persistent ones must have a key, and it must be an IRI (but normally a URI).

      unordered-collection?

      (unordered-collection? x)

      true iff x quacks like an unordered collection, else false.

      \ No newline at end of file diff --git a/docs/codox/dog-and-duck.scratch.parser.html b/docs/codox/dog-and-duck.scratch.parser.html index 1d60fba..da2de6c 100644 --- a/docs/codox/dog-and-duck.scratch.parser.html +++ b/docs/codox/dog-and-duck.scratch.parser.html @@ -1,3 +1,3 @@ -dog-and-duck.scratch.parser documentation

      dog-and-duck.scratch.parser

      TODO: write docs

      clean

      (clean json)

      Take this json input, and return a sequence of ActivityPub objects represented by it.

      \ No newline at end of file +dog-and-duck.scratch.parser documentation

      dog-and-duck.scratch.parser

      TODO: write docs

      clean

      (clean json)

      Take this json input, and return a sequence of ActivityPub objects represented by it.

      \ No newline at end of file diff --git a/docs/codox/dog-and-duck.scratch.scratch.html b/docs/codox/dog-and-duck.scratch.scratch.html index 45ff4af..b993d57 100644 --- a/docs/codox/dog-and-duck.scratch.scratch.html +++ b/docs/codox/dog-and-duck.scratch.scratch.html @@ -1,3 +1,3 @@ -dog-and-duck.scratch.scratch documentation

      dog-and-duck.scratch.scratch

      Scratchpad where I try to understand how to do this stuff.

      account

      TODO: write docs

      account-handle

      TODO: write docs

      \ No newline at end of file +dog-and-duck.scratch.scratch documentation

      dog-and-duck.scratch.scratch

      Scratchpad where I try to understand how to do this stuff.

      account-handle

      TODO: write docs

      \ No newline at end of file diff --git a/docs/codox/dog-and-duck.utils.process.html b/docs/codox/dog-and-duck.utils.process.html index 4e8ae03..9a4c9f7 100644 --- a/docs/codox/dog-and-duck.utils.process.html +++ b/docs/codox/dog-and-duck.utils.process.html @@ -1,5 +1,5 @@ -dog-and-duck.utils.process documentation

      dog-and-duck.utils.process

      TODO: write docs

      get-hostname

      return the hostname of the current host.

      +dog-and-duck.utils.process documentation

      dog-and-duck.utils.process

      TODO: write docs

      get-hostname

      return the hostname of the current host.

      Java’s methods for getting the hostname are quite startlingly slow, we do not want todo this repeatedly!

      get-pid

      Get the process id of the current process.

      OK, this is hacky as fuck, but I hope it works. The problem is that the way to get the process id has changed several times during the history of Java development, and the code for one version of Java won’t even compile in a different version.

      \ No newline at end of file diff --git a/docs/codox/index.html b/docs/codox/index.html index 5c9c887..df3f44e 100644 --- a/docs/codox/index.html +++ b/docs/codox/index.html @@ -1,3 +1,3 @@ -Dog-and-duck 0.1.0-SNAPSHOT

      Dog-and-duck 0.1.0-SNAPSHOT

      Released under the GPL-2.0-or-later

      A playground for hacking ActivityPub stuff.

      Installation

      To install, add the following dependency to your project or build file:

      [dog-and-duck "0.1.0-SNAPSHOT"]

      Topics

      Namespaces

      dog-and-duck.quack.picky.control-variables

      Control variables for the picky validator.

      Public variables and functions:

      dog-and-duck.quack.picky.fault-messages

      Narrative values for fault reports of specific types, used by the picky validator.

      Public variables and functions:

      dog-and-duck.quack.picky.required-properties

      TODO: write docs

      Public variables and functions:

        dog-and-duck.quack.quack

        Validator for ActivityPub objects: if it walks like a duck, and it quacks like a duck…

        dog-and-duck.scratch.core

        TODO: write docs

        Public variables and functions:

        dog-and-duck.scratch.parser

        TODO: write docs

        Public variables and functions:

        dog-and-duck.scratch.scratch

        Scratchpad where I try to understand how to do this stuff.

        Public variables and functions:

        dog-and-duck.utils.process

        TODO: write docs

        Public variables and functions:

        \ No newline at end of file +Dog-and-duck 0.1.0

        Dog-and-duck 0.1.0

        Released under the GPL-2.0-or-later

        A playground for hacking ActivityPub stuff.

        Installation

        To install, add the following dependency to your project or build file:

        [dog-and-duck "0.1.0"]

        Topics

        Namespaces

        dog-and-duck.quack.picky.control-variables

        Control variables for the picky validator.

        Public variables and functions:

        dog-and-duck.quack.picky.distribution

        TODO: write docs

        Public variables and functions:

        dog-and-duck.quack.picky.scratch

        Development scratchpad

        Public variables and functions:

          dog-and-duck.quack.picky.time

          Time, gentleman, please! Recognising and validating date time values.

          dog-and-duck.quack.quack

          Validator for ActivityPub objects: if it walks like a duck, and it quacks like a duck…

          dog-and-duck.scratch.parser

          TODO: write docs

          Public variables and functions:

          dog-and-duck.scratch.scratch

          Scratchpad where I try to understand how to do this stuff.

          Public variables and functions:

          dog-and-duck.utils.process

          TODO: write docs

          Public variables and functions:

          \ No newline at end of file diff --git a/docs/codox/intro.html b/docs/codox/intro.html index b19fbd1..7ad3c51 100644 --- a/docs/codox/intro.html +++ b/docs/codox/intro.html @@ -1,16 +1,41 @@ -Introduction

          Introduction

          -

          The Old Dog and Duck

          +The Old Dog and Duck

          The Old Dog and Duck

          A Clojure library designed to implement the ActivityPub protocol, obviously.

          -

          The Dog and Duck, Derby

          +

          The Dog and Duck, St George’s Fields, London, 1647

          +

          Introduction

          The Old Dog and Duck is clearly a pub, and it’s a pub related to an activity; to whit, hunting ducks with dogs. Yes, of course one could also hunt dogs with ducks, but in practice that doesn’t work so well. The point isn’t whether or not I approve of hunting ducks with dogs (or vice versa); to be clear, I don’t. The point is that it’s a pub related to an activity, and is therefore an ActivityPub.

          Are we clear?

          Good.

          Let us proceed.

          The Old Dog and Duck is intended to be a set of libraries to enable people to build stuff which interacts with ActivityPub. It isn’t intended to be a replacement for, or clone of, Mastodon. I do think I might implement my own ActivityPub server on top of The Old Dog and Duck, that specifically might allow for user-pluggable feed-sorting algorithms and with my own user interface/user experience take, but that project is not (yet, at any rate) this project.

          Status

          -

          This is a long way pre-alpha. Everything will change. Feel free to play, but do so at your own risk. Contributions welcome.

          +

          This is still pre-alpha. Everything will change. There’s still a lot of code that was written when I was feeling my way around the problems, which is redundant and should now be pruned. Feel free to play, but do so at your own risk. Contributions welcome.

          +

          Usage

          +

          At present, only the duck-typing validator works. To play with it, build the uberjar (or download it from github) and run it with

          +
          java -jar target/dog-and-duck-0.1.0-standalone.jar -i resources/activitystreams-test-documents/vocabulary-ex10-jsonld.json -f html -o report.html -s info
          +
          +

          The full range of command-line switches is as follows:

          +
              -i, --input SOURCE    standard input   The file or URL to validate
          +    -o, --output DEST     standard output  The file to write to, defaults to standard out
          +    -f, --format FORMAT   :edn             The format to output, one of `edn` `csv` `html`
          +    -l, --language LANG   en-GB            The ISO 639-1 code for the language to output
          +    -s, --severity LEVEL  :info            The minimum severity of faults to report
          +    -h, --help                             Print this message and exit
          +
          +

          Note, though, that internationalisation files for languages other than British English have not yet been written, and that one is not complete.

          +

          The following severity levels are understood:

          +
            +
          1. info things which are not actuallys fault, but issues noted during validation;
          2. +
          3. minor things which I consider to be faults, but which don’t actually breach the spec;
          4. +
          5. should instances where the spec says something SHOULD be done, which isn’t;
          6. +
          7. must instances where the spec says something MUST be done, which isn’t;
          8. +
          9. critical instances where I believe the fault means that the object cannot be meaningfully processed.
          10. +
          +

          Note that it is almost certain that in some places I have misinterpreted the spec. Of all 205 documents in the activitystreams-test-documents repository, not a single one passes validation, and that must be wrong.

          +

          Nevertheless I think that this is a basis on which a useful validator can be built. Feedback and contributions welcome.

          +

          Documentation

          +

          Full documentation is here.

          Architecture

          There are a number of separate concerns required to implement ActivityPub. They include

            @@ -48,11 +73,25 @@

            Quack

            Duck-typing for ActivityStreams objects.

            As of version 0.1.0, this is substantially the only part that is yet at all useful, and it is still a long way from finished or robust.

            +

            Bouncer

            +

            Enhanced tools for moderators (I have as yet absolutely no idea what this looks like).

            Scratch

            What the dog does when bored. Essentially, a place where I can learn how to make this stuff work, but perhaps eventually an ActivityPub server in its own right.

            -

            Usage

            -

            At present, only the duck-typing functions work. To play with them, use

            -
            (require '[dog-and-duck.quack.quack :as q])
            +

            Building

            +

            clj-activitypub

            +

            NOTE THAT dog-and-duck depends on Jahfer’s clj-activitypub, which is also currently not yet released and under rapid development and consequently currently very unstable. For this reason it’s probably best to clone my fork rather than the original, because that way you are less likely to encounter version incompatibilities.

            +

            clj-activitypub is configured to build with tools.bui;d. To prepare clj-activitypub before building dog-and-duck, do

            +
            $ git clone git@github.com:simon-brooke/clj-activitypub.git
            +$ cd clj-activitypub/
            +$ clj -T:build jar
            +$ clj -T:build install
            +
            +

            I shall keep dog-and-duck and my fork of clj-activitypub in sync at least until Jahfer makes a production release of his project to Clojars.

            +

            Leiningen

            +

            dog-and-duck itself is still set up to build with Leiningen. Yes, I know that’s not what the cool kids are using any more but hey, I’m an old man, leave me be. To get dog-and-duck up to a point where you can start to play,

            +
            $ git clone git@github.com:simon-brooke/dog-and-duck.git
            +$ cd dog-and-duck
            +$ lein repl
             

            Testing

            Prior to testing, you should clone activitystreams-test-documents into the resources directory. You can then test with

            diff --git a/src/dog_and_duck/scratch/scratch.clj b/src/dog_and_duck/scratch/scratch.clj index 54aee05..abb6f90 100644 --- a/src/dog_and_duck/scratch/scratch.clj +++ b/src/dog_and_duck/scratch/scratch.clj @@ -56,11 +56,11 @@ ;; how we make a public/private key pair. But this key pair is not the one ;; known to mastodon.scot as my key pair, so that doesn't get us very far... ;; I think. -(let [rsa (pgp-gen/rsa-keypair-generator 2048) - kp (pgp-gen/generate-keypair rsa :rsa-general) - public (-> kp .getPublicKey .getEncoded) - private (-> kp .getPrivateKey .getPrivateKeyDataPacket .getEncoded)] - (println (str "Public key: " public)) - (println (str "Private key: " private)) - ) +;; (let [rsa (pgp-gen/rsa-keypair-generator 2048) +;; kp (pgp-gen/generate-keypair rsa :rsa-general) +;; public (-> kp .getPublicKey .getEncoded) +;; private (-> kp .getPrivateKey .getPrivateKeyDataPacket .getEncoded)] +;; (println (str "Public key: " public)) +;; (println (str "Private key: " private)) +;; )