Categories
React TypeScript

Using tableLayout CSS property with TypeScript

Using table-layout property with React and TypeScript was giving a type error like below:

ERROR in /var/jenkins/workspace/dev/resources/packages/sites/table.tsx
[tsl] ERROR in /var/jenkins/workspace/dev/resources/packages/sites/table.tsx(79,25)
      TS2322: Type '{ padding: number; width: string; margin: string; tableLayout: string; }' is not assignable to type 'CSSProperties'.
  Types of property 'tableLayout' are incompatible.
    Type 'string' is not assignable to type 'TableLayout'.

This was the styling code:

const getListStyle = () => ({
    padding: 8,
    width: '100%',
    margin: '0px auto',
    tableLayout: 'auto',
});

React/Typescript JSX code:

<table style={getListStyle()}>
    <Header />
    <Body />
</table>

For some reason, table-layout doesn’t seem to accept string values. Although, as per the doc, it should be accepted.

This is the full typescript error reported by the IDE on the style attribute:

TS2322: Type ‘{ padding: number; width: string; margin: string; tableLayout: string; }’ is not assignable to type ‘CSSProperties’.   Types of property ‘tableLayout’ are incompatible.     Type ‘string’ is not assignable to type ‘TableLayout’. index.d.ts(1765, 9): The expected type comes from property ‘style’ which is declared here on type ‘DetailedHTMLProps, HTMLTableElement>’

The fix was to simply add the return type CSSProperties explicity like this:

const getListStyle = (): CSSProperties => ({
    padding: 8,
    width: '100%',
    margin: '0px auto',
    tableLayout: 'auto',
});

Categories
Web Development

CSS Flip Animation for Font Awesome Icons

This flip animation will be a perfect use-case for enable/disable UI actions.

We’ll be using rotateY transform function to achieve this flip animation.

Here is a simple HTML page with a few font awesome icons in both enabled and disabled states (we’ll be adding CSS for these states later) and jquery included for click actions.

<html>
  <head>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />
  </head>
  <body>
    <i class="fa fa-envelope enabled"></i>
    <i class="fa fa-comment-alt enabled"></i>
    <i class="fa fa-camera disabled"></i>
    <i class="fa fa-bell disabled"></i>
  </body> 
</html>

Here is the CSS for enabled and disabled states with the rotateY animation.

.flip {
    animation: flip-icon 0.3s ease-in-out;
}

body {
  text-align: center; 
  width: 100%;
}

.fa {
  cursor: pointer;
  font-size: 48px; 
  margin: 24px;
}

.fa.enabled {
     color: #f44336;
 }
 .fa.disabled {
     color: grey;
 }

@keyframes flip-icon {
    0% {
        transform: rotateY(180deg);
    }
    100% {
        transform: rotateY(0deg);
    }
}

Here is the relevant click actions that will add or remove appropriate classes to start and reset the animation.

 $('body').on('click', '.fa.enabled, .fa.disabled', function iconClick() {
            const icon = $(this);
            icon.addClass('flip');
            setTimeout(() => { icon.removeClass('flip'); }, 300);
            if (icon.hasClass('enabled')) {
                setTimeout(() => {  
                  icon.removeClass('enabled');
                  icon.addClass('disabled'); 
                }, 150);
            } else {
              setTimeout(() => {  
                  icon.removeClass('disabled');
                  icon.addClass('enabled'); 
                }, 150);
            }
        });

Once you have the above code, you’ll achieve something like this:

Full Source Code can be found in this pen.

Categories
Flutter

Flutter – Local push notifications

Create a Flutter project

To create a new project in Flutter execute the following command line in the terminal.

flutter create tryoutflutter

We are using tryoutflutter as the project name. It will create a folder with the name of the project. Inside the project, the files structure should like this:

Let’s look at the folders and files in the project.

android folder: this includes specific files to configure our Android application.

  • To get special permissions,
  • Firebase configuration,
  • To set a logo, splash screen, display name, etc,.

iOS folder, this includes specific files to configure our iOS application.

Build folder, this includes the debug application build APK. This will automatically create once you run the project.

lib folder, this includes Dart files. In this folder, we need to write and manage Dart code.

pubspec.yaml, in this file we can manage the packages (libraries), assets (images, icons, etc,.) that we will use for our application.

Dependencies

Have to install the following packages,

  • RxDart this package adds the additional capabilities to receive the sequence of events (stream) and controls (stream controllers),
  • Flutter local notifications to compose, schedule and manage notifications.

Android setup

Add an event receiver to receive scheduled notification while the application was not running in the foreground.

Paste the following code in AndroidManifest.xml file which is located in this <projectName>/android/app/src/main path.

<receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
                <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
                <action android:name="android.intent.action.QUICKBOOT_POWERON" />
                <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
            </intent-filter>
        </receiver>

iOS setup

For iOS add this code snippet in AppDelegate.swift file to receive notification, which is located in the following path <projectname>/ios/Runner/AppleDelegate.swift and also have to get permission from user to receive notifications.

To know more about UNUserNotificationCenter,

if #available(iOS 10.0, *) {
   UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}

Trigger Local Notifications

  • For iOS devices have to get permissions to receive notifications from our applications,
void _requestPermissions() {
    flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            IOSFlutterLocalNotificationsPlugin>()
        ?.requestPermissions(
          alert: true,
          badge: true,
          sound: true,
        );
    flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            MacOSFlutterLocalNotificationsPlugin>()
        ?.requestPermissions(
          alert: true,
          badge: true,
          sound: true,
        );
  }
  • Call _requestPermissions() from initState() if the platform is iOS, the initState( ) will called once the widget is initialized,
@override
  void initState() {
    super.initState();
    if (Platform.isIOS) {
      _requestPermissions();
    }
  }
  • After those steps, we are ready to compose and receive notifications locally, let see an example to display a quick notification.
  • This will be used as a welcome greeting message during user onboard, or an alert if the event triggers.
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
    FlutterLocalNotificationsPlugin();
final BehaviorSubject<ReceivedNotification> didReceiveLocalNotificationSubject =
    BehaviorSubject<ReceivedNotification>();
final BehaviorSubject<String> selectNotificationSubject =
    BehaviorSubject<String>();
NotificationAppLaunchDetails notificationAppLaunchDetails;

class ReceivedNotification {
  final int id;
  final String title;
  final String body;
  final String payload;
  ReceivedNotification({
    @required this.id,
    @required this.title,
    @required this.body,
    @required this.payload,
  });
}
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  notificationAppLaunchDetails =
      await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
  var initializationSettingsAndroid =
      AndroidInitializationSettings('ic_launcher');
  var initializationSettingsIOS = IOSInitializationSettings(
      requestAlertPermission: false,
      requestBadgePermission: false,
      requestSoundPermission: false,
      onDidReceiveLocalNotification:
          (int id, String title, String body, String payload) async {
        didReceiveLocalNotificationSubject.add(ReceivedNotification(
            id: id, title: title, body: body, payload: payload));
      });
  var initializationSettings = InitializationSettings(
      android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
  await flutterLocalNotificationsPlugin.initialize(initializationSettings,
      onSelectNotification: (String payload) async {
    if (payload != null) {
      debugPrint('notification payload: ' + payload);
    }
    selectNotificationSubject.add(payload);
  });
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter local push notification'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  void _requestPermissions() {
    flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            IOSFlutterLocalNotificationsPlugin>()
        ?.requestPermissions(
          alert: true,
          badge: true,
          sound: true,
        );
    flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            MacOSFlutterLocalNotificationsPlugin>()
        ?.requestPermissions(
          alert: true,
          badge: true,
          sound: true,
        );
  }
  @override
  void initState() {
    super.initState();
    if (Platform.isIOS) {
      _requestPermissions();
    }
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              RaisedButton(
                onPressed: () {
                  showNotification();
                },
                child: Text("Show notification"),
              )
            ],
          ),
        ));
  }
}
Future<void> showNotification() async {
  const AndroidNotificationDetails androidPlatformChannelSpecifics =
  AndroidNotificationDetails(
    'your channel id', 
    'your channel name', 
    'your channel description',
    importance: Importance.max,
    priority: Priority.high,
    ticker: 'ticker'
  );
  
  const NotificationDetails platformChannelSpecifics = NotificationDetails(
    android: androidPlatformChannelSpecifics,
  );
  await flutterLocalNotificationsPlugin.show(
    1, 
    'plain title', 
    "Test Message", 
    platformChannelSpecifics,
    payload: 'item x'
  );
}
  • Function to display a scheduled notification, have to pass the following parameters channelId need to be unique, channelName, title, description, dateTime.
//ScheduleNotification
Future<void> scheduleNotificationFunction( String channelId, String channelName, String channelDes, String title, String description, DateTime dateTime) async {
  print("Notification schduled for $channelId");

  var platformChannelSpecifics = NotificationDetails(
      AndroidNotificationDetails(channelId, channelName, channelDes,
          playSound: true, enableLights: true, enableVibration: true),
      IOSNotificationDetails());

  await flutterLocalNotificationsPlugin.schedule(
      0, title, description, dateTime, platformChannelSpecifics);
}
  • Function to display a daily repeat notification, have to pass the following parameters channelId need to be unique, channelName, title, description, onlyTime 24 hour format.
//Daily repeat notification
Future<void> _repeatNotificationDailyAtSpecificTime() async {
  const AndroidNotificationDetails androidPlatformChannelSpecifics =
      AndroidNotificationDetails('repeating channel id',
          'repeating channel name', 'repeating description');
  const NotificationDetails platformChannelSpecifics =
      NotificationDetails(android: androidPlatformChannelSpecifics);
// ignore: deprecated_member_use
  await flutterLocalNotificationsPlugin.showDailyAtTime(2443, "Good Morning",
      "Message body", Time(10, 00, 00), platformChannelSpecifics);
}
  • This function to cancel a scheduled notification requires channelId
// To cancel notification
Future<void> _cancelNotification(id) async {
  await flutterLocalNotificationsPlugin.cancel(id);
}

Android

iOS

I have attached the complete code in this GitHub repo.

Categories
Flutter

Push Notification in Flutter using Firebase

In this article, you will learn about how to integrate Firebase Cloud Messaging (FCM) with a Flutter mobile application.

If you want to install Flutter in your environment follow this article: Flutter Setup

Create Flutter project

To create a new project in Flutter execute the following command line in the terminal.

flutter create tryoutflutter

We are using tryoutflutter as the project name. It will create a folder with the name of the project. Inside the project, the files structure should like this:

Let’s look at the folders and files in the project.

android folder: this includes specific files to configure our Android application.

  • To get special permissions,
  • Firebase configuration,
  • To set a logo, splash screen, display name, etc,.

iOS folder, this includes specific files to configure our iOS application.

Build folder, this includes the debug application build APK. This will automatically create once you run the project.

lib folder, this includes Dart files. In this folder, we need to write and manage Dart code.

pubspec.yaml, in this file we can manage the packages (libraries), assets (images, icons, etc,.) that we will use for our application.

Create Firebase Project

Follow the steps to create a new firebase project,

  • Goto Firebase and login with your Google account
  • Then navigate to the Firebase console in that you can create a project
  • Give a name to your project. If you want Google Analytics for your project, enable it otherwise it’s optional only. Wait for a moment once your project is ready, we can start setup.

Android setup

  • Open your Firebase project, click the Android icon or add app button, it will return a form. Fill out the form to register the app.
  • To know the package name of your application look into the androidmanifest.xml or app level build.gradle file
Paste the file in this location PATH: <projectname>/android/app/’Paste the file here’
  • Firebase returns a config file in JSON format. Download and place in your project folder, in the following path <projectName>/android/app.
  • Click the next button. Then add Firebase SDK by following the Firebase assistant.
  • Add this dependency implementation 'com.google.firebase:firebase-messaging:' in your app level build.gradle file, to enable receive messages in the background. Check this link to get the latest version.

iOS setup

  • Open your Firebase project, click the iOS button or add app button, it will return a form. Fill out the form to register the app.
  • To know the app bundle id, open your project in Xcode. Select runner in file navigator and select runner in the target also. Copy the bundle id from the identity section.
To know Bundle id from Xcode
  • Now you have registered your app in Firebase, then skip the upcoming steps. That’s only required if you use native development.

Follow the steps to enable APN’s:

  • To send push notification to iOS devices have to enable the Apple Push Notification service to setup that follow this documentation,
  • After the creation of an identifier key and a provisioning profile in your developer account, add your team id in Firebase iOS Flutter project settings.
  • Download the latest config file. Place the file in your Flutter project folder, in the following path <project name>/ios/Runner.xcworkspace
  • Open the project in Xcode, select Runner in the project navigator. Select capability add following capabilities, Push notifications and background modes.
  • Also enable background fetch and remote notifications under the background modes.

Compose notification

Android setup is done, if you send any notification from Firebase Cloud Messaging (FCM), the devices will be able to receive for (Android only). For iOS only we need to get the permissions, refer to the code attached below.

Let see how to compose notification to send,

  • Navigate to Cloud Messaging in your Firebase project, then Click the new notification.
  • Give the title and select the target applications.
  • Click the review button, and publish the notification. It will send a notification to all devices.

Configuring FCM

Let see how to send a notification to a specific device and to a group of devices which is subscribed to a topic.

Now it’s time to code, add this package firebase_messaging: in pubspec.yaml file under the dependencies.

To receive notification in device

import 'package:flutter/material.dart';
import 'package:firebase_messaging/firebase_messaging.dart'; //Imported firebase_messaging package

//A statefull widget
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final FirebaseMessaging _fcm = FirebaseMessaging(); // Here _fcm is instance of FirebaseMessaging 
  @override
  void initState() {
    super.initState();
    if (Platform.isIOS) {
      _fcm.requestNotificationPermissions(IosNotificationSettings()); //Geting permission for iOS only
    }
    _fcm.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message"); //It fires when the app is open, if notification received
      },
      onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message"); //If notification received, it fires when app is running in backgroud
      },
      onResume: (Map<String, dynamic> message) async {
        print("onResume: $message"); // If notification received, it fires when app is fully terminated
      },
    ); //With 
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Container()
    );
  }
}

Send notification to a topic

It’s simple, we have to configure in both composing the notification as well as also while receiving the notification in the device.

Type your topic name in the message topic field, with this reference it will send the notification to all subscribed devices.

Subscribe to an topic

import 'package:flutter/material.dart';
import 'package:firebase_messaging/firebase_messaging.dart'; //Imported firebase_messaging package

//A statefull widget
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final FirebaseMessaging _fcm = FirebaseMessaging(); // Here _fcm is instance of FirebaseMessaging 
  @override
  void initState() {
    super.initState();
    if (Platform.isIOS) {
      _fcm.requestNotificationPermissions(IosNotificationSettings()); //Geting permission for iOS only
    }
    _fcm.subscribeToTopic('topic'); // Topic subscribed
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Container()
    );
  }
}

Send notification to a specific device

FCM provides unique tokens to all devices, the code below will show how to get the device token.

getToken() async {
    //print(await _fcm.getToken());
    //The getToken() will return a Future string, so async is used 
    return await _fcm.getToken(); 
  }

We need to set up a server to send a notification to specific devices, store the device token in the database, and use it with your logic. Refer to this documentation for more detail about the setup.

To send dynamic notification using FCM, have to go for Firebase Cloud functions.

I have attached the complete code in this GitHub repo.

Categories
Flutter

Flutter Setup

HelloWorld! In this article, you will learn about how to setup Flutter in Windows, Linux, and Mac OS through step by step simple procedure. Let’s get started,

Prerequisites

  • Android Studio
  • X Code and CocoaPods only for Mac OS
  • VS Code editor or any other editor. VS code editor is recommended because it has the Flutter plugin which helps in fast debugging
  • Git version control
  • Install Flutter and Dart plugin in Android studio.

Setup for Windows

Minimum requirements

  • Require at least Windows 7 SP1 or later
  • Disk space around 1.3 GB required only for flutter setup
  • Flutter depends on Windows PowerShell 5.0 or newer (this is pre-installed with windows 10), if your using windows 10 don’t worry about it and if you want to clone the GitHub repository have to install Git

To download Git for Windows x86 and Windows x64.

Download and setup Flutter

  • Download the Flutter SDK from this link or clone the Flutter’s repository by executing the following command in your terminal,
git clone https://github.com/flutter/flutter.git -b stable
  • After completion of the download, extract the file in your desired location for example (C:\flutter),
  • Don’t place the Flutter SDK inside the program files it may require some elevated privileges.

Next step is to add the Flutter path in your system environment variable, by the following steps. This enables us to execute all Flutter commands from the command prompt:

  1. From the Windows search look for ‘env’, then select edit environment variables for your account
  2. In under the user variables check there is any variable named Path if exists paste the Flutter SDK full path, example (C:\Flutter\bin) in values if any values exist separate between the two values using ‘;’, if not exists create a new variable name it as path and paste the Flutter SDK location in the value.

Last step in Flutter setup

Open the command prompt, run flutter doctor a command will let you know there are any dependencies to complete the Flutter setup.

If it shows an error like Android license status unknown, you can resolve this by executing the following command line in your terminal.

flutter doctor --android-licenses

If you get logs like the following screenshot you have done.

Setup for Linux

Minimum requirements

  • Minimum disk space around 600MB
  • Operating system Linux 64Bit

Download and setup Flutter

  • Download the Flutter SDK from this link or clone the Flutter’s repository by executing the following command in your terminal,
git clone https://github.com/flutter/flutter.git -b stable --depth 1
  • After completion of the download, extract the file in your desired location for example (/home/`userName`/flutter)
  • Add Flutter tool to your path, open the .bashrc file from your home folder then paste the following line
  • By default the file was been hidden, so check the show hidden files from menu, then open and the add this line
export PATH="$PATH:`pwd`/flutter/bin"

Last step in Flutter setup

Open the terminal, run flutter doctor a command will let you know there are any dependencies to complete the Flutter setup.

If it shows an error like Android license status unknown, you can resolve this by executing the following command line in your terminal

flutter doctor --android-licenses

If you get logs like the following screenshot you have done.

Setup for Mac OS

Minimum requirements

  • Minimum disk space around 2.8Gb and
  • Operating system Linux 64Bit

Download and setup Flutter

  • Download the Flutter SDK from this link or clone the Flutter’s repository from this link,
git clone https://github.com/flutter/flutter.git -b stable --depth 1
  • After completion of the download, unzip the file in the desired location for example (/home/`userName`/flutter)
  • To add the Flutter tool to your path, before that by typing echo $SHELL it will show what shell you are using
  • If you are using the Z shell (ZSH) edit ./zshrc file which is located in your home folder. command + shift + h a shortcut to navigate Home folder in Finder
  • If you’re using Bash shell, edit $HOME/.bashrc or $HOME/.bash_profile. Next is add the following line in the corresponding file by using editor

Last step in Flutter setup

Open the terminal, run flutter doctor a command will let you know there are any dependencies to complete the Flutter setup.

If it shows an error like Android license status unknown, you can resolve this by executing the following command line in your terminal

flutter doctor --android-licenses

If you get logs like the following screenshot you have done.

Categories
Elasticsearch

Shrinking indices in Elasticsearch

Optimizing Elasticsearch: How Many Shards per Index? | Qbox HES

The Problem

Today, we started receiving the following error from our production Elasticsearch cluster when a new index was about to be created:

{
  "error": {
    "root_cause": [
      {
        "type": "validation_exception",
        "reason": "Validation Failed: 1: this action would add [10] total shards, but this cluster currently has [991]/[1000] maximum shards open;"
      }
    ],
    "type": "validation_exception",
    "reason": "Validation Failed: 1: this action would add [10] total shards, but this cluster currently has [991]/[1000] maximum shards open;"
  },
  "status": 400
}

The error description was obvious that we would breach the shard limit of 1,000 when creating a new index.

Confirming the number from the error message using _cat/shards endpoint, we see that we had 991 shards in our only data node.

$ curl -s https://<aws_es_url>.es.amazonaws.com:443/_cat/shards | wc -l
991

We had about 99 indices and each index had 5 shards with one replica which contributes to 5 shards as well. So a total of 10 shards per index. We can confirm that by checking the index endpoint:

$ curl -s https://<aws_es_url>.es.amazonaws.com:443/<index_name>?pretty"

which shows the following output (shortened for brevity):

{
  "settings": {
    "number_of_shards": "5",
    "number_of_replicas": "1"
  }
}

Looking around in AWS help docs, they have suggested three solutions:

Suggested fixes

The 7.x versions of Elasticsearch have a default setting of no more than 1,000 shards per node. Elasticsearch throws an error if a request, such as creating a new index, would cause you to exceed this limit. If you encounter this error, you have several options:

  • Add more data nodes to the cluster.
  • Increase the _cluster/settings/cluster.max_shards_per_node setting.
  • Use the _shrink API to reduce the number of shards on the node.

We chose the shrink option because all our indices are small enough that they do not need 5 shards.

How to Shrink?

It is a 3 step process:

Step 1: Block writes on the current index

$ curl -XPUT -H 'Content-Type: application/json' https://<aws_es_url>.es.amazonaws.com:443/<current_index_name>/_settings -d'{
  "settings": {
    "index.number_of_replicas": 0,                                
    "index.routing.allocation.require._name": "shrink_node_name", 
    "index.blocks.write": true                                    
  }
}'

Step 2: Start shrinking with the new shard count

$ curl -XPOST -H 'Content-Type: application/json' https://<aws_es_url>.es.amazonaws.com:443/<current_index_name>/_shrink/<new_index_name> -d'{
  "settings": {
    "index.number_of_replicas": 1,
    "index.number_of_shards": 1, 
    "index.routing.allocation.require._name": null,
    "index.blocks.write": null
  }
}'

You can track the progress of the shrinking via the /_cat/recovery endpoint. Once the shrinking is complete, you can verify the document count via the _cat/indices endpoint.

Once you are happy with the shrinking, go to the next step.

Step 3: Delete the old index

$ curl -XDELETE https://<aws_es_url>.es.amazonaws.com:443/<current_index_name>

You can run the above commands for multiple indices through a shell script like below (place the index names in /tmp/indices.txt as one index name per line):

while read source; do
   <curl command>
done </tmp/indices.txt

Permanent Fix

All the above 3 steps only fixes the existing indices. We’ll need to make some code changes to ensure new indices created from now on is also created with the new setting of one shard.

Include settings.number_of_shards and settings.number_of_replicas in the request payload along with mappings when creating a new index. PHP code for reference:

[
    'mappings' => [
        'properties' => [
            .......
        ],
    ],
    'settings' => [
        'number_of_shards' => 1,
        'number_of_replicas' => 1,
    ]
];

You are now done! 👏

You have successfully fixed both existing indices and new indices.

Further Reading

Categories
AWS

Static Websites with AWS CloudFront and S3

Amazon S3 + Amazon CloudFront: A Match Made in the Cloud | Networking &  Content Delivery

Why CloudFront & S3 is better for hosting static sites?

AWS CloudFront is a CDN that can be used to serve static HTML sites backed by S3 storage.

S3 storage is very cheap. Combined with CloudFront, you can make your sites serve in low latency speeds.

Deploy to S3 and start CloudFront cache invalidation

Why automated deployment?

Automated deployments allows your changes to be made available on the live site instantly and automatically whenever you push to the repository.

You can forget about copying the code manually and uploading to your S3 bucket.

Why trigger cache invalidation?

Once the files are copied to S3, we need to trigger an invalidation for the cache in CloudFront. Otherwise, CloudFront will continue to server the old content from its cache.

We’ll see examples and code snippets for both Bitbucket and Github below.

Using Bitbucket Pipelines

Pipelines is Bitbucket’s CI/CD tool.

Steps to setup deployment:

  • Create a file named bitbucket-pipelines.yml in the project directory.
  • Paste the below code in the file and give your AWS_Access_key_ID, AWS_Secret_access_key, $CloudFront_Distribution_Id values through
  • After adding your AWS key save and push your changes into the bitbucket repo, it is done!
image: node:10.15.0

pipelines:
  default:
    - step:
        name: Deploy to S3
        deployment: production
        script:
          - pipe: atlassian/aws-s3-deploy:0.4.4
            variables:
              AWS_ACCESS_KEY_ID: $AWS_Access_key_ID
              AWS_SECRET_ACCESS_KEY: $AWS_Secret_access_key
              AWS_DEFAULT_REGION: 'us-east-1'
              S3_BUCKET: 'jiga.com.au'
              LOCAL_PATH: $BITBUCKET_CLONE_DIR
              ACL: 'public-read'
    - step:
        name: Invalidate CloudFront cache
        script:
          - pipe: atlassian/aws-cloudfront-invalidate:0.3.3
            variables:
              AWS_ACCESS_KEY_ID: $AWS_Access_key_ID
              AWS_SECRET_ACCESS_KEY: $AWS_Secret_access_key
              AWS_DEFAULT_REGION: 'us-east-1'
              DISTRIBUTION_ID: $CloudFront_Distribution_Id

You can add the variables directly in the YML file but it is recommended to add them through ‘Repository variables’ under Pipelines Settings in Repository settings:

Using GitHub Actions

Actions is GitHub’s CI/CD tool.

Steps to setup deployment:

  • Create a file deploy-to-s3.yml in the project directory.
  • Add the required variables to secrets
  • After adding your secrets, push your changes into the Github repo and see the magic!
name: Deploy Website
'on':
  push:
    branches:
      - master
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Deploy to S3
        uses: jakejarvis/s3-sync-action@master
        with:
          args: '--acl public-read --delete'
        env:
          AWS_S3_BUCKET: '${{ secrets.AWS_PRODUCTION_BUCKET_NAME }}'
          AWS_ACCESS_KEY_ID: '${{ secrets.AWS_ACCESS_KEY_ID }}'
          AWS_SECRET_ACCESS_KEY: '${{ secrets.AWS_SECRET_ACCESS_KEY }}'
          AWS_REGION: '${{ secrets.AWS_REGION }}'
          SOURCE_DIR: build
      - name: Invalidate CloudFront Cache
        uses: awact/cloudfront-action@master
        env:
          SOURCE_PATH: ./public
          AWS_REGION: us-east-1
          AWS_ACCESS_KEY_ID: '${{ secrets.AWS_ACCESS_KEY_ID }}'
          AWS_SECRET_ACCESS_KEY: '${{ secrets.AWS_SECRET_ACCESS_KEY }}'
          DISTRIBUTION_ID: '${{ secrets.DISTRIBUTION_ID }}'
 

Now that you know how to automatically deploy to S3 on every change to the repository, you can learn some two tricks about routing options with S3 hosting.

Route pages without index.html filename suffix

By default, CloudFront expects the full URL to match with the actual path in S3. For example, if you have a path <bucket_url>/<subdirectory>/index.html as below, you will need to open https://example.com/subdirectory/index.html in your browser to open the file.

AWS S3 FILE NAVIGATION

If we try to open the URL without index.html suffix, we will get AccessDenied error like below.

But there is a small trick that we can apply to open just https://example.com/subdirectory from your browser and let CloudFront/S3 serve the index.html automatically.

From an SEO perspective, it is also good to strip out the HTML suffix anyway, because the overall URL length is smaller which search engines like.

So here is the trick, the Origin Domain Name has to be updated in CloudFront in the following format:

CLOUDFRONT DISTRIBUTION -> ORIGIN AND ORIGIN GROUPS

Click ‘Edit’:

EDIT ORIGIN FORM

Change the Origin Domain Name from bucket.s3.amazonaws.com to bucket.s3-website-us-east-1.amazonaws.com (based on your region name)

Once the CloudFront Distribution is updated after the above settings change, try opening the subdirectory URL directly and it will work:

Route pages without *.html filename suffix

It is also possible to route a file such as <bucket_path>/page.html to an URL example.com/page. Yet again, this is beneficial for SEO purposes and your URL looks neat.

To get this working, follow these two steps:

  1. Remove .html suffix/extension from your original file either before uploading to S3 or after.
  2. Override the metadata Content-type of this file to text/html. Select the file -> Actions -> Change Metadata. (The default value for files without an extension is binary/octet-stream which triggers a download when opened from the browser)

Now you should be able to access your URL without the HTML suffix:

If you have more than one file to update the metadata or if you are deploying from Bitbucket Pipelines or Github Actions, you can automate this update on Metadata. Example step:

    - step:
        name: Update Metadata on HTML files
        image: fuinorg/atlassian-default-image-awscli:latest
        script:
          - >-
            while read file; do
              AWS_ACCESS_KEY_ID=$AWS_Access_key_ID AWS_SECRET_ACCESS_KEY=$AWS_Secret_access_key AWS_DEFAULT_REGION=us-east-1 aws s3 cp --content-type="text/html" --metadata-directive="REPLACE" --acl=public-read s3://<bucket>/$file s3://jiga.com.au/$file
            done <$BITBUCKET_CLONE_DIR/files_to_update_in_s3.txt

files_to_update_in_s3.txt should contain the list of files that needs to be metadata set. You can generate this file dynamically locally using gulp.

The doctype has to be set correctly in the HTML file for this to work. Example:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
Hello world
</body>
</html>

Note there is a little difference between the previous subdirectory approach and this one. We will have a trailing slash in the earlier approach and no slash in this one.

Categories
Java

MojoFailureException: Fix Maven’s Compilation Failure:

Today, I faced a compilation failure in Bitbucket pipelines for a simple Java project. The project compiles successfully in the local machine.

The stack trace of the failure was not useful at all:

------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  9.655 s
[INFO] Finished at: 2020-10-14T22:24:42Z
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:testCompile (default-cli) on project automation: Compilation failure -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:testCompile (default-cli) on project automation: Compilation failure
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:215)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:156)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:148)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:305)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
    at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105)
    at org.apache.maven.cli.MavenCli.execute (MavenCli.java:957)
    at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:289)
    at org.apache.maven.cli.MavenCli.main (MavenCli.java:193)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:566)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:282)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:225)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:406)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347)
Caused by: org.apache.maven.plugin.compiler.CompilationFailureException: Compilation failure
    at org.apache.maven.plugin.compiler.AbstractCompilerMojo.execute (AbstractCompilerMojo.java:1224)
    at org.apache.maven.plugin.compiler.TestCompilerMojo.execute (TestCompilerMojo.java:180)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:210)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:156)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:148)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:305)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
    at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105)
    at org.apache.maven.cli.MavenCli.execute (MavenCli.java:957)
    at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:289)
    at org.apache.maven.cli.MavenCli.main (MavenCli.java:193)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:566)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:282)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:225)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:406)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347)
[ERROR] 
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

The stack trace doesn’t report any file names or lines numbers. This means the compilation error is not with the source code itself.

Reading the help doc gave me some clue about the problem. After spending some time in investigation, I found that this issue occurred because I was using a custom executable for maven-compiler-plugin that includes a environment variable:

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <source>11</source>
          <target>11</target>
          <executable>${env.JAVA_11_HOME}/bin/javac</executable>
          <fork>true</fork>
        </configuration>
</plugin>

Since this environment variable was present in my local system but was not set in Bitbucket pipelines, I was getting the above exception. Setting the environment variable like below in my bitbucket-pipelines.yml file resolved the issue:

image: maven:3.6.3-openjdk-11
pipelines:
  default:
    - step:
        script:
          - JAVA_11_HOME=/usr/local/openjdk-11 mvn -e -X clean compile compiler:testCompile

If you are using Jenkins/Ubuntu, the path to Java may be different. Use sudo update-alternatives --config java to find out the path. Eg. on my Ubuntu, Java 11 is installed into /usr/lib/jvm/java-11-openjdk-amd64

Categories
Flutter

Introduction to Flutter

HelloWorld! In this article, you will learn about what is Flutter, why Flutter, then a comparison between Flutter with other frameworks, and finally Flutter’s pros and cons.

What is Flutter

Flutter is an SDK for UI development. It is developed by Google to build standard UI & UX for mobile, web & desktop applications from a single code base. Flutter has released its first version in 2017. For now, the Flutter as stable release only for mobile applications, for desktop and web apps are still in the development stage. It’s based on Dart programming language, which is also developed by Google.

Why Flutter

Using Flutter we can develop a mobile app for both Android and iOS from a single code base, we only have to modify the native code while getting into permissions like for notifications, accessing the file manager, to change logo and splash screen, etc,. Apart from those, we use Flutter to code for the logics, functionality of the application.

Flutter has hot reloading and restart, both will help to build applications faster.

  • Hot reloading! It loads the code in VM and rebuilds the widget, it won’t change the app state. It doesn’t rerun the main() and initState() function.
  • Hot restart It loads the code in VM, and restart the Flutter app. It will run main() and initState() function.

Flutter is based on Dart programming language so it directly compiles to arm machine code using the Dart compilers. This helps in improving app performance.

Flutter uses Skia as a 2D graphic engine written in C++ and it’s also developed by Google, which is used to render widgets and draw animations smoothly.

What is widget

In Flutter everything is a widget, widgets are the elements in the screen used to construct the UI. Flutter has a lot of predefined widgets, to build the standard applications. There are two types of widgets,

  • State full widgets and
  • Stateless widgets

Comparison


Frameworks/
Attributes

Flutter ReactNative Xamarin Ionic

Programming language
DartJava scriptC# with .net Javascript
PerformanceAmazingClose to nativeClose to nativeModerate
GUIUsing proprietary widgetsUsing Native UI controllersUsing Native UI controllersUsing HTML, CSS
Community SupportIt’s being popularVery strong supportStrongStrong

Popular apps built

Google Ads, Alibaba, Hamilton

Facebook, Instagram, Airbnb

Storoyo,
World Bank, Insightly, Just giving 

Just Watch, Honeyfi,
Chefsteps
Developed byGoogleFacebookMicrosoftDrifty

Pros

  • Flutter hot reloading helps in rapid prototyping,
  • Reusable widgets,
  • Directly compile to ARM machine code, using Dart compilers helps in to make app performance closer to native,
  • From a single code base able to develop an app for both Android and iOS, helps in code maintenance also reduces the cost of development,
  • Ahead of time compiling etc.

Cons

  • When compared to React Native, Flutter has limited library support,
  • For now, Flutter mobile-only has a stable version, Flutter web in beta, and for a desktop application in alpha release.

Categories
React Native

Learn React Native with a Board Game (Part 1 of 4)

In this tutorial, you’ll be learning about basic concepts, developing an application from scratch, state, props, and components. This tutorial is for both beginners and professionals.

Introduction to React Native

React Native is a JavaScript framework created by Facebook, Inc. It is used to develop mobile applications for Android and iOS. The mobile applications are developed only using JavaScript, and it is supported by both iOS and Android. It uses native components instead of web components as building blocks. We just put those building blocks together using React and JavaScript.

React Native is not used for developing web applications or an HTML application; it is only used to develop real, native mobile applications that are indistinguishable from mobile applications built using Java or C++. The apps developed using React Native does not load on any browsers, they run on a mobile device only.”

Prerequisite

For better understanding, you must have the basic knowledge of HTML and CSS, OOPs concepts, and JavaScript.

Advantages of React Native

  • Cross-Platform: It works for both Android and iOS devices.
  • JavaScript: The knowledge of JavaScript can be used to build native mobile apps.
  • Reloading: Changes made in the code of the app will be visible during development.

Limitations of React Native

  • Updates: Few components of Android and iOS are not supported.
  • Native Components: If the developer wants to create native functionality that is yet to be created, then the developer has to write platform-specific code.

React Native Environment Setup

The instructions and environment setup are quite different for each operating system. Here, we have chosen Windows as our development OS and Android as our target OS.

To set up the React Native environment, you have to download and install NodeJS, React Native CLI, Android Studio, Python, and JDK software.

Steps to Setup React Native CLI Environment

Step 1: Install NodeJS and NPM (Node Package Manager)

Click here to download NodeJS. NPM comes with NodeJS.

To verify whether NodeJS and NPM are installed or not, open the command prompt and type “node -v” and hit enter. This command will give the version of NodeJS installed.

Next, type “npm -v” and hit enter, this command will give you the version of NPM installed.

Note: Before going to the next step, make sure that you have installed Python, JDK, NPM, and NodeJS software on your system.

Step 2: Install React Native CLI

Open the command prompt and run the below code:

npm install -g react-native-cli

This command will install React Native.

To install Expo CLI use the below command.

npm install -g expo-cli

Note: We’ll discuss Expo CLI later in this post.

Download and setup Android studio

Step 1: Download the latest version of Android Studio from “https://developer.android.com/studio.
Step 2: Go to the download folder, double click on the downloaded android studio set (.exe) file. In the Welcome to Andriod Studio Setup Window click Next.

Step 3: Make sure the Android virtual device is checked. Click Next.

Step 4: Choose the destination folder and click Next.


Step 5: Follow the prompts and click install.

Step 6: Let the installation complete and click Next.

Step 7: Click Finish.

Step 8: Click Next, select an installation type and click Next.

Step 9: Select the UI theme and click Next.

Step 10: In the Verify Settings Window, click Finish.

Step 11: In the Downloading components Window, click Finish.

Configuring AVD Manager

Once Android Studio is installed, we’re going to configure the AVD Manager.
Step 1: At the bottom left of the Android Studio Window, you’ll find a configure option, click on it, and select AVD Manager.

Step 2: Click “Create Virtual Device.”

Step 3: Choose a device, click Next.

Step 4: Download any Android version for your Virtual Device. Click download, accept the license agreement. Click Next.

Step 5: Once the Android version is downloaded on a virtual device, click Next.

Step 6: Give a name to the AVD and click Finish.

Step 7: Click on the Play icon under the Actions column to start your AVD.

Hurray! You have completed the React Native Environment Setup. Create a new project and start working.

Expo

In this guide, we’ll teach you how you can create your first app, installing tools, and give you an overview of how to work with Expo tools, limitations, and environment setup.

The easiest way to get started with mobile development is with Expo CLI. Expo is a set of tools built around React Native. Expo allows you to build universal real native apps using JavaScript. To build apps using Expo CLI, all you need is a recent version of NodeJS, Android Studio, and an emulator or phone.

Expo allows you to run an app on a physical device without setting up a development environment. To run the React Native app on your physical device, all you have to do is to install the Expo application from the play store and scan the QR code displayed on Command Line Interface.

Limitations of Expo

  • If your app wants a specific thing, Expo may not support it.
  • Many features of iOS and Android APIs are still not available, for example, Bluetooth is not available.
  • Does not support the background execution of the code.
  • Only minimum versions of iOS and Android are supported.
  • The managed workflow may not be the best option, to keep the size of the app too less.

Advantages

  • Better development experience.
  • Easy to upgrade to newer versions.
  • Easy to deploy the application in Apple or Google store.
  • Setup takes less time.

Difference between Expo CLI and React Native CLI

Expo CLI

  • No access to Native Applications.
  • Expo CLI makes the development process smoother.
  • Bigger App size.
  • Android Studio or Xcode is not required.
  • The code is almost the same for both.

React Native CLI

  • Have access to Native applications.
  • The development process is not smoother.
  • Small App size.
  • Android Studio or Xcode is required.
  • The code is almost the same.

Expo CLI Installation and Setup

Step 1:

npm install -g expo-cli

The above command will install the Expo CLI.

To check the installed version of the Expo CLI, enter the below command in the command prompt and hit enter.

expo -- version

Step 2: Create a new project. Use the below command to create a new project.

expo init my-project

Note: In place of my-project you can give any name.

Step 3: Navigate to the project directory and type the below command to open iOS or Android.

Hit enter after each command.

cd my-project
npm start 

Step 4: Now, the Expo dev tool will automatically get started.

Now, if you wish to connect your physical device, then download the Expo app (Android) or the camera app (iOS) on your physical device and scan the QR code displayed in your CLI.

If you wish to use Android emulator, press a, or w to run on web.

If you press a, then it will start downloading and installing Expo on your AVD.

Hurray! You have completed the Expo Installation, Setup, Creating a new project. You can start working on it now.