サーバーから収集する

サーバー上で、Flipで分析するために必要なデータを収集する方法を紹介します。

1. 関数を定義する

初めに、後のステップで利用する関数を定義します。これらの関数はHTTP APIをベースに作成されています。

  • 下のコードを、適当なファイルにコピー&ペーストして下さい。
class Flip {
  INGESTION_KEY = "プロジェクトの収集キー"; // プロジェクトの設定画面に表示されている値に変更して下さい
  TRACK_ENDPOINT = "https://app.flip.inc/v1/track";
  IDENTIFY_ENDPOINT = "https://app.flip.inc/v1/identify";

  track({ name, userId, anonymousId, properties, userProperties }) {
    this.post(this.TRACK_ENDPOINT, {
      name,
      originalUserId: userId,
      anonymousId,
      properties,
      userProperties: {
        ...userProperties,
        platform: "server",
      },
    });
  }

  identify({ userId, anonymousId, properties }) {
    this.post(this.IDENTIFY_ENDPOINT, {
      originalUserId: userId,
      anonymousId,
      properties: {
        ...properties,
        platform: "server",
      },
    });
  }

  async post(endpoint, requestBody) {
    try {
      const response = await fetch(endpoint, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          ...requestBody,
          ingestionKey: this.INGESTION_KEY,
        }),
      });

      if (!response.ok) {
        const responseBody = await response.json();
        console.error(`[Flip] ${response.status} ${responseBody.message}`);
      }
    } catch (err) {
      console.error(`[Flip] ${err.message}`);
    }
  }
}
class Flip {
  INGESTION_KEY = "プロジェクトの収集キー"; // プロジェクトの設定画面に表示されている値に変更して下さい
  TRACK_ENDPOINT = "https://app.flip.inc/v1/track";
  IDENTIFY_ENDPOINT = "https://app.flip.inc/v1/identify";

  track({
    name,
    userId,
    anonymousId,
    properties,
    userProperties,
  }: {
    name: string;
    userId?: string;
    anonymousId?: string;
    properties?: Record<string, any>;
    userProperties?: Record<string, any>;
  }) {
    this.post(this.TRACK_ENDPOINT, {
      name,
      originalUserId: userId,
      anonymousId,
      properties,
      userProperties: {
        ...userProperties,
        platform: "server",
      },
    });
  }

  identify({
    userId,
    anonymousId,
    properties,
  }: {
    userId?: string;
    anonymousId?: string;
    properties?: Record<string, any>;
  }) {
    this.post(this.IDENTIFY_ENDPOINT, {
      originalUserId: userId,
      anonymousId,
      properties: {
        ...properties,
        platform: "server",
      },
    });
  }

  async post(endpoint: string, requestBody: Record<string, any>) {
    try {
      const response = await fetch(endpoint, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          ...requestBody,
          ingestionKey: this.INGESTION_KEY,
        }),
      });

      if (!response.ok) {
        const responseBody = await response.json();
        console.error(`[Flip] ${response.status} ${responseBody.message}`);
      }
    } catch (err: any) {
      console.error(`[Flip] ${err.message}`);
    }
  }
}
import requests
import json

class Flip:
  INGESTION_KEY = "プロジェクトの収集キー"  # プロジェクトの設定画面に表示されている値に変更して下さい
  TRACK_ENDPOINT = "https://app.flip.inc/v1/track"
  IDENTIFY_ENDPOINT = "https://app.flip.inc/v1/identify"

  def track(self, name, user_id=None, anonymous_id=None, properties=None, user_properties=None):
    if user_properties is None:
      user_properties = {}

    user_properties["platform"] = "server"

    self._post_to_flip(
      self.TRACK_ENDPOINT,
      {
        "name": name,
        "originalUserId": user_id,
        "anonymousId": anonymous_id,
        "properties": properties,
        "userProperties": user_properties,
      }
    )

  def identify(self, user_id=None, anonymous_id=None, properties=None):
    if properties is None:
      properties = {}

    properties["platform"] = "server"

    self._post_to_flip(
      self.IDENTIFY_ENDPOINT,
      {
        "originalUserId": user_id,
        "anonymousId": anonymous_id,
        "properties": properties,
      }
    )
   
  def _post_to_flip(self, endpoint, request_body):
    headers = {"Content-Type": "application/json"}
    request_body["ingestionKey"] = self.INGESTION_KEY

    try:
      response = requests.post(endpoint, headers=headers, data=json.dumps(request_body))
      response.raise_for_status()
    except requests.HTTPError as e:
      print(f"[Flip] {response.status_code} {response.json().get('message', '')}")
    except Exception as e:
      print(f"[Flip] {e}")
import (
  "bytes"
  "encoding/json"
  "fmt"
  "io/ioutil"
  "net/http"
)

const (
  ingestionKey     = "プロジェクトの収集キー" // プロジェクトの設定画面に表示されている値に変更して下さい
  trackEndpoint    = "https://app.flip.inc/v1/track"
  identifyEndpoint = "https://app.flip.inc/v1/identify"
)

type TrackParams struct {
  Name           string                 `json:"name"`
  UserId         *string                `json:"userId,omitempty"`
  AnonymousId    *string                `json:"anonymousId,omitempty"`
  Properties     map[string]interface{} `json:"properties,omitempty"`
  UserProperties map[string]interface{} `json:"userProperties,omitempty"`
}

type IdentifyParams struct {
  UserId         *string                `json:"userId,omitempty"`
  AnonymousId    *string                `json:"anonymousId,omitempty"`
  Properties     map[string]interface{} `json:"properties,omitempty"`
}

func getStringPointer(s string) *string {
  return &s
}

func postToFlip(endpoint string, requestBody map[string]interface{}) {  
  requestBody["ingestionKey"] = ingestionKey
  
  data, err := json.Marshal(requestBody)
  if err != nil {
    fmt.Printf("[Flip] %s\n", err)
    return
  }

  req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(data))
  if err != nil {
    fmt.Printf("[Flip] %s\n", err)
    return
  }
  
  req.Header.Set("Content-Type", "application/json")

  client := &http.Client{}
  resp, err := client.Do(req)
  if err != nil {
    fmt.Printf("[Flip] %s\n", err)
    return
  }
  defer resp.Body.Close()

  if resp.StatusCode != http.StatusOK {
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
      fmt.Printf("[Flip] %s\n", err)
      return
    }
    
    var responseBody map[string]interface{}
    err = json.Unmarshal(body, &responseBody)
    if err != nil {
      fmt.Printf("[Flip] %s\n", err)
      return
    }

    message, ok := responseBody["message"].(string)
    if !ok {
      message = "Error message cannot be parsed as string"
    }

    fmt.Printf("[Flip] %d %s\n", resp.StatusCode, message)
  }
}

func Track(params TrackParams) {
  body := map[string]interface{}{
    "name":           params.Name,
    "originalUserId": params.UserId,
    "anonymousId":    params.AnonymousId,
    "properties":     params.Properties,
    "userProperties": params.UserProperties,
  }
  
  if body["userProperties"] == nil {
    body["userProperties"] = make(map[string]interface{})
  }
  
  body["userProperties"].(map[string]interface{})["platform"] = "server"
  
  postToFlip(trackEndpoint, body)
}

func Identify(params IdentifyParams) {
  body := map[string]interface{}{
    "originalUserId": params.UserId,
    "anonymousId":    params.AnonymousId,
    "properties":     params.Properties,
  }
  
  if body["properties"] == nil {
    body["properties"] = make(map[string]interface{})
  }

  body["properties"].(map[string]interface{})["platform"] = "server"
  
  postToFlip(identifyEndpoint, body)
}
require 'net/http'
require 'json'
require 'uri'

module Flip
  INGESTION_KEY = "プロジェクトの収集キー" # プロジェクトの設定画面に表示されている値に変更して下さい
  TRACK_ENDPOINT = "https://app.flip.inc/v1/track"
  IDENTIFY_ENDPOINT = "https://app.flip.inc/v1/identify"

  class << self
    def track(name:, user_id: nil, anonymous_id: nil, properties: nil, user_properties: nil)
      user_properties ||= {}
      user_properties[:platform] = "server"
      
      post_to_flip(TRACK_ENDPOINT, {
        name: name,
        originalUserId: user_id,
        anonymousId: anonymous_id,
        properties: properties,
        userProperties: user_properties
      })
    end

    def identify(user_id: nil, anonymous_id: nil, properties: nil)
      properties ||= {}
      properties[:platform] = "server"
      
      post_to_flip(IDENTIFY_ENDPOINT, {
        originalUserId: user_id,
        anonymousId: anonymous_id,
        properties: properties
      })
    end
    
    private

    def post_to_flip(endpoint, request_body)
      request_body[:ingestionKey] = INGESTION_KEY
      
      uri = URI.parse(endpoint)
      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = true if uri.scheme == 'https'
      
      headers = {
        'Content-Type' => 'application/json'
      }
      
      request = Net::HTTP::Post.new(uri.path, headers)
      request.body = JSON.generate(request_body)
      
      begin
        response = http.request(request)
        
        if response.code.to_i != 200
          response_body = JSON.parse(response.body)
          puts "[Flip] #{response.code} #{response_body['message']}"
        end
      rescue => err
        puts "[Flip] #{err}"
      end
    end
  end
end
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import org.json.JSONObject;

public class Flip {
  private static final String INGESTION_KEY = "プロジェクトの収集キー"; // プロジェクトの設定画面に表示されている値に変更して下さい
  private static final String TRACK_ENDPOINT = "https://app.flip.inc/v1/track";
  private static final String IDENTIFY_ENDPOINT = "https://app.flip.inc/v1/identify";
  
  public static void track(
    String name,
    String userId,
    String anonymousId,
    JSONObject properties,
    JSONObject userProperties
  ) {
    JSONObject requestBody = new JSONObject();
    requestBody.put("name", name);
    requestBody.put("originalUserId", userId);
    requestBody.put("anonymousId", anonymousId);
    requestBody.put("properties", properties);
    
    if (userProperties == null) {
    	userProperties = new JSONObject();
    }
    userProperties.put("platform", "server");
    requestBody.put("userProperties", userProperties);

    post(TRACK_ENDPOINT, requestBody);
  }
  
  public static void identify(
    String userId,
    String anonymousId,
    JSONObject properties
  ) {
    JSONObject requestBody = new JSONObject();
    requestBody.put("originalUserId", userId);
    requestBody.put("anonymousId", anonymousId);
    requestBody.put("properties", properties);
    
    if (properties == null) {
    	properties = new JSONObject();
    }
    properties.put("platform", "server");
    requestBody.put("properties", properties);

    post(IDENTIFY_ENDPOINT, requestBody);
  }
  
  private static void post(String endpoint, JSONObject requestBody) {
    try {
      requestBody.put("ingestionKey", INGESTION_KEY);
      
      URL url = new URL(endpoint);
      HttpURLConnection connection = (HttpURLConnection) url.openConnection();
      
      connection.setRequestMethod("POST");
      connection.setRequestProperty("Content-Type", "application/json");
      connection.setDoOutput(true);
      
      try (OutputStream os = connection.getOutputStream()) {
        byte[] input = requestBody.toString().getBytes("utf-8");
        os.write(input, 0, input.length);
      }
      
      int responseCode = connection.getResponseCode();
      if (responseCode != HttpURLConnection.HTTP_OK) {
        try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getErrorStream(), "utf-8"))) {
          StringBuilder response = new StringBuilder();
          String responseLine;
          while ((responseLine = br.readLine()) != null) {
            response.append(responseLine.trim());
          }

          JSONObject jsonResponse = new JSONObject(response.toString());
          System.err.println("[Flip] " + responseCode + " " + jsonResponse.getString("message"));
        }
      }
      
      connection.disconnect();
    } catch (Exception e) {
      System.err.println("[Flip] " + e.getMessage());
    }
  }
}
class Flip {
  private const INGESTION_KEY = "プロジェクトの収集キー"; // プロジェクトの設定画面に表示されている値に変更して下さい
  private const TRACK_ENDPOINT = "https://app.flip.inc/v1/track";
  private const IDENTIFY_ENDPOINT = "https://app.flip.inc/v1/identify";

  public function track($data) {
    $defaults = [
      'name' => '',
      'userId' => null,
      'anonymousId' => null,
      'properties' => [],
      'userProperties' => [],
    ];
    $data = array_merge($defaults, $data);
    
    $this->post(self::TRACK_ENDPOINT, [
      'name' => $data['name'],
      'originalUserId' => $data['userId'],
      'anonymousId' => $data['anonymousId'],
      'properties' => $data['properties'],
      'userProperties' => array_merge($data['userProperties'], ['platform' => 'server']),
    ]);
  }

  public function identify($data) {
    $defaults = [
      'userId' => null,
      'anonymousId' => null,
      'properties' => [],
    ];
    $data = array_merge($defaults, $data);

    $this->post(self::IDENTIFY_ENDPOINT, [
      'originalUserId' => $data['userId'],
      'anonymousId' => $data['anonymousId'],
      'properties' => array_merge($data['properties'], ['platform' => 'server']),
    ]);
  }

  private function post($endpoint, $requestBody) {
    $requestBody['ingestionKey'] = self::INGESTION_KEY;
    $body = json_encode($requestBody);
    $opts = [
      'http' => [
        'method' => 'POST',
        'header' => 'Content-Type: application/json',
        'content' => $body,
      ],
    ];
    
    $context = stream_context_create($opts);
    $result = @file_get_contents($endpoint, false, $context);
    
    if ($result === false) {
      $error = error_get_last();
      echo "[Flip] " . $error['message'];
    }
  }
}

2. イベントを収集する

イベントを収集することで、ユーザーの一つ一つの操作を記録できるようになります。各引数の具体的な役割に関しては、HTTP APIを参考にして下さい。

  • ユーザーが特定の操作を実行した直後(例. 機能を利用)に、次のコードが呼ばれるようにペーストして下さい。コードの内容は自由に変更して構いません。
const flip = new Flip();

flip.track({
  name: "message_sent",
  userId: "usr_GXfryD0JQivyGa97TgGsT",
  properties: {
    message_type: "画像付きテキスト",
    character_count: 32,
  },
  userProperties: {
    email: "[email protected]",
    name: "Tanaka Masaki",
    position: "プロダクトマネージャー",
  },
});
flip = Flip()

flip.track(
  name="message_sent",
  user_id="usr_GXfryD0JQivyGa97TgGsT",
  properties={
    "message_type": "画像付きテキスト",
    "character_count": 32,
  },
  user_properties={
    "email": "[email protected]",
    "name": "Tanaka Masaki",
    "position": "プロダクトマネージャー",
  },
)
trackParams := TrackParams{
  Name: "message_sent",
  UserId: getStringPointer("usr_GXfryD0JQivyGa97TgGsT"),
  Properties: map[string]interface{}{
    "message_type": "画像付きテキスト",
    "character_count": 32,
  },
  UserProperties: map[string]interface{}{
    "email": "[email protected]",
    "name": "Tanaka Masaki",
    "position": "プロダクトマネージャー",
  },
}
Track(trackParams)
Flip.track(
  name: "message_sent",
  user_id: "usr_GXfryD0JQivyGa97TgGsT",
  properties: {
    message_type: "画像付きテキスト",
    character_count: 32
  },
  user_properties: {
    email: "[email protected]",
    name: "Tanaka Masaki",
    position: "プロダクトマネージャー"
  }
)
JSONObject properties = new JSONObject();
properties.put("message_type", "画像付きテキスト");
properties.put("character_count", 32);

JSONObject userProperties = new JSONObject();
userProperties.put("email", "[email protected]");
userProperties.put("name", "Tanaka Masaki");
userProperties.put("position", "プロダクトマネージャー");

Flip.track(
  "message_sent",
  "usr_GXfryD0JQivyGa97TgGsT",
  null,
  properties,
  userProperties
);
$flip = new Flip();

$flip->track([
  "name" => "message_sent",
  "userId" => "usr_GXfryD0JQivyGa97TgGsT",
  "properties" => [
    "message_type" => "画像付きテキスト",
    "character_count" => 32
  ],
  "userProperties" => [
    "email" => "[email protected]",
    "name" => "Tanaka Masaki",
    "position" => "プロダクトマネージャー"
  ]
]);

3. ユーザー情報を更新する

下のコードを追加することで、イベントを収集することなく、特定のユーザーの情報を更新できます。各引数の具体的な役割に関しては、HTTP APIを参考にして下さい。

  • ユーザーの情報が変更された直後(例. 新規登録・オンボーディング)に、次のコードが呼ばれるように下のコードをペーストして下さい。コードの内容は自由に変更して構いません。
const flip = new Flip();

flip.identify({
  userId: "usr_GXfryD0JQivyGa97TgGsT",
  properties: {
    email: "[email protected]",
    name: "Tanaka Masaki",
    position: "プロダクトマネージャー",
  },
});
flip = Flip()

flip.identify(
  user_id="usr_GXfryD0JQivyGa97TgGsT",
  properties={
    "email": "[email protected]",
    "name": "Tanaka Masaki",
    "position": "プロダクトマネージャー",
  }
)
identifyParams := IdentifyParams{
  UserId: getStringPointer("usr_GXfryD0JQivyGa97TgGsT"),
  Properties: map[string]interface{}{
    "email": "[email protected]",
    "name": "Tanaka Masaki",
    "position": "プロダクトマネージャー",
  },
}
Identify(identifyParams)
Flip.identify(
  user_id: "usr_GXfryD0JQivyGa97TgGsT",
  properties: {
    email: "[email protected]",
    name: "Tanaka Masaki",
    position: "プロダクトマネージャー"
  }
)
JSONObject properties = new JSONObject();
properties.put("email", "[email protected]");
properties.put("name", "Tanaka Masaki");
properties.put("position", "プロダクトマネージャー");

Flip.identify(
  "usr_GXfryD0JQivyGa97TgGsT",
  null,
  userProperties
);
$flip = new Flip();

$flip->identify([
  "userId" => "usr_GXfryD0JQivyGa97TgGsT",
  "properties" => [
    "email" => "[email protected]",
    "name" => "Tanaka Masaki",
    "position" => "プロダクトマネージャー"
  ]
]);