How do you use the serviceNameFilter when calling QueryClient.GetServiceListAsync - azure-service-fabric

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

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.

REST Resource route naming for get and ResourceByID and ResourceByName

I am trying to write 2 Rest GET methods.
Get user by Id
Get user by userName.
I need to know if there is any resource naming convention for this. Both my id and username are strings.
I came up with:
/api/{v}/users/{userid}
/api/{v}/users/username/{username}
However, 2) doesn't seem correct and if I change 2) to /api/{v}/users/{username}, I am mapping to 1) as both id and username are strings. Or is it considered acceptable to use /api/{v}/userbyName/{username}?
How should I name my resource route in case 2) ?
First of all: https://vimeo.com/17785736 (15 minutes which will solve all your questions)
And what is unique? Is the username unique or only the id or both are unique?
There is a simple rule for all that:
/collection/item
However, 2) doesn't seem correct and if I change 2) to /api/{v}/users/{username}, I am mapping to 1) as both id and username are strings.
If your item can be identified with an id and also with an unique username - it doesn't matter if it's the username or the id - simply look for both (of course your backend needs to handle that) and retrieve it.
According to your needs this would be correct:
/api/{v}/users/{userid}
/api/{v}/users/{username}
but I would choose only to use: /api/{v}/users/{userid} and filter by username only with a query parameter (description for that down there below)
Also will I break any rules if I come up with
/api/{v}/userbyName/{username}
Yes - /api/{v}/userbyName/{username} will break the rule about /collection/item because userByName is clearly not a collection it would be a function - but with a real restful thinking api there is no function in the path.
Another way to get the user by name would be using a filter/query paramter - so the ID will be available for the PathParameter and the username only as filter. which than would look like this:
/api/{v}/users/?username={username}
This also don't break any rules - because the query parameter simply filters the whole collection and retrieves only the one where username = username.
How should I name my resource route in case 2) ?
Your 2) will break a rule - so I can't/won't suggest you a way to do it like this.
Have a look at this: https://vimeo.com/17785736 this simple presentation will help you a lot about understanding rest.
Why would you go this way?
Ever had a look at a javascript framework like - let's say ember. (Ember Rest-Adapter). If you follow the idea described up there and maybe also have a look at the json format used by ember and their rest adapter - you can make your frontend developer speed up their process and save a lot of money + time.
By REST you send back links, which can contain URI templates. For example: /api/{v}/users/{userid} in your case, where v and userid are template variables. Since the URI structure does not matter from a client perspective you can use whatever structure you want. Ofc. it is more convenient to use nice and short URIs, because it is easier to write the routing with them.
According to the URI standard the path contains the hierarchical while the query contains the non-hierarchical part of the URI, but this is just a loose constraint, in practice ppl use both one.
/api/{v}/users/name/{username}
/api/{v}/users/name:{username}
/api/{v}/users?name="{username}"
Ofc. you can use a custom convention, for example I use the following:
I don't use plural resource name by collections
I end collection path with slash
I use slash by reducing a collection to sub-collections or individual items
I don't use slash to give the value of a variable in the path, I use colon instead
I use as few variables and as short URI as I can
I use query by reducing a collection to sub-collections especially by defining complex filters with logical operators
So in you case my solution would be
/api/{v}/user/
/api/{v}/user/name:{username}
/api/{v}/user/{userid}
and
/api/{v}/user/?firstName="John"
/api/{v}/user/?firstName="John|Susan"&birthYear="1980-2005"
or
/api/{v}/user/firstName:John/
/api/{v}/user/firstName:John|Susan/birthYear:1980-2005/
etc...
But that's just my own set of constraints.
Each resource should have a unique URI.
GET /users/7
{
"id": 7,
"username": "jsmith",
"country": "USA"
}
Finding the user(s) that satisfy a certain predicate should be done with query parameters.
GET /users?username=jsmith
[
"/users/7"
]

Sharepoint URL to reference a lookup

I have two SharePoint lists used for Support case management. The first list contains Case Numbers and information about the case. The second list contains exhibits that support the case itself.
We have a convention that the Case Number is a String supplied by the worker, ex 20150205-001. When the exhibits are joined to the Case it is through a Lookup. I want the Exhibit ID, a String, to be of the form Case Number + _[A-Z] -- and be auto-assigned.
I want to use a Workflow (MS Sharepoint Designer 2013) to assign the Exhibit ID. The problem I face is that I cannot get the actual Case Number from the Lookup. The closest I have gotten so far is to get the ID (1, 2, etc) but not the actual String value represented.
Tried working with the following URL:
http://[mySiteURL]/_api/web/lists/getbytitle([listName])/items?$select=Title,Case/Id&$expand=Case/Id&$filter=Case/Id%20eq%2020150205%45001
substituted ascii: $filter=Case/ID eq 20150205-001
without the filter I get all list items (understandably) but the filter does not work properly because the ID is not the actual lookup value.
This is a SPD 2013 limitation. You have to use a web service call from within Designer to get the specifics of a lookup column from SharePoint. You make a REST call ad then parse the JSON response for the specific data from the lookup column. It gives you access to all of the columns from the list item that you looked up:
Build {...} Dictionary (Output to Variable: requestHeader )
Call [%Workflow Context:Current Site URL%]... HTTP web service with Variable: Request (ResponseContent to Variable: PoleIDData |ResponseHeaders to Variable: dictionary |ResponseStatusCode to Variable: responseCode )
Get d/Pole_x0020_ID from Variable: PoleIDData (Output to Variable: PoleID )
Set Name to Variable: PoleID
Your actual web service call will be formatted like this:
[%Workflow Context:Current Site URL%]/_api/web/lists/GetByTitle('List Name')/Items([%Current Item:ID%])/LookupColumnNameOnOtherList
Sorry for the formatting, I would post a screenshot but I cannot.
This article is good for showing you some of the other specifics about formatting your HTTP Request, especially the Request Headers which must be setup right.
http://www.fiechter.eu/Blog/Post/37/SharePoint-2013--HTTP-Web-Service-Action---Use-Managed-Metadata-as-Text-in-Workflow

Performing Sticky load balancing in Camel

Hi I would like to perform sticky load balncing in apache camel based on the SOAP session, whcih is embedded in ServiceGroupID node of the first response.
I wrote a small route as follow:
from(uri)
.loadBalance().sticky(xpath(query).namespaces(env).namespaces(wsa).namespaces(ax))
.to(BE1,BE2);
Where URI is the string to which the requests are passed and BE1 and BE2 are the two backend servers.
And my query is
String query = "/soapenv:Envelope/soapenv:Header/wsa:ReplyTo/wsa:ReferenceParameters/axis2:ServiceGroupId/text()";
If i am not wrong this query would extract the servicegroupID from my SOAP header.
But when I try to perform the balancing, due to some reason whatsoever, the requets are not being passed to the same backend server.
and my env, wsa and ax are the namespaces, which are :
Namespaces env = new Namespaces("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
Namespaces wsa = new Namespaces("wsa", "http://www.w3.org/2005/08/addressing");
Namespaces ax = new Namespaces("axis2", "http://ws.apache.org/namespaces/axis2");
Am I doing something wrong here?
If so what? I would appreciate any help.
Also being discussed at the mailing list
http://camel.465427.n5.nabble.com/Performing-Sticky-load-balancing-in-Camel-tp5719170.html
Please dont start the same topic in multiple places at the same time. And if you do, then at least tell us, so we would know this.
People get upset when they spend time to help you, when its already been answered in another place!

Can I use paging when getting service identities from ACS

Background
I require a list of all the service identity names I have registered in the Azure ACS. I have an Azure Management Service reference I got from https://myaccesscontrol.accesscontrol.windows.net/v2/mgmt/service. The “myaccesscontrol” prefix is arbitrary, for this discussion. You could use a different subscription namespace prefix and get the same results, if I understand correctly. This is the service endpoint that Azure gives me when I subscribe. It exposes a ManagementService interface. When I get a list of service identities
DataServiceQuery<ServiceIdentity> identities = managementService.ServiceIdentities;
I get back an object that has a count of all the identities I expect. When I expand the list I get the first 50. This is typical of a paged response, and I expect that there is a continuation token that will allow me to get the next “page”.
Problem
I can’t see how the ManagementServiceReference.ManagementService interface can be used to obtain a continuation token.
Discussion
How to: Load Paged Results (WCF Data Services) at http://msdn.microsoft.com/en-us/library/ee358711.aspx provides an example where a QueryOperationResponse response from LINQ context can be queried for continuation with
token = response.GetContinuation()
The QueryOperationResponse is retrieved from a LINQ context Execute().
In some Azure sample code I have, there are examples of paging for blobs, tables, and queues, where data is collected in a ResultSegment. A ResultSegment has a Boolean HasMoreResults member, a ResultContinuationToken ContinuationToken member, and methods that accept and maintain these to support paging operations.
I don’t see how to obtain a Continuation from a DataServiceQuery. I don’t see that the ManagementServiceReference.ManagementService exposed by Azure supports a paged list of service identities, even though the service is, apparently, paging the results it sends me. Can you point me to the right article that will show me how the DataServiceQuery can be treated in a way that I get a Continuation back?
Using the management service sample project that's available here, what you want would look something like this:
ManagementService mgmtSvc = ManagementServiceHelper.CreateManagementServiceClient();
List<ServiceIdentity> serviceIdentities = new List<ServiceIdentity>();
// Get the first page
var queryResponse = mgmtSvc.ServiceIdentities.Execute();
serviceIdentities.AddRange( queryResponse.ToList() );
// Get the rest
while ( null != ( (QueryOperationResponse)queryResponse ).GetContinuation() )
{
DataServiceQueryContinuation<ServiceIdentity> continuation =
( (QueryOperationResponse<ServiceIdentity>)queryResponse ).GetContinuation();
queryResponse = mgmtSvc.Execute( continuation );
serviceIdentities.AddRange( queryResponse.ToList() );
}