diff --git a/.gitignore b/.gitignore index 87163ec7..708d8b25 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ public/typo3conf/l10n public/typo3temp public/uploads var +http-client.private.env.json diff --git a/api.http b/api.http index ed304544..6b225a1d 100644 --- a/api.http +++ b/api.http @@ -10,19 +10,3 @@ GET https://ep-reisen.ddev.site/api/hotel &key=abcabc123 ### - -### GET chatbot content manifest (cmsApiKey in extension configuration) -GET https://ep-reisen.ddev.site/api/content/manifest -X-Api-Key: abcabc123 - -### GET chatbot content records (type: product, hotel, region, country, city, concept, news; at most 100 uids) -GET https://ep-reisen.ddev.site/api/content/records - ?type=product - &uids=1,2,3 -X-Api-Key: abcabc123 - -### GET the content-only render of one page (use a contentUrl from the manifest) -GET https://ep-reisen.ddev.site/skireisen/infos/skiurlaub-infos/schneepchen/liegestuhl?type=1730 -X-Api-Key: abcabc123 - -### diff --git a/content-feed.http b/content-feed.http new file mode 100644 index 00000000..162b1fc4 --- /dev/null +++ b/content-feed.http @@ -0,0 +1,141 @@ +# Chatbot content feed (see .ddev/plans/CHATBOT-MCP-01-TYPO3-CONTENT-FEED.md) +# +# Pick the "local" environment in PhpStorm and run the whole file top to bottom: the manifest +# request stores uids and a content URL that the later requests use. +# +# Needs, locally: +# - http-client.private.env.json with {"local": {"cmsApiKey": ""}} (gitignored) +# - the same key in TYPO3, e.g. +# ddev exec vendor/bin/typo3cms configuration:set EXTENSIONS/ep_products/cmsApiKey +# plus contentFeedSites etc. - or open and save the ep_products extension configuration once +# in the backend, which writes all the defaults. +# +# The kill switch (contentFeedEnabled = 0) cannot be toggled from here; with it off, every keyed +# request below answers 503. + +### Manifest without a key is refused +GET {{host}}/api/content/manifest + +> {% + client.test("403 without key", function () { + client.assert(response.status === 403, "expected 403, got " + response.status); + }); +%} + +### Manifest +GET {{host}}/api/content/manifest +X-Api-Key: {{cmsApiKey}} + +> {% + client.test("manifest is complete and well-formed", function () { + client.assert(response.status === 200, "expected 200, got " + response.status); + client.assert(response.body.items.length > 0, "manifest is empty"); + client.assert(response.body.sites.length > 0, "no sites"); + client.assert( + response.body.items.every(function (item) { return item.type && item.uid && item.modified; }), + "an item lacks type, uid or modified" + ); + }); + + var uidsOf = function (type, limit) { + return response.body.items + .filter(function (item) { return item.type === type; }) + .slice(0, limit) + .map(function (item) { return item.uid; }) + .join(","); + }; + + client.global.set("productUids", uidsOf("product", 5)); + client.global.set("hotelUids", uidsOf("hotel", 5)); + client.global.set("regionUids", uidsOf("region", 5)); + client.global.set("newsUids", uidsOf("news", 3)); + + var page = response.body.items.filter(function (item) { return item.type === "page" && item.contentUrl; })[0]; + client.global.set("contentUrl", page.contentUrl); + + var tooMany = []; + for (var i = 1; i <= 101; i++) { tooMany.push(i); } + client.global.set("tooManyUids", tooMany.join(",")); +%} + +### Products (with FAQs) +GET {{host}}/api/content/records?type=product&uids={{productUids}} +X-Api-Key: {{cmsApiKey}} + +> {% + client.test("every requested product comes back, links resolved", function () { + client.assert(response.status === 200, "expected 200, got " + response.status); + client.assert(response.body.items.length === client.global.get("productUids").split(",").length, "products missing"); + client.assert(JSON.stringify(response.body).indexOf("t3://") === -1, "unresolved t3:// link"); + }); +%} + +### Hotels (codes include matchcode aliases) +GET {{host}}/api/content/records?type=hotel&uids={{hotelUids}} +X-Api-Key: {{cmsApiKey}} + +> {% + client.test("hotels carry their codes", function () { + client.assert(response.status === 200, "expected 200, got " + response.status); + client.assert(response.body.items.every(function (hotel) { return Array.isArray(hotel.codes); }), "hotel without codes"); + }); +%} + +### Regions +GET {{host}}/api/content/records?type=region&uids={{regionUids}} +X-Api-Key: {{cmsApiKey}} + +### Blog articles +GET {{host}}/api/content/records?type=news&uids={{newsUids}} +X-Api-Key: {{cmsApiKey}} + +### Unknown type is a 400 +GET {{host}}/api/content/records?type=page&uids=1 +X-Api-Key: {{cmsApiKey}} + +> {% + client.test("400 for unknown type", function () { + client.assert(response.status === 400, "expected 400, got " + response.status); + }); +%} + +### More than 100 uids is a 400 +GET {{host}}/api/content/records?type=product&uids={{tooManyUids}} +X-Api-Key: {{cmsApiKey}} + +> {% + client.test("400 above 100 uids", function () { + client.assert(response.status === 400, "expected 400, got " + response.status); + }); +%} + +### Content-only render of the first page in the manifest +GET {{contentUrl}} +X-Api-Key: {{cmsApiKey}} + +> {% + client.test("bare article, never cached by proxies", function () { + client.assert(response.status === 200, "expected 200, got " + response.status); + client.assert(response.body.indexOf("
") === 0, "not a bare
fragment"); + client.assert(response.headers.valueOf("Cache-Control") === "private, no-store", "unexpected Cache-Control"); + }); +%} + +### Content-only render without a key is a 404 (the type must not be discoverable) +GET {{contentUrl}} + +> {% + client.test("404 without key", function () { + client.assert(response.status === 404, "expected 404, got " + response.status); + }); +%} + +### Content-only render with a wrong key is a 404 +GET {{contentUrl}} +X-Api-Key: definitely-wrong + +> {% + client.test("404 with wrong key", function () { + client.assert(response.status === 404, "expected 404, got " + response.status); + }); +%} diff --git a/http-client.env.json b/http-client.env.json new file mode 100644 index 00000000..7eb75191 --- /dev/null +++ b/http-client.env.json @@ -0,0 +1,8 @@ +{ + "local": { + "host": "https://ep-reisen.ddev.site" + }, + "staging": { + "host": "https://staging.ep-reisen.de" + } +}