Tuesday, 13 June 2023

where is my docker-compose

After upgrade docker desktop, docker-compose command can not be found.

But look at docker information the below, docker compose should be there.

which docker
/usr/local/bin/docker

cd /usr/local/bin/docker

//see a symoblic link
docker-compose -> /Applications/Docker.app/Contents/Resources/bin/docker-compose/docker-compose

//go to /Applications/Docker.app/Contents/Resources/bin/docker-compose and not find docker-compose
//ha ha directory structure changed in this new version of docker desktop
//to fix, in /usr/local/bin
rm docker-compose
ln -s /Applications/Docker.app/Contents/Resources/bin/docker-compose docker-compose

Tuesday, 23 May 2023

AWS Certificate Manager

DNS validation

ACM provides you with one or more CNAME records that must be added to this database. These records contain a unique key-value pair that serves as proof that you control the domain.

If use AWS Route 53, you can create a record of CNAME type for that domain. The CNAME name and CNAME value provided by ACM should be used.

Thursday, 27 April 2023

Modify Request Headers in Chrome

  • Install ModHeader extension in Chrome
  • Use that extension to set a header
  • Open developer tool, network tab
  • Visit a page
  • View request headers in developer tool
  • Make sure header sent indeed

Friday, 21 April 2023

docker compose

extra_hosts

Add hostname mappings. Function the same as the docker client --add-host parameter.

//example
extra_hosts:
  - "somehost:162.242.195.82"
  - "otherhost:50.31.209.229"

An entry with the ip address and hostname is created in /etc/hosts inside containers for this service

host.docker.internal

For docker for mac or window, it is docker host ip. However, for docker for Linux, need to add this in compose file

#For docker for Mac or Window, should not add this. Otherwise,
#host.docker.internal may not be host ip.  It is for Linux only
extra_hosts:
    - host.docker.internal:host-gateway

Sunday, 9 April 2023

Google Tag Management System

What is tag

Tag is a JS code snippet inserted into a web page to track user event. Meta pixel and Goggle ad are some example of tags

What problem we have if we use multiple tags in a web page

  • Will need to add a code snippet for every tag. For example, if we use five track tools from ten different company, we need to add 10 extra code snippets in different pages
  • If we want to change some thing such as account id, we need to go to every page to modify them

Google Tag Management System

Tag management system likes an container. You set up and update tags in the tag management system, and you only need one extra code snippent in a web page no matter how many tags you want to use. Google Tag Management System is one of these systems.

Use Google Tag Management System, and you can

  • Set up tags
  • Set up event rules

gtag.js

gtag.js is the JavaScript framework that is used to add Google tags directly to web pages. It can not add third party tags.

Here what we need is a tag id

// Google tag (gtag.js)
<script async src="https://www.googletagmanager.com/gtag/js?id=TAG_ID"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments)};
  gtag('js', new Date());

  gtag('config', 'TAG_ID');
</script>

The Google tag lets you send data from your website to linked Google product destinations. By now only Google Ads accounts and web data streams in GA4 properties can be destinations.

You can use a single Google tag across your entire website and connect the tag ID to multiple destinations.

Shopify

Enable custom app development from the Shopify admin

Before you can create a custom app for your store, you need to enable custom app development. You need to be the store owner or a staff member with the Enable app development permission to enable custom app development.

  1. From your Shopify admin, click Settings
  2. Click Apps and sales channels.
  3. Click Develop apps.
  4. Click Allow custom app development.
  5. Read the warning and information provided, and then click Allow custom app development.

Create an app

  • create a app called my-test
  • click Config Admin API scopes
  • choose services app needs such as customer, draft order, order and fullfillement services
  • click save configuration
  • install app and copy admin api token: shpat_559dummydummy77e77437574

Friday, 31 March 2023

Meta Tracking

Mata Pixel

Using Mata Pixel, we can send tracking data to Facebook. There are two ways to send tracking data.

  • Javascript
  • Conversion API

Javascript method

You need pixel id and event name. The below is sample code to send a PageView event

<script>
  !function(f,b,e,v,n,t,s)
  {if(f.fbq)return;n=f.fbq=function(){n.callMethod?
  n.callMethod.apply(n,arguments):n.queue.push(arguments)};
  if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
  n.queue=[];t=b.createElement(e);t.async=!0;
  t.src=v;s=b.getElementsByTagName(e)[0];
  s.parentNode.insertBefore(t,s)}(window, document,'script',
  'https://connect.facebook.net/en_US/fbevents.js');
  fbq('init', '{your-pixel-id-goes-here}');
  fbq('track', 'PageView');
</script>

There are two options to send event using Javascript

  • Emebed the above code snippet in the webpage
  • Run that code snippet directly. For example, for React page, we can run this Javascript code snippet directly using react hook useDidMountEffect

Test web browser events using the test events tool

  • Log into Facebook business account
  • Go to Events Manager
  • Click the Data sources icon on the middle side of the page
  • Click Test events
  • Find the browser events section
  • If do not see the browser events section, click clear event
  • Enter your website URL and click Open website. This opens your website in a separate window or tab
  • Do what you normally do to this website. Event will be recorded
  • Go back to Test Event section to check events recorded

Deduplication

If we send the same event using both conversion API and embedded javascript, Meta can use event id to deduplication. Meta will treat event with the same event id as the same event.

//use JS to send out event id
fbq('track', 'Purchase', {value: 12, currency: 'USD'}, {eventID: 'EVENT_ID'});

fbq('track', 'Lead', {}, {eventID: 'EVENT_ID'});

Specifications for Meta Pixel standard events

Developer Guide for Mata Pixel

Mata Conversion API

Coversion API Docs

React Tech Stack

Built in React Hook

Use Effect

Wednesday, 8 March 2023

Selenium

Install Selenium in Ubuntu

  1. pip install selenium
  2. download chrome webdriver Driver needs to match Chrome version installed in your machine
  3. unzip download files and copy chromedriver to /usr/bin folder
//hello_world.py
from selenium import webdriver

driver = webdriver.Chrome()
driver.maximize_window()

driver.get("https://developer-leoshen.blogspot.com/2023/03/selenium.html")

driver.find_element_by_link_text("download chrome webdriver").click()

driver.find_element_by_id("age3").click()

firstName = driver.find_element_by_id("first_name").send_keys("leo")
python hello_world.py

PHP Selenium

//installation
1. down stand alone selenium server

2. install php library
compose require php-webdriver/webdriver ^1.13.1

3. install chrome driver
//test.php
<?php
require_once(__DIR__ . '/vendor/autoload.php');

$host = 'http://localhost:4444/wd/hub';

$driver = Facebook\WebDriver\Remote\RemoteWebDriver::create($host, Facebook\WebDriver\Remote\DesiredCapabilities::chrome());

$driver->get("https://ca.yahoo.com");

$driver->manage()->window()->maximize();

$driver->quit();
//run standalone server
java -jar selenium-server-standalone-3.141.59.jar

//run test script
php test.php

The following is used for test script

Please select your age:






First Name :

Upgrade Chrome in my linux machine

sudo apt update
sudo apt upgrade

Thursday, 2 March 2023

AWS CLI Commands and profiles

multiple profiles in one machine

//who is calling
aws sts get-caller-identity

//list profiles
aws configure list-profiles

//add new profile for myone. set format: json default output format
//if does not add profile flag, the profile name is default
aws configure --profile myone

//use profile
aws sts get-caller-identity --profile myone

Some commands

# list lambda function
aws lambda list-functions

# overwrite default region
aws lambda list-functions --region us-west-1

Wednesday, 1 March 2023

postman

add variable in postman

  1. select environment such as local, sandbox
  2. select the environment quick look icon
  3. edit
  4. save

To use variable

{{api-key}}

See network info of a response

network icon is beside status:200 ok. can see remote ip etc

Wednesday, 15 February 2023

AWS Troubleshooting

CloudFormation stack stuck in an IN_PROGRESS state

AWS Docs For Troubleshooting

Here is an example

1. one stack is stuck at UPDATE_IN_PROGRESS
2. click resource tab and I can see AWS::ECS::Service is stuck in UPDATE_IN_PROGRESS
3. Go to ECS and find that service and click Logs tab
4. show get something from logs

ec2-user does not have permission to access mysql log file

After log into a EC2 machine, I try to access /vol/mysql/data/my_logs.txt. However, I get a permission deny.

# see all users in machine
vi /etc/passwd

# switch to root
sudo su - root

# now can see log
vi /vol/mysql/data/my_logs.txt

Wednesday, 8 February 2023

Access Container On AWS Fargate

Here is the documentation for Access Container On Fargate

#!/bin/bash

# if see error, most likely task id is changed because of new build
aws ecs execute-command \
--region us-west-1 \
--cluster my-ecs-microservice \
--task 709909fdf605c4d7f9c3c206af862abc \
--container sandbox-contauner-foo-1 \
--command "/bin/bash"  --interactive

Find Target Group of A ECS Service

  • Click that service
  • Click network tab

Friday, 27 January 2023

Jenkins

Available Environment Variables

//See a list of environment variables
https://jenkins.mycomany.dev/env-vars.html/

//access environment variable in Jenksfile
build = "Build:${BUILD_NUMBER}"

See work place

cd /home/jenkins/workspace 

Check which docker image used by stack

In template file, image is mapped to BuildVersion.

  • go to Cloudformation
  • find the stack
  • click parameters
  • check BuildVersion

withCredentials explained

When the withCredentials block is executed, the specified credentials are made available only within that block by injecting enviornment variables. Once the block execution is complete, the environment variables are no longer available, ensuring the secrets are not exposed outside the block

stages {
        stage('Example') {
            steps {
                // Wrap steps with the credentials
                withCredentials([usernamePassword(credentialsId: 'my-credentials-id', 
                                                  usernameVariable: 'USER', 
                                                  passwordVariable: 'PASS')]) {
                    // Use the credentials. It shows we can access env variables USER and PASS
                    // because these two env variables have been injected by withCredentials
                    // in real life, you will not echo these credentials, but use it in your codes
                    sh 'echo "Username: $USER"'
                    sh 'echo "Password: $PASS"'
                }
            }
        }
    }

To manage credentials in Jenkins, go to Manage Jenkins; go to Manage Credentials

Run bash in Jenkins

sh "rm -rf composer/composer.json composer/composer.lock composer/vendor"
sh "composer.phar require aws/aws-sdk-php ^3.69 --ignore-platform-reqs --working-dir=composer"

Tuesday, 24 January 2023

Magic function __invoke

In PHP, __invoke() method is called when a script tries to call an object as a function

//example 1
<?php
class CallableClass
{
    public function __invoke($x)
    {
        var_dump($x);
    }
}
$obj = new CallableClass;

//treat object as function. Here var dump 5
$obj(5);

//will get true
var_dump(is_callable($obj));

//example 2
<?php
class CallableClass
{
    public function __invoke($x)
    {
        return $x + 100;
    }
}
$obj = new CallableClass;

$ans = $obj(5);

//Here var dump 105
var_dump($ans);

Thursday, 17 November 2022

browscap/browscap-php

What is browscap

browscap provides a database which can be used to find browser info if we have user agent.

//sample code for browscap 6.0.0
//sample user agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36
$cacheDir = APPLICATION_PATH . '__cache/';
$fileCache = new \League\Flysystem\Local\LocalFilesystemAdapter($cacheDir);
$filesystem = new \League\Flysystem\Filesystem($fileCache);
$cache = new \MatthiasMullie\Scrapbook\Psr16\SimpleCache(
    new \MatthiasMullie\Scrapbook\Adapters\Flysystem($filesystem)
);

$logger = new \Monolog\Logger('name');

$bc = new \BrowscapPHP\Browscap($cache, $logger);
$browser = $bc->getBrowser($userAgent);

Offical Documentation

How to find cache directory

Need two steps to set up cache directory. Use --cache flag to specify cache directory. For the following set up, cache directory is /var/src/__cache

  • vendor/bin/browscap-php browscap:fetch
  • vendor/bin/browscap-php browscap:convert --cache /var/src/__cache

If see the error below, do two steps above again and make sure $cacheDir is the same as what specify in step 2.

there is no active cache available, please use the BrowscapUpdater and run the update command

Slove BrowscapPHP\Exception: there is no active cache available, please use the BrowscapUpdater and run the update

$cacheDir = APP_ROOT . '/composer/__cache';
$fileCache = new \League\Flysystem\Local\LocalFilesystemAdapter($cacheDir);
$filesystem = new \League\Flysystem\Filesystem($fileCache);
$cache = new \MatthiasMullie\Scrapbook\Psr16\SimpleCache(
    new \MatthiasMullie\Scrapbook\Adapters\Flysystem($filesystem)
);

$logger = new \Monolog\Logger('name');

//run these two lines once and the problem is gone
// $bc = new \BrowscapPHP\BrowscapUpdater($cache, $logger);
//$bc->update(\BrowscapPHP\Helper\IniLoaderInterface::PHP_INI_FULL);

$bc = new \BrowscapPHP\Browscap($cache, $logger);

Wednesday, 2 November 2022

Mysql docker container not working

One day, all of sudden, Mysql db in my local does not work. I am using mysql docker container. Check docker container status, I can see it restarts continuely. Look at docker-compose file, and I see this line

restart: on-failure

Ok, it restarts because of failure. Try to container log

docker logs -f  docker_mysql_1

There is a line in log

InnoDB: Error number 28 means 'No space left on device'

Ah, run of space. Here is how to solve this problem

  • list all dangling volumes
    docker volume ls -qf dangling=true
  • remove these volues
    docker volume rm $(docker volume ls -qf dangling=true)
  • restart container

Before delete the volume, you can check which containers are using it

docker ps -a --filter volume=VOLUME_NAME

Find container details

docker container inspect container_id

Friday, 30 September 2022

Leetcode Note

use ascii table to solve question

If string consis of all lower english letter, we can define an array of [26]int. 'a' is 97

Ascii Table

Leetcode questions

integer to binary string

XOR

//try to find x
5^x = 2
5^5^x = 2^5
0^x = 2^5
x=2^5

Linked List

Set (golang does not have set)

Regular Expression

Sort String and remove duplicated characters

Math

Integer boundary

Design

Recursive Memoization

Backtracking

Here is a good explanation for Backtracking. What is Backtracking

One general method to solve combination related backtracking is to use breadth first search tree travesal. I found it is very handy, but may not efficient.

  • use a linked list to store element of every level
  • initiate the first level
  • start a while. Based on the question, decide when to exit the loop
  • inside the while loop, level by level using size and poll

Solution for Leetcode 17

class Solution {
    public List<String> letterCombinations(String digits) {
        List<String> ans = new ArrayList<>();
        if (digits.length()==0) return ans;

        HashMap<Character, String> map = new HashMap<>();
        map.put('2', "abc");
        map.put('3', "def");
        map.put('4', "ghi");
        map.put('5', "jkl");
        map.put('6', "mno");
        map.put('7', "pqrs");
        map.put('8', "tuv");
        map.put('9', "wxyz");

        int l = digits.length();

        LinkedList<String> list = new LinkedList<>();

        int h = 0;

        //the first level
        char c = digits.charAt(h);
        String s = map.get(c);
        for (int i=0; i<s.length(); i++) {
            list.add(Character.toString(s.charAt(i)));
        }

        while (h<l-1) {
            h++;
            int size = list.size();
            for (int i=0; i<size; i++) {
                String s1 = list.poll();
                String toAdd = map.get(digits.charAt(h));

                for (int j=0; j<toAdd.length(); j++) {
                    StringBuilder sb = new StringBuilder();
                    sb.append(s1);
                    sb.append(toAdd.charAt(j));
                    list.add(sb.toString());
                }
            }
        }

       
        while (list.size()>0) {
            ans.add(list.poll());
        }

        return ans;
        
    }
}


Check is Parentheses valid

private boolean isValid(String s) {
        int left = 0, right = 0;
        for (int i=0; i<s.length(); i++) {
            char c = s.charAt(i);
            if (c=='(') {
                left++;
            } else {
                right++;
            }

            if (right > left) return false;
        }

        return left==right;
    }

Thursday, 8 September 2022

Stop http to https redirect in Chrome

When do development, once a while Chrome starts to do this redirect. Because my local does not have valid certs, then a warning page shows up. The below is how to disable it.

Thursday, 1 September 2022

Braintree Integration and Apple Pay

How drop in UI works

  • Use code provided by Braintree to set up a page
  • Need to add merchant tokenization key in the above code. The key can be found in Braintree console
  • Also need to set the url where ajax can call when the submit button is clicked
  • In server side for that ajax call
    • get paymentMethodNonce such as tokencc_bf_jq8mhh_skhmmj_vf9883_9hzp6y***** which represent payment info customer provided.
    • use paymentMethodNonce to create a payment method
    • use payment method to create a transaction. Of course, that ajax call should also pass along amount and customer info
    • When done, send back response to that ajax call
  • After get ajax call response, we can show receipt page or show error based on response.
//use payment method nonce to generate a payment method. After get payment method, we can create transaction
$result = $gateway->paymentMethod()->create([
            'customerId' => '691651999',
            'paymentMethodNonce' => "tokencc_bf_jq8mhh_skhmmj_vf9883_9hzp6y_xxxxx"
        ]);

How paymentMethod nonce is generated

  • add a click event listener for submit button
  • inside listener, call requestPaymentMethod on the branstree instance
  • the second parameter payload has none. That is payload.nonce
var button = document.querySelector('#submit-button');

            braintree.dropin.create({
                authorization: 'sandbox_7bj5fsqp_j52y8m6kfndzsjr2',
                container: '#dropin-container'
            }, function (createErr, instance) {
                button.addEventListener('click', function () {
                    instance.requestPaymentMethod(function (requestPaymentMethodErr, payload) {
                        // Submit payload.nonce to your server
                        console.log("payload", payload)
                    });
                });
            })  ;

Setting up Apple Pay

Here is some general steps

However, to set up Apple Pay through Braintree, we need to adjust some steps

Debug Javascript of Web page on iPad by mac

  • In iPad, Settings - Safari - Advanced - enable Web Inspector
  • In Mac, go to Safari - Prefences - Advanced - Show Develop menu bar
  • Connect iPad with mac using usb cable
  • In iPad, open the webpag in Safari
  • In mac, open Safari, develop menu bar and find iPad; then click the web page
  • Console log will show up