Google.Cloud.AppEngine.V1 client libraries and traffic splitting in .NET - google-app-engine

I am trying to use the Client Libraries provided by Google to move traffic from one version of an app in AppEngine to another. However, the documentation for doing this just talks about using the rest API and not the client libraries.
Here is some example code:
var servicesClient = Google.Cloud.AppEngine.V1.ServicesClient.Create();
var updateServiceRequest = new UpdateServiceRequest();
updateServiceRequest.Name = "apps/myProject/services/myService";
var updateMask = new Google.Protobuf.WellKnownTypes.FieldMask();
updateServiceRequest.UpdateMask = updateMask;
// See below for what should go here...
var updateResponse = servicesClient.UpdateService(updateServiceRequest);
My question is what format do I use for the update mask?
According to the documentation I should put in:
split {"split": { "allocations": { "newVersion": 1 } } }
But when I try: updateMask.Paths.Add(#"split { ""split"": { ""allocations"": { ""myNewVersion"": 1 } } }");
... I get the exception:
"This operation is only supported on the following field(s): [labels, migration_config, network_settings, split, tag_to_target_map], but got field(s): [split { "split": { "allocations": { "myNewVersion": 1 } } }] from the update request.
Any ideas where I should put the details of the split in the field mask object? The property Paths just seems to be a collection of strings.
The examples for these libraries in Google's doco is pretty poor :-(

I raised a support ticket with Google and despite them suggesting a solution which didn't work exactly (due to trying to assign a string to the UpdateMask which needs a FieldMask object), I managed to use it to find the correct solution.
The code should be:
// appService is a previously retrieved Service object from the ListServices method
var updateServiceRequest = new UpdateServiceRequest();
updateServiceRequest.Name = appService.Name;
updateServiceRequest.UpdateMask = new Google.Protobuf.WellKnownTypes.FieldMask();
updateServiceRequest.UpdateMask.Paths.Add("split");
appService.Split.Allocations.Clear();
appService.Split.Allocations["newServiceVerison"] = 1;
updateServiceRequest.Service = appService;

Related

Datastudio Community Connector - Add filter

I have a site with hundreds of members who would like to see activity relating to their products. We use datastudio at the moment, creating a report manually for a few who have asked.
We would like to be able to send out a single report that grabs the member details from the url and sets the report to that member. We followed the datastudio docs https://developers.google.com/datastudio/solution/viewers-cred-with-3p-credentials but it's not very clear
function getAuthType() {
var response = { type: 'NONE' };
return response;
}
function getConfig(request) {
var cc = DataStudioApp.createCommunityConnector();
var config = cc.getConfig();
config
.newTextInput()
.setId('token')
.setName('Enter user token')
.setAllowOverride(true);
config.setDateRangeRequired(false);
config.setIsSteppedConfig(false);
return config.build();
}
function getFields(request) {
var cc = DataStudioApp.createCommunityConnector();
var fields = cc.getFields();
var types = cc.FieldType;
fields.newDimension()
.setId('tokenValue')
.setType(types.TEXT);
return fields;
}
function getSchema(request) {
var fields = getFields(request).build();
return { schema: fields };
}
function getData(request) {
var token = request.configParams.token;
}
Has anyone set up a community connector that would allow multiple users to see a single report but only see what's specific to them?
I'm not sure if the token is being set property. It displays as the placeholder only. Is there a way to be sure what value my parameter is assigned?
We haven't got the the point of passing a url parameter. What we would like to do is pass the token value (Member details) to an existing filter. Is this possible in a community connector?
You can use the Filter by email address feature to filter your data based on the viewer's email address. This works out of the box and won't require you to build a custom connector.
Alternatively, if you do want to build a custom connector, follow this guide that seems more suitable for your use case.

Play Framework 2.2.x and Angular: Redirect to mobile index page

I'm working on an AngularJS project with the Play Framework 2.2. I'm supposed to develop a mobile version for the web application (not responsive, its part of a given uni project). For the desktop version I'm loading the index page with:
def index(any: String) = Assets.at(path = "/public", file = "app/html/index.html")
which works fine. Detection of the mobile browser works as well by examining the user agent in a Scala Action.
I changed the above code as follows to get the request header:
def index(any: String) = Action { implicit request: RequestHeader =>
if(isMobile(request)) {
// result for mobile version
}
else //result for desktop version
}
However, I don't know how to serve the different asset files as result type.
Any help is appreciated.
If I understand your question correctly, you wish to serve different files from Assets.at() based on your isMobile test, but can't work out how to get the types to line up?
Assets.at() returns an Action[AnyContent] which is at its simplest a function from Request[AnyContent] to Future[Result].
So knowing this, we just need a couple of tweaks to your index function and everything fits:
def index(any: String) = Action.async { request: Request[AnyContent] =>
if(isMobile(request)) {
Assets.at(path = "/public", file = "mobile.html").apply(request)
} else {
Assets.at(path = "/public", file = "desktop.html")(request)
}
}
Explanations:
The inner call returns a Future[Result] so we've become an Action.async
implicit is not needed here so I dropped it
An Action needs to be given a Request not a RequestHeader so I changed that
I'm showing both .apply(request) and just (request) - they are exactly the same

Having trouble converting a complex javascript object to a query string

Here is the javascript object I'm trying to convert to a query string
{$and: [{topic: categoryIds} , {$or :[ {'groups 1': {$ne: ''}}, {groups: $scope.myGroups}]}]};
Basically I'm looking to match a topic that equals a categoryIds and grab documents that have an empty groups array or that the groups array has values and matches one in the array $scope.mygroups
My question is what would be best practice to convert this in an easily parseable format so I can append it to a GET request, and how would you go about parsing it on the express server.
I am using this code in production for querying against a server with a MongoDB backend (using angular and lodash):
.factory('mongoQuery', function() {
return {
fromJson: function(json) {
return JSON.parse(json, fromJsonReviver);
},
toJson: function(object) {
return JSON.stringify(object, toJsonReplacer);
}
};
function fromJsonReviver(key, value) {
var val = value;
if (_.isPlainObject(value)) {
if (_.isNumber(value.$date)) {
val = new Date(0);
val.setUTCMilliseconds(value.$date * 1000);
} else if (_.isString(value.$regexp)) {
var match = /^\/(.*)\/([gimy]*)$/.exec(value.$regexp);
val = new RegExp(match[1], match[2]);
}
}
return val;
}
function toJsonReplacer(key, value) {
var val = value;
if (_.isPlainObject(value)) {
val = _.extend({}, value);
for (var k in value) {
val[k] = toJsonReplacer(k, val[k]);
}
} else if (_.isDate(value)) {
val = {$date: (new Date(value)).valueOf() / 1000};
} else if (_.isRegExp(value)) {
val = {$regexp: value.toString()};
}
return val;
}
})
It includes many of the suggestions mentioned by others in the comments and supports dates and regular expressions.
Other than that, if you need to send the query with a GET request, just use encodeURIComponent like others have mentioned.
Here is a working example: http://plnkr.co/edit/b9wJiUkrHMrDKWFC1Sdd?p=preview
What you're thinking here is basically to redevelop an API server, coupled with some mongodb database, and querying it with some format.
REST is the "best practise" you're looking for. It's a standard that encapsulates some common actions to http ressources.
You should know that you don't need to redevelop an ecosystem based on such a standard. Full-featured REST API servers exist, some are even based on express.js. Loopback and sails.js. These one provide some extra features like
Model abstraction through ORM's, and database-engine agnostism
Automatic REST actions, from database schema or model finition
Advanced querying through "extended REST" ("where", "limit", "order", ...)
Realtime with REST-like websockets
Client side libraries that help you query the server
Some standalone exernal libraries, such as js-data or Restangular can deal with REST server pretty well, and act as a frontend connector to backend
Now, if I had to purely answer your question, and if you realy wanted to go with your solution, I'd just add the mongo query to a where query param on the http call with encodeURIComponent as stated before.

Process client side array in KRL raised to Kynetx app

Overview
I am working on building a Kynetx ruleset that will find a bunch of Facebook ids that are on the page and then use the Kynetx Facebook module to get the Facebook avatar associated with that Facebook id. I have the JS that creates an array of Facebook ids on the page and I can process an array in KRL to retrieve Facebook avatars. What I don't have is how to get an array from the client side to the server side in KRL.
How can I get the array from the client side to the server side of KRL?
You can take a JavaScript array and convert it into a string and it will work if you decode it on the server side of KRL.
Example app code => https://gist.github.com/722536
Example app bookmarklet => http://mikegrace.s3.amazonaws.com/forums/stack-overflow/send-array-to-kns-dev-bookmarklet.html
ruleset a60x442 {
meta {
name "array-passing-test"
description <<
array-passing-test
>>
author "Mike Grace"
logging on
}
rule start_your_engines {
select when pageview ".*"
{
notify("Running","...sending array to KNS") with sticky = true;
emit <|
app = KOBJ.get_application("a60x442");
var numbers = [1,2,3,4,5];
nums = JSON.stringify(numbers);
app.raise_event("process_array", {"numbers":nums});
$K("div.KOBJ_message").append("<br/>"+nums);
|>;
}
}
rule process_array {
select when web process_array
foreach event:param("numbers").decode() setting (number)
{
notify("number",number) with sticky = true;
}
}
}
Results of running app from bookmarklet on http://example.com/
Answer
Unfortunately, the KRL JS runtime doesn't yet support sending arrays to the server side. There is a way to accomplish what you are wanting to do though.
Example
I built an example app that runs on this page with a bookmarklet that gets the tags that the question is tagged with and sends them to the server to be processed and then they come back.
Example app code => https://gist.github.com/707561
Example app bookmarklet => http://mikegrace.s3.amazonaws.com/forums/stack-overflow/client-side-array-to-server-bookmarklet.html
Step by step explination of code example
collect text in JS array
convert array into csv string and append comma to make regex splitting easier
raise event to KNS with csv string
process rule pulls first value off
rest of the values are saved to a new variable
first value goes into a notify
postlude sends remaining values to itself
loops until done and returns directives back to the browser
Results of running app from bookmarklet:
You can also do arrays of hashes if you JSON.stringify the array of hashes.
Example app:
ruleset a60x449 {
meta {
name "pass-hash-in-web-event-test"
description <<
pass-hash-in-web-event-test
>>
author "Mike Grace"
logging on
}
rule start_this_party {
select when pageview ".*"
{
notify("Now running","Building arrays to send to KNS") with sticky = true;
emit <|
var data = {};
data.userData = JSON.stringify(
[
{"name":"MikeGrace","id":234232344},
{"name":"TelegramSam","id":234089790234},
{"name":"Alex","id":2300234234234}
]
);
app = KOBJ.get_application("a60x449");
app.raise_event("process_me_data", data);
|>;
}
}
rule process_arrays_of_data {
select when web process_me_data
foreach event:param("userData").decode() setting (user)
pre {
userName = user.pick("$.name");
userId = user.pick("$.id");
output =<<
<p>
userName: #{userName}<br/>
userId: #{userId}<br/>
</p>
>>;
}
{
append("body", output);
}
}
}
Results of running on example.com

WebClient issue

I am trying to get contents of http://www.yahoo.com using WebClient#DownloadStringAsync(). However as Silverlight doesn't allow cross domain calls i am getting TargetInvocationException. I know we have to put clientaccesspolicy.xml and crossdomain.xml in our web server root but that is possible only if i have control on my services. Currently Google is not under my control ;), so how do i handle it?
I've did a workaround by making a WCF service in my web application and then calling WebClient. This works perfectly but it is rather ineffecient. Is there any other better way than this?
Thanks in advance :)
Silverlight's cross domain restricitions cause many developers to implement workarounds. If you need to display the html page you get back you should look into Silverlight 4 (WebBrowser) control although this only seems to work when running out-of-browser mode.
If you need to parse through the content you can try some of the following:
For a managed code solution the proxy service you have already implemented is your best option.
Write a Java applet that returns this information. Silverlight can interopt to javascript which can interopt into Java applets. This also works in the reverse but a little difficult to setup. (If you need more info on this let me know).
Use javascript XmlHttpRequest to get the data you want from the source. This can be difficult when supporting multiple browsers. This link shows an example of how to do this (you will need to scroll down). Javascript get Html
Code:
var xmlHttpRequestHandler = new Object();
var requestObject;
xmlHttpRequestHandler.createXmlHttpRequest = function(){
var XmlHttpRequestObject;
if(typeof XMLHttpRequest != "undefined")
{
XmlHttpRequestObject = new XMLHttpRequest();
}
else if(window.ActiveXObject)
{
var tryPossibleVersions =["MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp","Microsoft.XMLHttp"];
for(i=0;i<tryPossibleVersions.length;i++)
{
try
{
XmlHttpRequestObject = new ActiveXObject(tryPossibleVersions[i]);
break;
}
catch(xmlHttpRequestObjectError)
{
// Ignore Exception
}
}
}
return XmlHttpRequestObject;}
function getHtml(){
var url = document.getElementById('url').value;
if(url.length > 0)
{
requestObject = xmlHttpRequestHandler.createXmlHttpRequest();
requestObject.onreadystatechange=onReadyStateChangeResponse;
requestObject.open("Get",url, true);
requestObject.send(null);
}}
function onReadyStateChangeResponse(){
var ready, status;
try
{
ready = requestObject.readyState;
status = requestObject.status;
}
catch(e) {}
if(ready == 4 && status == 200)
{
alert(requestObject.responseText);
}}

Resources