AEM internal Redirect and Resolve nested content - aem

I have the below content structure.
site
- de
- category1
- 2001
- quarter1
- blog_about_vegan
- blog_about_flowers
- quarter2
- blog_about_something
- 2002
- quarter1
- blog_about_vegan
- blog_about_flowers
- quarter2
- blog_about_something
I want customers to use shorthand urls. For instance; Customer should get blog_about_vegan when he tries the following url : https://www.somedomain.com/site/de/category1/blog_about_vegan. He should not be worried about the year and quarter as they are just meant for categorization.
I have configured sling mappings in /etc/map to remap the url to search for content in /content. But i am not able to figure out how can I ask sling to look into all year and quarter folders for a particular category to find this article.
I am looking for something like a dynamic internal redirect with nested search capability. could you please advice

The Sling RequestResolver will NOT do any searching or querying. So you need to manipulate your URL (in the web-server), that it matches with a Servlet of you. Then your Servlet will query/resolve the remaining part of the URL and forward the rendering to Sling again.
I recommend to use the Sling-Suffix for category and article. So the URL in AEM would be http://localhost:4502/content/site/de.article-search.html/category1/blog_about_vegan. This is easily done in the web server (e.g. mod_rewrite). Then you register a servlet for the 'de'-resourceType and selector 'article-search'.
Then with request.getRequestPathInfo().getSuffix() you can find the article. If you find the page/resource, with request.getRequestDispatcher(...).forward(...) you can let Sling do the rendering.

Related

Apache Sling Resource Mapping in CQ 5.6.1

I've been taking several hours fiddling this Resource Mapping for no luck. I have put a new node in /etc/map/http named GoogleAnalyticsMap. It had the following properties:
Name: jcr:primaryType
Type: Name
Value: sling:Mapping
Name: sling:internalRedirect
Type: String
Value: /content/dam/website/Common/my_google_analytics_key.html
Name: sling:match
Type: String
Value: [^/]+/[^/]+/my_google_analytics_key.html
Funnily, when i checked to /system/console/jcrresolver or Adobe CQ Web Console Sling Resource Resolver. It resolves into the following
Pattern: ^http/[^/]+/[^/]+/my_google_analytics_key.html
Replacement: /content/dam/website/Common/my_google_analytics_key.html
Redirect: internal
I have no idea how they could register ^[^/]+/[^/]+/welcome$ as shown in the example and did not have the 'http' there. Nevertheless, whenever i try to hit the pattern locally (i.e. http://localhost:4502/my_google_analytics_key.html) it only shows resource not found. Also checked through the Configuration test section that available in Adobe CQ Web Console Sling Resource Resolver it says that it can't resolve. Did i miss something?
, type=sling:nonexisting, path=/my_google_analytics_key.html, resource=[NonExistingResource, path=/my_google_analytics_key.html]
Thank you for the help.
p.s. i've checked the Sling documentation also, however, it baffles me that i can't add sling:alias, perhaps its also something to do with the sling version. Nevertheless, i should ask this as second question later, once the sling:match is working properly.
Edit: fiddling a bit, changing the sling:match to localhost.4502/google.html does let it to redirect localhost:4502/google.html to the desired DAM item. Well, i'm still asking for 'domain-free' matching though.. so i have no trouble when using the mapping for all environment i have..
Edit, Updates:
Apparently due to me put the new sling:Mapping under http folder it will always prefix the path with http. Perhaps the example is meant to be put not under http folder.
Next item is, it will always assume the matching item will be protocols, domain, and port separated by dots (yes, even \. is not a valid input and CQ or rather sling will sanitize it somehow). Something like *.(4502|4503|80|443)/my_google_analytics_key.html will accept any domains and those designated ports. I do wonder if it really the case, since they says regex but it does not feel so much regex.
Some says that sling:internalRedirect should be a String[] - yes i know the docs says String. I have not test this further and take it as it is..

What is the proper URI scheme for RESTful creation of a tree of nodes?

I have a ProductInstance, which has zero or more parents, also ProductInstances. They are a tree. For example:
- ProductInstance VM
- ProductInstance RAID
- ProductInstance DISK
- ProductInstance CPU
I'd like my URI-scheme to create these, to be clean and RESTful. How should I set this up?
One scheme I came up with, is to nest the URI resources:
GET http://example.com/product_instances/new - HTML form
POST http://example.com/product_instances - Create a new resource
GET http://example.com/product_instances/1/children/new - HTML form
POST http://example.com/product_instances/1/children - Create new resource who's parent is product_instance 1.
The other scheme I thought of, is to provide params and re-use the "ordinary new and create":
GET http://example.com/product_instances/new - HTML form
POST http://example.com/product_instances - Create a new resource
GET http://example.com/product_instances/new?parent=1 - HTML form
POST http://example.com/product_instances/ - include parent=1 in payload.
Are there any standards for this pattern? Is there some rule or guideline that explains how to deal with tree-ed items in RESTful URI schemes?
Note: I focus on the new and create only, because I would say that the show, update/edit and delete are no different for items with and without parents, since they act on an already stored ProductInstance and therefore the parent is known and needs not to be provided in the URI or payload.

How to design complex update actions in REST API

I'm currently working on a REST API, trying to design it with most best practices as possible.
I work with Symfony2 PHP framework but some of my questions are valid for any REST API i guess.
Starting from the base design for a particular resource :
GET /resource - Get all resources
POST /resource - Create resource
GET /resource/{id} - Get resource with id={id}
PUT|PATCH /resource/{id} - Edit the resource with id={id}
DELETE /resource/{id} - Delete the resource with id={id}
Supposing my resource has complex rules while updating.
It has a "status" field, (a float for example), that can be updated only by following a particular scheme
It has a "schedule" field (a datetime), with different choices available that are not always the same
How am I supposed to expose those rules to the API consumer ? For the schedule field, how am I supposed to provide the different choices available at the current time ?
About the Symfony server-side part, I followed most of the recommandations of this walkthrough : http://williamdurand.fr/2012/08/02/rest-apis-with-symfony2-the-right-way/
My POST, PUT & PATCH actions are handled with Symfony Forms, so most of the rules are processed by Symfony constraints/validations features.
But form binding is quite limited, let's supposed I want to trigger a particular event if the user change the status field from 2 to 3? What is the best way to do that ?
Thanks in advance.
HTTP has another verb you aren't using: OPTIONS. You can use this to list the schedule options.
Here's a blog article about it: http://zacstewart.com/2012/04/14/http-options-method.html
As for updating the status, I would reuse POST and include an action in the field. Example:
POST
{
"type": "update",
"status": 3
}
Modified REST:
GET /resource - Get all resources
POST /resource - Create resource
GET /resource/{id} - Get resource with id={id}
PUT|PATCH /resource/{id} - Edit the resource with id={id}
DELETE /resource/{id} - Delete the resource with id={id}
OPTIONS /resource/{id} - Retrieve options of resource with id={id}
Keep in mind that you can pass params along in the body for everything but GET and you can pass any params in the URL for GET.
I have zero knowledge on Symfony2, so I'll just concentrate on your more generic REST how-to qustion about exposing rules.
Give the consumers of your REST API a documentation. It's the first thing they will hit before actually playing with your API. Use tools for that, from auto-generated help pages to 3'rd party providers like Apiary.io or alike.
Create meaningful responses when consumers send "wrong" requests: use correct http response status codes (Bad request, Conflict, etc.) when request parameters are missing or invalid.
If your REST api is relaxed, it can also include information about what went wrong and how to resolve the problem in the response body.
What worked well for me in the past was to have a generic ErrorMessage entity that was returned upon each non-successful request, containing a title, an error description, and a dedicated more technical "dev-description" which can be enabled/disabled for test/production on the server side.
In my case, my consumers all know that they can get either the expected response entity in case of success, or that generic ErrorMessage entity in the response in case of failure.
If you can desribe your rules, why not provide those as meta information for your service? Eg. in my case I know I have a set of parameters, each having a set of available options. Think of the parameters as the key in a query string, and the options as the values for that key. In a complex world, parameter options depend on other parameter options, eg. in my case the available options for parameter B are dependent of what option(s) are "selected" for parameter A. I can expose those dependencies by providing a "metadata" resource in my REST api, eg. a JSON stucture listing all parameters and all options for those parameters, and for each option adding a "requires" section desribing that that option is only "available" if parameter xy has selected option p and q.
This allows my consumers to - with a single request to that meta data resource - create a "state-machine" on the client side. I hope you get the picture.
Here is my understanding of REST-full way to handle updates and advertise update operations to API client.
This is based on this wonderful book and Fowler's article about REST with some additions of File Levels of Media Type and article about Restfull CQRS. Basically you use PUT for update and pass the operation via content type and advertise content type via mediaType in hypermedia controls.
All operations which are available for current state of your resource are listed among hypermedia controls which are passed with representation of resource like this:
<myresource>
<status>ACTIVE</status>
<some-field with-attribute="value"/>
<some-other-field/>
<!-- other fields representing state of resource -->
<link rel = "self"
uri = "/resource/1234"/>
<link rel = "/linkrels/resource/changeStatus"
uri = "/resource/1234"
mediaType = "application/vnd.myapp+xml;domain-model=ChangeStatusCommand"/>
<link rel = "/linkrels/resource/changeSchedule"
uri = "/resource/1234"
mediaType = "application/vnd.myapp+xml;domain-model=ChangeScheduleCommand"/>
<link rel = "/linkrels/help"
uri = "/help/resource"/>
</myresource>
Links together with mediaType gives enough information what command is allowed. In many cases this should be something very specific to current state of resource. For example if you can move it from status ACTIVE to TRASHED than command should be named not StatusChange but TrashCommand and so on.

Symfony2 Form Rest Api only add to Relation

I am using Symfony2 as Rest Api for a JS Frontend App. I came across a scenario where I want users to "invite" (=add) Users to a Group. But I want to only allow them to add Users to the existing Relation and not "overwrite" the whole relation, which is the standard behaviour in combination with a regular Symfony2 Form.
What would be the best practice to achieve this behaviour?
Additional Comment:
I am using Ember-Data in the frontend and my frontend would probably send a put request with the whole Group including additional users (but not all).
My JSON Payload would look something like this:
{
"usergroup": {
"name":"yxcv2",
"stake":"sdfghj",
"imageName":null,
"userCount":5,
"users":[
5,
6,
7
],
"gameGroup":"13",
}
}
In this scenario User 1,2,3 and 4 are already members of the group. And instead of replacing 1,2,3,4 with 5,6,7, I want to ADD 5,6,7 to the already existing members.
A LINK request should be used to add an item to an existing collection instead of overwriting it with a POST request.
Using a symfony form you'd post the User (id) plus a hidden field _method with value LINK to something like /groups/{id}.
routing would be something like this:
group_invite:
path: /groups/{id}
defaults: { _controller: YourBundle:Group:inviteUser }
methods: [LINK]
You could use FOSRestBundle's implicit resource name definition, too.
For the method override to work the config setting framework.http_method_override needs to be set to true ( = default value - available since symfony version 2.3).
More information can be found in the documentation chapter:
How to use HTTP Methods beyond GET and POST in Routes

Order of API endpoint declaration in Nancy Web Framework

I am using NancyFX to host our REST APIs for Web site. We have user table in database, which I would like to update for:
1) Full user update - updates all fields
2) Partial user update - updates only single field
We are using Nancy 0.7 - so currently it does not have PATCH support - I can only use PUT
I have defined my API like
PUT ["/user/{username}"] - for complete update using passed-in user object value
PUT ["/user/{username}/id/{newid}"] - for updating user id only
However, when I call the second API (to update id only) - it never gets trapped by Nancy - and Nancy always call the method to fully update user i.e. PUT ["/user/{username}"]
No matter, what order I declare the APIs, Nancy always call the full user update endpoint only.
Need help, so that I can use both APIs using PUT from our client applications properly.
In general, it is a good idea to UrlEncode any dynamic data components of your URI.
So, in your case:
PUT - /user/xyz#yahoo.com/id/123
would become
PUT - /user/xyz%40yahoo.com/id/123
Nancy will take care of decoding the value for you, so when you extract it from your parameters dynamic object it will be back to xyz#yahoo.com
Found the problem -
It is to do with '#' character in user name - special character.
if username contains '#' character then Nancy never matches the route for
PUT - /user/xyz#yahoo.com/id/123 to
PUT ["/user/{username}/id/{newid}"]
it always matches route for
PUT - /user/xyz#yahoo.com/id/123 to
PUT ["/user/{username}"]