text:
from distutils.version import LooseVersion

import cms
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models

class Migration(SchemaMigration):

    def forwards(self, orm):

        if LooseVersion(cms.__version__) >= LooseVersion('3'):
            if 'cmsplugin_twittersearch' in db.connection.introspection.table_names():
                db.rename_table(u'cmsplugin_twittersearch', u'djangocms_twitter_twittersearch')
                db.rename_table(u'cmsplugin_twitterrecententries', u'djangocms_twitter_twitterrecententries')

    def backwards(self, orm):

        if LooseVersion(cms.__version__) >= LooseVersion('3'):
            if 'djangocms_twitter_twittersearch' in db.connection.introspection.table_names():
                db.rename_table(u'djangocms_twitter_twittersearch', u'cmsplugin_twittersearch')
                db.rename_table(u'djangocms_twitter_twitterrecententries', u'cmsplugin_twitterrecententries')

    models = {
        'cms.cmsplugin': {
            'Meta': {'object_name': 'CMSPlugin'},
            'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
            'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
            'depth': ('django.db.models.fields.PositiveIntegerField', [], {}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
            'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
            'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
            'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
            'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
            'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
            'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'})
        },
        'cms.placeholder': {
            'Meta': {'object_name': 'Placeholder'},
            'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'slot': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
        },
        u'djangocms_twitter.twitterrecententries': {
            'Meta': {'object_name': 'TwitterRecentEntries', '_ormbases': ['cms.CMSPlugin']},
            u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
            'count': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '3'}),
            'link_hint': ('django.db.models.fields.CharField', [], {'max_length': '75', 'blank': 'True'}),
            'title': ('django.db.models.fields.CharField', [], {'max_length': '75', 'blank': 'True'}),
            'twitter_id': ('django.db.models.fields.CharField', [], {'max_length': '75'}),
            'twitter_user': ('django.db.models.fields.CharField', [], {'max_length': '75'})
        },
        u'djangocms_twitter.twittersearch': {
            'Meta': {'object_name': 'TwitterSearch', '_ormbases': ['cms.CMSPlugin']},
            u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
            'count': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '3'}),
            'query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}),
            'title': ('django.db.models.fields.CharField', [], {'max_length': '75', 'blank': 'True'}),
            'twitter_id': ('django.db.models.fields.CharField', [], {'max_length': '75'})
        }
    }

    complete_apps = ['djangocms_twitter']

redpajama_set_name: RedPajamaGithub


text:
title: Interview with iAdvize
created_at: 2017-05-17
kind: article
author_name: Brian Brazil
---

*Continuing our series of interviews with users of Prometheus, Laurent
COMMARIEU from iAdvize talks about how they replaced their legacy Nagios and
Centreon monitoring with Prometheus.*

## Can you tell us about iAdvize does?

I am Laurent COMMARIEU, a system engineer at iAdvize.  I work within the 60
person R&D department in a team of 5 system engineers. Our job is mainly to
ensure that applications, services and the underlying system are up and
running. We are working with developers to ensure the easiest path for their
code to production, and provide the necessary feedback at every step. That's
where monitoring is important.

iAdvize is a full stack conversational commerce platform.  We provide an easy
way for a brand to centrally interact with their customers, no matter the
communication channel (chat, call, video, Facebook Pages, Facebook Messenger,
Twitter, Instagram, WhatsApp, SMS, etc...). Our customers work in [ecommerce,
banks, travel, fashion, etc. in 40
countries](http://www.iadvize.com/en/customers/). We are an international
company of 200 employees with offices in France, UK, Germany, Spain and Italy.
We raised $16 Million in 2015.

## What was your pre-Prometheus monitoring experience?

I joined iAdvize in February 2016. Previously I worked in companies specialized
in network and application monitoring. We were working with opensource software
like [Nagios](https://www.nagios.org/), [Cacti](http://www.cacti.net/),
[Centreon](https://www.centreon.com/), [Zabbix](http://www.zabbix.com/),
[OpenNMS](https://www.opennms.org/en), etc. and some non-free ones like [HP
NNM](https://saas.hpe.com/en-us/software/network-node-manager-i-network-management-software),
[IBM Netcool
suite](http://www-03.ibm.com/software/products/en/netcool-network-management),
[BMC Patrol](http://www.bmc.com/it-solutions/brands/patrol-proactivenet.html),
etc.

iAdvize used to delegate monitoring to an external provider. They ensured 24/7
monitoring using Nagios and Centreon. This toolset was working fine with the
legacy static architecture (barebone servers, no VMs, no containers). To
complete this monitoring stack, we also use [Pingdom](https://www.pingdom.com/).

With the moving our monolithic application towards a Microservices architecture
(using Docker) and our will to move our current workload to an infrastructure
cloud provider we needed to have more control and flexibility on monitoring. At
the same time, iAdvize recruited 3 people, which grew the infrastructure team
from 2 to 5.  With the old system it took at least a few days or a week to add
some new metrics into Centreon and had a real cost (time and money).

## Why did you decide to look at Prometheus?

We knew Nagios and the like were not a good choice. Prometheus was the rising
star at the time and we decided to PoC it. [Sensu](https://sensuapp.org/) was
also on the list at the beginning but Prometheus seemed more promising for our
use cases.

We needed something able to integrate with Consul, our service discovery
system.  Our micro services already had a /health route; adding a /metrics
endpoint was simple. For about every tool we used, an exporter was available
(MySQL, Memcached, Redis, nginx, FPM, etc.).

On paper it looked good.

![One of iAdvize's Grafana dashboards](/assets/blog/2017-05-17/iadvize-dashboard-1.png)

## How did you transition?

First of all, we had to convince the developers team (40 people) that
Prometheus was the right tool for the job and that they had to add an exporter
to their apps. So we did a little demo on RabbitMQ, we installed a RabbitMQ
exporter and built a simple [Grafana](https://grafana.com/) dashboard to
display usage metrics to developers. A Python script was written to create some
queue and publish/consume messages.

They were quite impressed to see queues and the messages appear in real time.
Before that, developers didn't have access to any monitoring data. Centreon was
restricted by our infrastructure provider. Today, Grafana is available to
everyone at iAdvize, using the Google Auth integration to authenticate. There
are 78 active accounts on it (from dev teams to the CEO).

After we started monitoring existing services with Consul and cAdvisor, we
monitored the actual presence of the containers. They were monitored using
Pingdom checks but it wasn't enough.

We developed a few custom exporters in Go to scrape some business metrics from
our databases (MySQL and Redis).

Soon enough, we were able to replace all the legacy monitoring by Prometheus.

![One of iAdvize's Grafana dashboards](/assets/blog/2017-05-17/iadvize-dashboard-2.png)

## What improvements have you seen since switching?

Business metrics became very popular and during sales periods everyone is
connected to Grafana to see if we're gonna beat some record.  We monitor the
number of simultaneous conversations, routing errors, agents connected, the
number of visitors loading the iAdvize tag, calls on our API gateway, etc.

We worked for a month to optimize our MySQL servers with analysis based on the
[Newrelic exporter](https://github.com/jfindley/newrelic_exporter) and [Percona
dashboard for grafana] (https://github.com/percona/grafana-dashboards). It was
a real success, allowing us to discover inefficiencies and perform
optimisations that cut database size by 45% and peak latency by 75%.

There are a lot to say. We know if a AMQP queue has no consumer or if it is
Filling abnormally. We know when a container restarts.

The visibility is just awesome.

That was just for the legacy platform.

More and more micro services are going to be deployed in the cloud and
Prometheus is used to monitor them. We are using Consul to register the
services and Prometheus to discover the metrics routes. Everything works like a
charm and we are able to build a Grafana dashboard with a lot of critical
business, application and system metrics.

We are building a scalable architecture to deploy our services with
[Nomad](https://www.nomadproject.io/). Nomad registers healthy services in
Consul and with some tags relabeling we are able to filter those with a tag
name "metrics=true". It offers to us a huge gain in time to deploy the
monitoring. We have nothing to do ^^.

We also use the EC2 service discovery. It's really useful with auto-scaling
groups. We scale and recycle instances and it's already monitored. No more
waiting for our external infrastructure provider to notice what happens in
production.

We use alertmanager to send some alerts by SMS or in to our
[Flowdock](https://www.flowdock.com/).

## What do you think the future holds for iAdvize and Prometheus?

* We are waiting for a simple way to add a long term scalable storage for our
  capacity planning.
* We have a dream that one day, our auto-scaling will be triggered by
  Prometheus alerting. We want to build an autonomous system base on response
  time and business metrics.
* I used to work with [Netuitive](http://www.netuitive.com/), it had a great
  anomaly detection feature with automatic correlation. It would be great to
  have some in Prometheus.


text:
require 'test_helper'

class RemotePayGateXml3DTest < Test::Unit::TestCase
  def setup
    @gateway = PayGateXmlGateway.new(fixtures(:pay_gate_xml))

    @amount        = 245000
    @credit_card   = credit_card('4000000000000002')
    @declined_card = credit_card('4000000000000036')

    @options = {
        :order_id        => generate_unique_id,
        :billing_address => address,
        :description     => 'Store Purchase',
        :nurl => "http://localhost",
        :rurl => "http://localhost"
    }
  end

  def test_successful_purchase
    assert response = @gateway.purchase(@amount, @credit_card, @options)
    assert_equal 'https://www.paygate.co.za/rpmh/3dsecure.trans', response.params['url']
    assert_not_nil response.params['tid']
    assert_not_nil response.params['cref']
    assert_not_nil response.params['chk']
  end

  def test_authorize_and_capture
    amount = @amount
    assert auth = @gateway.authorize(amount, @credit_card, @options)
    assert_success auth
    assert_equal 'Auth Done', auth.message
    assert auth.authorization
    assert capture = @gateway.capture(amount, auth.authorization)
    assert_success capture
  end

  def test_failed_capture
    assert response = @gateway.capture(@amount, '')
    assert_failure response
    assert_equal 'Transaction ID Must Only Contain Digits', response.message
  end

  def test_invalid_login
    gateway = PayGateXmlGateway.new(
        :login    => '',
        :password => ''
    )
    assert response = gateway.authorize(@amount, @credit_card, @options)
    assert_failure response
    assert_equal 'Incorrect Credentials Supplied', response.message
  end
end


text:
import { server } from '../services';
import { asyncAction } from './async_action';
import UserConstants from '../constants/user_constants';

const UserActionCreators = {
  login: (creds) => (dispatch) => {
    dispatch({
      type: UserConstants.USER_LOGGED_IN,
      creds,
    });
  },

  fetchUserDetails: asyncAction({
    type: UserConstants.USER_FETCH_USER_DETAILS,
    asyncCall: server.fetchUserDetails,
  }),

  logout: asyncAction({
    type: UserConstants.USER_LOGOUT,
    asyncCall: server.logout,
  }),
};

export default UserActionCreators;


text:
import angular from 'angular';
import _ from  'lodash';
import $ from  'jquery';
import 'jquery.flot';
import 'jquery.flot.time';

angular.module('grafana.directives').directive('barchartLegend', function() {
  return {
    link: function(scope, elem) {
      var $container = $('

');
      var firstRender = true;
      var ctrl = scope.ctrl;
      var panel = ctrl.panel;
      var data;
      var seriesList;
      var i;

      ctrl.events.on('render', function() {
        data = ctrl.series;
        if (data) {
          for(var i in data) {
            data[i].color = ctrl.data[i].color;
          }
          render();
        }
      });

      function render() {
        if (firstRender) {
          elem.append($container);
          firstRender = false;
        }

        seriesList = data;

        $container.empty();

        var tableLayout = panel.legendTable && panel.legend.values;

        $container.toggleClass('graph-legend-table', tableLayout);

        if (tableLayout) {
          var header = '
	';
          if (panel.legend.values) {
            header += '	';
          }
          header += '
';
          $container.append($(header));
        }

        for (i = 0; i < seriesList.length; i++) {
          var series = seriesList[i];

          // ignore empty series
          if (panel.legend.hideEmpty && series.allIsNull) {
            continue;
          }
          // ignore series excluded via override
          if (!series.legend) {
            continue;
          }

          var html = '
';
          html += '';
          html += '';
          html += '';

          html += '';
          html += '' + series.label + '';
          html += '';

          if (panel.legend.values) {
            html += '
' + series.formatValue(series.stats[ctrl.panel.valueName]) + '
';
          }

          html += '
';
          $container.append($(html));
        }
      }
    }
  };
});


text:
batch-processing: Chunk oriented Batch 1.0 processing
=====================================================
Author: Rafael Benevides
Level: Intermediate
Technologies: CDI, Batch 1.0, JSF
Summary: Shows how to use chunk oriented batch jobs to import a file to a database.
Target Product: EAP
Source: <https://github.com/jboss-developer/jboss-eap-quickstarts>

What is it?
-----------

This quickstart simulates a file importation using batch jobs. To make it easy, this quickstart offers the user a way to generate files. The generate file can have its name and the number of records customized. The user may also specify if the file contains an error or not.

The Job contains two steps (tasks):

1. Import the file (Chunk oriented) - The chunk size was set to `3`. The `RecordsReader` is responsible for parsing the file and create an instance of `Contact`. The `ContactsFormatter` applies the proper case to the Contact name and it also applies a mask to the phone number. Finally, `ContactsPersister` will send the Contact instance to the Database.
2. Log the number of records imported

The database schema defines that the column for name is unique. For that reason, any atempt to persist a duplicate value will throw an exception. On the second attempt to run the job, the `ChunkCheckpoint` will provide information to skip the Contacts that were already persisted.

_Note: This quickstart uses the H2 database included with Red Hat JBoss Enterprise Application Platform 7. It is a lightweight, relational example datasource that is used for examples only. It is not robust or scalable, is not supported, and should NOT be used in a production environment!_

_Note: This quickstart uses a `*-ds.xml` datasource configuration file for convenience and ease of database configuration. These files are deprecated in JBoss EAP and should not be used in a production environment. Instead, you should configure the datasource using the Management CLI or Management Console. Datasource configuration is documented in the [Product Documentation](https://access.redhat.com/documentation/en/jboss-enterprise-application-platform/) for Red Hat JBoss Enterprise Application Platform._

System requirements
-------------------

The application this project produces is designed to be run on Red Hat JBoss Enterprise Application Platform 7 or later.

All you need to build this project is Java 8.0 (Java SDK 1.8) or later and Maven 3.1.1 or later. See [Configure Maven for JBoss EAP 7](https://github.com/jboss-developer/jboss-developer-shared-resources/blob/master/guides/CONFIGURE_MAVEN_JBOSS_EAP7.md#configure-maven-to-build-and-deploy-the-quickstarts) to make sure you are configured correctly for testing the quickstarts.

Use of EAP7_HOME
---------------

In the following instructions, replace `EAP7_HOME` with the actual path to your JBoss EAP installation. The installation path is described in detail here: [Use of EAP7_HOME and JBOSS_HOME Variables](https://github.com/jboss-developer/jboss-developer-shared-resources/blob/master/guides/USE_OF_EAP7_HOME.md#use-of-eap_home-and-jboss_home-variables).

Start the JBoss EAP Server
-------------------------

1. Open a command line and navigate to the root of the  JBoss EAP directory.
2. The following shows the command line to start the server with the default profile:

        For Linux:   EAP7_HOME/bin/standalone.sh
        For Windows: EAP7_HOME\bin\standalone.bat

Build and Deploy the Quickstart
-------------------------

1. Make sure you have started the JBoss EAP server as described above.
2. Open a command line and navigate to the root directory of this quickstart.
3. Type this command to build and deploy the archive:

        mvn clean package wildfly:deploy
4. This will deploy `target/jboss-batch-processing.war` to the running instance of the server.

Access the application
---------------------

Access the running application in a browser at the following URL:  <http://localhost:8080/jboss-batch-processing/>

You're presented with a simple form that allows you to generate sample files to be imported.

### Usage 1: Import the file without any errors ###

Click on `Generate a new file and start import job` button. This will generate a new file with 10 unique records to be imported. After the file is generated, the import job will start.

You will see a table containing information about the task that was just started. You can click on `Update jobs list` button and verify that the job was completed.

#### Investigate the Console Output ####

At the logs you will see that the files with 10 records were processed using 3 records at a time.

    INFO  [org.jboss.as.quickstarts.batch.controller.BatchController] (default task-3) Starting to generate 10 in file /var/folders/j8/63sgdmbn5tqdkyw0tz6df53r0000gn/T/temp-file.txt
    INFO  [org.jboss.as.quickstarts.batch.controller.BatchController] (default task-3) File generated at /var/folders/j8/63sgdmbn5tqdkyw0tz6df53r0000gn/T/temp-file.txt
    INFO  [org.jboss.as.quickstarts.batch.job.listener.JobListener] (Batch Thread - 1) Job import-file - Execution #1 starting.
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsPersister] (Batch Thread - 1) No checkpoint detected. Cleaning the Database
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 1) Register #1 - Changing name ZIqYKITxiM -> Ziqykitxim | phone  978913851 -> (978)-913-851
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 1) Register #2 - Changing name JbHjnaThps -> Jbhjnathps | phone  095108018 -> (095)-108-018
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 1) Register #3 - Changing name FJTlXRtCdR -> Fjtlxrtcdr | phone  286847939 -> (286)-847-939
    INFO  [org.jboss.as.quickstarts.batch.job.listener.PersistListener] (Batch Thread - 1) Preparing to persist 3 contacts
    INFO  [org.jboss.as.quickstarts.batch.job.listener.PersistListener] (Batch Thread - 1) Persisting 3 contacts
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 1) Register #4 - Changing name mlmBABWzfL -> Mlmbabwzfl | phone  744478648 -> (744)-478-648
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 1) Register #5 - Changing name jVlTYiBRMP -> Jvltyibrmp | phone  135063841 -> (135)-063-841
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 1) Register #6 - Changing name DwEFbSjfQE -> Dwefbsjfqe | phone  404572175 -> (404)-572-175
    INFO  [org.jboss.as.quickstarts.batch.job.listener.PersistListener] (Batch Thread - 1) Preparing to persist 3 contacts
    INFO  [org.jboss.as.quickstarts.batch.job.listener.PersistListener] (Batch Thread - 1) Persisting 3 contacts
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 1) Register #7 - Changing name niDXWwGJuQ -> Nidxwwgjuq | phone  949448390 -> (949)-448-390
    15:57:40,850 INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 1) Register #8 - Changing name VZBArfowSe -> Vzbarfowse | phone  902370961 -> (902)-370-961
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 1) Register #9 - Changing name aSpyWCWwje -> Aspywcwwje | phone  246977695 -> (246)-977-695
    INFO  [org.jboss.as.quickstarts.batch.job.listener.PersistListener] (Batch Thread - 1) Preparing to persist 3 contacts
    INFO  [org.jboss.as.quickstarts.batch.job.listener.PersistListener] (Batch Thread - 1) Persisting 3 contacts
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 1) Register #10 - Changing name TofTfbRBzI -> Toftfbrbzi | phone  868339088 -> (868)-339-088
    INFO  [org.jboss.as.quickstarts.batch.job.listener.PersistListener] (Batch Thread - 1) Preparing to persist 1 contacts
    INFO  [org.jboss.as.quickstarts.batch.job.listener.PersistListener] (Batch Thread - 1) Persisting 1 contacts
    INFO  [org.jboss.as.quickstarts.batch.job.ReportBatchelet] (Batch Thread - 1) Imported 10 to Database
    INFO  [org.jboss.as.quickstarts.batch.job.listener.JobListener] (Batch Thread - 1) Job import-file - Execution #1 finished. Status: COMPLETED

### Usage 2: Import an error file and fix it ###

Now we will simulate a file with duplicate records. This will raise an exception and stop the processing. After that, we will fix the file and continue the importing where it stopped.

Mark the `Generate a duplicate record` checkbox and click on `Generate a new file and start import job` button. If you click on `Update jobs list` button, you will see that the job failed with the following Exit Status: `Error : org.hibernate.exception.ConstraintViolationException: could not execute statement`. This was caused because the job tried to insert a duplicate record at the Database. You will also see `org.h2.jdbc.JdbcSQLException: Unique index or primary key violation` exception stacktraces in the server log.

Now we will fix the file and restart that job execution. Uncheck the `Generate a duplicate record` checkbox and click on `Generate a new file` button. This will generate file without errors.

Click on `Restart` button in the last column for that job instance in the `List of Jobs` table. If you  click on `Update jobs list` button, you will see that the job was completed.

Analyze the logs and check that the job started from the last checkpoint.

    16:08:56,323 INFO  [org.jboss.as.quickstarts.batch.job.RecordsReader] (Batch Thread - 3) Skipping to line 3 as marked by previous checkpoint

#### Investigate the Console Output ####

    INFO  [org.jboss.as.quickstarts.batch.job.listener.JobListener] (Batch Thread - 3) Job import-file - Execution #3 starting.
    INFO  [org.jboss.as.quickstarts.batch.job.RecordsReader] (Batch Thread - 3) Skipping to line 3 as marked by previous checkpoint
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 3) Register #4 - Changing name HdeqwzEjbA -> Hdeqwzejba | phone  686417040 -> (686)-417-040
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 3) Register #5 - Changing name veEEbtpYTJ -> Veeebtpytj | phone  367981821 -> (367)-981-821
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 3) Register #6 - Changing name bQIKTUyqMW -> Bqiktuyqmw | phone  103363182 -> (103)-363-182
    INFO  [org.jboss.as.quickstarts.batch.job.listener.PersistListener] (Batch Thread - 3) Preparing to persist 3 contacts
    INFO  [org.jboss.as.quickstarts.batch.job.listener.PersistListener] (Batch Thread - 3) Persisting 3 contacts
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 3) Register #7 - Changing name KVLIGXhCry -> Kvligxhcry | phone  117327691 -> (117)-327-691
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 3) Register #8 - Changing name PBAZgernHy -> Pbazgernhy | phone  066203468 -> (066)-203-468
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 3) Register #9 - Changing name DGtNZdteGB -> Dgtnzdtegb | phone  908779587 -> (908)-779-587
    INFO  [org.jboss.as.quickstarts.batch.job.listener.PersistListener] (Batch Thread - 3) Preparing to persist 3 contacts
    INFO  [org.jboss.as.quickstarts.batch.job.listener.PersistListener] (Batch Thread - 3) Persisting 3 contacts
    INFO  [org.jboss.as.quickstarts.batch.job.ContactsFormatter] (Batch Thread - 3) Register #10 - Changing name mhmIHhZMhv -> Mhmihhzmhv | phone  094518410 -> (094)-518-410
    INFO  [org.jboss.as.quickstarts.batch.job.listener.PersistListener] (Batch Thread - 3) Preparing to persist 1 contacts
    INFO  [org.jboss.as.quickstarts.batch.job.listener.PersistListener] (Batch Thread - 3) Persisting 1 contacts
    WARN  [org.jberet] (Batch Thread - 3) JBERET000018: Could not find the original step execution to restart.  Current step execution id: 0, step name: reportBatchelet
    INFO  [org.jboss.as.quickstarts.batch.job.ReportBatchelet] (Batch Thread - 3) Imported 10 to Database
    INFO  [org.jboss.as.quickstarts.batch.job.listener.JobListener] (Batch Thread - 3) Job import-file - Execution #3 finished. Status: COMPLETED

### Usage 3: Import an error file and do not fix the errors ###

Check the `Generate a duplicate record` checkbox and click on `Generate a new file ans start import job` button. If you click on `Update jobs list` button, you will see that the job failed with the following Exit Status: `Error : org.hibernate.exception.ConstraintViolationException: could not execute statement`. This was caused because we tried to insert a duplicate record at the Database.

This time we won't fix the file. Just click on `Restart` button again. If you  click on `Update jobs list` button, you will see that the job was marked as `ABANDONED` this time because it was restarted once. Notice that there's a new parameter: `restartedOnce=true`. This behavior was implemented at `JobListener` for demonstration purpose to avoid that a `FAILED` job that was already restarted once, to be restarted twice.

Server Log: Expected warnings and errors
-----------------------------------

_Note:_ You will see the following warnings in the server log. You can ignore these warnings.

    WFLYJCA0091: -ds.xml file deployments are deprecated. Support may be removed in a future version.

    HHH000431: Unable to determine H2 database version, certain features may not work

Undeploy the Archive
--------------------

1. Make sure you have started the JBoss EAP server as described above.
2. Open a command prompt and navigate to the root directory of this quickstart.
3. When you are finished testing, type this command to undeploy the archive:

        mvn wildfly:undeploy

Run the Quickstart in Red Hat JBoss Developer Studio or Eclipse
-------------------------------------

You can also start the server and deploy the quickstarts or run the Arquillian tests from Eclipse using JBoss tools. For general information about how to import a quickstart, add a JBoss EAP server, and build and deploy a quickstart, see [Use JBoss Developer Studio or Eclipse to Run the Quickstarts](https://github.com/jboss-developer/jboss-developer-shared-resources/blob/master/guides/USE_JBDS.md#use-jboss-developer-studio-or-eclipse-to-run-the-quickstarts)

Debug the Application
------------------------------------

If you want to debug the source code of any library in the project, run the following command to pull the source into your local repository. The IDE should then detect it.

    mvn dependency:sources

<!-- Build and Deploy the Quickstart to OpenShift - Coming soon! -->


text:
[![Build][build-badge]][build]
[![Coverage][coverage-badge]][coverage]
[![Downloads][downloads-badge]][downloads]
[![Sponsors][sponsors-badge]][collective]
[![Backers][backers-badge]][collective]
[![Chat][chat-badge]][chat]

[vfile][] utility to create a report.

![Example screenshot of vfile-reporter][screenshot]

## Contents

*   [What is this?](#what-is-this)
*   [When should I use this?](#when-should-i-use-this)
*   [Install](#install)
*   [Use](#use)
*   [API](#api)
    *   [`reporter(files[, options])`](#reporterfiles-options)
*   [Types](#types)
*   [Compatibility](#compatibility)
*   [Security](#security)
*   [Related](#related)
*   [Contribute](#contribute)
*   [License](#license)

## What is this?

This package create a textual report from a file showing the warnings that
occurred while processing.
Many CLIs of tools that process files, whether linters (such as ESLint) or
bundlers (such as esbuild), have similar functionality.

## When should I use this?

You can use this package whenever you want to display a report about what
occurred while processing to a human.

There are [other reporters][reporters] that display information differently
listed in vfile.

## Install

This package is [ESM only][esm].
In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]:

```sh
npm install vfile-reporter
```

In Deno with [`esm.sh`][esmsh]:

```js
import {reporter} from 'https://esm.sh/vfile-reporter@7'
```

In browsers with [`esm.sh`][esmsh]:

```html
<script type="module">
  import {reporter} from 'https://esm.sh/vfile-reporter@7?bundle'
</script>
```

## Use

Say our module `example.js` looks as follows:

```js
import {VFile} from 'vfile'
import {reporter} from 'vfile-reporter'

const one = new VFile({path: 'test/fixture/1.js'})
const two = new VFile({path: 'test/fixture/2.js'})

one.message('Warning!', {line: 2, column: 4})

console.error(reporter([one, two]))
```

…now running `node example.js` yields:

```txt
test/fixture/1.js
  2:4  warning  Warning!

test/fixture/2.js: no issues found

⚠ 1 warning
```

## API

This package exports the identifier `reporter`.
That value is also the default export.

### `reporter(files[, options])`

Create a report from an error, one file, or multiple files.

##### `options`

Configuration (optional).

###### `options.color`

Use ANSI colors in report (`boolean`, default: depends).
The default behavior in Node.js is the check if [color is
supported][supports-color].

###### `options.verbose`

Show message [`note`][message-note]s (`boolean`, default: `false`).
Notes are optional, additional, long descriptions.

###### `options.quiet`

Do not show files without messages (`boolean`, default: `false`).

###### `options.silent`

Show errors only (`boolean`, default: `false`).
This does not show info and warning messages.
Also sets `quiet` to `true`.

###### `options.defaultName`

Label to use for files without file path (`string`, default: `'<stdin>'`).
If one file and no `defaultName` is given, no name will show up in the report.

##### Returns

Report (`string`).

## Types

This package is fully typed with [TypeScript][].
It exports the additional type `Options`.

## Compatibility

Projects maintained by the unified collective are compatible with all maintained
versions of Node.js.
As of now, that is Node.js 12.20+, 14.14+, 16.0+, and 18.0+.
Our projects sometimes work with older versions, but this is not guaranteed.

## Security

Use of `vfile-reporter` is safe.

## Related

*   [`vfile-reporter-json`](https://github.com/vfile/vfile-reporter-json)
    — create a JSON report
*   [`vfile-reporter-pretty`](https://github.com/vfile/vfile-reporter-pretty)
    — create a pretty report
*   [`vfile-reporter-junit`](https://github.com/kellyselden/vfile-reporter-junit)
    — create a jUnit report
*   [`vfile-reporter-position`](https://github.com/Hocdoc/vfile-reporter-position)
    — create a report with content excerpts

## Contribute

See [`contributing.md`][contributing] in [`vfile/.github`][health] for ways to
get started.
See [`support.md`][support] for ways to get help.

This project has a [code of conduct][coc].
By interacting with this repository, organisation, or community you agree to
abide by its terms.

## License

[MIT][license] © [Titus Wormer][author]

Forked from [ESLint][]s stylish reporter
(originally created by Sindre Sorhus), which is Copyright (c) 2013
Nicholas C. Zakas, and licensed under MIT.

<!-- Definitions -->

[build-badge]: https://github.com/vfile/vfile-reporter/workflows/main/badge.svg

[build]: https://github.com/vfile/vfile-reporter/actions

[coverage-badge]: https://img.shields.io/codecov/c/github/vfile/vfile-reporter.svg

[coverage]: https://codecov.io/github/vfile/vfile-reporter

[downloads-badge]: https://img.shields.io/npm/dm/vfile-reporter.svg

[downloads]: https://www.npmjs.com/package/vfile-reporter

[sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg

[backers-badge]: https://opencollective.com/unified/backers/badge.svg

[collective]: https://opencollective.com/unified

[chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg

[chat]: https://github.com/vfile/vfile/discussions

[npm]: https://docs.npmjs.com/cli/install

[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c

[esmsh]: https://esm.sh

[typescript]: https://www.typescriptlang.org

[contributing]: https://github.com/vfile/.github/blob/main/contributing.md

[support]: https://github.com/vfile/.github/blob/main/support.md

[health]: https://github.com/vfile/.github

[coc]: https://github.com/vfile/.github/blob/main/code-of-conduct.md

[license]: license

[author]: https://wooorm.com

[eslint]: https://github.com/eslint/eslint

[vfile]: https://github.com/vfile/vfile

[reporters]: https://github.com/vfile/vfile#reporters

[supports-color]: https://github.com/chalk/supports-color

[message-note]: https://github.com/vfile/vfile-message#note

[screenshot]: screenshot.png


text:
<?php

namespace spec\gries\Rcon;

use gries\Rcon\Connection;
use gries\Rcon\Message;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class MessengerSpec extends ObjectBehavior
{
    function let(\gries\Rcon\Connection $connection)
    {
        $this->beConstructedWith($connection);
    }

    function it_is_initializable()
    {
        $this->shouldHaveType('gries\Rcon\Messenger');
    }

    function it_sends_a_message(\gries\Rcon\Connection $connection)
    {
        $expectedMessage = new Message('list');
        $expectedResponseMessage = new Message('a,b,c');
        $connection->sendMessage($expectedMessage)->willReturn($expectedResponseMessage);

        $this->send('list')->shouldReturn('a,b,c');
    }

    function it_parses_the_response_with_a_given_callable(\gries\Rcon\Connection $connection)
    {
        $expectedMessage = new Message('list');
        $expectedResponseMessage = new Message('a,b,c');
        $connection->sendMessage($expectedMessage)->willReturn($expectedResponseMessage);

        $callable = function($arg) {
            return explode(',', $arg);
        };

        $this->send('list', $callable)->shouldReturn(['a', 'b', 'c']);
    }
}


text:
DMTF Redfish specification
--------------------------

This directory contains the current references from the DMTF on the Redfish
specification (1.0.0 at the time of the writing)
In order to ease test, the DMTF has published a mockup environment to simulate
a Redfish based system so it is possible to write programs without real Redfish
compliant hardware platform.

Docker container
----------------

In order to help testing python-redfish, this directory provides a script which
you should be able to run on your system (providing you have docker support and
a docker registry) which will create a docker container running the DMTF Redfish
mockup on the port 8000.

To build your container, just issue:  ./buildImage.sh
To launch it, just issue: ./run-redfish-simulator.sh
To use it, just issue: firefox http://localhost:8000/redfish/v1

Systems entry point:
http://localhost:8000/redfish/v1/Systems

Chassis entry point:
http://localhost:8000/redfish/v1/Chassis

Managers entry point:
http://localhost:8000/redfish/v1/Managers


text:
/*
 @ 0xCCCCCCCC
*/

#include <future>
#include <iostream>
#include <random>

#include "count_down_latch.h"

const int kWorkerCount = 3;
std::random_device rd;
std::default_random_engine rde(rd());
std::uniform_int_distribution<> rn_gen(1, 3);

void DoWork(int work_id, CountDownLatch& start_signal, CountDownLatch& done_signal)
{
    std::cout << "worker[" << work_id << "] is waiting for start" << std::endl;
    start_signal.Wait();
    std::cout << "worker[" << work_id << "] starts working" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(rn_gen(rde)));
    std::cout << "worker[" << work_id << "] is done." << std::endl;
    done_signal.Countdown();
}

int main()
{
    CountDownLatch start_signal(1);
    CountDownLatch done_signal(kWorkerCount);

    std::cout << "main thread is preparing" << std::endl;
    {
        std::thread th1(DoWork, 1, std::ref(start_signal), std::ref(done_signal));
        std::thread th2(DoWork, 2, std::ref(start_signal), std::ref(done_signal));
        std::thread th3(DoWork, 3, std::ref(start_signal), std::ref(done_signal));

        std::cout << "main thread is doing sth" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(2));

        std::cout << "main thread signals workers" << std::endl;
        start_signal.Countdown();
        done_signal.Wait();

        // Make sure thread objects get destroyed before return from main.
        th1.join();
        th2.join();
        th3.join();
    }
    std::cout << "all workers have finished their job" << std::endl;

    return 0;
}


text:
package org.talend.components.marketo.runtime.data;

import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;

import java.util.Collections;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.talend.components.api.component.runtime.BoundedReader;
import org.talend.components.api.component.runtime.BoundedSource;
import org.talend.components.marketo.MarketoConstants;
import org.talend.components.marketo.MarketoProvideConnectionProperties;
import org.talend.components.marketo.data.MarketoDatasetProperties;
import org.talend.components.marketo.data.MarketoDatasetProperties.Operation;
import org.talend.components.marketo.data.MarketoDatastoreProperties;
import org.talend.components.marketo.data.MarketoInputProperties;
import org.talend.components.marketo.runtime.MarketoInputReader;
import org.talend.components.marketo.runtime.MarketoRuntimeTestBase;
import org.talend.components.marketo.wizard.MarketoComponentWizardBaseProperties.InputOperation;
import org.talend.daikon.properties.ValidationResult;
import org.talend.daikon.properties.ValidationResult.Result;

public class MarketoDatasetSourceTest extends MarketoRuntimeTestBase {

    MarketoDatasetSource dsSource;

    MarketoInputProperties properties;

    MarketoDatasetProperties dataset;

    MarketoDatastoreProperties datastore;

    private transient static final Logger LOG = LoggerFactory.getLogger(MarketoDatasetSourceTest.class);

    @Before
    public void setUp() throws Exception {
        super.setUp();

        datastore = new MarketoDatastoreProperties("test");
        datastore.init();
        datastore.setupProperties();
        datastore.endpoint.setValue("https://abc.mktorest.com/rest");
        datastore.clientAccessId.setValue("fake");
        datastore.secretKey.setValue("sekret");
        dataset = new MarketoDatasetProperties("test");
        dataset.setupProperties();
        dataset.setupLayout();
        dataset.setDatastoreProperties(datastore);
        properties = new MarketoInputProperties("test");
        properties.setupProperties();
        properties.setupLayout();
        properties.setDatasetProperties(dataset);
        dsSource = new MarketoDatasetSource();
    }

    @Test
    public void testSplitIntoBundles() throws Exception {
        List<? extends BoundedSource> bundles = dsSource.splitIntoBundles(1, null);
        assertEquals(1, bundles.size());
        assertTrue(bundles.get(0) instanceof BoundedSource);
    }

    @Test
    public void testGetEstimatedSizeBytes() throws Exception {
        assertEquals(1, dsSource.getEstimatedSizeBytes(null));
    }

    @Test
    public void testProducesSortedKeys() throws Exception {
        assertFalse(dsSource.producesSortedKeys(null));
    }

    @Test
    public void testCreateReader() throws Exception {
        dataset.operation.setValue(Operation.getLeads);
        dsSource.initialize(null, properties);
        BoundedReader reader = dsSource.createReader(null);
        assertNotNull(reader);
        assertEquals(MarketoInputReader.class, reader.getClass());
    }

    @Test
    public void testGetSchemaNames() throws Exception {
        assertEquals(Collections.emptyList(), dsSource.getSchemaNames(null));
    }

    @Test
    public void testGetEndpointSchema() throws Exception {
        dataset.operation.setValue(Operation.getLeads);
        dsSource.initialize(null, properties);
        assertEquals(MarketoConstants.getRESTSchemaForGetLeadOrGetMultipleLeads(), dsSource.getEndpointSchema(null, "leads"));
        dataset.operation.setValue(Operation.getLeadChanges);
        dataset.afterOperation();
        assertEquals(MarketoConstants.getRESTSchemaForGetLeadChanges(), dsSource.getEndpointSchema(null, "changes"));
        dataset.operation.setValue(Operation.getLeadActivities);
        dataset.afterOperation();
        assertEquals(MarketoConstants.getRESTSchemaForGetLeadActivity(), dsSource.getEndpointSchema(null, "activities"));
        MarketoDatasetProperties spy = spy(dataset);
        doNothing().when(spy).retrieveCustomObjectList();
        doNothing().when(spy).retrieveCustomObjectSchema();
        dsSource.initialize(null, properties);
        spy.operation.setValue(Operation.getCustomObjects);
        spy.afterOperation();
        assertEquals(MarketoConstants.getCustomObjectRecordSchema(), dsSource.getEndpointSchema(null, "customobjects"));
    }

    @Test
    public void testValidate() throws Exception {
        dataset.operation.setValue(Operation.getLeads);
        MarketoDatasetSource spy = spy(dsSource);
        spy.initialize(null, properties);
        doReturn(ValidationResult.OK).when(spy).validateConnection(any(MarketoProvideConnectionProperties.class));
        ValidationResult vr = spy.validate(null);
        assertEquals(Result.ERROR, vr.getStatus());
        properties.leadKeyType.setValue("email");
        properties.leadKeyValue.setValue("john.doe@gmail.com");
        vr = spy.validate(null);
        assertEquals(Result.OK, vr.getStatus());
        dataset.operation.setValue(Operation.getLeadChanges);
        vr = spy.validate(null);
        assertEquals(Result.OK, vr.getStatus());
        dataset.operation.setValue(Operation.getLeadActivities);
        vr = spy.validate(null);
        assertEquals(Result.OK, vr.getStatus());
        properties.sinceDateTime.setValue("2018 01 01");
        vr = spy.validate(null);
        assertEquals(Result.ERROR, vr.getStatus());
        properties.sinceDateTime.setValue("");
        vr = spy.validate(null);
        assertEquals(Result.ERROR, vr.getStatus());
        dataset.operation.setValue(Operation.getCustomObjects);
        vr = spy.validate(null);
        assertEquals(Result.ERROR, vr.getStatus());
        dataset.customObjectName.setValue("smartphone_c");
        vr = spy.validate(null);
        assertEquals(Result.ERROR, vr.getStatus());
        dataset.filterType.setValue("brand");
        vr = spy.validate(null);
        assertEquals(Result.ERROR, vr.getStatus());
        dataset.filterValue.setValue("apple");
        vr = spy.validate(null);
        assertEquals(Result.OK, vr.getStatus());
        doReturn(new ValidationResult(Result.ERROR)).when(spy).validateConnection(any(MarketoProvideConnectionProperties.class));
        vr = spy.validate(null);
        assertEquals(Result.ERROR, vr.getStatus());
    }

    @Test
    public void testInitialize() throws Exception {
        ValidationResult res = dsSource.initialize(null, properties);
        assertEquals(Result.OK, res.getStatus());
    }

    @Test
    public void testGetInputProperties() throws Exception {
        dsSource.initialize(null, properties);
        assertEquals(InputOperation.getMultipleLeads, dsSource.getInputProperties().inputOperation.getValue());
        dataset.operation.setValue(Operation.getLeadChanges);
        assertEquals(InputOperation.getLeadChanges, dsSource.getInputProperties().inputOperation.getValue());
        dataset.operation.setValue(Operation.getLeadActivities);
        assertEquals(InputOperation.getLeadActivity, dsSource.getInputProperties().inputOperation.getValue());
        dataset.operation.setValue(Operation.getCustomObjects);
        assertEquals(InputOperation.CustomObject, dsSource.getInputProperties().inputOperation.getValue());
    }

}


text:
var crypto = require('crypto'),
	BigInteger = require('jsbn').BigInteger,
	ECPointFp = require('./jsbn/ec.js').ECPointFp;

/*** Static functions ***/

exports.BigInteger = BigInteger;
exports.Curves = require('./jsbn/sec.js');

exports.getCurve = function(name) {
	if(!exports.Curves[name])
		throw new Error('Curve `' + name + '` is not supported');

	return exports.Curves[name]();
};

exports.getBytesLength = function(curve) {
	return Math.ceil(curve.getN().bitLength() * 0.125);
};

exports.generateR = function(curve, callback) {
	var n = curve.getN();
	return crypto.randomBytes(n.bitLength(), callback);
};

exports.generateKeys = function(curve, r) {
	var privateKey = PrivateKey.generate(curve, r);

	return {
		publicKey: privateKey.derivePublicKey(),
		privateKey: privateKey
	};
};

/*** PublicKey class ***/

//var HEADER_XY = 0x04,
//	HEADER_X_EVEN = 0x02,
//	HEADER_X_ODD = 0x03,

var PublicKey = exports.PublicKey = function(curve, Q, buf) {
	this.curve = curve;
	this.Q = Q;

	if(buf) {
		this.buffer = buf;
	} else {
		var bytes = exports.getBytesLength(curve),
			size = (bytes * 2);

		this.buffer = new Buffer(size);
		fillBuffer(this.Q.getX().toBigInteger().toString(16), bytes, this.buffer, 0);
		fillBuffer(this.Q.getY().toBigInteger().toString(16), bytes, this.buffer, bytes);
	}
};

PublicKey.fromBuffer = function(curve, buf) {
	var bytes = exports.getBytesLength(curve),
		size = (bytes * 2);

	if(buf.length !== size)
		throw new Error('Invaild buffer length');

	var x = buf.slice(0, bytes), // skip the 04 for uncompressed format
		y = buf.slice(bytes),
		c = curve.getCurve(),

	P = new ECPointFp(c,
		c.fromBigInteger(new BigInteger(x.toString('hex'), 16)),
		c.fromBigInteger(new BigInteger(y.toString('hex'), 16))
	);

	return new PublicKey(curve, P, buf);
};

//PublicKey.compress = function() {
//	// this will work only on U curve
//
//	var x = this.Q.getX().toBigInteger(),
//		y = this.Q.getY().toBigInteger();
//
//	var xBa = hexToBuffer(x.toString(16), 'hex'),
//	buf = new Buffer(xBa.length+1);
//
//	if (y.isEven())
//		buf[0] = HEADER_X_EVEN;
//	else
//		buf[0] = HEADER_X_ODD;
//
//	xBa.copy(buf, 1);
//	return buf;
//};

PublicKey.prototype.verifySignature = function(hash, signature) {
	var data = deserializeSig(signature),
	r = data.r,
	s = data.s,
	Q = this.Q,
	e = new BigInteger(hash.toString('hex'), 16),
	n = this.curve.getN(),
	G = this.curve.getG();

    if(r.compareTo(BigInteger.ONE) < 0 || r.compareTo(n) >= 0)
		return false;

    if(s.compareTo(BigInteger.ONE) < 0 || s.compareTo(n) >= 0)
		return false;

    var c = s.modInverse(n),
	u1 = e.multiply(c).mod(n),
	u2 = r.multiply(c).mod(n),

	// TODO we may want to use Shamir's trick here:
	point = G.multiply(u1).add(Q.multiply(u2)),

	v = point.getX().toBigInteger().mod(n);

    return v.equals(r);
};

/*** PrivateKey class ***/

var PrivateKey = exports.PrivateKey = function(curve, key, buf) {
	this.curve = curve;
	this.d = key;

	if(buf) {
		this.buffer = buf;
		this._size = buf.length;
	} else {
		this._size = exports.getBytesLength(curve);
		this.buffer = zeroBuffer(key.toString(16), this._size);
	}
};

PrivateKey.generate = function(curve, r) {
	r = new BigInteger(r || exports.generateR(curve));

	var n1 = curve.getN().subtract(BigInteger.ONE),
	priv = r.mod(n1).add(BigInteger.ONE);

	return new PrivateKey(curve, priv);
};

PrivateKey.fromBuffer = function(curve, buf) {
	var size = exports.getBytesLength(curve);

	if(buf.length !== size)
		throw new Error('Invaild buffer length');

	var key = new BigInteger(buf.toString('hex'), 16);
	return new PrivateKey(curve, key, buf);
};

PrivateKey.prototype.derivePublicKey = function() {
	var P = this.curve.getG().multiply(this.d);

	return new PublicKey(this.curve, P);
};

PrivateKey.prototype.deriveSharedSecret = function(publicKey) {
	if(!publicKey || !publicKey.Q)
		throw new Error('publicKey is invaild');

    var S = publicKey.Q.multiply(this.d);
    return zeroBuffer(S.getX().toBigInteger().toString(16), this._size);
};

PrivateKey.prototype.sign = function(hash, algorithm) {
	if(!hash || !hash.length)
		throw new Error('hash is invaild');
	if(!algorithm)
		throw new Error('hash algorithm is required');

	var n = this.curve.getN(),
	e = new BigInteger(hash.toString('hex'), 16),
	length = exports.getBytesLength(this.curve);

	do {
		var k = deterministicGenerateK(hash, this.buffer, algorithm, length),
		G = this.curve.getG(),
		Q = G.multiply(k),
		r = Q.getX().toBigInteger().mod(n);
	} while(r.compareTo(BigInteger.ZERO) <= 0);

	var s = k.modInverse(n).multiply(e.add(this.d.multiply(r))).mod(n);

	return serializeSig(r, s);
};

/*** local helpers ***/

var DER_SEQUENCE = 0x30,
	DER_INTEGER = 0x02;

function hexToBuffer(hex) {
	if(hex.length % 2 === 1)
		hex = '0' + hex;

	return new Buffer(hex, 'hex');
}

function zeroBuffer(hex, bytes) {
	return fillBuffer(hex, bytes, new Buffer(bytes), 0);
}

function fillBuffer(hex, bytes, buf, start) {
	if(hex.length % 2 === 1)
		hex = '0' + hex;

	var length = (hex.length * 0.5),
	pos = start + bytes-length;

	buf.fill(0, start, pos);
	buf.write(hex, pos, length, 'hex');

	return buf;
}

// generate K value based on RFC6979
function deterministicGenerateK(hash, key, algorithm, length) {
	var v = new Buffer(length),
		k = new Buffer(length);

	v.fill(1);
	k.fill(0);

	var hmac = crypto.createHmac(algorithm, k);
	hmac.update(v);
	hmac.update(new Buffer([0]));
	hmac.update(key);
	hmac.update(hash);
	k = hmac.digest();

	hmac = crypto.createHmac(algorithm, k);
	hmac.update(v);
	v = hmac.digest();

	hmac = crypto.createHmac(algorithm, k);
	hmac.update(v);
	hmac.update(new Buffer([1]));
	hmac.update(key);
	hmac.update(hash);
	k = hmac.digest();

	hmac = crypto.createHmac(algorithm, k);
	hmac.update(v);
	v = hmac.digest();

	hmac = crypto.createHmac(algorithm, k);
	hmac.update(v);
	v = hmac.digest();

	return new BigInteger(v.toString('hex'), 16);
}

function serializeSig(r, s) {
	var rBa = hexToBuffer(r.toString(16), 'hex');
	var sBa = hexToBuffer(s.toString(16), 'hex');

	var buf = new Buffer(6 + rBa.length + sBa.length),
	end = buf.length - sBa.length;

	buf[0] = DER_SEQUENCE;
	buf[1] = buf.length - 2;

	buf[2] = DER_INTEGER;
	buf[3] = rBa.length;
	rBa.copy(buf, 4);

	buf[end-2] = DER_INTEGER;
	buf[end-1] = sBa.length;
	sBa.copy(buf, end);

	return buf;
}

function deserializeSig(buf) {
	if(buf[0] !== DER_SEQUENCE)
		throw new Error("Signature is not a valid DERSequence");

	if(buf[1] > buf.length-2)
		throw new Error("Signature length is too short");

	if(buf[2] !== DER_INTEGER)
		throw new Error("First element in signature must be a DERInteger");

	var pos = 4,
	rBa = buf.slice(pos, pos+buf[3]);

	pos += rBa.length;
	if(buf[pos++] !== DER_INTEGER)
		throw new Error("Second element in signature must be a DERInteger");

	var sBa = buf.slice(pos+1, pos+1+buf[pos]);

	return {
		r: new BigInteger(rBa.toString('hex'), 16),
		s: new BigInteger(sBa.toString('hex'), 16)
	};
}


text:
package com.amazonaws.services.cloudtrail.model;

import javax.annotation.Generated;

/**
 * <p>
 * This exception is thrown when there is an issue with the specified KMS key and the trail can't be updated.
 * </p>
 */
@Generated("com.amazonaws:aws-java-sdk-code-generator")
public class KmsException extends com.amazonaws.services.cloudtrail.model.AWSCloudTrailException {
    private static final long serialVersionUID = 1L;

    /**
     * Constructs a new KmsException with the specified error message.
     *
     * @param message
     *        Describes the error encountered.
     */
    public KmsException(String message) {
        super(message);
    }

}


text:
<?php

namespace Notadd\Foundation\Flow\Events;

use Illuminate\Container\Container;
use Notadd\Foundation\Flow\FlowManager;

/**
 * Class FlowRegister.
 */
class FlowRegister
{
    /**
     * @var \Notadd\Foundation\Flow\FlowManager
     */
    protected $flow;

    /**
     * FlowRegister constructor.
     *
     * @param \Notadd\Foundation\Flow\FlowManager $flow
     */
    public function __construct(FlowManager $flow)
    {
        $this->flow = $flow;
    }
}


text:
from kgb import SpyAgency

from reviewboard.diffviewer.chunk_generator import DiffChunkGenerator
from reviewboard.scmtools.core import PRE_CREATION
from reviewboard.testing import TestCase

class DiffChunkGeneratorTests(SpyAgency, TestCase):
    """Unit tests for DiffChunkGenerator."""

    fixtures = ['test_scmtools']

    COMMIT_1_DIFF = (
        b'diff --git a/README b/README\n'
        b'index 94bdd3e..197009f 100644\n'
        b'--- README\n'
        b'+++ README\n'
        b'@@ -1,1 +1,1 @@\n'
        b'-Hello, world!\n'
        b'+Hi, world!\n'
    )

    COMMIT_2_DIFF = (
        b'diff --git a/README b/README\n'
        b'index 197009f..87abad9 100644\n'
        b'--- README\n'
        b'+++ README\n'
        b'@@ -1,1 +1,1 @@\n'
        b'-Hi, world!\n'
        b'+Yo, world.\n'
    )

    COMMIT_3_DIFF = (
        b'diff --git a/README b/README\n'
        b'index 87abad9..fe1678a 100644\n'
        b'--- README\n'
        b'+++ README\n'
        b'@@ -1,1 +1,1 @@\n'
        b'-Yo, world.\n'
        b'+Yo, dog.\n'
    )

    COMMIT_1_2_SQUASHED_DIFF = (
        b'diff --git a/README b/README\n'
        b'index 94bdd3e..87abad9 100644\n'
        b'--- README\n'
        b'+++ README\n'
        b'@@ -1,1 +1,1 @@\n'
        b'-Hello, world!\n'
        b'+Yo, world.\n'
    )

    COMMIT_1_3_SQUASHED_DIFF = (
        b'diff --git a/README b/README\n'
        b'index 94bdd3e..fe1678a 100644\n'
        b'--- README\n'
        b'+++ README\n'
        b'@@ -1,1 +1,1 @@\n'
        b'-Hello, world!\n'
        b'+Yo, dog.\n'
    )

    def setUp(self):
        super(DiffChunkGeneratorTests, self).setUp()

        self.repository = self.create_repository(tool_name='Test')
        self.diffset = self.create_diffset(repository=self.repository)
        self.filediff = self.create_filediff(diffset=self.diffset)
        self.generator = DiffChunkGenerator(None, self.filediff)

    def test_get_chunks_with_empty_added_file(self):
        """Testing DiffChunkGenerator.get_chunks with empty added file"""
        self.filediff.source_revision = PRE_CREATION
        self.filediff.extra_data.update({
            'raw_insert_count': 0,
            'raw_delete_count': 0,
        })

        self.assertEqual(len(list(self.generator.get_chunks())), 0)

    def test_get_chunks_with_explicit_encoding(self):
        """Testing DiffChunkGenerator.get_chunks with explicit encoding on
        FileDiff
        """
        self.filediff.diff = (
            b'--- README\n'
            b'+++ README\n'
            b'@@ -1,1 +1,1 @@\n'
            b'-%s\n'
            b'+%s\n'
            % ('Hello, world!'.encode('utf-16'),
               'Hi, everybody!'.encode('utf-16'))
        )
        self.filediff.source_file = '/test-file;encoding=utf-16'
        self.filediff.extra_data['encoding'] = 'utf-16'

        self.spy_on(self.repository.get_file)

        chunks = list(self.generator.get_chunks())
        self.assertEqual(len(chunks), 1)
        self.assertEqual(
            chunks[0],
            {
                'change': 'replace',
                'collapsable': False,
                'index': 0,
                'lines': [
                    [
                        1, 1,
                        'Hello, world!',
                        None,
                        1,
                        'Hi, everybody!',
                        None,
                        False,
                    ]
                ],
                'meta': {
                    'left_headers': [],
                    'right_headers': [],
                    'whitespace_chunk': False,
                    'whitespace_lines': [],
                },
                'numlines': 1,
            })

        self.assertTrue(self.repository.get_file.last_returned(
            'Hello, world!\n'.encode('utf-16')))

    def test_get_chunks_with_replace_in_added_file_with_parent_diff(self):
        """Testing DiffChunkGenerator.get_chunks with replace chunks in
        added file with parent diff
        """
        self.filediff.diff = (
            b'--- README\n'
            b'+++ README\n'
            b'@@ -1,1 +1,1 @@\n'
            b'-line\n'
            b'+line.\n'
        )
        self.filediff.parent_diff = (
            b'--- README\n'
            b'+++ README\n'
            b'@@ -0,0 +1,1 @@\n'
            b'+line\n'
        )
        self.filediff.source_revision = PRE_CREATION
        self.filediff.extra_data.update({
            'raw_insert_count': 1,
            'raw_delete_count': 1,
            'insert_count': 0,
            'delete_count': 0,
        })

        self.assertEqual(len(list(self.generator.get_chunks())), 1)

    def test_get_chunks_with_commit_and_base_filediff(self):
        """Testing DiffChunkGenerator.get_chunks with commit using base_filediff
        """
        commit1 = self.create_diffcommit(
            commit_id='abc1234',
            parent_id='abc1233',
            diffset=self.diffset,
            diff_contents=self.COMMIT_1_DIFF)
        commit2 = self.create_diffcommit(
            commit_id='abc1235',
            parent_id='abc1234',
            diffset=self.diffset,
            diff_contents=self.COMMIT_2_DIFF)

        self.diffset.finalize_commit_series(
            cumulative_diff=self.COMMIT_1_2_SQUASHED_DIFF,
            validation_info=None,
            validate=False,
            save=True)

        base_filediff = commit1.files.get()
        tip_filediff = commit2.files.get()

        generator = DiffChunkGenerator(request=None,
                                       filediff=tip_filediff,
                                       base_filediff=base_filediff)

        chunks = list(generator.get_chunks())
        self.assertEqual(len(chunks), 1)

        chunk = chunks[0]
        self.assertEqual(chunk['index'], 0)
        self.assertEqual(chunk['change'], 'replace')
        self.assertEqual(
            chunk['lines'],
            [[
                1,
                1, 'Hi, world!', [(0, 2), (9, 10)],
                1, 'Yo, world.', [(0, 2), (9, 10)],
                False,
            ]])

    def test_get_chunks_with_commit_and_no_base_filediff(self):
        """Testing DiffChunkGenerator.get_chunks with commit and no
        base_filediff
        """
        self.create_diffcommit(
            commit_id='abc1234',
            parent_id='abc1233',
            diffset=self.diffset,
            diff_contents=self.COMMIT_1_DIFF)
        self.create_diffcommit(
            commit_id='abc1235',
            parent_id='abc1234',
            diffset=self.diffset,
            diff_contents=self.COMMIT_2_DIFF)
        commit3 = self.create_diffcommit(
            commit_id='abc1236',
            parent_id='abc1235',
            diffset=self.diffset,
            diff_contents=self.COMMIT_3_DIFF)

        self.diffset.finalize_commit_series(
            cumulative_diff=self.COMMIT_1_3_SQUASHED_DIFF,
            validation_info=None,
            validate=False,
            save=True)

        filediff = commit3.files.get()

        generator = DiffChunkGenerator(request=None,
                                       filediff=filediff)

        chunks = list(generator.get_chunks())
        self.assertEqual(len(chunks), 1)

        chunk = chunks[0]
        self.assertEqual(chunk['index'], 0)
        self.assertEqual(chunk['change'], 'replace')
        self.assertEqual(
            chunk['lines'],
            [[
                1,
                1, 'Hello, world!', None,
                1, 'Yo, dog.', None,
                False,
            ]])

    def test_get_chunks_with_commit_and_base_tip_same(self):
        """Testing DiffChunkGenerator.get_chunks with commit and base_filediff
        same as filediff
        """
        commit = self.create_diffcommit(
            commit_id='abc1234',
            parent_id='abc1233',
            diffset=self.diffset,
            diff_contents=self.COMMIT_1_DIFF)

        self.diffset.finalize_commit_series(
            cumulative_diff=self.COMMIT_1_DIFF,
            validation_info=None,
            validate=False,
            save=True)

        filediff = commit.files.get()

        generator = DiffChunkGenerator(request=None,
                                       filediff=filediff,
                                       base_filediff=filediff)

        chunks = list(generator.get_chunks())
        self.assertEqual(len(chunks), 1)

        chunk = chunks[0]
        self.assertEqual(chunk['index'], 0)
        self.assertEqual(chunk['change'], 'equal')
        self.assertEqual(
            chunk['lines'],
            [[
                1,
                1, 'Hi, world!', [],
                1, 'Hi, world!', [],
                False,
            ]])

    def test_get_chunks_with_commit_and_file_recreated(self):
        """Testing DiffChunkGenerator.get_chunks with commit and file recreated
        in prior commit
        """
        commit1, commit2 = self._make_delete_recreate_commits()
        filediff = commit2.files.get()

        generator = DiffChunkGenerator(request=None,
                                       filediff=filediff)

        chunks = list(generator.get_chunks())
        self.assertEqual(len(chunks), 1)

        chunk = chunks[0]
        self.assertEqual(chunk['index'], 0)
        self.assertEqual(chunk['change'], 'insert')
        self.assertEqual(
            chunk['lines'],
            [[
                1,
                '', '', [],
                1, 'This is a new file.', [],
                False,
            ]])

    def test_get_chunks_with_commit_and_file_recreated_and_base_deleted(self):
        """Testing DiffChunkGenerator.get_chunks with commit and file recreated
        in prior commit with base_filediff as deleted file
        """
        commit1, commit2 = self._make_delete_recreate_commits()
        base_filediff = commit1.files.get()
        filediff = commit2.files.get()

        generator = DiffChunkGenerator(request=None,
                                       filediff=filediff,
                                       base_filediff=base_filediff)

        chunks = list(generator.get_chunks())
        self.assertEqual(len(chunks), 1)

        chunk = chunks[0]
        self.assertEqual(chunk['index'], 0)
        self.assertEqual(chunk['change'], 'insert')
        self.assertEqual(
            chunk['lines'],
            [[
                1,
                '', '', [],
                1, 'This is a new file.', [],
                False,
            ]])

    def test_line_counts_unmodified_by_interdiff(self):
        """Testing that line counts are not modified by interdiffs where the
        changes are reverted
        """
        self.filediff.source_revision = PRE_CREATION
        self.filediff.diff = (
            b'--- README\n'
            b'+++ README\n'
            b'@@ -0,0 +1,1 @@\n'
            b'+line\n'
        )

        # We have to consume everything from the get_chunks generator in order
        # for the line counts to be set on the FileDiff.
        self.assertEqual(len(list(self.generator.get_chunks())), 1)

        line_counts = self.filediff.get_line_counts()

        # Simulate an interdiff where the changes are reverted.
        interdiff_generator = DiffChunkGenerator(request=None,
                                                 filediff=self.filediff,
                                                 interfilediff=None,
                                                 force_interdiff=True)

        # Again, just consuming the generator.
        self.assertEqual(len(list(interdiff_generator.get_chunks())), 1)

        self.assertEqual(line_counts, self.filediff.get_line_counts())

    def _make_delete_recreate_commits(self):
        """Finalize and return commits for a delete/re-create test.

        This creates two commits. In the first one, an upstream file is
        deleted. In the second, it's re-created.

        The commits are finalized before being returned.

        Returns:
            list of reviewboard.diffviewer.models.diffcommit.DiffCommit:
            The newly-created commits.
        """
        commits = [
            self.create_diffcommit(
                commit_id='abc1234',
                parent_id='abc1233',
                diffset=self.diffset,
                diff_contents=(
                    b'diff --git a/README b/README\n'
                    b'deleted file mode 100644\n'
                    b'index 94bdd3e..0000000\n'
                    b'--- README\n'
                    b'+++ /dev/null\n'
                    b'@@ -1,1 +0,0 @@\n'
                    b'-Hello, world!\n'
                )),
            self.create_diffcommit(
                commit_id='abc1235',
                parent_id='abc1234',
                diffset=self.diffset,
                diff_contents=(
                    b'diff --git a/README b/README\n'
                    b'new file mode 100644\n'
                    b'index 0000000..ba178ca\n'
                    b'--- /dev/null\n'
                    b'+++ README\n'
                    b'@@ -0,0 +1,1 @@\n'
                    b'+This is a new file.\n'
                )),
        ]

        self.diffset.finalize_commit_series(
            cumulative_diff=(
                b'diff --git a/README b/README\n'
                b'index 94bdd3e..0000000 10644\n'
                b'--- README\n'
                b'+++ README\n'
                b'@@ -1,1 +1,1 @@\n'
                b'-Hello, world!\n'
                b'+This is a new file.\n'
            ),
            validation_info=None,
            validate=False,
            save=True)

        return commits


text:
from hazelcast.protocol.client_message import OutboundMessage, REQUEST_HEADER_SIZE, create_initial_buffer
from hazelcast.protocol.builtin import StringCodec
from hazelcast.protocol.builtin import DataCodec
from hazelcast.protocol.builtin import ListMultiFrameCodec

# hex: 0x013B00
_REQUEST_MESSAGE_TYPE = 80640
# hex: 0x013B01
_RESPONSE_MESSAGE_TYPE = 80641

_REQUEST_INITIAL_FRAME_SIZE = REQUEST_HEADER_SIZE

def encode_request(name, projection):
    buf = create_initial_buffer(_REQUEST_INITIAL_FRAME_SIZE, _REQUEST_MESSAGE_TYPE)
    StringCodec.encode(buf, name)
    DataCodec.encode(buf, projection, True)
    return OutboundMessage(buf, True)

def decode_response(msg):
    msg.next_frame()
    return ListMultiFrameCodec.decode_contains_nullable(msg, DataCodec.decode)


text:
layout: post
title: "4 Ways To Turn Customer Support Into A Growth Factor"
description: Customer Support is crucial - Delight your users more than you think you need to and it will pay off big
date: 2016-03-04 1:30
alias: /2016/03/04/4-ways-to-turn-customer-support-into-a-growth-factor/
category: Growth, Growth Hacking
is_non-tech: true
author:
  name: Diego Poza
  mail: diego.poza@auth0.com
  url: https://twitter.com/diegopoza
  avatar: https://avatars3.githubusercontent.com/u/604869?v=3&s=200
design:
  bg_color: "#222228"
  image: https://cdn.auth0.com/blog/customer-support/logo.png
tags:
- customer-service
- growth-hacking
- customer-support
related:
- 2016-04-18-progressive-profiling
- 2016-04-19-supercharge-your-registration-process
- 2016-02-03-getting-started-with-auth0-lock-series-implementing-lock
---

Paul Graham, co-founder of prestigious startup fund Y Combinator, [believes](http://paulgraham.com/ds.html) that early founders don't try hard enough to delight their customers.

This is a bit counter-intuitive, especially since Y Combinator also expects its companies to grow at the tremendous rate of [5-7% a week](http://www.paulgraham.com/growth.html). When we think about that kind of growth, we often think in terms of massive land-grabs and clever hacks, trying to find huge, underutilized acquisition channels and milking them for all they're worth.

Customer service, on the other hand, seems like something you work on down the line when you want to bump your retention numbers and build a referral program—not something you do for growth right now. But this is a misconception. Growth, whether now or in the future, [is all about retention](http://avc.com/2015/07/growth-vs-retention/). You can't grow unless you retain the users you acquire.

For Graham, taking "extraordinary measures" to make your customers happy is one of the most powerful tools a startup has both for acquiring new users and retaining the ones it has. Do things to make every customer "feel that signing up with you was one of the best choices they ever made," he says, and you'll have them for life. Plus, you'll get their friends, and their friends, and their friends, and so on.

Customer support is actually an incredible kind of growth hack—put a little more effort into it than everyone else, and you'll bend your retention curve up while bringing in new people and establishing a foundation for awesome growth.

Here are four ways to get started.

## 1. Create A Personalized, Educational Email Campaign

When you get a customer's email address, you are getting access to something very powerful: their "direct line." Email is still the most personal way you can reach out to your customers, short of a handwritten note or a phone call, and that potential for intimacy offers you an incredible opportunity to do some customer service as marketing. And unlike with a note or a phone call, you can send great emails at scale.

They can't, however, be one-size-fits-all. If the value your emails offer isn't tied into a customer's specific needs at that time, they'll end up archived or marked as spam.

That's why, to make customer support work as a growth factor at scale,  it's essential that your email campaigns are both behavioral—responsive to what people are actually doing—and automated. Add a specific goal that you can track and iterate on, and you're ready to go.

### Triggered campaigns

Good behavioral email campaigns rely on triggers, or specific user actions that *trigger *emails to be sent. Let's say you want to email those users who haven't logged in for a couple days with a list of reasons why coming back and subscribing would help them:

![Customer.io automated emails](https://cdn.auth0.com/blog/customer-support/Customer-io-sample.png)

With an email marketing manager like [Customer.io](http://customer.io/), you can set up even more specific triggers to automate your campaigns. For instance, you could have an email sent to everyone who used your product a half-dozen times or less, spent 3-5 minutes in your Help documentation, and then logged off.

You can use Auth0 to get those metrics into your marketing tool easily, first use [rules](https://auth0.com/docs/rules) to [send your data](https://github.com/auth0/rules/blob/master/rules/send-events-segmentio.md) into Segment, then enable [Segment](http://segment.io/)'s integration with Customer.io. Once you do that, every action your users take will send an "event" to Segment with a timestamp, and you'll be free to use it in targeting your messaging with Customer.io. Here's the rule for sending a "Loggin in" or "Signed up" event:

```
function(user, context, callback) {
  if(context.stats.loginsCount > 1){
    sendEvent('Logged in');
  } else {
    sendEvent('Signed up');
  }

  function sendEvent (e) {
    var sioTrack = {
      userId: user.user_id,
      event: e,
      properties: {
        application: context.clientName
      },
      context: {
        "ip" : context.request.ip,
        "userAgent" : context.request.userAgent
      }
    };

    // Segment API returns 200 OK for all its request. For possible errors
    // you must use Segment's Debugger (https://segment.com/docs/libraries/http/#errors)
    request({
      method: 'POST',
      url: 'https://SEGMENTIO_WRITE_KEY@api.segment.io/v1/track',
      headers: {
        'Content-type': 'application/json',
      },
      body: JSON.stringify(sioTrack),
    });
  }

  // don't wait for the Segment API call to finish, return right away (the request will continue on the sandbox)`
  callback(null, user, context);
}
```

You'll want to be fairly precise with the triggers you choose to collect and use, but you don't need to nitpick too much: if you're providing value with your emails, it'll pay off at some point.

Shopify knows the value of pre-emptive customer support: after you sign up, after you finish some set up, but before you officially launch your store, someone from customer service reaches out to you directly.

![Shoppify Customer Support Onboarding Email](https://cdn.auth0.com/blog/customer-support/shoppify-onboarding-email.png)

Even if you know exactly what you're doing with Shopify, this email works. It demonstrates that this company really cares about what you're trying to accomplish. It also implies that you will, in the future, have extremely easy access to help when you need it. That's the kind of messaging that's going to keep you from going to a competitor in the long run.

And that's not just intuition talking. Relevant, automated emails have been demonstrated to drive [18x more revenue](http://myemma.com/blog/article/21-must-know-email-automation-stats) than one-size-fits-all broadcast emails. They get more clicks, they lift conversions, and even those who don't buy are more likely to make larger purchases in the future.

From building the beginnings of trust to upselling, email is one of the most powerful ways to turn the delight of your customers into revenue and growth.

## 2. Create A Customized Onboarding Flow

Onboarding is another process that needs to be structured around how individual users interact with your product.

If you can't get your customers to their "aha! moments" fast enough, then you stand a good chance of losing them. After all, there's no momentum carrying them forward. They took a chance on your product, they're trying to see if it can do the job for them, and you need to show them that it can.

{% include tweet_quote.html quote_text="If you can't get your customers to their "aha! moments" fast enough, then you might lose them." %}

Signposts that point out essential parts of your product's UI can be invaluable for getting a new user to see its core value quickly. If you want to know where people are having trouble finding it, you can either go quantitative and look at your analytics or go qualitative and try a [user test](https://www.usertesting.com/). That will let you watch people navigate your product in real time so you can track their experiences, warts and all.

Once they're ramped up, you can use in-app announcements and indicators to show that new features or other enhancements are available. The easiest way to keep your service at the front of a customer's mind is to be compulsively informative about changes you're making.

[Appcues](http://appcues.com/) makes a tool strictly for designing onboarding flows that lets you do all this. There's no coding involved so your marketing or customer support teams can do all the work of designing onboarding themselves. They can add hotspots, for example, little triggered buttons that give users information depending on what they're trying to do, or flows, which add overlays that guide users through entire sequences of pages:

![Appcues Onboarding helpers](https://cdn.auth0.com/blog/customer-support/appcues-onboarding.png)

Each step that you design into your onboarding flow can be modified and segmented based on the same kinds of user metrics used for marketing. This is essential because while the core value of your product may be basically the same for whoever uses it, different buyer personas are going to require different approaches.

Sending the right message, at the right time, to the right person—that's what an amazing onboarding experience is all about. When you're brought into a service gracefully, and with what feels like a full support team behind you, then you're much more likely to stick around not just in the short-term, but for a long time.

If a good onboarding process brings you to an "aha! moment" quickly, then it's likely to have an outsized influence on the [entire customer lifecycle](http://blog.profitwell.com/why-churn-is-the-silent-killer-of-your-saas-company). It cascades. So when you're looking for places to help your customers understand your product and what it's really about, look to the first few minutes and seconds of your onboarding process. Hard work there will pay off down the road.

## 3. Use Video Support To Build A Human Connection

Dropbox, way back in 2009, was a crazy idea. It's a good thing, for instance, that Drew Houston didn't listen to most of the comments he got after submitting it for consideration to [Hacker News](https://news.ycombinator.com/item?id=8863). But there was one that [must have](https://news.ycombinator.com/item?id=8869) caught his eye: "If you are looking for a wider audience... make a video where you lay out the case for use using simple examples from user point of view."

That's just what they did. Realizing that most people couldn't even wrap their heads around the idea of a cloud-based storage system yet, Dropbox hired a studio to make them an "explainer video," which they then embedded on their homepage:

![Dropbox introduction video](https://cdn.auth0.com/blog/customer-support/dropbox-video.png)

The move paid off with an immediate [10% jump in conversions](http://wistia.com/blog/explainify-five-benefits). But this kind of power is just scratching the surface of what video can do.

As Wistia founder Chris Savage [points out](http://wistia.com/blog/evolution-of-video-goals), making videos that increase conversion rates is the first step—after that, you can use your videos to teach people about the problems your product solves and bring in *new *kinds of customers.

Using your subject matter expertise and experience to make videos about the *problems* expands your company's reach and builds legitimacy, like in this Wistia guide to [shooting video](http://wistia.com/library/shooting-video-with-an-iphone) with your iPhone:

![Wistia shooting video](https://cdn.auth0.com/blog/customer-support/wistia-tutorial.png)

It's not about getting customers. It's not about conversions. It's about teaching people some valuable skill, and that's an incredibly powerful tool.

"In the long term, teaching videos can save you time, automate conversions, and better educate your audience," [Savage says](http://wistia.com/blog/evolution-of-video-goals), "It can mean the difference between working with your customers to help them achieve their goals, and being stuck helping them understand what the heck your product does in the first place."

{% include tweet_quote.html quote_text="In the long term, teaching videos can save you time, automate conversions, and better educate your audience." %}

Making videos like this will save your support staff time and energy responding to the kinds of questions that are better answered with videos, but it's also a great way to market your company.

People see a video like that and they think, "Wow, this company spent money making a video just to help me solve a problem"—now that's customer support as marketing.

## 4. Let Customers Know When Features They Want Have Been Built

You always think you know what's best for your product, but you're probably wrong. The truth is, you don't even really know what your product is *for *until people start using it and telling you. And they will—as soon as you launch, you'll start hearing customers request new features.

But these requests can be difficult to deal with. At some point, they can come to sound like a Greek chorus of various interpretations of your product: "x" user wants "y" while "z" user wants "b" and "a" doesn't want anything to change, etc.

What's important is to maintain perspective. Don't drop everything because one customer said so, but don't let that prevent you from paying attention when you have a critical mass of requests for something.

[StatusPage.io](http://statuspage.io/), a website status as-a-service provider, has figured out a way to efficiently track feature requests, implement the most important ones first, and delight their customers in the process. To do it, they created an Excel chart where they write down, for each feature:

1. The customer who made the request
2. How much they expect to gain from fulfilling it

![Statuspage.io sample](https://cdn.auth0.com/blog/customer-support/statuspage.png)

This way, they can actually track whether or not the features customers want are beneficial to StatusPage's mission of [increasing ARPU](http://blog.statuspage.io/3-steps-to-increasing-our-average-revenue-per-user-by-2-4x). Then, when they build one, they can pull a list of customers who requested it and send out personalized emails to let them know what's going on:

![Email for New feature added](https://cdn.auth0.com/blog/customer-support/new-feature-added-statuspage.png)

Not only is this a great way to show your customers that you take their input seriously, it can be a great opportunity to upsell. After all, no one's going to your pricing page over and over to see what new features you're offering.  If a customer is flexible on price, but won't upgrade unless a certain feature becomes available on the next tier, an email like this is the only way to *ensure *yourself a chance at that revenue boost.

When a technique lets you systematically add revenue and make your customers really happy at the same time, it's a no-brainer.

## Make Customer Support Your Competitive Advantage

If you stop thinking about customer support as fundamentally reactive—user has problem, we solve problem—then you can start using it to open up whole new avenues for growth.

It can sometimes take a bit of work to overcome that mental hurdle. We're not used to great customer service. We're used to waiting on hold and terrible menus and reps who speak in programmatic sentences. But as Paul Graham says, it's doable.

"Once you realize that existing conventions are not the upper bound on user experience," [he says](http://paulgraham.com/ds.html), "it's interesting in a very pleasant way to think about how far you could go to delight your users."

It's interesting, yes. It can also be a driving factor in your company's revenues and growth, so pick one of these strategies, get started today, and let us know how it goes.


text:
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace System.Security.Cryptography
{
    public sealed class SafeEvpPKeyHandle : SafeHandle
    {
        internal static readonly SafeEvpPKeyHandle InvalidHandle = new SafeEvpPKeyHandle();

        private SafeEvpPKeyHandle() :
            base(IntPtr.Zero, ownsHandle: true)
        {
        }

        public SafeEvpPKeyHandle(IntPtr handle, bool ownsHandle)
            : base(handle, ownsHandle)
        {
        }

        protected override bool ReleaseHandle()
        {
            Interop.Crypto.EvpPkeyDestroy(handle);
            SetHandle(IntPtr.Zero);
            return true;
        }

        public override bool IsInvalid
        {
            get { return handle == IntPtr.Zero; }
        }

        /// <summary>
        /// Create another instance of SafeEvpPKeyHandle which has an independent lifetime
        /// from this instance, but tracks the same resource.
        /// </summary>
        /// <returns>An equivalent SafeEvpPKeyHandle with a different lifetime</returns>
        public SafeEvpPKeyHandle DuplicateHandle()
        {
            if (IsInvalid)
                throw new InvalidOperationException(SR.Cryptography_OpenInvalidHandle);

            // Reliability: Allocate the SafeHandle before calling UpRefEvpPkey so
            // that we don't lose a tracked reference in low-memory situations.
            SafeEvpPKeyHandle safeHandle = new SafeEvpPKeyHandle();

            int newRefCount = Interop.Crypto.UpRefEvpPkey(this);

            // UpRefEvpPkey returns the number of references to this key, if it's less than 2
            // (the incoming handle, and this one) then someone has already Disposed() this key
            // into non-existence.
            if (newRefCount < 2)
            {
                Debug.Fail("Called UpRefEvpPkey on a key which was already marked for destruction");
                throw Interop.Crypto.CreateOpenSslCryptographicException();
            }

            // Since we didn't actually create a new handle, copy the handle
            // to the new SafeHandle.
            safeHandle.SetHandle(handle);
            return safeHandle;
        }
    }
}


text:
using System;
using System.Windows.Data;
using Ozeki.VoIP;

namespace OzekiDemoSoftphoneWPF.GUI.Converters
{
    /// <summary>
    /// Converts an IPhoneLine object to a string.
    /// </summary>
    class MessageSummaryToStringConverter : IValueConverter
    {
        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            IPhoneLine line = value as IPhoneLine;
            if (line == null)
                return "No lines selected";

            if (line.MessageSummary == null)
                return "No message info";

            return "View messages";
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value;
        }

        #endregion
    }
}


text:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class PhaseButton : MonoBehaviour
{

    public Vector2 p1Location, p2Location;

	public void OnPhase(GameController.Phase phase)
    {
        Button button = GetComponent<Button>();
        button.interactable = (phase != GameController.Phase.Results);
    }
}