Apache Sling Resource Mapping in CQ 5.6.1 - aem

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..

Related

CDK BasePathMapping for existing custom domain is created but doesn't work

This is all done using CDK.
I created a REST API and custom domain associated with it via a base path mapping (domain.addBasePathMapping()). That worked fine.
Due to some requirement, I also need to redirect a particular path from another custom domain (I'll call this the old domain) to this api. In theory this should be straightforward - just create a base path mapping from the old domain to the new API.
This is how I tried doing it:
const domain = DomainName.fromDomainNameAttributes(this, 'oldDomain', {
domainName: 'the old custom domain name',
domainNameAliasTarget: 'the "API Gateway domain name" value from the console for that domain',
domainNameAliasHostedZoneId: 'the "Hosted zone ID" value from the console for that domain',
});
new BasePathMapping(this, 'myMapping', {
domainName: domain,
restApi: this.api,
basePath: 'foo',
});
First I created a DomainName object by looking up the old domain, then created a mapping to my new API with some path. Note that I cannot call addBasePathMapping() on the domain name created, as that method returns an IDomainName which doesn't have that method.
When I ran this, it created the base path mapping in the old custom domain, pointing to my new api, correct stage, specified path. Great!
Except it didn't work. Invoking [old domain]/foo/bar (where bar is the resource path in the new API) returned 404.
The strange thing is that when I create that mapping manually via the console, it works perfectly.
Another weird thing is that if I create it via CDK, and then edit it in the console, it starts working. If I then delete it (manually or via CDK) and then create it again via CDK, it continues to work. But of course this isn't a proper solution.
I can only assume that creating it manually performs some extra operation not done via the CDK construct, but as the docs don't say what else may need to be done, I have no idea what.
The solution is to use the CfnApiMapping construct from aws-cdk-lib/aws-apigatewayv2. In fact this is a lot easier as you don't need to get the hosted zone id etc, just pass it some readily available information and it creates a base path mapping that actually works:
new CfnApiMapping(this, 'myMapping', {
apiId: this.api.restApiId,
domainName: 'old custom domain'
stage: this.api.deploymentStage.stageName,
apiMappingKey: 'foo',
});
I should warn that this comes with strange behaviour.
First an overview of my setup:
The old api has the following path on it: [old api]/foo/bar. The old custom domain is mapped straight to the old api with no path, so the old endpoint url is [old custom domain]/foo/bar. The new endpoint is [new custom domain]/bar. In order for the old URL to map to the new api, I need a base path mapping for foo on the old custom domain to point to the new api, so that [old custom domain]/foo/bar will be directed to [new api]/bar. (Note there are no other resources on foo and nothing new will be added, so this is fine.)
So currently calling [old custom domain]/foo/bar invokes the /foo/bar path on the old api. Once I deploy the CfnApiMapping resource, calling that same URL invokes the correct path on the new api.
Weird behaviour 1: If I delete that base path mapping, I would expect it to go back to the original api. Instead I get a 403 error. If I create it again, it resolves to the new API again, and deleting it again gives the 403 error again.
Weird behaviour 2: If instead of deleting it, I change the path value so it no longer maps "foo", the /foo/bar path works with the old endpoint again. I can then delete the mapping and everything keeps working fine.
Weird behaviour 3: I am unable to recreate this as I can't remember which sequence of steps I took, but it happened a couple of times where I deleted the base path mapping and it continued to work as if the mapping was still there. There was no mapping visible in the console, and I gave it plenty of time for the change to take effect, but it continued to work.
All this is done with the CDK, not manually. Doing this manually or via regular cloudformation works with no issues.

How do you use the serviceNameFilter when calling QueryClient.GetServiceListAsync

I'm using Azure Service Fabric with stateless services. I have a list of services deployed under an application, and there's a naming convention used with those service names. I'd like to get a list of services that match a filter expression.
Here is a link to a screenshot of my service fabric explorer. I don't have the reputation points to post an image.
Service Fabric Explorer screenshot
In this example, the name of my application is SFApp1, and the name of my service is HelloWorldStateless. I'd like to query the service fabric cluster to locate all services with the name "HelloWorldSt*" (under the SFApp1 application of course).
I know I can query to find all services with the application name "fabric:/SFApp1", and it'll return all services under that application. This overload of GetServiceListAsync takes just an application URI.
FabricClient client = new FabricClient();
ServiceList serviceList = client.QueryManager.GetServiceListAsync(new Uri("fabric:/SFApp1")).Result;
I also know I can query to find a specific service. This overload takes an application URI AND a service URI and will return a single-item list.
FabricClient client = new FabricClient();
ServiceList serviceList = client.QueryManager.GetServiceListAsync(new Uri("fabric:/SFApp1"), new Uri("fabric:/SFApp1/HelloWorldStateless")).Result;
What I'm trying to find out is if there's any way to do something like a wildcard search.
FabricClient client = new FabricClient();
ServiceList serviceList = client.QueryManager.GetServiceListAsync(new Uri("fabric:/SFApp1"), new Uri("fabric:/SFApp1/HelloWorldSt*")).Result;
The name of the parameter where the service name is specified is serviceNameFilter, and the method returns a list. I'm wondering why they would return a list for this overload if the result was always going to be a single-item list. Also, the parameter name "serviceNameFilter" suggests (to me at least) that there's the ability to supply some kind of expression to narrow down your list.
Here's what I've tried already. I've tried the code above, where I chop off a few characters and put an asterisk. I've tried without the asterisk to see if it was a substring match. I've tried SQL-style, with a percent symbol. I've tried a question mark. All of those attempts returned an empty list.
My current workaround is just to ask for all services under that application, and I'll filter them on the client code end with a linq expression. That'll work, but I worry about performance if my list of services gets really big.
Would be nice if I could inspect the source code to answer this myself.
Is there a way to do what I'm trying to do, or am I just misinterpreting what "serviceNameFilter" means, and it just means you have to put the entire service URI that you're looking for?
Thanks for any help you can provide!
Unfortunately that API parameter is terribly named. It's not really a filter at all, it's just the name of the service (since there's no other query that just returns one service, this is how you "filter" from all the services in an application down to just one in particular).
The nearest thing to what you're looking for is EnumerateSubnames. It's not a wildcard search, but you can get all the names that exist "underneath" a given name (for example, all of the service names that exist within an application, or all names with some specific prefix). Depending on the structure of how you create your service names this could work for you.
// System.Fabric.FabricClient.PropertyManagementClient
public Task<NameEnumerationResult> EnumerateSubNamesAsync(Uri name, NameEnumerationResult previousResult, bool recursive)
For example: Presume the following names exist in the cluster:
fabric:/SomeApplication/Zone1/Service1
fabric:/SomeApplication/Zone1/Service2
fabric:/SomeApplication/Zone2/Service1
Note that in this case the application would have been created with the name "fabric:/SomeApplication" and then the services with the detailed names above incorporating the "Zone" segment.
If you now EnumerateSubnames("fabric:/SomeApplication/Zone1", null, true) you'd get back a result that gave you the full names that matched (1 & 2 above).

AEM resource resolver failing

I am working on the AEM resource resolver and I currently have the following configuration on the Adobe Sling Resource Resolver Factory.
/content/mywebsite/>/
I have this a tag being generated from a component that looks something like:
<a data-desktop='/content/mywebsite/desktop.html'
data-android-href='/content/mywebsite/android.html'
data-ios-href='/content/mywebsite/mywebsite/ios.html'
href='/content/mywebsite/normal.html'>Click here</a>
This should have ideally been resolved to something likeL
<a data-desktop='/desktop.html'
data-adroid-href='/android.html'
data-ios-href='ios.html'
href='/normal.html'>Click here</a>
The irony is the last href in the above a tag is also not resolved by the resource resolver and I'm still getting the unresolved URL on dispatcher.
All the other resource resolver for a tags with just <a href='/content/mywebsite/something.html></a>' gets resolved. I don't understand why the resource resolver does not pick up the one with multiple data elements.
Are you sure it depends on the number of attributes? It looks like your configuration only affects resource resolution and not the outgoing mapping. The rule will be applied for incoming requests so when the user hits your AEM instance with /something.html, the Resource Resolver will look for the resource at /content/mywebsite.something, as well as other paths mapped to the root (if any) but as far as I understand, it will not affect the way AEM renders links to that content.
Try changing your rule so that it uses a two way mapping: /content/mywebsite/:/
To quote the description of the URL Mappings field in the Apache Sling Resource Resolver Factory configuration in the OSGi console:
List of mappings to apply to paths. Incoming mappings are applied to request paths to map to resource paths, outgoing mappings are applied to map resource paths to paths used on subsequent requests. Form is <internalPathPrefix><op><externalPathPrefix> where <op> is ">" for incoming mappings, "<" for outgoing mappings and ":" for mappings applied in both directions. Mappings are applied in configuration order by comparing and replacing URL prefixes. Note: The use of "-" as the <op> value indicating a mapping in both directions is deprecated. (resource.resolver.mapping)
You can easily test the configurations you setin the Apache Sling Resource Resolver Factory using the Configuration Test field on the Resource Resolver page in the OSGi console. You'll find it at http://<host>:<port>/system/console/jcrresolver
Use the Resolve and Map buttons to se.e how the path is transformed both ways.
Also, if you're getting inconsistent behaviour between components, check if they both map the URLs using resourceResolver#map if not covered by the Externalizer already.

Can I add multiple servlets to a WebAppContext?

I have the following Scala code to setup a Jetty server with Scalatra.
val server = new Server(8080)
val context = new WebAppContext()
context.setResourceBase("visualization")
context.addServlet(new ServletHolder(new CallTreeServlet(dataProvider)), "/*")
context.addServlet(new ServletHolder(new DataLoadingServlet(dataProvider)), "/*")
server.setHandler(context)
My problem is that it seems to work only if I register a single servlet.
If I register more than one, like I do in the code I posted, it loads only one of them.
Is it possible to load multiple servlets? I guess it is, but I can't figure out how.
If I try to load a page from the first servlet I got this error message that references only pages belonging to the second servlet:
Requesting "GET /callTrees" on servlet "" but only have:
GET /components
POST /load
POST /searchCallTrees
POST /selectPlugIn
To troubleshoot this, you should verify the servlet lifecycle. One convenient way to do this is to peruse the servlet container's logs to see what it reports while starting up the web application. It should tell you about each web app ( servlet context ) and each servlet . . .
However, I think I see what your problem is. Your servlet path mappings are kind of funky. It looks to me that you are mapping both servlets to receive ALL requests. This can't work, from a practical point of view, and might not work in terms of the servlet rules. From the servlet specification:
SRV.11.2
Specification of Mappings
In the Web application deployment descriptor, the following syntax is used to define
mappings:
• A string beginning with a ‘/’ character and ending with a ‘/*’ suffix is used
for path mapping.
• A string beginning with a ‘*.’ prefix is used as an extension mapping.
• A string containing only the ’/’ character indicates the "default" servlet of
the application. In this case the servlet path is the request URI minus the con-
text path and the path info is null.
• All other strings are used for exact matches only.
I suggest you make them both unique. As it looks now, you have them both at "/*" which is kind of like the "default servlet", but not . . .
Why not try "/first/" and "/second/" as a sanity check. Then move from there toward getting the configuration how you like.

Cannot use REST comments in Swagger

I have downloaded swagger ui and experimenting it locally. It works fine in scenarios like "path", "body" , and "query" . But most of my use cases use rest comments.
i.e /resourcePath/;tags
URI to retrieve the tags of a specific resource.
When I try this the the UI gets jumbled when adding the semi colon and malformed the sorted UI and cannot go beyond this.
So is this a known limitation ? Is there a workaround to accomplish this target ?
Appreciate any input to this..
Swagger is expecting you to specify path params in curly-brackets like {tags} and query params as comma-delimited, such as id=1,2,3,4. Some frameworks use semi-colons as delimiters but swagger likes commas.
Can you describe more what you're looking to do, with a more concrete example? Per design, comments on the api belong in the description and notes fields for operations, please see swagger-core wiki for details.
The Swagger codegen project has a validator which can be used to verify that your spec is properly formatted.