Saturday, February 25, 2023

useMemo Hook

useMemo prevents unnecessary rendering of child components:

import React, { useState, useEffect, useMemo } from "react";
 
const Child = ({ param }) => {
  useEffect(() =>
    console.log("child rendered at ", new Date().toLocaleTimeString())
  );
  return <div>prop in child: {param}</div>;
};
 
const App = () => {
  const [counter, setCounter] = useState(3);
  const [childProp, setChildProp] = useState(30);
 
  const handleChange = () => setCounter((prev) => prev + 1);
  const handleChangeChild = () => setChildProp((prev) => prev + 10);
 
  const memoChild = useMemo(() => <Child param={childProp} />, [childProp]);
 
  return (
    <>
      {counter}
      <br />
      <button onClick={handleChange}>change parent counter</button>
      <button onClick={handleChangeChild}>change child prop</button>
      {memoChild}
    </>
  );
};
 
export default App;
 

Monday, October 31, 2022

React useState Hook

Working with Array of Objects and Object


another example with delete update



Thursday, September 1, 2022

NodeJS and ES6

Bable can transpile ES6 code to JS code that can be processed by Node.

Folder and file structure:






Here is package.json
{
  "name": "node-server-using-es6",
  "version": "1.0.0",
  "description": "Node Server compiles code to ES6 using babel",
  "main": "lib/index.js",
  "scripts": {
    "build": "rimraf build && babel src -d dist",
    "start": "nodemon --exec babel-node src/index",
    "prod": "npm run build && node dist/index.js"
  },
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.4.0",
    "@babel/node": "^7.18.10",
    "@babel/preset-env": "^7.4.2",
    "@babel/register": "^7.4.0",
    "nodemon": "^1.17.5"
    "rimraf": "^3.0.2"
  },
  "dependencies": {
    "express": "^4.18.1"
  }
}

Here is .babelrc
{
  "presets": ["@babel/preset-env"]
}

Here is src/index.js in ES6
import express from 'express';
const app = express();
const PORT = 4000;

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(4000, () => {
  console.log(`app is running on ${PORT}`);
});

for production npm run build this will populate build folder
for development npm start

Thursday, January 27, 2022

Launch Browser in Code

For React project create folder .vscode an in it launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "chrome",
            "request": "launch",
            "name": "Launch Chrome on localhost:3000",
            "url": "http://localhost:3000",
            "webRoot": "${workspaceFolder}/src",
            "userDataDir": "${workspaceFolder}/.vscode/chrome"
        }
    ]
}

This will create separate browser for development.

Friday, January 21, 2022

React callback

Passing function as argument and callback in setState.
import React from 'react';

export default class App extends React.Component {
  state = {
    users: [],
    status: 'initial',
  };

  componentDidMount() {
    this.getUsers();

    this.myCoolFunction('first', 'second', (result) => console.log('yay the result is', result));
    this.myCoolFunction('first', 'second');
  }

  myCoolFunction = ( param1, param2, callback = () => console.log("function is not passed") ) => {
    let result = 'do stuff with ' + param1 + ' ' + param2;
    console.log(callback)
    console.log('just before callback(result)');
    callback(result);
  };

  getUsers = async (id) => {
    setTimeout(() => {
      this.setState({
        status: 'searching',
        users: [],
      });
    }, 3000);

    setTimeout(() => {
      this.setState(
        {
          status: 'done',
          users: [
            { id: 23, name: 'gdfgd' },
            { id: 24, name: 'weqweeq' }
          ],
        },
        () => this.state.users.map((user) => console.log(user.name))
      );
    }, 5000);
  };

  render() {
    if (this.state.status === 'initial') {
      return <div>ini</div>;
    }
    if (this.state.status === 'searching') {
      return <div>searching</div>;
    }
    if (this.state.status === 'done') {
      return (
        <ul>
          {this.state.users.map((user) => (
            <li key={user.id}>{user.name}</li>
          ))}
        </ul>
      );
    }
  }
}

Monday, November 22, 2021

Gulp & React

Replace & Delete files in build folder using Gulp gulpfile.js
 
const gulp       = require('gulp');
const replace    = require('gulp-string-replace');
const rimraf     = require('gulp-rimraf');

function changeLang() {
    const searchFor = '<html lang="en">';
    const replaceBy = '<html lang="de">';
  
    return gulp.src('./build/*.html')
      .pipe(replace(searchFor, replaceBy))
      .pipe(gulp.dest('./build'));
}

function deleteFiles () {
  return gulp
    .src('./build/static/js/*.*.txt', { read: false })
    .pipe(rimraf());
}

exports.change_lang = changeLang;
exports.delete_files = deleteFiles;

package.json

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "4.0.3",
    "web-vitals": "^1.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build && npm run gulp-change-language && npm run gulp-delete_files",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "gulp-change-language": "gulp change_lang",
    "gulp-delete_files": "gulp delete_files"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "gulp": "^4.0.2",
    "gulp-rimraf": "^1.0.0",
    "gulp-string-replace": "^1.1.2"
  }
}

Test gulp

npm run build

Friday, November 19, 2021

CSP and Dynamically Generated JavaScript tag

CSP HTTP header and Meta Tag must be synchronized. There must be two hashes one for hard coded JavaScript other for generated JavaScript tag. 

index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Content Security Policy Demo</title>
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; font-src 'self' ; img-src 'self' https://images.unsplash.com ; script-src 'self' 'sha256-B/8L7bCoRRLbK80hqzXcLc/NSWDtHVADDkdElr18Q2U=' 'sha256-BQBWQMByG186hXi3h+x/zUK8eOCr3iy91FDzS9uKCNs='; style-src 'self' https://cdn.jsdelivr.net; frame-src 'self'">
    <link rel="stylesheet" href="style.css" />
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css"
      integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2"
      crossorigin="anonymous"
      />
    <script src="node_modules/jquery/dist/jquery.min.js"></script>
  </head>
  <body>
    <script type = "text/javascript" >
      $(document).ready(function() {
         $('div.grid').after('<div>new element</div>');
      });
      let newScript = document.createElement("script");
      let inlineScript = document.createTextNode("alert('Hello World!');");
      newScript.appendChild(inlineScript);
      $(document.body).append(newScript);
    </script>
    <div class="box">
      <div class="grid">
        <img
          src="https://images.unsplash.com/photo-1535089780340-34cc939f9996?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80"
          alt=""
          />
        <img
          src="https://images.unsplash.com/photo-1587081917197-95be3a2d2103?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80"
          alt=""
          />
        <img
          src="https://images.unsplash.com/photo-1502248103506-76afc15f5c45?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"
          alt=""
          />
      </div>
    </div>
  </body>
</html>
server.js
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const app = express();
 
app.use(function (req, res, next) {
  res.setHeader(
    'Content-Security-Policy',
    "default-src 'self'; font-src 'self' ; img-src 'self' https://images.unsplash.com ; script-src 'self' 'sha256-B/8L7bCoRRLbK80hqzXcLc/NSWDtHVADDkdElr18Q2U=' 'sha256-BQBWQMByG186hXi3h+x/zUK8eOCr3iy91FDzS9uKCNs='; style-src 'self' https://cdn.jsdelivr.net; frame-src 'self'"
  );
  next();
});
 
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname)));
 
app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname + '/index.html'));
});
 
const server = app.listen(process.env.PORT || 5500, () => {
  const { port } = server.address();
  console.log(`Server running on PORT ${port}`);
});
 
package.json
{
  "name": "csp-javascript",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "npx nodemon server.js"
  },
  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "jquery": "^3.6.0"
  },
  "devDependencies": {
    "nodemon": "^2.0.6"
  }
}
 

Monday, November 15, 2021

GitLab Pipeline and React

GitLab for React

Make sure you are using same node image in the pipeline and Dockerfiles

optimised .gitlab-ci.yml using artifacts
stages:
  - build
  - test
  - push

image:
  name: node:16-alpine
  entrypoint: [""]

build_r:
  stage: build
  artifacts:
    paths:
      - node_modules/
      - package.json
      - package-lock.json
    expire_in: 1 day
  before_script:
    - apk -U upgrade
  script:
    - node -v
    - ls -lA
    - npm install
    - ls -lA
  only:
    - main

test_r:
  stage: test
  before_script:
    - apk -U upgrade
  script:
    - ls -lA
    - CI=true npm test
  only:
    - main

push_r:
  stage: push
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
    - /kaniko/executor --cache=false --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile-prod --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA 
  only:
    - main
 
.gitlab-ci.yml using cache
stages:
  - build
  - test
  - push

cache:
  key: "$CI_COMMIT_REF_SLUG"
  paths:
    - node_modules/

image:
  name: node:16-alpine

build_r:
  stage: build
  before_script:
    - export ENV_VAR="value"
  script:
    - node -v
    - echo "$ENV_VAR"
    - echo "$CI_COMMIT_REF_SLUG"
    - npm install
  only:
    - main

test_r:
  stage: test
  script:
    - echo "$CI_COMMIT_REF_SLUG"
    - CI=true npm test
  only:
    - main

push_r:
  stage: push
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
    - /kaniko/executor --cache=false --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile-prod --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA 
  only:
    - main
 
Dockerfile
FROM node:16-alpine

RUN apk -U upgrade

WORKDIR /app

COPY package.json ./

RUN npm install

COPY ./ ./

CMD ["npm", "start"]
Dockerfile-prod
FROM node:16-alpine as build

WORKDIR /usr/app

COPY package.json .

RUN npm install

COPY  . .

RUN REACT_APP_BASE_URL=example.com yarn build

FROM nginx:1.19.0

WORKDIR /usr/share/nginx/html

RUN rm -rf ./*

COPY --from=build /usr/app/build /usr/share/nginx/html

ENTRYPOINT ["nginx", "-g", "daemon off;"]
This will work for any react app that has tests.

On target machine:
docker login registry.gitlab.com
docker run -it --name react registry.gitlab.com/USER-NAME/IMAGE-NAME:TAG

Friday, September 10, 2021

Apache on Ubuntu

Dockerfile for installing Apache on Ubuntu:

FROM ubuntu:20.04

ENV TZ=Europe/Berlin

RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN apt-get update && apt-get -y install apache2

EXPOSE 80

CMD ["/usr/sbin/apachectl", "-D", "FOREGROUND"]

Build and run

docker build -t ubu-apache .
docker run --name=ubu-apache -d -p 80:80 -v $(pwd)/html/:/var/www/html/ ubu-apache

Enter in container. Tail logs and list files in html folder

docker exec -it ubu-apache /bin/bash

root@8c138884f9a3:/# tail -f /var/log/apache2/error.log

root@8c138884f9a3:/# tail -f /var/log/apache2/access.log

root@8c138884f9a3:/# ls -l /var/www/html

Sunday, July 18, 2021

React CheckBox

Working with React Bootstrap checkbox


 import React, { PureComponent } from "react";
import Form from "react-bootstrap/Form";

interface CheckBoxState {
  checkedColors: any[];
}

interface CheckBoxProps {
  foo: string;
}

class CheckBox extends PureComponent<CheckBoxProps, CheckBoxState> {
  constructor(props: CheckBoxProps) {
    super(props);
    this.state = {
      checkedColors: [
        { isChecked: false, name: "chk1", label: "blue" },
        { isChecked: false, name: "chk2", label: "red" },
      ],
    };
  }

  render() {
    return (
      <div>
        {this.state.checkedColors.map((item: any) => (
          <Form.Check
            type="checkbox"
            id={item.name}
            inline
            key={item.name}
            checked={item.isChecked}
            onChange={(event: any) => this.handleChange(event)}
            label={item.label}
          />
        ))}
        <button onClick={() => console.log(this.state.checkedColors)}>
          Show checked colors
        </button>

        <hr />
        {this.props.foo}
      </div>
    );
  }

  handleChange(event: any): void {
    const checkedColors = [...this.state.checkedColors];

    checkedColors.map((item: any) => {
      if (item.name === event.target.id) item.isChecked = event.target.checked;
    });

    this.setState({
      checkedColors,
    });
  }
}

export default CheckBox;
Note that this.state.checkedColors can be cloned using spread operator 'cause the array is shallow.

Friday, May 7, 2021

Postgres & Adminer on Docker

Postgres & Adminer on Docker stack.

version: '3'

services:

  postgres:
    image: postgres:13.2-alpine
    restart: always
    environment:
      POSTGRES_PASSWORD: secret
      POSTGRES_USER: pguser
      POSTGRES_DB: pgdb
    ports:
      - "5432:5432"
    volumes:
      - pg-data:/var/lib/postgresql/data
    networks:
      - pg-network
 

  adminer:
    image: adminer:4.8.0-standalone
    restart: always
    ports:
      - "8080:8080"
    networks:
      - pg-network

networks:
  pg-network:
    driver: bridge

volumes:
  pg-data:
    driver: local
This will set up the two services on the same network and assign a volume for the database.

Thursday, April 15, 2021

TypeScript Question Mark and Exclamation Mark

Here is how to use in combination to determine if property is null or undefined.

interface Product {
    id: number,
    name?: any 
}

let product: Product = {id: 5, name: "some str"};

if(product!.name) console.log("ok")

product = {id: 5}
if(product!.name) console.log("not printed")

product = {id: 5, name: null}
if(product!.name) console.log("not printed")

Second if will not be excecated 'cause property name is not defined. Third if will not be execute 'cause property name is null.

Thursday, February 4, 2021

Oracle Spool

Here is how to create a log file for your script:
SET echo ON
SET feedback ON
SET SERVEROUTPUT ON
SET linesize 500
COL spoolname new_value filename
SELECT TO_CHAR(systimestamp, 'YYYY_MM_DD_HH24_MI_') || 
	(SELECT SYS_CONTEXT ('userenv', 'current_schema') FROM  dual) || 
	'_log_name.log' AS spoolname FROM dual;
SPOOL &filename

-- start main SQL script
INSERT INTO my_table (total) VALUES (25);
-- end main SQL script

SPOOL OFF
EXIT;
This will create a log file with name like this 2019_11_11_23_00_schema_name_log_name.log.

Thursday, January 21, 2021

Oracle Package

Oracle package with procedure, function and global variable

CREATE OR REPLACE PACKAGE my_package AS
    g_fav_num NUMBER := 5;
    PROCEDURE my_proc (val INTEGER);
    FUNCTION  double_func (in_val IN NUMBER) RETURN NUMBER; 
END my_package;
/

CREATE OR REPLACE PACKAGE BODY my_package ASPROCEDURE my_proc (val INTEGER) IS
  BEGIN
      g_fav_num := g_fav_num + val;
      INSERT INTO my_table ( total ) VALUES ( double_func(g_fav_num) );
  END;

  FUNCTION double_func (in_val IN NUMBER) RETURN NUMBER IS 
  BEGIN 
    RETURN in_val*2;
  END;

END my_package;
/

exec my_package.my_proc(1000);

Thursday, December 17, 2020

Logging Table in Oracle

In Oracle 12c  you create table:

CREATE TABLE msg_log (
    id            NUMBER
        GENERATED BY DEFAULT ON NULL AS IDENTITY,
    ts            TIMESTAMP WITH LOCAL TIME ZONE,
    schema_name   VARCHAR2(15),
    message       VARCHAR2(250)
);

 Create procedure:

CREATE OR REPLACE PROCEDURE log_message (
    message IN VARCHAR2
) AS
    PRAGMA autonomous_transaction;
    schema_name VARCHAR2(15);
BEGIN
    SELECT
        sys_context('userenv', 'current_schema')
    INTO schema_name
    FROM
        dual;

    INSERT /*+ APPEND */ INTO msg_log (
        ts,
        schema_name,
        message
    ) VALUES (
        SYSDATE,
        schema_name,
        message
    );

    COMMIT;
END log_message;
Test
EXEC log_message('foo');

SELECT
    id,
    TO_CHAR(ts, 'YYYY-MM-DD HH24:MM:SS') AS log_date,
    message
FROM
    msg_log; -- where ts > '17-DEC-20 05.05.52.000000000 PM';

Wednesday, December 9, 2020

Oracle Stored Procedure

Sample of Oracle stored procedure with in & out params & exceptions.

CREATE OR REPLACE PROCEDURE proc_name (
  p1 IN VARCHAR2, 
  p2 OUT VARCHAR2
) 
AS 
  msg VARCHAR2(30) := 'hello ';
  ex_some_exception EXCEPTION;

BEGIN 
  IF length(p1) < 2 THEN RAISE ex_some_exception;
  END IF;
  p2 := msg || p1;
EXCEPTION 
  WHEN ex_some_exception THEN raise_application_error(-20001, 'name to short');
  WHEN OTHERS THEN raise_application_error(-20002, 'An error was encountered');
END proc_name;
/

Testing in & out params.

SET SERVEROUTPUT ON
 
DECLARE 
  full_hello VARCHAR(50);
BEGIN 
  proc_name('Mike', full_hello);
  DBMS_OUTPUT.put_line(full_hello);
END;
/

Sunday, August 30, 2020

Kill Session in Oracle

Launch SQLPlus:

[oracle@localhost ~]$ sqlplus
Enter user-name: hr

Enter password: 

EXEC HI -- HI is long running stored procedure

Connect to Oracle as DBA and replace HR with you user

sqlplus sys as sysdba

select sid, serial#, status, program from v$session where username='HR';

ALTER SYSTEM KILL SESSION 'sid, serial#';

In SQL Developer:

select s.username,s.sid,s.serial#,s.last_call_et running,q.sql_text from v$session s 
join v$sqltext_with_newlines q
on s.sql_address = q.address
where status='ACTIVE'
and type <>'BACKGROUND'
and s.username = 'HR'
order by sid,serial#,q.piece;

Tuesday, June 30, 2020

OpenShift on Public IP

Install OpenShift Origin on cloud machine with public IP

Login to remote host

$ ssh -i private_key user@nnn.nnn.nnn.nn

Firewall settings cat /etc/firewalld/zones/public.xml

<?xml version="1.0" encoding="utf-8"?>
<zone>
  <short>Public</short>
  <description>For use in public areas..</description>
  <service name="ssh"/>
  <service name="dhcpv6-client"/>
  <service name="http"/>
  <service name="https"/>
  <port protocol="tcp" port="443"/>
  <port protocol="tcp" port="3000"/>
  <port protocol="tcp" port="3008"/>
  <port protocol="tcp" port="8000"/>
  <port protocol="tcp" port="8080"/>
  <port protocol="tcp" port="8443"/>
  <port protocol="udp" port="53"/>
</zone>

Install Docker

Docker version

# docker version
Client:
 Version:            19.03.11-ol



Download OpenShift and install as root

wget https://github.com/openshift/origin/releases/download/v3.7.2/openshift-origin-client-tools-v3.7.2-282e43f-linux-64bit.tar.gz
tar xf openshift-origin-client-tools-v3.7.2-282e43f-linux-64bit.tar.gz
cd openshift-origin-client-tools-v3.7.2-282e43f-linux-64bit/
mv oc /usr/bin
oc cluster up --public-hostname=


Friday, June 12, 2020

NodeJS Template Engine

Node file app.js

var nunjucks = require('nunjucks')

var res = nunjucks.render(
  'foo.html', {
    messages: ['foo', 'bar'],
    username: "mike"
  }
);

console.log(res)
template file foo.html
{% if username === 'mike' %}
    {{ username }}
{% endif %}

{% for message in messages %}
    {{ message }}
{% endfor %}
Run
node app.js
Practical example for oracle log message
var nunjucks = require('nunjucks')

var res = nunjucks.renderString(
  `
  '{{ "debug output " }}' || {% for var in variables %} '{{ var }} = ' || {{ var }} || {% endfor %}
`, {
    variables: ['foo', 'bar', 'baz']
  }
);

console.log(res.slice(0,-4))
Output
'debug output ' ||  'foo = ' || foo ||  'bar = ' || bar ||  'baz = ' || baz

Tuesday, December 17, 2019

Helm Chart for MySQL in GKE

Here is how to deploy and test connectivity of MySQL in Google Kubernetes Engine GKE

# connect to cluster in Google Kubernetes Engine
$ gcloud container clusters get-credentials <your-cluster> --zone <your-zone> --project <your-project>
$ git clone --depth=1 https://github.com/helm/charts.git
$ cd charts/stable/mysql
$ vim values.yaml
$ helm install --name dbserver -f values.yaml stable/mysql
$ kubectl get pods
$ MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default dbserver-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo)
$ echo $MYSQL_ROOT_PASSWORD
$ kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il
root@ubuntu:/# apt-get update && apt-get install mysql-client -y
root@ubuntu:/#  mysql -h dbserver-mysql -p
mysql> exit
root@ubuntu:/# exit
$ kubectl get pvc
$ kubectl get pv
 
# clean up
$ helm delete --purge dbserver
$ kubectl delete pod/ubuntu