Monday, 25 December 2023

Testing with Laravel Dusk

Example

<?php

namespace Tests\Browser;

use App\Models\User;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;

class PageListRonzanNotRecordedTest extends DuskTestCase
{
    /**
     * test_after_login_goto_list_ronzan_not_recorded
     */
    public function test_after_login_goto_list_ronzan_not_recorded(): void
    {
        $user = User::factory()->create();

        $this->browse(function (Browser $browser) use ($user) {
            $browser
                ->loginAs($user)
                ->visit('/ronzan-contract/list-not-record')
                ->assertSee('ロンザン 未計上一覧')
                ->assertSee('お選びください')
                ->assertSee('オプション');
        });
    }

     /**
     * test_click_menu_and_show_menus
     */
    public function test_click_menu_and_show_menus(): void
    {
        $user = User::factory()->create();

        $this->browse(function (Browser $browser) use ($user) {
            $browser
                ->loginAs($user)
                ->visit('/ronzan-contract/list-not-record')
                ->click('#app > div > nav > div.space-y-6.div-main > div > div:nth-child(1) > div > i')
                ->assertSee('社員マスタ');
        });
    }

    /**
     * test_click_menu_and_show_menus
     */
    public function test_click_select_action(): void
    {
        $user = User::factory()->create();

        $this->browse(function (Browser $browser) use ($user) {
            $browser
                ->loginAs($user)
                ->visit('/ronzan-contract/list-not-record')
                ->waitFor('main > div > div > div.bg-dropDownOption > div > select')
                ->click('main > div > div > div.bg-dropDownOption > div > select')
                ->assertSee('お選びください')
                ->assertSee('CSVデータインポート')
                ->assertSee('新規案件を取得する')
                ->assertSee('再度吸い上げて編集する')
                ->assertSee('吸い上げずに編集する')
                ->assertSee('仮計上する')
                ->assertSee('本計上する')
                ->assertSee('削除する')
                ->assertSee('CSVデータインポート');
        });
    }

    /**
     * test_click_menu_and_show_menus
     */
    public function test_click_select_load_new_contract(): void
    {
        $user = User::factory()->create();

        $this->browse(function (Browser $browser) use ($user) {
            $browser
                ->loginAs($user)
                ->visit('/ronzan-contract/list-not-record')
                ->waitFor('main > div > div > div.bg-dropDownOption > div > select')
                ->click('main > div > div > div.bg-dropDownOption > div > select')
                ->waitFor('main > div > div > div.bg-dropDownOption > div > select > option:nth-child(3)')
                ->click('main > div > div > div.bg-dropDownOption > div > select > option:nth-child(3)')
                ->waitForText('新規案件を取得しますか?')
                ->waitForText('はい')
                ->waitFor('#headlessui-portal-root > div > div > div > div > div > div > div > button:nth-child(1)')
                ->click('#headlessui-portal-root > div > div > div > div > div > div > div > button:nth-child(1)')
                ->waitForText('新規案件を取得するのが成功しました');
        });
    }
}

 Thank you

Thursday, 21 December 2023

Laravel + Inertia + Vue3 + Typescript add plugin multi languages vue-i18n

1. Install package i18n

npm install vue-i18n

2. Add files languages

 resources\lang\en.json

{
    "world": "HELLO EN"
}

 resources\lang\vi.json

{
    "world": "HELLO VI"
}

3. Add plugin resources\js\plugins\langPlugin.ts

import { App } from 'vue';
import { createI18n, type I18nOptions } from 'vue-i18n';
import en from '../../lang/en.json';
import vi from '../../lang/vi.json';

const langPlugin = {
install(app: App, pluginOptions: any) {
const langOptions: I18nOptions = {
legacy: false,
locale: pluginOptions.locale ? pluginOptions.locale : 'en',
messages: {
en: en,
vi: vi,
},
};
const i18n = createI18n(langOptions);
app.use(i18n);
console.log(app);
},
};

export default langPlugin;

* need: legacy: false, see more >>

4. Update resources\js\app.ts

createInertiaApp({
    title: (title) => `${title} - ${appName}`,
    resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob<DefineComponent>('./Pages/**/*.vue')),
    setup({ el, App, props, plugin }) {
        createApp({ render: () => h(App, props) })
            .use(plugin)
            .use(ZiggyVue)
            .use(langPlugin, {})
            .mount(el);
    },
    progress: {
        color: '#4B5563',
    },
});

5. Using I18n

- In template

   {{ $t('world') }}

- in <script setup>

import { useI18n } from 'vue-i18n';
const { t } = useI18n();
console.log(t('hello'));

Thank you

Tuesday, 17 October 2023

Using Cors in Adonisjs 5

1: enable false => true

/*
|--------------------------------------------------------------------------
| Enabled
|--------------------------------------------------------------------------
|
| A boolean to enable or disable CORS integration from your AdonisJs
| application.
|
| Setting the value to `true` will enable the CORS for all HTTP request. However,
| you can define a function to enable/disable it on per request basis as well.
|
*/
enabled: true,

2. set origin value


// You can also use a function that return true or false.
// enabled: (request) => request.url().startsWith('/api')

/*
|--------------------------------------------------------------------------
| Origin
|--------------------------------------------------------------------------
|
| Set a list of origins to be allowed for `Access-Control-Allow-Origin`.
| The value can be one of the following:
|
| https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
| Boolean (true) - Allow current request origin.
| Boolean (false) - Disallow all.
| String - Comma separated list of allowed origins.
| Array - An array of allowed origins.
| String (*) - A wildcard (*) to allow all request origins.
| Function - Receives the current origin string and should return
| one of the above values.
|
*/
origin: ['http://localhost'],



Thank you


Sunday, 15 October 2023

Open port for PostgreSQL

=====================

- sudo nano /etc/postgresql/{version}/main/postgresql.conf

+ listen_addresses = '*'

// + port = 5433 (no needed)

- sudo nano /etc/postgresql/{version}/main/pg_hba.conf

+ host    all             all             [Your_IP_Can_Access]/32        md5 

- sudo service postgresql restart

- sudo ufw allow 5433/tcp
=====================
- access as root
$ sudo -u postgres psql
- show all dbs
$ \l

Connect to the PostgreSQL Database 
\c taximailinhdb;

Grant all privileges on the database:
GRANT ALL PRIVILEGES ON DATABASE hangchatgiagocdb TO hangchatgiagocuser;

Grant all privileges on all existing tables in the database: 
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO hangchatgiagocuser;

Grant all privileges on all sequences (if you are using sequences): 
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO hangchatgiagocuser;

Optionally, grant privileges on all future tables and sequences:
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO hangchatgiagocuser;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO hangchatgiagocuser;

Step 3: Verify Permissions (Optional) 
\dp

Tuesday, 5 September 2023

Laravel CI/CD with Jenkins, Docker, AWS

1. Install docker, docker compose

- add current $USER to docker group

sudo usermod -aG docker $USER

2. Download source code and build in local using docker-compose

https://github.com/thecaringdeveloper/laravel-cicd-tutorial (1)

docker-compose.yml

3. Install, start Jenkins

- install java

- install jenkins

- start jenkins

- access jenkins localhost:8090

- add user jenkins to docker group: sudo usermod -aG docker jenkins

- add plugin for jenkins

+ Pipeline Utility Steps
+ File Operations
+ SSH Agent

4. Create github repository

- push code in (1) to repository

5. Setup pipeline in source local (a job)

- create Makefile

- create Jenkinsfile

- create a job:

 Enter an item name/ Pipeline 

/ General: Testing CICD

/ Pipe script from SCM

/ Repository URL: get from repository in github

/ Credentials select credential create below

/ Branch: select branch in git repository

/ Script path: Jenkinsfile

6. create Credential for jenkins connect to github

Manage Jenkins/ manage credentials/ global /add credentials

    / Kind: SSH username with private key 

    / scope: Global (jenkins, nodes, items, all child items, ect)

    / id: github

    / username: your_github_username

    / create private, public key: private_key for jenkins, public_key  for github

    / Create

* Create a job for CICD

    / name: laravel-test

    / Pipeline: Piple script from SCM

    / SCM: git

    / Repository Url: Your_repo_url

    / Credentials:credential already create above

    / Script path: jenkinsfile 

7. Populate .env by using jenkin

+ copy .env file to jenkin: 

    / create folder in workspace jenkins include .env file: var/lib/jenkins/workspace/envs/laravel-test

    / create .env file in var/lib/jenkins/workspace/envs/laravel-test

    / add script to copy this .env file when deploy 

stage("Populate .env file") {
steps {
dir("/var/lib/jenkins/workspace/envs/LaravelTest02") {
fileOperations([fileCopyOperation(excludes: '', flattenFiles: true, includes: '.env', targetLocation: "${WORKSPACE}")])
}
}
}

- Run pipeline

6. Setup EC2

- get key-ec2.pem to ssh to ec2

- chmode  400 key-ec2.pem

- create credential for ec2 in jenkins

7. Create Artifact 

Create artifact.zip file

8. Copy Artifact artifact.zip file to ec2

update Jenkinsfile to copy Artifact artifact.zip file to ec2

9. Deploy on ec2, install server on ec2

- install server: sudo yum -y install httpd

- start server: sudo service httpd start

- edit inbound, outbound rules

- install php and extension: sudo yum install php-{...}

- set permission: sudo chown ec2-user:ec2-user /var/www/html

- sudo server httpd restart

Jenkinsfile

pipeline {
agent any
stages {
stage("Verify SSH connection to server") {
steps {
sshagent(credentials: ['aws-ec2']) {
sh '''
ssh -o StrictHostKeyChecking=no ec2-user@52.221.199.29 whoami
'''
}
}
}
stage("Verify tooling") {
steps {
sh '''
docker info
docker version
docker compose version
'''
}
}
stage("Clear all running docker containers") {
steps {
script {
try {
sh 'docker rm -f $(docker ps -a -q)'
} catch (Exception e) {
echo 'No running container to clear up...'
}
}
}
}

stage("Start Docker") {
steps {
sh 'make up'
sh 'docker compose ps'
}
}

stage("Run Composer Install") {
steps {
sh 'docker compose run --rm composer install'
}
}

stage("Populate .env file") {
steps {
dir("/var/lib/jenkins/workspace/envs/LaravelTest02") {
fileOperations([fileCopyOperation(excludes: '', flattenFiles: true, includes: '.env', targetLocation: "${WORKSPACE}")])
}
}
}

stage("Run Tests") {
steps {
sh 'docker compose run --rm artisan test'
}
}
}

post {
success {
sh 'cd "/var/lib/jenkins/workspace/LaravelTest02"'
sh 'rm -rf artifact.zip'
sh 'zip -r artifact.zip . -x "*node_modules**"'
withCredentials([sshUserPrivateKey(credentialsId: "aws-ec2", keyFileVariable: 'keyfile')]) {
sh 'scp -v -o StrictHostKeyChecking=no -i ${keyfile} /var/lib/jenkins/workspace/LaravelTest02/artifact.zip ec2-user@52.221.199.29:/home/ec2-user/artifact'
}
sshagent(credentials: ['aws-ec2']) {
sh 'ssh -o StrictHostKeyChecking=no ec2-user@52.221.199.29 unzip -o /home/ec2-user/artifact/artifact.zip -d /var/www/html'
script {
try {
sh 'ssh -o StrictHostKeyChecking=no ec2-user@52.221.199.29 sudo chmod 777 /var/www/html/storage -R'
} catch (Exception e) {
echo 'Some file permissions could not be updated.'
}
}
}
}
always {
sh 'docker compose down --remove-orphans -v'
sh 'docker compose ps'
}
}
}

Makefile

#!/usr/bin/make

SHELL = /bin/sh

UID := $(shell id -u)
GID := $(shell id -g)
USER:= $(shell whoami)

export UID
export GID
export USER

up:
docker compose up -d

Thank you

 

Git merge, reset, commit --amend, rebase, pick cherry, handle conflict

There are two branch
commit on branch A: 123|4.......5
commit on branch B: 123|...678 (checkout from branh A from commit 3, after add commit 678)
---------------------------------------------------
* Git merge: 
add commits from branch B to branch A follow time create commit
- git checkout A
- git merge B
- handle conflict commits: 3 5 8
- git add .
- git commit -m"M"
=> A: 123|4|678|5|M
--------------------------------------------------
* Git rebase: 
add all commits from branch B to branch A at left side of branch A from postion of commit of both branch
- git checkout A
- git rebase B
- handle conflict commit: 8 to 4 -> git add . -> git rebase --continue => A: 123|678|4
- handle conflict commit: 4 to 5 -> git add . -> git rebase --continue => A: 123|678|45
- alert result rebase, handle commits with p,s,.. can change name commit in nano, ex 4 => 4'
- ctrl+x -> y to save
=> A: 123|678|4'5
* git rebase itself: select commit want to keep
- git rebase -i idCommit
- git checkout A (A: 12345)
- git rebsae -i 2
- handle conflict commit: 2 to 3 -> git add . -> git rebase --continue => A: 12|3
- handle conflict commit: 3 to 4 -> git add . -> git rebase --continue => A: 12|34
- handle conflict commit: 4 to 5 -> git add . -> git rebase --continue => A: 12|345
- alert result rebase, handle commits with p,s,.. can change name commit in nano, ex 3 => 3', 4->4'
- ctrl+x -> y to save
=> A: 12|3'4'5
---------------------------------------------------
* Git reset: remove commit from a branch 
+ git reset --soft idCommit  # Removes from HEAD to idCommit, keeps changed staged
+ git reset --hard idCommit # Removes from HEAD to idCommit, DONT keeps changed staged
---------------------------------------------------
* Git commit --amend: to change a last commit message.
+ git commit --amend -m "........"
+ git push origin BranchName -f
* Note: 
 
- Have branh A, PR A to DEV, PM agree merge
- if you commit --amend branch A, after PR A to DEV -> Conflict
---------------------------------------------------

* Git pick cherry: pick commit from a Branch to add for orther branch
- git checkout A
- git cherry-pick -e 7 (git cherry-pick -e idCommit)
-> A: 12345|7
or
- git checkout B
- git cherry-pick -e 5 (git cherry-pick -e idCommit)
-> B: 123678|5
---------------------------------------------------
* Git handle conflict when PR
- git checkout A
- git add .
- git commit -m"..."
- git push origin A
- PR: from A to DEV -> conflict
=> handle
- git checkout A
- git checkout -b A-fix-conflict // create this branch to fix-conflict // because dont want get code from DEV to merge to A
- git checkout DEV
- git pull origin DEV
- git checkout A-fix-conflict
- git merge DEV
- handle fix conflict in local
- git push origin A-fix-conflict
- PR from A-fix-conflict to DEV 
- PM aggree merge
=> now A can PR to DEV


Thank you


 

Publish npm package

  Để publish   pav-kit  lên NPM, bạn hãy làm theo các bước dưới đây. Tôi đã tạo thêm file  index.js  để đảm bảo gói tin hợp lệ. Bước 1: Tạo ...