Closed elkmod closed 3 years ago
Hello @elkmod :) we spent some time on preparing the headless endpoints.
CC: @mkucmus @patzick
Please find all the proposed interfaces here: https://github.com/DivanteLtd/shopware-pwa/tree/feature/107-rfc-for-page-content-resolving-endpoint/rfc and inside that pull request https://github.com/DivanteLtd/shopware-pwa/pull/110
Resolver.ts This is an entry point to the Page Content Resolve Endpoint. We may call it with URL path, PageCriteria and sw-context-token.
PageCriteria may contain all the filterings / sortings, that will be used to filter the data inside the "data" in the response. This way we will be able to use Page Resolving Endpoint for getting all the data for the given page filtered and sorted up to the needs of the user.
ResolverInterface.ts This is an interface of a response, that we would love to get. In the response, we may receive Category Page, Product Page or CMS Page. Response for the specific page contains:
If we go into the data object
, we may get one of the following structures:
Our initial recommendation for the data fields in each data object
type (Category, Product for Category Page, Product for Product Page) is available in https://github.com/DivanteLtd/shopware-pwa/tree/feature/107-rfc-for-page-content-resolving-endpoint/rfc/Resolver. We tried to add as few fields as possible to make the responses as small as possible, just like we would use includes
parameter.
Data object should contain only products / categories, that meet the criteria given in PageCriteria parameter. It shoudln't contain products and categories, that are not active or not visible for the storefront.
We also came up with a few questions: https://github.com/DivanteLtd/shopware-pwa/blob/feature/107-rfc-for-page-content-resolving-endpoint/rfc/ResolverQuestions.md. The biggest risk in terms of provided question seems to be associatiod with using the cmsPages and the question number 5.
This is very important step. We think, that would be very helpful to have a call on that. Please, let us know if recommendation of the solution looks reasonable. : )
Hey guys, thank you for the profound analysis and the examples you prepared.
Having a path and a criteria parameter is a solid approach. I would even generalize as far as having only two types of pages:
Navigation Page
Contains either a list of products and content or really just content. Content and products are handled equally as we are thinking in "sections", "blocks" and "slots" which are populated with content (which may be static or dynamic).
Ultimately, a product listing is just a content block of type product-listing
which contains product data (and of course the search configuration to highlight filters/sortings etc.). And something that needs some sort of criteria
to configure the type of results you want to see.
One thing still left unresolved in this context would be how to deal with search result pages (as those usually just contain a result listing). However, users might be able to populate them with more data at some point. I will try to come up with a solution for that (it may lead to one of two things - either have a separate endpoint for search results - or add a flag to the request to indicate you're requesting a search result and not a navigation page.)
Product Page
Contains one product along with all its information, but will at some point be fully individualizable as well. Currently the CMS part is only "prepared" within products.
Please let me know your thoughts on that.
@chrissor
I will answer your detailed questions seperately.
Questions copied from the ResolverQuestions.ts file.
How will CMS pages behave? In exactly the same way as category pages? Will CMS pages get exactly the same route name, which is "frontend.navigation.page"?
Will this behavior of CMS pages change in the future?
How would /cms-page endpoint work in comparission with /category endpoint?
What kind of Media do you include in Category entity as Category.Media field? Is it some thumbnail of a category?
In Category entity there is a cmsPage field? How shall we use that? We are not 100% about its structure. Shall Category contain a CmsPage or CmsPage should contain a Category?
Would it be possible to get only active / visible categories and products? Only the data that the storefront needs to display.
Right now breadcrumbs are
"breadcrumb": ["Catalogue #1", "Clothing"]
would it be possible to get the breadcrumbs objects with links included? Like below:
"breadcrumb": [
{
name: "Man",
path: "/man"
},
{
name: "Shirts",
path: "/shirts"
}
]
8.In terms of getting the Product data in Category Page response, which is ProductForCategoryPageHeadless.ts
. Could we get ListingPrices for currencyId and ruleId, which apply to the current sw-context-token
only? If prices from-to would be exactly the same then we can display exact price instead of a range in Category Page. All the price objects are pretty complex in Product entities but we are not sure if we need all of these. Example of the prices returned with Product objects to Category Page below:
"listingPricesForHeadless":
{
"from": {
"net": 59.03,
"gross": 70.25,
},
"to": {
"net": 670.04,
"gross": 797.35,
},
}
What are the differences between prices, calculatedPrice, calculatedPrices, listingPrices within' a product entity?
What are the placeholder types which can be used in "Product Page" layout? Some kind of data mapping (SW admin panel naming convention).
When resolving a Product Page for not-active products can we return an error code instead of returning a product? From headless perspective we should not be able to access this product as it is not active.
In terms of stock we left only Product.available
field. We are not sure if we need exact stocks and available stock in the frontend layer within' the Product Page. That would be useful to create a separate endpoint for /checkProductStock?quantity=5 and in result we would get a response saying "yes, you can buy that quantity of that product". Does it sound ok?
When receivina a cmsPage, will we get a mustache placeholders or will we get a final content filled with exact values just to display?
How shall we use Product.children
, Product.options
, Product.properties
in the Product Page? Which array to use for displaying the links to other variants of the product? Shall we use product.properties to display some table of the properties or we will get such table as CMS content? We came up with an idea in ProductForProductPageHeadless.ts
interface, that we might get options []
with all the variants, that end-user can go to and "available" boolean to mark if that variant is available right now. This way even when entering a Simple Product Page (Option in Shopware terminology) we would get all the required data to display the other variants of that product without the need of calling additional endpoint of a parent product to get all the variants.
What is Product.sortedProperties
field? We found only nulls in that field. Is it something important for us?
What is product.displayGroup
field? Is it something important for us?
During the workshop we had a short discussion associated with fetching the navigation. One way that we see to display the navigation in the frontend is to use /category
endpoint to get the whole structure of categories, external links and structing elements. However, this endpoint is pretty big. Right now we are not 100% sure what would be the most efficient way to fetch the navigation / menu. How do you currently solve that in Shopware? What's the best way to fetch it in a light way? Assuming the eCommerce may have even more than 1000 categories we would love to find the way of fetching not only everything at once but also sub-menu or level only.
Answers below. Please follow up on questions 6, 10 :)
As proposed above, I would consider CMS pages and category pages the same entity, hence they would also be associated with the same route name frontend.navigation.page
(which might change later on).
We've already introduced the additional structural layer of sections which adds the ability to group two columns of blocks in one section. Further on, multiple pieces of the page will be separated into dedicated blocks. The current iteration of SW enables you to place your category navigation and listing filters as blocks on your page.
With the intended implementation, both will become unused and replaced by the /page
endpoint.
It is an image that's intended to be shown next to first-level-categories within the dropdown-navigation. See example by hovering 'Mountain Air & Adventure
See 3. for this question. The /page
endpoint will be providing all the data based on the path you've queried before. E.g. querying a category page will yield a result including a cmsPage
object, whereas querying a product page will yield a result including a product
object. (Please note, that I put this on discussion with you and with my colleagues internally)
It should be doing this already - if not, could you provide an example/path to reproduce?
Yes, can we make this as a separate issue?
Even though the question is really reasonable, it is a pretty big topic internally, which we have to work on. It comes down to some preprocessing we have to do in order to minify the responses. This is currently handled in a generic way which doesn't allow for a lot of interference without breaking anything.
Until we've got the issue above sorted I'll try to get an answer for that. For immediate help refer to our existing storefront templates (CMS elements and product pages).
Could you specify that question? Are you referring to the placeholder types which can be associated to a category page just for product pages?
Yes, this should be doable, I'll add this to the API spec.
Please also add an issue for that. We will check whether that's something we can deliver. The cart will return an error, if the product is not available in the desired quantity.
You will get the fully populated page and no placeholders.
That's a topic we have yet to solve as well. Currently this is implemented using a "variant switch" endpoint which gets the configuration of options that yield a specific variant and returns a link to the corresponding variant. Would that work for you as well?
It is possible to sort properties, so after fetching products Shopware will create a sorted list of properties (if there are ones) but without overwriting the existing properties field.
The displayGroup
is internally used to group variants for a parent product. All variants of the same parent product share the same displayGroup
value whereas the parent product itself has displayGroup = NULL
. You shouldn't be concerned with that unless you want to group variants in listings on the client side.
This should be a separate issue as well.
Ok! This week we started digging into CMS pages because we couldn't postpone its implementation anymore so we got better understanding of that. THank you!
Clear. @mkucmus became Shopware Page Builder Guru on our side and he played around with that. In case of any questions we may discuss at during the call.
We spoke about that with @patzick and initially I had the same understanding. However, understanding of Patryk is a little bit different in terms of any data loaded after time-to-interact with the page. We may discuss that during the call.
Clear! :)
Ok!
We will try to find that and also check in Shopware code. We added that question not because we found some products, which are not active but because in the response we saw "activie" field. In fact, that was always false but created a though in our minds - if we're getting that field then it might be false and we would prefer to get only trues. If an endpoint returns only trues then it is perfect. : )
Ok! Clear! :) https://github.com/elkmod/SwagVueStorefront/issues/16 https://github.com/elkmod/SwagVueStorefront/issues/17
Ok!
The question is associated with Page Builder and variables that we may place in the CMS page. @mkucmus help :) we may discuss that during the call.
Thank you!
That surprised us a lot! :) Thanks! We've already been thinking about the ways how to populate CMS pages with the data. : )
@mkucmus already implemented some way to solve that. We may show that during the call. : )
Ok!
Ok!.
@elkmod I couldn't find the way to get the media information related to the slots when I call the /page endpoint.
Is there any way to get the media urls or any details which can be used to render the proper media item on the frontend?
the only thing we've got during the requests is mediaId included in slot.config.media.value, but there is no other endpoint for sales-channel to ask for the detailed info.
"type": "image",
"slot": "left",
"block": null,
"blockId": "44dd4bf84fb14280ae69104d0a36b89f",
"config": {
"url": {
"value": null,
"source": "static"
},
"media": {
"value": "86e7e69a414143ee8f07e6f4d1124726",
"source": "static"
},
maybe the deeper hydration mode on block/slots' level would be enough to get the details in the same request. what do you think?
I'll try to take a look later today. Is it the same for every block you're using or only for specific blocks? @mkucmus
@elkmod in every block where the usage of media is possible, where reference to media exist.
Those types:
in each one, where we have media slot and reference to media by id (it's definitely not enough due to lack of extra media endpoint)
As of today's commit (#c2969b8 - Added route params to fetch correct category and added category image)
So I've tested for product-box
where the image URL for a slot should be available via data.cover.url
. The other ones should follow analogously.
For categories and mappings, they should be hydrated automatically for every request.
Please let me know if there are any issues.
(I also detected that the category path was not resolved correctly internally, so you wouldn't get the corresponding products, but instead all the products)
I just checkout this revision on test server and it worked well! Besides the config object with the id only, we've got those media resolved in data node with proper urls to the images, so I think It's done right and efficient as well. Thank you, @elkmod :+1:
"data": {
"media": {
"userId": "e7fcae1311294d9ea3b8973909529b5f",
"mimeType": "image/png",
"fileExtension": "png",
"fileSize": 592310,
"title": null,
"metaData": {
"type": 3,
"width": 1239,
"height": 584
},
"mediaType": {
"name": "IMAGE",
"flags": [
"transparent"
],
"extensions": []
},
"uploadedAt": "2019-10-29T10:39:07+00:00",
"alt": null,
"url": "https://shopware-2.vuestorefront.io/media/6d/48/de/1572345547/bannerH.png",
"fileName": "bannerH",
"user": null,
"translations": null,
"categories": null,
"productManufacturers": null,
"productMedia": null,
"avatarUser": null,
"thumbnails": [
{
@elkmod we decided how could the information about the filtering and sorting look like, and here's the RFC prepared: https://github.com/DivanteLtd/shopware-pwa/pull/148
In a couple of words: we need two extra fields next to "cmsPage", avaialbleSorting
and availableFiltering
within the response like that:
{
availableSorting: Sort
availableFiltering: Array<OptionFilter | RangeFilter | BooleanFilter>
cmsPage: CmsPage | null
}
all the interfaces used in example you can see in https://github.com/DivanteLtd/shopware-pwa/blob/master/rfc/Resolver/CategoryForHeadless.ts
@elkmod make sure to provide page resolving for technical routes (/navigation/{uuid}
) as well.
@mkucmus - just to make sure.
@elkmod I confirm.
There is one thing that would be great to have as well:
seoPathUrl
, or seoPath
- pretty links included to Product entity globally: within hydrated products' collection on /vsf/page (for category / navigation ) and /product itself.
pretty URL should be properly generated according to the current context.
Resolve /
as default-URL as well. @elkmod create default case within page resolving (to the likes of homeAction
within the Storefront NavigationController)
There should be an endpoint which provides content for the routes which are resolved. Similar to the PageLoader concept in the Shopware Storefront.
Motivation: We want to be able to provide both, route lookup and content fetching within one API request.