Monday, 4 February 2019

Side-Effects Driven Development

Just came across a wonderful example of how a simple small code can get you in questioning the common sense ;-)

The task was to migrate some documents in a mongodb collection:
- find a document which have a particular old name
- create modifications (the property values for tariff and tariffTable)
- update the existing document with the new property values

The following code was supposed to do the job:

const MongoClient = require('mongodb').MongoClient;
const mongoClient = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
const offerCollection = mongoClient.db('offermatrix').collection('offers');

const oldName = 'my ALL in One', newName = 'my All in One';

const offerCursor = offerCollection.find({ tariff: oldName });
console.log('Found %s offers with tariff name %s.', await offerCursor.count(), oldName);
offerCursor.rewind();

offerCursor.limit(1);

while (await offerCursor.hasNext()) {
  const offer = await offerCursor.next();
  console.log('%s has tariff names %s.', offer.promoCode, offer.tariff);

  const updateDoc = {
    tariff: _.map(offer.tariff, tariffName => tariffName === oldName ? newName : tariffName),
    tariffTable: _.map(offer.tariffTable, tt => tt.tariff === oldName ? _.assign(tt, { tariff: newName }) : tt),
  };
  console.log(updateDoc);
  const result = await offerCollection.updateOne(offer, { '$set': updateDoc });
  console.log(result.result);
}

await offerCursor.close();
await Promise.all([mongoClient.close(false)]);

To my surprise the document is not being updated (the result is { n: 0, nModified: 0, ok: 1 }), so how is it possible that the very same document given by the cursor is not there any more?!

The problem is that the assign method from lodash is actually mutating the first argument, so the document object is not the very same as at the point the cursor gave it out.

In this case the remedy is simple: use the offer id as a selector (instead of the whole document). I find it still quite amazing how mutation can cause the problem, even in such a short code segments where most of us believe to know what is going on.


Monday, 14 June 2010

Creating Chrome Extensions with GWT

After creating some Chrome extensions and GWT applications, it came natural to try combining both and get rid of JavaScript ;-)

I tried creating 3 GWT modules for background, options and popup with corresponding EntryPoint classes. An interesting effect came up ... while options' onModuleLoad() gets invoked every time, as expected, the behaviour of the popup is somewhat different ... it gets invoked only the very first time. Since I am not able to notice when the popup is re-opened, it is unfortunately a dead-end.

There is no a simple how-to anywhere on internet, so I followed the most famous example, the Speed Tracer, in hope to be able to understand and reuse the code there.

First, the good part, there is a GWT module in the Speed Tracer source doing partial wrapping of the Chrome Extension JavaScript API (com.google.gwt.chrome.crx.Extension), as well as the implementation of a special linker for producing Extensions.

Unfortunately, the following basic questions remain open:

  1. Should the IFRAME or the SSO linker be used?
  2. How should the extension's manifest file be produced? The Speed Tracer is using an abstract class, extending BrowserAction and being annotated with @ManifestInfo (specifying the name and icons for the browser action type of extension). My hope was to be able to provide the manifest file directly, but there might be a much nicer solution in the GWT Chrome Extension module.
  3. How should the wrapper classes be used? They contain protected default constructors, so I guess something along the lines of GWT.create() ... but not sure.

Also, just came to my mind that there is yet no official name for the thing, so before I start using multiple variations, it is required to define the name and the goal:

Ideally, the creation of a Chrome Extension should be possible with usage of a special SSO linker and a GWT module wrapping up all Chrome Extensions API's in JavaScript.  (possible name: GWT4CE - GWT for Chrome Extensions)

Very likely, all required knowledge is already in the code repository of the Speed Tracer project. Maybe there is some nice person involved in the project who is willing to extract the code and provide some jump-start notes.

So much for now ... more after the very much needed 8 hours of sleep ;-)

Update:  After watching the IO session on GWT linkers found out the following additional resources:

  1. gwt-firefox-extension on http://code.google.com/p/gwt-firefox-extension
  2. gwt-chrome on http://code.google.com/p/gwt-chrome
  3. chrudson chrome extension with gwt on http://code.google.com/p/chrudson
  4. gwt-gae-bookmarks, a weekend project from gal.dolber using lots of technology including chrome extension with gwt on http://code.google.com/p/gwt-gae-bookmarks
The gwt-firefox-extension is maintained by Matt Mastracci from DotSpot who did a very nice session about GWT Linkers on this year's Google IO.  I am wondering why didn't he publish something similar for Chrome Extension?!
Despite it's very promising title, the gwt-chrome seems to be abandon. The last two projects look rather promising and I will keep this post updated as I go through them.


Tuesday, 18 May 2010

Using OpenID in Java on GAE

After a week of reading and searching through diverse blogs and discussion groups, here are my findings:

Google App Engine (GAE) is supporting the Google Account API. My understanding is that the OAuth protocol is used. Hence in this blog I will refer to it as OAuth. Additionally, GAE is also supporting the OpenID schema, referred as the [Experimental] Federated Login (the word "[Experimental]" is being added in the release 1.3.4)

The User API is providing methods for obtaining information about the authenticated user, as well as the redirection URL's for logging in and out.

Here is the example how it is being used in the Authentication DemoApp running on http://super-easy.appspot.com

UserService userService = UserServiceFactory.getUserService();

User user = userService.getCurrentUser();

String logoutURL = userService.createLogoutURL(destinationURL, authDomainMap.get(provider));

String loginURL = userService.createLoginURL(destinationURL, authDomainMap.get(provider), federatedIdentityMap.get(provider), attributesRequest);
where the authDomainMap and the federatedIdentityMap are defined like:

Map authDomainMap = new HashMap();
authDomainMap.put(ProviderEnum.Google, "google.com");
authDomainMap.put(ProviderEnum.Yahoo, "yahoo.com");

Map federatedIdentityMap = new HashMap();
federatedIdentityMap.put(ProviderEnum.Google, "https://www.google.com/accounts/o8/id");
federatedIdentityMap.put(ProviderEnum.Yahoo, "http://open.login.yahooapis.com/openid20/www.yahoo.com/xrds");
I would hope that there is a well defined way to obtain those two values automatically, knowing the provider of an OpenID, but currently my best is to hard code them.

Additionally, the last argument is a set of attributs requested, even thought it looks more like OpenID configuration (will investigate further):
Set attributesRequest = new HashSet();
attributesRequest.add("openid.mode=checkid_immediate");
attributesRequest.add("openid.ns=http://specs.openid.net/auth/2.0");
attributesRequest.add("openid.return_to=http://super-easy.appspot.com");
Currently I am getting the following data when authenticated with my Google account:
adminFlag: True
authDomain: https://www.google.com/accounts/o8/ud
email: drasko.kokic@googlemail.com
federatedIdentity: https://www.google.com/accounts/o8/id?id=AItOawl5p-d5O-OFO4Lv2iNWsboV3jLWr1Bp1qk
nickname: drasko.kokic@googlemail.com
userId: 108555035464369674882
I am still not sure about the meaning of some fields but definitely sure that the nickname is wrong. I would really appreciate some pointers about it.

One more thing, when using the RPXNOW service, the following is the information obtained from my provider:
{"profile":{"googleUserId":"103067513055141045393","verifiedEmail":"drasko.kokic@googlemail.com","name":{"givenName":"Drasko","familyName":"Kokic","formatted":"Drasko Kokic"},"displayName":"drasko.kokic","preferredUsername":"drasko.kokic","providerName":"Google","identifier":"https:\/\/www.google.com\/accounts\/o8\/id?id=AItOawkw-h3geWs619E2MYmexqY2W5FjdL-cBbo","email":"drasko.kokic@googlemail.com"},"stat":"ok"}