Sunday, June 08, 2025

I Tried Firebase "Prototype with AI" - First Impressions

Google launched Firebase Studio this April.  I tried its "Prototype with AI" feature and recorded my experience here.

TL;DR; It is great for creating an interactive mock UI. It is NOT designed to bring you the full stack web application that is production ready. 

I spent an hour learning and playing with it. Everything is super intuitive. I did not need to look for help anywhere else. During this hour, I wrote prompts (essentially requirement specs and provided a few feedback after I was able to try the user experience), waited for code generation, reported bugs and waited for AI to fix the bugs. By the end of the hour, it created an interactive web app that is good for a concept demo.

A few things I found interesting:

  • There is a feature to "Select" UI element and write prompt. This is very helpful. Made the iteration process feel like a proper UX design session
  • Bug fix takes just a click of the "Fix Error" button. AI made some mistakes, but had 100% success rate to fix bugs without requiring manual code changes
  • It is capable to generate a decent interactive column chart 

Initial Prompt 

 Create a web application for order picking. Requirements below: 

 

  • Top left is logo placeholder, top right is a user avatar
  • Color theme: main color is Teal and can use contract or complimenting color in the theme
  • Site name: Order Picking
  • Filters:
    • Stores: examples "Downtown", "West Side"
    • Assignment: "Assigned to me", "Unassigned"
  • Order list in a grid
    • Grid has columns: Order ID, Store Name, Priority, Assignment, and Created Time 
    • Grid is sortable, default sort by Priority then Created Time
  • If user click any order, will show order details
  • Order details contain the following information:
    • Order header information
    • Grid for order lines with columns: and recommended pick order (starting from 1), item name, description, picture, quantity, location (example: Aisle 1, Bay 4),
    • A link to navigate back to order list 
    • Two action buttons on order details page:
      •  One button to confirmed order is picked, and will ask user to enter the order container ID
      • Another button to confirm the order is unpickable, and will ask user to provide reason through dropdown with values: "No item at location", "Not enough item at location", "Wrong item at location", "Other". If user select Other, will have a free text box to optionally provide details 

 

Iterations

After the initial prompt, I am able to play with the app, and suggest some changes then try again. Two of the prompts Italic below, I was able to use the "Select' feature to to pinpoint the web component that I am providing feedback on. 
  • Add 2000 sample orders and a few order lines for each sample order. Add pagination to the order list, with page size preferences: 10, 25, 50.
  • If order is assigned to somebody else, this order will be in read-only mode. And the "Assigned To" area should show an icon to alert user to not pick this order (for element <OrderDetailHeader>)
  • Add "Assign to me" button if order is not assigned to anyone yet. Once order assigned to the user, then the action buttons will be available. (for element <CardContent>)
  • When user click Avatar, show a menu. For now, just one option to show user's pick statistics. When user navigate to that page, show user's past 30 day pick statistics in a chart. 

Screenshots 

Generated UI - Interactive Chart

Generated UI - Dialog

Generated UI - Data Grid

Generated UI - Grid with filter and pagination
Design Experience - One Click Bug Fix

Design Experience - "Select" element and write prompt


 

Saturday, October 05, 2019

Enterprise Integration Recipe: File Transfer Using Google Cloud Storage


This is one of a series of recipes on how to use products offered by Google Cloud Platform to implement enterprise integration solutions.

GCP Product used in this article:

Implementation


System Diagram
System Diagram

Flow Chart for Subscriber
Flow Chart for Subscriber


Recipe:
  • gcs.tf: TerraForm code that provision and configure all GCP resource needed
  • README.md: up to date instruction on how to use the recipe
Noteworthy Tips:
  • GCS Object Lifecycle: make it easy to configure archive and error box. We should move old files to cold or nearline storage and eventually purge them. No more custom CRON jobs for this chore;
  • Pubsub Notification for GCS: we configured notification for inbox "OBJECT_FINALIZE" event. Again no more CRON jobs needed to scan folders for new files. Another advantage is that the subscriber do not need to worry if we might be dealing with partially uploaded file;
  • Stackdriver Monitoring or GCS: not configured in the recipe. But definitely worth exploring. For example, "object_count" might be used to monitor error box, and send alert when we see the count increasing too fast;
  • Retry: this recipe implemented retry by simply do not "ACK" CPS messages. Cloud Pubsub will send notification again after ACK deadline expired for 7 days. So retry is implemented by CPS subscription's At-Least-Once Delivery feature. If want a shorter total retry period, you can add a line of code to check age of a message and mark it non-retriable if message age is over threshold. This approach saves the trouble of implementing reliably retry mechanism. Downside is lost control of retry intervals (for example, there is no exponential backoff retry)

Monday, July 18, 2016

"Permission denied to generate login hint for target domain."

Google OAuth Error Message: Permission denied to generate login hint for target domain.

Root Cause: development server, was using http://127.0.0.1:8080, even if I added it to cloud config, does not work. But once I change to http://localhost:8080, it starts to work.

Tuesday, June 21, 2016

Summer Fun with Google BigQuery

Nothing beats a game that can keep the whole family entertained for a few hours, while teaching kids some programming sense.

Just two weeks into summer, I already ran out of ideas to keep kids entertained.

Today, Google BigQuery come to the rescue. To be more precise, the USA Name Data provided hours of entertainment.
"This public dataset was created by the Social Security Administration and contains all names from Social Security card applications for births that occurred in the United States after 1879."

After showing my kids a few very simple queries of the USA Name Data, we started to play "Family Feud" game:

Most Popular Names

  • Everyone write down 5 names in secret
  • After we are done, each need to write their own query and run it in BQ to find out total count of these five names in all years
  • Whoever gets the most counts wins
  • Cannot reuse names in the following rounds
(After three rounds, we covered almost all the top 20 names in USA.)

Then we played another game with the dataset with these rules

Least Popular Names

  • Write down 5 names in secret
  • The names must draw at least one count
  • Whoever gets the least counts wins
(We actually got one name with only 5 in all the last 103 years!)

Here is a sample query

SELECT SUM(number), name 
FROM [bigquery-public-data:usa_names.usa_1910_2013] 
WHERE name in ('John', 'Jason', 'Mary', 'David', 'William')
GROUP BY name

Now, after we are done with the game, the following two queries will show us the top answers.

Least Popular Names

SELECT SUM(number), name 
FROM [bigquery-public-data:usa_names.usa_1910_2013] 
GROUP BY name
ORDER BY 1

Most Popular Names

SELECT SUM(number), name 
FROM [bigquery-public-data:usa_names.usa_1910_2013] 
GROUP BY name
ORDER BY 1 DESC

Some Follow Up Questions

While the kids are still keen on it, some follow up questions will keep their brains from getting rusty:
  • How many kids were born in the same year, and with the same name as yours?
  • Which year has the most kids with the same name as yours?
  • Are there any kids with your name in the opposite gender?
  • Which years are "baby boom" years? (Hint: save to Google Sheet, and plot year vs. count(number))
  • Which years have the most "Jacqueline"s and why? (Hint: opportunity for some history lessons about first ladies)
  • Most gender neutral names (Hint: refer to this page)

Tuesday, May 17, 2016

Angular 2: Building a Google Map Component

Live Demo
Github Repo
 
In order to force myself dig deeper into Angular 2, I tried to build a component wrapper for Google Map JavaScript API. The end results is a demo component here: google_map.component.ts.

 

Sample Client Code

A sample client code using the component looks like this:

Features

The features including:
  • Drawing through Angular binding
  • Map events published as Angular events 

 

Tips

Here are some code snippets the highlights some of the lessons I have learned.

First, make sure Google Maps library is loaded from here:
    https://maps.googleapis.com/maps/api/js?key={YOUR_API_KEY}&libraries=visualization,place&callback=initMap
Add this line in typings.ts, so that we can have type definitions for Google Maps JavaScript API
    "googlemaps": "github:DefinitelyTyped/DefinitelyTyped/googlemaps/google.maps.d.ts"
Event handlers turns out to be tricky. My naive first implementation was something like this
this.map.addListener('click', function(e: google.maps.MouseEvent) {      
  this.mapClick.emit(e.latLng));
);
Turns out there are two big problems in this implementation:
  1. this inside the event handler does not have member mapClick. Because it is no longer the component class
  2. Once scope of this is fixed, I found data changes in the event handler does not trigger Angular change detection
Solutions for those problems? See the comments in my final implementation:

On the CSS side, it is very important to set height of Google Map component. If not done, it is automatically collapsed to nothing, and you won't see a map.

Demo 

Finally, a functional demo is here: google_map.html. Below is screenshot of the demo in action:

Monday, May 02, 2016

Angular 2: Injecting Parent and Child Component

Live Demo
Github Repo

To inject children component into a parent component, use ViewChildren (notice there is also ConentChildren which will inject children from content projection).

To inject a parent into child component, you simply need to inject parent in the constructor of child component. However, there is a bit of problem due to circular reference. The parent component has to reference child component as directive. If the child also depends on parent, we will get an error like this: EXCEPTION; Cannot resolve all parameters for '[ParentComponent]'.... So, the following slightly twisted injection is needed:
        contructor(@Inject(forwardRef(() => ParentComponent)) private _parent: ParentComponent)

A small working example showing both child and parent component injection can be downloaded here: https://github.com/huguogang/ng2tsdemo/blob/master/view_child.html.

Angular 2 Content, Content Projection, Transclusion

Live Demo
Github Repo

I guess under all these fancy words (especially Transclusion), there is a simple concept. And it seems to me, it is way easier to learn it by starting with looking at a working sample, make some changes and observe the changes.

I have created a bare minimum working example here: https://github.com/huguogang/ng2tsdemo/blob/master/content.html

Here are the key code fragments. Related elements are color coded.
Template for the directive:
<div>
  <h1><ng-content select="my-title"></ng-content></h1>
  <div>
    <ng-content select="content"></ng-content>
  </div>
  <br/>
  <em style="font-size:smaller"><ng-content select="footer"></ng-content></em>
</div>
Markup in the client component:
<multi-slot-content>
  <my-title>Angular 2 Content Demo</my-title>
  <content>The selectors defined in MultiSlotContent directive will find 
    the content element in this template, insert it into it's own template 
    section.</content>
  <footer>Footer: this feature was called transclusion in Angular 1.</footer>
</multi-slot-content>
Resulting HTML Markup:
<multi-slot-content>
  <div>
    <h1><my-title>Angular 2 Content Demo</my-title></h1>
    <div>
      <content>The selectors defined in MultiSlotContent directive will find 
        the content element in this template, insert it into it's own template 
        section.</content>
    </div>
    <br>
    <em style="font-size:smaller"><footer>Footer: this feature was called transclusion in Angular 1.</footer></em>
  </div>
</multi-slot-content>
Notice the key is to define proper Angular 2 selectors in the directive. Then in the client component, it just need to fill the content in the right selector target. The end result is the directive's ng-content elements got replaced by the fragments from client.

Sunday, May 01, 2016

Angular 2 HTTP JSON Service

Live Demo
Github Repo

Here is a bare minimum demo of how to use Angular 2 HTTP JSON Service:https://github.com/huguogang/ng2tsdemo/blob/master/json_service.html

The demo uses observable instead of promise. A few potential catches:
  • Import Rx.js in html file:
    <script src="node_modules/rxjs/bundles/Rx.js"></script>
    
  • Import rxjs/Rx in service:
    import 'rxjs/Rx'
  • Register HTTP_PROVIDERS

Angular 2 Renderer

Live Demo
Github Repo

Renderer in Angular 2 is a fascinating topic. It does not have an official documentation yet. As of this writing 5/1/2016, there is only a list of public method signatures here: https://angular.io/docs/ts/latest/api/core/Renderer-class.html.

It turns out it is very easy to get a hold of the renderer in your components. All it takes is to declare a Renderer in contructor, Angular 2 will inject it for you.

Code sample to inject renderer:
constructor(private _renderer: Renderer) { }

A working demo on github: https://github.com/huguogang/ng2tsdemo/blob/master/renderer.html

Friday, April 01, 2016

Angular 2 Resources

Official web site

https://angular.io

Official Github Repo

https://github.com/angular/angular
A few notable sub-directories:

Angular CLI

https://github.com/angular/angular-cli
Command line interface. It helps to build skeleton code for a new project that follows best practice. Not officially released yet. But very helpful. It generates project with proper folder structure, unit testing and end2end tests are also included.

npm packages


Blog about Angular 2 (and other Software topics) by Victor Savkin

http://victorsavkin.com
There are not many tutorials here, but those few entries will give us a deep dive into Angular 2 design philosophy.

egghead.io

https://egghead.io/technologies/angular2
A series of short video tutorials about angular 2.

Stackoverflow tagged Angular 2

http://stackoverflow.com/questions/tagged/angular2


Friday, July 31, 2015

ExtJS 6.0.0 GPL is out

Go here: https://www.sencha.com/legal/gpl/, select "Sencha Ext JS" and give your email address.

This version has quite a few feature and performance improvements. https://docs.sencha.com/extjs/6.0/whats_new/6.0.0/whats_new.html

Monday, July 06, 2015

JPA Join Fetch

A couple of key points to have a successful join fetch in JPA (backed by Hibernate):
  • Why do we want join fetch? JPA default fetch strategy is "Lazy", which has better performance under most circumstances. But, sometimes we do want to retrieve all children (and sometimes children's children). In that case, join fetch allows us to get everything in one Database round-trip instead of N (or N*N round-tips). This change can easily speed up those queries for 100 times (in my case it went from 20 seconds to 0.2 seconds).
  • Make sure in your Entity classes, the child collections are "Set" not "List". If you get the error message: "Hibernate cannot simultaneously fetch multiple bags". This is the root cause.
  • How to join multiple levels: 
        root
          .fetch([childAttributeName], JoinType.LEFT)
          .fetch([grandChildAttributeName], JoinType.LEFT);
  • Fetch join will return multiple duplicated rows for the parent entity. This is usually undesirable, wrap the return set in a LinkedHashSet will get the unique parent entities in the original select order. (See this Stack Overflow post)

Monday, March 16, 2015

Troubleshoot Networking Problems in Google Chrome Browser

* Developer Tools > Network

* chrome://net-internals/
Screenshot for the socket view: chrome://net-internals/#sockets
* wireshark (hopefully, do not need to go that far)

Wednesday, February 25, 2015

Coldfusion 10 Solr Indexing Zip File that Contains PDF files

Seems like I should be surprised, if I don't find some surprises in Coldfusion every week. :) Here is another one that took me a few hours to find a solution. And hopefully will save a few hours for someone else.

Environment
Coldfusion 10 Update 15
Windows Server 2012

Symptom
When indexing a bunch of files, Coldfusion stopped indexing without any exception or getting into any error state. It just stopped in the middle of indexing. If I was not looking at it closely, I would not have noticed that it has failed.

Again, Coldfusion stopped  the execution without throwing a fuss is a big surprise for me. If I run it in Brower, there is no usual 500 server error. Everything is just hunky-dory as far as Coldfusion is concerned!?

Cause
After some digging, I found out the following
  • It stopped on a zip file
  • The zip file has some PDF files in it
  • Coldfusion-error.log has the following message
Feb 25, 2015 8:30:28 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [CfmServlet] in context with path [/] threw exception [ROOT CAUSE: 
java.lang.NoClassDefFoundError: org/apache/pdfbox/pdmodel/PDDocument
 at org.apache.tika.parser.pdf.PDFParser.parse(PDFParser.java:53)
 at org.apache.tika.parser.CompositeParser.parse(CompositeParser.java:120)
 at org.apache.tika.parser.AutoDetectParser.parse(AutoDetectParser.java:101)
 at org.apache.tika.parser.DelegatingParser.parse(DelegatingParser.java:52)
 at org.apache.tika.parser.pkg.PackageParser.parseArchive(PackageParser.java:78)
 at org.apache.tika.parser.pkg.ZipParser.parse(ZipParser.java:49)
 at org.apache.tika.parser.CompositeParser.parse(CompositeParser.java:120)
 at org.apache.tika.parser.AutoDetectParser.parse(AutoDetectParser.java:101)
 at coldfusion.tagext.search.SolrUtils.getMetadata(SolrUtils.java:599)
 at coldfusion.tagext.search.SolrUtils.getSolrDocument(SolrUtils.java:753)
 at coldfusion.tagext.search.SolrUtils.addDocument(SolrUtils.java:1339)
 at coldfusion.tagext.search.IndexTag.doUpdate(IndexTag.java:651)
 at coldfusion.tagext.search.IndexTag.doStartTag(IndexTag.java:340)

So, obviously, our Coldfusion distribution is missing some libraries.

Solution
Short answer: find jar file for PDFBox, throw them under Coldfusion lib folder and restart Coldfusion. And I got the jar file from here: pdfbox-0.8.0-incubating.jar

Long Answer: However, as with all Open Source projects, there is not much consideration of backward compatibility or official supported bundled distribution. I tried to download latest version of PDFBox, and it just does not work. So, I will need to find the original bundled version, and here is the journey (without detours I took :( ) to the right jar file

<dependency>
  <groupId>org.apache.pdfbox</groupId>
  <artifactId>pdfbox</artifactId>
  <version>0.8.0-incubating</version>
</dependency>

  • Google "pdfbox 0.8.0 incubating"
  • voila


Another Challenge (unsolved)
There is still some unsolved challenge for Solr. For example, Verity can index our PDF files correctly, but Solr's PDF reader seem to be sub-par. It only got some fragmented text from our PDF file, and it's missing a lot of keywords in our PDF files.

Monday, February 23, 2015

CFScript bug?

An extra semicolon at the end of "if" block is causing a lot of head scratching for me recently. Please see the code snippet below. Expected output should be:
Figure 1. Expected Output
However, below is the actual output:
Figure 2. Actual Output
Now, please notice this extra ";" at the end of line 6. If I remove it, everything will just work as expected.
<cfscript>
    private struct function test() {
        if(1 == 1) {
            if(1 == 0) {
                writeOutput("1==0");
            }; //<- look at here
            writeOutput("true");
            return {data = 1};
        }
        writeOutput("false");
        return {data = 2};
    }
    writeDump(test());
</cfscript>
Due to the lack of specifications for CFScript language, I cannot tell if this grammar is even allowed. But I can tell you this: the journey of discovering and finding the root cause of this problem is not fun at all!

Symptom

CFScript fall through the "if-else" statement, and did not return to caller from the expected branch.

Cause

An extra semicolon at the end of a block is the root cause.

Friday, December 19, 2014

Node.JS Error "Cannot find module '...'"

Setting up NodeJS is generally a smooth process. There is one thing need to be taken care of manually: set up environment variable NODE_PATH. This variable must point to the npm global installation location, otherwise you NodeJS application may have problem starting with error message like this:
Error: Cannot find module 'XXX'

In Windows, the easiest way is to go Computer > Properties > Advanced System Settings > Environment Variables > User Variables > New

After the configuration, need to exit the current command line and start a new one in order to see the environment variable in effect.

Wednesday, November 19, 2014

IIS Error: Cannot write configuration file

Symptom

When trying to change IIS configuration, got this error message:
Filename:
    \\?\C:\Windows\System32\inetsrv\config\applicationHost.config
    Error: Cannot write configuration file

Error Message

Diagnosis

Turns out root cause of the problem is a full C: drive.

Windows Explorer

Solution

Luckily, this is a virtual machine with some spare disk capacity. So it's just a matter of allocating more resource, then extend C: drive.


Wednesday, November 05, 2014

Flex TextArea problem in DataGrid

Problem

Flex 4 TextArea does not take multi-line entry when used as editor in DataGrid.

Solution

Add editorUsesEnterKey="true" attribute to the column with TextArea editor.


P.S.

The solution is straightforward. But the route to finding this solution kind of proves open source or at least show your customer the source code is very important.

I first suspect the enter key is intercepted by DataGrid, but it appears that keydown event is intercepted by DataGrid before the TextArea. Some Googling with various keyword combination did not turn up anything interesting.

Not sure where to start, I added a itemEditEnd event handler to the DataGrid, and set a breakpoint there. After walking through the stack, I saw the following code segment, and thus the solution above pops out. Now I can simply go to documentation of editorUsedEnterKey and make sure this is what I want.

(Code of interest from DataGrid.as)
    /**
     *  @private
     */
    private function editorKeyDownHandler(event:KeyboardEvent):void
    {
        // ESC just kills the editor, no new data
        if (event.keyCode == Keyboard.ESCAPE)
        {
            endEdit(DataGridEventReason.CANCELLED);
        }
        else if (event.ctrlKey && event.charCode == 46)
        {   // Check for Ctrl-.
            endEdit(DataGridEventReason.CANCELLED);
        }
        else if (event.charCode == Keyboard.ENTER && event.keyCode != 229)
        {
            // multiline editors can take the enter key.
            if (!_editedItemPosition)
                return;

            if (columns[_editedItemPosition.columnIndex].editorUsesEnterKey)
                return;

            // Enter edits the item, moves down a row
            // The 229 keyCode is for IME compatability. When entering an IME expression,
            // the enter key is down, but the keyCode is 229 instead of the enter key code.
            // Thanks to Yukari for this little trick...
            if (endEdit(DataGridEventReason.NEW_ROW) && !dontEdit)
            {
                findNextEnterItemRenderer(event);
                if (focusManager)
                    focusManager.defaultButtonEnabled = false;
            }
        }
    }

Thursday, August 21, 2014

Ubuntu LTS 12 on VMWare Player Showing Multiple Columns of Identical Screens

Symptoms

After updating to Kernel 3.2.0.67 and reboot, my screen is showing three columns of duplicated desktop. 

Environment: 

  • VMWare Player 6.0.3
  • Ubuntu: 12.04 LTS, Kernel 3.2.0.67 (post upgrade version, I'm not sure what was the version before my upgrade)
  • The VM image was created way back in a very old VMWare Player, started from Ubuntu 10. It has been kept up-to-date by applying Ubuntu patches and eventually to 12.04 LTS. Turn out this probably was the root cause of all the troubles

Solution

After some Google search and reading up on posts, I was eventually lead to the following thread:
Guest display split into identical panes
Flip to page 2, on post 17 from thellstrom, there is the solution that worked for me. I used solution 1b mentioned in the post, and after reboot, everything is back to normal.

Cause

So, based on the article, root cause of the problem is due to VM created on old version of player with virtualHW version 7. I updated the config file from version 7 to 9, and everything appears to be working now.