Repository navigation
feat: add the product index, attribute values and category upsert endpoints - #1
Merged
Merged
Conversation
GET /V1/dagster-bridge/products/index?after=&limit= answers the identity of every product without the cost of the REST collection: entity id, sku, type_id, attribute_set_id, the store 0 status and updated_at, ordered by entity id and paged with a cursor instead of an offset, so a product deleted between two pages cannot make a client skip another one. The status value is joined through the MetadataPool link field, which is entity_id on Open Source and row_id on Commerce with staging, and only for store 0. The limit is validated against the contract's maximum of 20000 and rejected with an InputException naming it. The page annotation carries the fully qualified item interface, because the web API output processor reads that return type literally and does not resolve short names through the file's use statements. Verified: PHPUnit 14 tests and 113 assertions green, phpcs clean, PHPStan level 6 clean, and live on Magento 2.4.9 a limit=3 walk followed next_after through eight pages, returned all 21 products exactly once with type_id and status on every item, and ended on an empty page with next_after null. The route answers 401 without a token, naming the read resource.
POST /V1/dagster-bridge/products/attribute-values answers, for many products
at once, the value an attribute holds in a given store and the value it holds
in the default store:
{"skus": ["sku-1"], "attribute_codes": ["name", "price"], "store_id": 2}
One item comes back per requested pair, including pairs with no value at all,
so a caller can tell "no value" from "not asked", and the two values come back
separately so it can apply Magento's own fallback instead of guessing. Values
are read from the tables the EAV backend_type names, one query per table, and
the product join goes through the MetadataPool link field rather than a hard
coded entity_id. Static attributes live as columns of the product row and are
read from there, with no store value, because they exist once for the whole
catalog.
A call carries at most 1000 SKUs and 50 attribute codes; over that it answers
an InputException naming the limit instead of truncating, and an unknown code
is reported with every unknown code listed. The route carries the read
resource, and the item array annotation is fully qualified because the web API
output processor reads it literally.
Verified: PHPUnit 26 tests and 160 assertions green, phpcs clean, PHPStan
level 6 clean, and live on Magento 2.4.9 a store scoped name saved through
/rest/fr came back as store_value with the default beside it, while price,
which has no store value, came back with its default only. Magento omits null
keys from the JSON, so a client reads a missing store_value as null.
POST /V1/dagster-bridge/categories/upsert turns category paths into category
ids and creates what is missing:
{"paths": ["Default Category/Men/Tops/Hoodies"], "separator": "/"}
It answers every requested path with its id, whether the category was created
now or already existed, and the whole call runs in one transaction, so a
failure rolls back and the answer names the path that failed. This is the only
endpoint of the module that writes, and the only one behind the write
resource.
The native CatalogImportExport processor does the work when it can, because it
already knows how Magento builds a tree, and the paths are joined with a
delimiter chosen to appear in none of the level names. That processor uses a
slash as its own level separator, so a name that contains one cannot be
expressed through it: those calls walk the tree through the category
repository instead, parent first, and reuse what they find. A path that does
not start at the root gets the root prepended, and the path parser is a pure
class with its own tests.
The category object the repository saves comes from a factory this module
ships, because Magento's own CategoryFactory is generated: a standalone
install, which is what CI has, cannot see it, and neither can static analysis
or a unit test there.
Verified: PHPUnit 37 tests and 194 assertions green both through Magento's
unit bootstrap and standalone with the tooling CI installs, phpcs and PHPStan
level 6 clean in both trees, and live on Magento 2.4.9 the plan's check held:
the first call created four nodes and answered ids 16 and 17, the second call
answered the same two ids with zero rows added, and a path whose name contains
a slash ("Default Category|Tops/Tees" with the pipe separator) created exactly
one node. The README also carries the complete endpoint table now, which the
two earlier endpoint commits had left at the skeleton version.
The changelog records what this first release adds, endpoint by endpoint, and the README now explains which resource each endpoint needs and how to scope an integration to it: read for the capabilities probe, the product index and the attribute values, write for the category upsert, and nothing else.
The tagged line (`^1.0`) cannot work until a release exists, and the branch line does not work without minimum-stability/prefer-stable: the package is only visible through the VCS repository and only as a `dev-*` version. Both paths were run against a live sandbox, where the branch install resolved, installed under vendor/ddtcorex and reported the module enabled.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
DDTCoreX_DagsterBridge1.0.0: the optional Magento 2 module thedagster-magentoPython library can use. Four service contracts, each behind its own ACL resource,
plus a capabilities probe so a client degrades per capability instead of
all-or-nothing.
products.indexGET /V1/dagster-bridge/products/indexGET /V1/productsexistence scanproducts.attribute_valuesPOST /V1/dagster-bridge/products/attribute-valuescategories.upsertPOST /V1/dagster-bridge/categories/upsertPOST /V1/categoriesper missing nodeDesign decisions (review focus)
ResourceConnection, withevery EAV value join going through the
MetadataPoollink field (entity_idvs
row_id) and never a hard-codedentity_id; attribute tables come frombackend_type.after(an entity id, not anoffset), so a concurrent delete cannot shift rows under the client.
attribute_valuesanswersstore_valueanddefault_valueseparately andcaps one call at 1000 SKUs / 50 codes; unknown codes are rejected listing
every unknown code.
CategoryProcessor(
upsertCategoriesexists on both 2.4.6 and 2.4.9) and runs in onetransaction; it is the only writing endpoint and the only one behind the
writeACL resource.<Interface>Factoryexists only after the app has produced it once, and theunit tests, PHPStan and CI must not depend on that.
bitexpert/phpstan-magentoon purpose: amodule-only repo has no Magento application root, so local and CI must agree.
Verification
vendor/bin/phpcs --standard=Magento2clean,phpstan analyse --level=6no errors, PHPUnit
37 tests / 194 assertionsgreen, in the standalonerehearsal tree and inside both sandbox installs below.
resolves through the
vcsrepository, installs undervendor/ddtcorex/module-dagster-bridge, andbin/magento module:statusreports
DDTCoreX_DagsterBridge : Module is enabled. Installing from abranch needs
minimum-stability devandprefer-stableuntil a tagexists, which the README now says.
200 with all three capabilities; index walk returns every SKU exactly once
with
next_after: nullon the last page; attribute values match REST(store+default split verified, no mismatches); category upsert creates once
and reuses on repeat, including a name containing the separator.
use_bridge=requirelegs inddtcorex/dagster-magento).Access control
::readcovers the probe, the index and the values;::writecovers only theupsert. Create an integration with the read resource (or a role carrying it)
for read-only runs and add write when the run creates categories. An admin
token carries both.