[object Object]

← back to Dw Rotation Activator

Require confirmed Online Store availability before counting publication

0add27a785a2bb7821ea3d2ab3be6d99cc37f2a0 · 2026-09-09 12:21:58 -0700 · Steve Abrams

Files touched

Diff

commit 0add27a785a2bb7821ea3d2ab3be6d99cc37f2a0
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 9 12:21:58 2026 -0700

    Require confirmed Online Store availability before counting publication
---
 lib/publication-result.js       | 15 +++++++++++++++
 rotate-activate.js              | 11 ++++++++---
 test/publication-result.test.js | 26 ++++++++++++++++++++++++++
 3 files changed, 49 insertions(+), 3 deletions(-)

diff --git a/lib/publication-result.js b/lib/publication-result.js
new file mode 100644
index 0000000..856767b
--- /dev/null
+++ b/lib/publication-result.js
@@ -0,0 +1,15 @@
+'use strict';
+
+function confirmedPublishMutation(response) {
+  const payload = response?.json?.data?.publishablePublish;
+  return response?.status === 200 && !response.json?.errors?.length &&
+    Array.isArray(payload?.userErrors) && payload.userErrors.length === 0;
+}
+
+function confirmedOnlineStore(response, productId) {
+  const product = response?.json?.data?.product;
+  return response?.status === 200 && !response.json?.errors?.length &&
+    typeof productId === 'string' && product?.id === productId && product.onStore === true;
+}
+
+module.exports = { confirmedPublishMutation, confirmedOnlineStore };
diff --git a/rotate-activate.js b/rotate-activate.js
index a3a4443..39aaf77 100644
--- a/rotate-activate.js
+++ b/rotate-activate.js
@@ -50,6 +50,7 @@ const { mfrGate } = require('./lib/mfr-gate.js');
 const { canonicalProduct, widthFromLive, completeLiveProduct } = require('./lib/live-product-fields.js');
 const { buildRepairReport } = require('./lib/readiness-repairs.js');
 const { confirmedActivation } = require('./lib/activation-result.js');
+const { confirmedPublishMutation, confirmedOnlineStore } = require('./lib/publication-result.js');
 const { countHourlyActivations } = require('./lib/hourly-activation-count.js');
 const { activationAllowance, assertDailyCatchup } = require('./lib/activation-allowance.js');
 const { reusedMfrSet, stagingColorFor } = require('./lib/mfr-gate-resolve.js');
@@ -149,13 +150,17 @@ async function loadPubs() {
 async function publishToChannels(pid) {
   const pubs = await loadPubs();
   if (!pubs.length) return { published: false, why: 'no-publications-scope' };
+  const onlineStore = pubs.find(p => p.name === 'Online Store');
+  if (!onlineStore) return { published: false, why: 'Online Store publication is missing' };
   const input = pubs.map((p) => ({ publicationId: p.id }));
   const r = await gqlRetry(
     `mutation($id:ID!,$input:[PublicationInput!]!){publishablePublish(id:$id,input:$input){userErrors{field message}}}`,
     { id: pid, input });
-  const ue = r.json?.data?.publishablePublish?.userErrors || [];
-  const real = ue.filter((e) => !/already|cannot be published to itself/i.test(e.message || ''));
-  if (real.length) return { published: false, errors: real };
+  if (!confirmedPublishMutation(r)) return { published: false, why: 'Shopify did not confirm publication' };
+  const current = await gqlRetry(
+    `query($id:ID!,$publicationId:ID!){product(id:$id){id onStore:publishedOnPublication(publicationId:$publicationId)}}`,
+    { id: pid, publicationId: onlineStore.id });
+  if (!confirmedOnlineStore(current, pid)) return { published: false, why: 'Online Store publication was not confirmed by read-back' };
   return { published: true, channels: pubs.length };
 }
 
diff --git a/test/publication-result.test.js b/test/publication-result.test.js
new file mode 100644
index 0000000..39279df
--- /dev/null
+++ b/test/publication-result.test.js
@@ -0,0 +1,26 @@
+'use strict';
+const test = require('node:test');
+const assert = require('node:assert/strict');
+const { confirmedPublishMutation, confirmedOnlineStore } = require('../lib/publication-result');
+
+test('publication requires a complete successful mutation reply', () => {
+  assert.equal(confirmedPublishMutation({ status: 200, json: { data: { publishablePublish: { userErrors: [] } } } }), true);
+  for (const r of [null, {}, { status: 200, json: null }, { status: 200, json: {} },
+    { status: 429, json: { data: { publishablePublish: { userErrors: [] } } } },
+    { status: 200, json: { errors: [{ message: 'THROTTLED' }] } },
+    { status: 200, json: { data: { publishablePublish: { userErrors: [{ message: 'not allowed' }] } } } },
+    { status: 200, json: { data: { publishablePublish: {} } } }])
+    assert.equal(confirmedPublishMutation(r), false);
+});
+
+test('publication additionally requires an independent current Online Store read', () => {
+  const good = { status: 200, json: { data: { product: { id: 'p1', onStore: true } } } };
+  assert.equal(confirmedOnlineStore(good, 'p1'), true);
+  assert.equal(confirmedOnlineStore(good, 'p2'), false);
+  for (const r of [null, {}, { status: 200, json: null }, { status: 200, json: {} },
+    { status: 200, json: { data: { product: null } } },
+    { status: 200, json: { data: { product: { id: 'p1', onStore: false } } } },
+    { status: 500, json: good.json },
+    { status: 200, json: { ...good.json, errors: [{ message: 'access denied' }] } }])
+    assert.equal(confirmedOnlineStore(r, 'p1'), false);
+});

← 9720a3e Enable verified one-day activation catch-up within daily all  ·  back to Dw Rotation Activator  ·  Record verified 462-product loading and unresolved Google ch 0bd5974 →