database migration 1.0.4, store topic fetch etag/last-modified, provide these when...
authorJustin Wind <justin.wind+git@gmail.com>
Tue, 5 Apr 2022 01:04:02 +0000 (18:04 -0700)
committerJustin Wind <justin.wind+git@gmail.com>
Tue, 5 Apr 2022 01:04:02 +0000 (18:04 -0700)
16 files changed:
README.md
documentation/media/postgres-er.svg
documentation/media/sqlite-er.svg
src/communication.js
src/db/base.js
src/db/postgres/index.js
src/db/postgres/sql/schema/1.0.4/apply.sql [new file with mode: 0644]
src/db/postgres/sql/schema/1.0.4/er.dot [new file with mode: 0644]
src/db/postgres/sql/schema/1.0.4/revert.sql [new file with mode: 0644]
src/db/postgres/sql/topic-set-content.sql
src/db/sqlite/index.js
src/db/sqlite/sql/schema/1.0.4/apply.sql [new file with mode: 0644]
src/db/sqlite/sql/schema/1.0.4/er.dot [new file with mode: 0644]
src/db/sqlite/sql/schema/1.0.4/revert.sql [new file with mode: 0644]
src/db/sqlite/sql/topic-set-content.sql
test/src/communication.js

index 5af15f0668c5fca11c22b55e41276860633435ea..33c86749cca1ae140d0e3670cd8305a4a20e8400 100644 (file)
--- a/README.md
+++ b/README.md
@@ -148,7 +148,7 @@ This implementation is built atop an in-house API framework, for Reasons.  It wo
   - enum.js - invariants
   - errors.js - local Error types
   - link-helper.js - processes Link headers
-  - logger.js - a very simple logging class
+  - logger/ - adds service-specific data filters to our logging module
   - manager.js - process incoming requests
   - service.js - defines incoming endpoints, linking the API server framework to the manager methods
   - template/ - HTML content
index b7db5b485fa2f1b657fb961246e265d5342e3223..86105934cff913de1e5e60130c6f2bf4935cf35d 100644 (file)
@@ -11,7 +11,7 @@
 <polygon fill="white" stroke="transparent" points="-4,4 -4,-1079.63 918,-1079.63 918,4 -4,4"/>
 <text text-anchor="middle" x="457" y="-1050.83" font-family="Times,serif" font-size="26.00">Websub Hub Entity&#45;Relations</text>
 <text text-anchor="middle" x="457" y="-1021.83" font-family="Times,serif" font-size="26.00">Postgres</text>
-<text text-anchor="middle" x="457" y="-992.83" font-family="Times,serif" font-size="26.00">Schema 1.0.3</text>
+<text text-anchor="middle" x="457" y="-992.83" font-family="Times,serif" font-size="26.00">Schema 1.0.4</text>
 <!-- topic -->
 <g id="node1" class="node">
 <title>topic</title>
 <text text-anchor="start" x="92.5" y="-335.93" font-family="Times,serif" font-size="14.00">content_hash</text>
 <polygon fill="none" stroke="black" points="0,-308.13 0,-329.13 280,-329.13 280,-308.13 0,-308.13"/>
 <text text-anchor="start" x="93.5" y="-314.93" font-family="Times,serif" font-size="14.00">content_type</text>
+<polygon fill="none" stroke="black" points="0,-287.13 0,-308.13 280,-308.13 280,-287.13 0,-287.13"/>
+<text text-anchor="start" x="105.5" y="-293.93" font-family="Times,serif" font-size="14.00">http_etag</text>
+<polygon fill="none" stroke="black" points="0,-266.13 0,-287.13 280,-287.13 280,-266.13 0,-266.13"/>
+<text text-anchor="start" x="73.5" y="-272.93" font-family="Times,serif" font-size="14.00">http_last_modified</text>
 </g>
 <!-- topic_fetch_in_progress -->
 <g id="node2" class="node">
index e4a9c87feb8cb36a80907a60ed7d3b1f118e1e03..91752cb05f53004e6ff7ab63463d4a943b54a9f0 100644 (file)
@@ -11,7 +11,7 @@
 <polygon fill="white" stroke="transparent" points="-4,4 -4,-1079.63 918,-1079.63 918,4 -4,4"/>
 <text text-anchor="middle" x="457" y="-1050.83" font-family="Times,serif" font-size="26.00">Websub Hub Entity&#45;Relations</text>
 <text text-anchor="middle" x="457" y="-1021.83" font-family="Times,serif" font-size="26.00">SQLite</text>
-<text text-anchor="middle" x="457" y="-992.83" font-family="Times,serif" font-size="26.00">Schema 1.0.3</text>
+<text text-anchor="middle" x="457" y="-992.83" font-family="Times,serif" font-size="26.00">Schema 1.0.4</text>
 <!-- topic -->
 <g id="node1" class="node">
 <title>topic</title>
 <text text-anchor="start" x="92.5" y="-335.93" font-family="Times,serif" font-size="14.00">content_hash</text>
 <polygon fill="none" stroke="black" points="0,-308.13 0,-329.13 280,-329.13 280,-308.13 0,-308.13"/>
 <text text-anchor="start" x="93.5" y="-314.93" font-family="Times,serif" font-size="14.00">content_type</text>
+<polygon fill="none" stroke="black" points="0,-287.13 0,-308.13 280,-308.13 280,-287.13 0,-287.13"/>
+<text text-anchor="start" x="105.5" y="-293.93" font-family="Times,serif" font-size="14.00">http_etag</text>
+<polygon fill="none" stroke="black" points="0,-266.13 0,-287.13 280,-287.13 280,-266.13 0,-266.13"/>
+<text text-anchor="start" x="73.5" y="-272.93" font-family="Times,serif" font-size="14.00">http_last_modified</text>
 </g>
 <!-- topic_fetch_in_progress -->
 <g id="node2" class="node">
index dc4d464c70111b4ea2a8971575940c099d14b379..3b436786fabf3ebfddfb245b479da669894f8377 100644 (file)
@@ -196,6 +196,8 @@ class Communication {
     const acceptPreferred = [topic.contentType, acceptWildcard].filter((x) => x).join(', ');
     return Communication._axiosConfig('GET', topic.url, undefined, {}, {
       [Enum.Header.Accept]: acceptPreferred,
+      ...(topic.httpEtag && { [Enum.Header.IfNoneMatch]: topic.httpEtag }),
+      ...(topic.httpLastModified && { [Enum.Header.IfModifiedSince]: topic.httpLastModified }),
     });
   }
 
@@ -467,6 +469,7 @@ class Communication {
 
     switch (common.httpStatusCodeClass(response.status)) {
       case 2:
+      case 3:
         // Fall out of switch on success
         break;
 
@@ -481,6 +484,12 @@ class Communication {
         return;
     }
 
+    if (response.status === 304) {
+      this.logger.info(_scope, 'content has not changed, per server', logInfoData);
+      await this.db.topicFetchComplete(dbCtx, topicId);
+      return;
+    }
+
     const contentHash = Communication.contentHash(response.data, topic.contentHashAlgorithm);
     logInfoData.contentHash = contentHash;
     if (topic.contentHash === contentHash) {
@@ -505,6 +514,8 @@ class Communication {
     }
 
     const contentType = response.headers[Enum.Header.ContentType.toLowerCase()];
+    const httpETag = response.headers[Enum.Header.ETag.toLowerCase()];
+    const httpLastModified = response.headers[Enum.Header.LastModified.toLowerCase()];
 
     await this.db.transaction(dbCtx, async (txCtx) => {
       await this.db.topicSetContent(txCtx, {
@@ -512,6 +523,8 @@ class Communication {
         content: Buffer.from(response.data),
         contentHash,
         ...(contentType && { contentType }),
+        ...(httpETag && { httpETag }),
+        ...(httpLastModified && { httpLastModified }),
       });
 
       await this.db.topicFetchComplete(txCtx, topicId);
index 3346f92d421ac7d65932a6e21df5cddc2ddf635d..36cca60328875280620395ae5a4578ebef3136ee 100644 (file)
@@ -173,6 +173,8 @@ class Database {
     this._ensureTypes(data, ['content'], ['string', 'buffer']);
     this._ensureTypes(data, ['contentHash'], ['string']);
     this._ensureTypes(data, ['contentType'], ['string', 'null', 'undefined']);
+    this._ensureTypes(data, ['eTag'], ['string', 'null', 'undefined']);
+    this._ensureTypes(data, ['lastModified'], ['string', 'null', 'undefined']);
   }
 
 
@@ -614,6 +616,8 @@ class Database {
    * @param {String} data.content
    * @param {String} data.contentHash
    * @param {String=} data.contentType
+   * @param {String=} data.eTag
+   * @param {String=} data.lastModified
    */
   async topicSetContent(dbCtx, data) {
     this._notImplemented('topicSetContent', arguments);
index fc4ec8daa1b229ec353ebfeff8cf5ac3d917a6d8..34511102ec5a0e596d0408e2a2bb8bfedeb94eab 100644 (file)
@@ -30,7 +30,7 @@ const schemaVersionsSupported = {
   max: {
     major: 1,
     minor: 0,
-    patch: 3,
+    patch: 4,
   },
 };
 
@@ -900,6 +900,8 @@ class DatabasePostgres extends Database {
     const _scope = _fileScope('topicSetContent');
     const topicSetContentData = {
       contentType: null,
+      httpETag: null,
+      httpLastModified: null,
       ...data,
     };
     const logData = {
@@ -916,7 +918,11 @@ class DatabasePostgres extends Database {
       if (result.rowCount !=  1) {
         throw new DBErrors.UnexpectedResult('did not set topic content');
       }
-      result = await dbCtx.result(this.statement.topicSetContentHistory, { topicId: data.topicId, contentHash: data.contentHash, contentSize: data.content.length });
+      result = await dbCtx.result(this.statement.topicSetContentHistory, {
+        topicId: data.topicId,
+        contentHash: data.contentHash,
+        contentSize: data.content.length,
+      });
       if (result.rowCount != 1) {
         throw new DBErrors.UnexpectedResult('did not set topic content history');
       }
diff --git a/src/db/postgres/sql/schema/1.0.4/apply.sql b/src/db/postgres/sql/schema/1.0.4/apply.sql
new file mode 100644 (file)
index 0000000..514a6be
--- /dev/null
@@ -0,0 +1,10 @@
+BEGIN;
+
+       ALTER TABLE topic
+               ADD COLUMN http_etag TEXT,
+               ADD COLUMN http_last_modified TEXT
+       ;
+
+       INSERT INTO _meta_schema_version (major, minor, patch) VALUES (1, 0, 4);
+
+COMMIT;
\ No newline at end of file
diff --git a/src/db/postgres/sql/schema/1.0.4/er.dot b/src/db/postgres/sql/schema/1.0.4/er.dot
new file mode 100644 (file)
index 0000000..4bd10b3
--- /dev/null
@@ -0,0 +1,139 @@
+digraph WebsubHubERD {
+       graph[
+               rankdir=LR,
+               overlap=false,
+               splines=true,
+               label="Websub Hub Entity-Relations\nPostgres\nSchema 1.0.4",
+               labelloc="t",
+               fontsize=26,
+       ];
+       // layout=neato;
+       node[shape=plain];
+       edge[arrowhead=crow];
+
+       topic [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">TOPIC</td></tr>
+               <tr><td port="pk_id">id</td></tr>
+               <tr><td port="">created</td></tr>
+               <tr><td port="">url</td></tr>
+               <tr><td port="">lease_seconds_preferred</td></tr>
+               <tr><td port="">lease_seconds_min</td></tr>
+               <tr><td port="">lease_seconds_max</td></tr>
+               <tr><td port="">publisher_validation_url</td></tr>
+               <tr><td port="">content_hash_algorithm</td></tr>
+               <tr><td port="">is_active</td></tr>
+               <tr><td port="">is_deleted</td></tr>
+               <tr><td port="">last_publish</td></tr>
+               <tr><td port="">content_fetch_next_attempt</td></tr>
+               <tr><td port="">content_fetch_attempts_since_success</td></tr>
+               <tr><td port="">content_updated</td></tr>
+               <tr><td port="">content</td></tr>
+               <tr><td port="">content_hash</td></tr>
+               <tr><td port="">content_type</td></tr>
+               <tr><td port="">http_etag</td></tr>
+               <tr><td port="">http_last_modified</td></tr>
+       </table>
+       >];
+
+       topic_fetch_in_progress [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">TOPIC_FETCH_IN_PROGRESS</td></tr>
+               <tr><td port="fk_id">id</td></tr>
+               <tr><td port="">claimant</td></tr>
+               <tr><td port="">claimed</td></tr>
+               <tr><td port="">claim_expires</td></tr>
+       </table>
+       >];
+       topic:pk_id -> topic_fetch_in_progress:fk_id;
+
+       topic_content_history [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">TOPIC_CONTENT_HISTORY</td></tr>
+               <tr><td port="fk_topic_id">topic_id</td></tr>
+               <tr><td port="">content_updated</td></tr>
+               <tr><td port="">content_size</td></tr>
+               <tr><td port="">content_hash</td></tr>
+       </table>
+       >];
+       topic:pk_id -> topic_content_history:fk_topic_id;
+
+       subscription [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">SUBSCRIPTION</td></tr>
+               <tr><td port="pk_id">id</td></tr>
+               <tr><td port="">created</td></tr>
+               <tr><td port="fk_topic_id">topic_id</td></tr>
+               <tr><td port="">callback</td></tr>
+               <tr><td port="">verified</td></tr>
+               <tr><td port="">expires</td></tr>
+               <tr><td port="">secret</td></tr>
+               <tr><td port="">signature_algorithm</td></tr>
+               <tr><td port="">http_remote_addr</td></tr>
+               <tr><td port="">http_from</td></tr>
+               <tr><td port="">content_delivered</td></tr>
+               <tr><td port="">latest_content_delivered</td></tr>
+               <tr><td port="">delivery_attempts_since_success</td></tr>
+               <tr><td port="">delivery_next_attempt</td></tr>
+       </table>
+       >];
+       topic:pk_id -> subscription:fk_topic_id;
+
+       subscription_delivery_in_progress [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">SUBSCRIPTION_DELIVERY_IN_PROGRESS</td></tr>
+               <tr><td port="fk_id">id</td></tr>
+               <tr><td port="">claimant</td></tr>
+               <tr><td port="">claimed</td></tr>
+               <tr><td port="">claim_expires</td></tr>
+       </table>
+       >];
+       subscription:pk_id -> subscription_delivery_in_progress:fk_id;
+
+       verification [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">VERIFICATION</td></tr>
+               <tr><td port="pk_id">id</td></tr>
+               <tr><td port="">created</td></tr>
+               <tr><td port="fk_topic_id">topic_id</td></tr>
+               <tr><td port="">callback</td></tr>
+               <tr><td port="">secret</td></tr>
+               <tr><td port="">signature_algorithm</td></tr>
+               <tr><td port="">http_remote_addr</td></tr>
+               <tr><td port="">http_from</td></tr>
+               <tr><td port="">mode</td></tr>
+               <tr><td port="">reason</td></tr>
+               <tr><td port="">lease_seconds</td></tr>
+               <tr><td port="">is_publisher_validated</td></tr>
+               <tr><td port="">request_id</td></tr>
+               <tr><td port="">attempts</td></tr>
+               <tr><td port="">next_attempt</td></tr>
+       </table>
+       >];
+       topic:pk_id -> verification:fk_topic_id;
+
+       verification_in_progress [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">VERIFICATION_IN_PROGRESS</td></tr>
+               <tr><td port="fk_id">id</td></tr>
+               <tr><td port="fk_topic_id">topic_id</td></tr>
+               <tr><td port="">callback</td></tr>
+               <tr><td port="">claimant</td></tr>
+               <tr><td port="">claimed</td></tr>
+               <tr><td port="">claim_expires</td></tr>
+       </table>
+       >];
+       verification:pk_id -> verification_in_progress:fk_id;
+       topic:pk_id -> verification_in_progress:fk_topic_id;
+
+       authentication [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">AUTHENTICATION</td></tr>
+               <tr><td port="">created</td></tr>
+               <tr><td port="">last_authenticated</td></tr>
+               <tr><td port="">identifier</td></tr>
+               <tr><td port="">credential</td></tr>
+       </table>
+       >];
+
+}
\ No newline at end of file
diff --git a/src/db/postgres/sql/schema/1.0.4/revert.sql b/src/db/postgres/sql/schema/1.0.4/revert.sql
new file mode 100644 (file)
index 0000000..19c6e73
--- /dev/null
@@ -0,0 +1,10 @@
+BEGIN;
+
+       ALTER TABLE topic
+               DROP COLUMN http_etag,
+               DROP COLUMN http_last_modified
+       ;
+
+       DELETE FROM _meta_schema_version WHERE major = 1 AND minor = 0 AND PATCH = 4;
+
+COMMIT;
index c14258b5f28080a75f27f47a5de080edaa6d0881..464410c1a89f7f758b2435127f3fae0d26fbe88c 100644 (file)
@@ -5,5 +5,7 @@ SET
        content_updated = now(),
        content = $(content),
        content_hash = $(contentHash),
-       content_type = $(contentType)
+       content_type = $(contentType),
+       http_etag = $(httpETag),
+       http_last_modified = $(httpLastModified)
 WHERE id = $(topicId)
index 31471bafb850c9d761ffc19931968658eb093258..56afa000cb886f900277e4bf819139b93e926707 100644 (file)
@@ -20,7 +20,7 @@ const schemaVersionsSupported = {
   max: {
     major: 1,
     minor: 0,
-    patch: 3,
+    patch: 4,
   },
 };
 
@@ -865,6 +865,8 @@ class DatabaseSQLite extends Database {
     const _scope = _fileScope('topicSetContent');
     const topicSetContentData = {
       contentType: null,
+      httpETag: null,
+      httpLastModified: null,
       ...data,
     };
     const logData = {
@@ -881,7 +883,11 @@ class DatabaseSQLite extends Database {
       if (result.changes !=  1) {
         throw new DBErrors.UnexpectedResult('did not set topic content');
       }
-      result = this.statement.topicSetContentHistory.run({ topicId: data.topicId, contentHash: data.contentHash, contentSize: data.content.length });
+      result = this.statement.topicSetContentHistory.run({
+        topicId: data.topicId,
+        contentHash: data.contentHash,
+        contentSize: data.content.length,
+      });
       if (result.changes != 1) {
         throw new DBErrors.UnexpectedResult('did not set topic content history');
       }
diff --git a/src/db/sqlite/sql/schema/1.0.4/apply.sql b/src/db/sqlite/sql/schema/1.0.4/apply.sql
new file mode 100644 (file)
index 0000000..bb4c297
--- /dev/null
@@ -0,0 +1,8 @@
+BEGIN;
+
+       ALTER TABLE topic ADD COLUMN http_etag TEXT;
+       ALTER TABLE topic ADD COLUMN http_last_modified TEXT;
+
+       INSERT INTO _meta_schema_version (major, minor, patch) VALUES (1, 0, 4);
+
+COMMIT;
diff --git a/src/db/sqlite/sql/schema/1.0.4/er.dot b/src/db/sqlite/sql/schema/1.0.4/er.dot
new file mode 100644 (file)
index 0000000..6d87839
--- /dev/null
@@ -0,0 +1,139 @@
+digraph WebsubHubERD {
+       graph[
+               rankdir=LR,
+               overlap=false,
+               splines=true,
+               label="Websub Hub Entity-Relations\nSQLite\nSchema 1.0.4",
+               labelloc="t",
+               fontsize=26,
+       ];
+       // layout=neato;
+       node[shape=plain];
+       edge[arrowhead=crow];
+
+       topic [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">TOPIC</td></tr>
+               <tr><td port="pk_id">id</td></tr>
+               <tr><td port="">created</td></tr>
+               <tr><td port="">url</td></tr>
+               <tr><td port="">lease_seconds_preferred</td></tr>
+               <tr><td port="">lease_seconds_min</td></tr>
+               <tr><td port="">lease_seconds_max</td></tr>
+               <tr><td port="">publisher_validation_url</td></tr>
+               <tr><td port="">content_hash_algorithm</td></tr>
+               <tr><td port="">is_active</td></tr>
+               <tr><td port="">is_deleted</td></tr>
+               <tr><td port="">last_publish</td></tr>
+               <tr><td port="">content_fetch_next_attempt</td></tr>
+               <tr><td port="">content_fetch_attempts_since_success</td></tr>
+               <tr><td port="">content_updated</td></tr>
+               <tr><td port="">content</td></tr>
+               <tr><td port="">content_hash</td></tr>
+               <tr><td port="">content_type</td></tr>
+               <tr><td port="">http_etag</td></tr>
+               <tr><td port="">http_last_modified</td></tr>
+       </table>
+       >];
+
+       topic_fetch_in_progress [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">TOPIC_FETCH_IN_PROGRESS</td></tr>
+               <tr><td port="fk_id">id</td></tr>
+               <tr><td port="">claimant</td></tr>
+               <tr><td port="">claimed</td></tr>
+               <tr><td port="">claim_expires</td></tr>
+       </table>
+       >];
+       topic:pk_id -> topic_fetch_in_progress:fk_id;
+
+       topic_content_history [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+                       <tr><td border="2" bgcolor="lightblue">TOPIC_CONTENT_HISTORY</td></tr>
+                       <tr><td port="fk_topic_id">topic_id</td></tr>
+                       <tr><td port="">content_updated</td></tr>
+                       <tr><td port="">content_size</td></tr>
+                       <tr><td port="">content_hash</td></tr>
+       </table>
+       >];
+       topic:pk_id -> topic_content_history:fk_topic_id;
+
+       subscription [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">SUBSCRIPTION</td></tr>
+               <tr><td port="pk_id">id</td></tr>
+               <tr><td port="">created</td></tr>
+               <tr><td port="fk_topic_id">topic_id</td></tr>
+               <tr><td port="">callback</td></tr>
+               <tr><td port="">verified</td></tr>
+               <tr><td port="">expires</td></tr>
+               <tr><td port="">secret</td></tr>
+               <tr><td port="">signature_algorithm</td></tr>
+               <tr><td port="">http_remote_addr</td></tr>
+               <tr><td port="">http_from</td></tr>
+               <tr><td port="">content_delivered</td></tr>
+               <tr><td port="">latest_content_delivered</td></tr>
+               <tr><td port="">delivery_attempts_since_success</td></tr>
+               <tr><td port="">delivery_next_attempt</td></tr>
+       </table>
+       >];
+       topic:pk_id -> subscription:fk_topic_id;
+
+       subscription_delivery_in_progress [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">SUBSCRIPTION_DELIVERY_IN_PROGRESS</td></tr>
+               <tr><td port="fk_id">id</td></tr>
+               <tr><td port="">claimant</td></tr>
+               <tr><td port="">claimed</td></tr>
+               <tr><td port="">claim_expires</td></tr>
+       </table>
+       >];
+       subscription:pk_id -> subscription_delivery_in_progress:fk_id;
+
+       verification [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">VERIFICATION</td></tr>
+               <tr><td port="pk_id">id</td></tr>
+               <tr><td port="">created</td></tr>
+               <tr><td port="fk_topic_id">topic_id</td></tr>
+               <tr><td port="">callback</td></tr>
+               <tr><td port="">secret</td></tr>
+               <tr><td port="">signature_algorithm</td></tr>
+               <tr><td port="">http_remote_addr</td></tr>
+               <tr><td port="">http_from</td></tr>
+               <tr><td port="">mode</td></tr>
+               <tr><td port="">reason</td></tr>
+               <tr><td port="">lease_seconds</td></tr>
+               <tr><td port="">is_publisher_validated</td></tr>
+               <tr><td port="">request_id</td></tr>
+               <tr><td port="">attempts</td></tr>
+               <tr><td port="">next_attempt</td></tr>
+       </table>
+       >];
+       topic:pk_id -> verification:fk_topic_id;
+
+       verification_in_progress [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">VERIFICATION_IN_PROGRESS</td></tr>
+               <tr><td port="fk_id">id</td></tr>
+               <tr><td port="fk_topic_id">topic_id</td></tr>
+               <tr><td port="">callback</td></tr>
+               <tr><td port="">claimant</td></tr>
+               <tr><td port="">claimed</td></tr>
+               <tr><td port="">claim_expires</td></tr>
+       </table>
+       >];
+       verification:pk_id -> verification_in_progress:fk_id;
+       topic:pk_id -> verification_in_progress:fk_topic_id;
+
+       authentication [label=<
+       <table cellspacing="0" cellborder="1" border="0">
+               <tr><td border="2" bgcolor="lightblue">AUTHENTICATION</td></tr>
+               <tr><td port="">created</td></tr>
+               <tr><td port="">last_authenticated</td></tr>
+               <tr><td port="">identifier</td></tr>
+               <tr><td port="">credential</td></tr>
+       </table>
+       >];
+
+}
\ No newline at end of file
diff --git a/src/db/sqlite/sql/schema/1.0.4/revert.sql b/src/db/sqlite/sql/schema/1.0.4/revert.sql
new file mode 100644 (file)
index 0000000..4cdf004
--- /dev/null
@@ -0,0 +1,8 @@
+BEGIN;
+
+       ALTER TABLE topic DROP COLUMN http_etag;
+       ALTER TABLE topic DROP COLUMN http_last_modified;
+
+       DELETE FROM _meta_schema_version WHERE major = 1 AND minor = 0 AND PATCH = 4;
+
+COMMIT;
index 222d44855bfc7b6d781ccba49e2b87e7bbfceafd..81480c9779b331ca46a44ab8f088cc6ec7e7fa7d 100644 (file)
@@ -5,5 +5,7 @@ SET
        content_updated = strftime('%s', 'now'),
        content = :content,
        content_hash = :contentHash,
-       content_type = :contentType
+       content_type = :contentType,
+       http_etag = :httpETag,
+       http_last_modified = :httpLastModified
 WHERE id = :topicId
index 960a0f994b27a572236a92131638b326c59f4ece..d5ebe54222664863411dd4fcf4898cf3a9028a6a 100644 (file)
@@ -552,6 +552,8 @@ describe('Communication', function () {
         headers: {
           'content-type': 'text/plain',
           link: '<https://example.com/hub/>; rel="hub"',
+          'last-modified': 'Thu, 18 Nov 2021 20:34:35 GMT',
+          'etag': '"9c104-1673e-5d1161636d742"',
         },
         data: 'Jackdaws love my big sphinx of quartz.',
       });
@@ -631,6 +633,20 @@ describe('Communication', function () {
       assert(!communication.db.topicSetContent.called);
     });
 
+    it('recognizes 304 response', async function () {
+      topic.httpLastModified = 'Thu, 18 Nov 2021 20:34:35 GMT';
+      topic.httpEtag = '"9c104-1673e-5d1161636d742"';
+      communication.db.topicGetById.resolves(topic);
+      communication.axios.resolves({
+        status: 304,
+      });
+
+      await communication.topicFetchProcess(dbCtx, topicId, requestId);
+
+      assert(communication.db.topicFetchComplete.called);
+      assert(!communication.db.topicSetContent.called);
+    });
+
     it('updates content', async function () {
       await communication.topicFetchProcess(dbCtx, topicId, requestId);