fbpx
x

開発者

InCountryプラットフォームは、開発者にデータのローカリゼーションを構成および管理する機能を提供します。多数のツールを備えたInCountryは、ほぼすべてのタイプのアプリと統合できます。

InCountryの開発者ツールセットには、SDK、API、プロキシ、および90か国以上を含むSaaS用のすぐに使用できる一連の統合が含まれています。

インカントリープラットフォーム

Diagram Developers

InCountryのツール

InCountry Platformは、次のように、データ常駐サービス用の様々な商品を提供します。

InCountry ポータル

.

InCountry Portalは、InCountryプラットフォーム内で開始するための最初のポイントです。組織アカウントを作成し、データストアをさらに制御するための登録オプションを提供します。ここでは、これらのデータストアにアクセスして特定の操作を実行する組織内の他の従業員のアカウントを管理することもできます。

InCountry 境界

.

InCountry Borderは、アプリケーションのフロントエンド部分とバックエンド部分を統合するデータ通信コンポーネントです。規制されたデータを使用してリクエストをフロントエンドからバックエンドにプロキシし、トークン化されたデータをバックエンドに返しながら、それをInCountryプラットフォームに書き込みます。最小限の労力でデータをその場で編集および編集解除できます。

InCountry REST API

.

InCountry REST APIは、InCountry SDKをアプリケーションに統合せずに、InCountryプラットフォームで規制されたデータをクエリできるようにするRESTfulメソッドのセットです。データ通信を簡素化し、データローカリゼーション機能を備えたアプリケーションをすばやく構築できます。

InCountry SDK

.

InCountry SDKは、次のプログラミング言語用のソフトウェア開発ツールのコレクションです。

  • Python
  • Java
  • Node.js
  • .Net(近日公開)

InCountry SDKをアプリケーションまたはシステムに統合して、複雑なデータ処理シナリオを開発し、データのローカリゼーションにInCountryPlatformのすべての利点を利用できます。 InCountry SDKを使用すると、包括的な統合を実装し、規制されたデータを必要な方法で処理できます。さらに、InCountry SDKを使用すると、暗号化キーを自分で管理し、必要に応じてカスタム暗号化方式を使用できます。

SaaS統合(Salesforceを含む)

.

InCountry SDKをアプリケーションまたはシステムに統合し、複雑なデータ処理シナリオを開発し、データのローカリゼーションにInCountry Platformのすべての利点を利用できます。 InCountry SDKを使用すると、包括的な統合を実装し、規制されたデータを必要な方法で処理できます。さらに、InCountry SDKを使用すると、暗号化キーを自分で管理し、必要に応じてカスタム暗号化方式を使用できます。
InCountry for Salesforceの統合により、Salesforceで規制対象のデータにアクセスするときに、元の国以外でデータを編集したり、特定の国のユーザーのアクセスを制限したり、地域のコンプライアンス規制に違反することなく他の国に複製したりできます。

InCountryの商品の特徴

次のセクションには、InCountryツールセットが提供する機能のリストが紹介されています。

InCountry ポータル

.
  • 新規ユーザーの初期登録とアカウントの回復
  • 組織とそのメンバーの管理
  • 組織アカウントへのアクセス管理
  • ストレージ環境とアクセスクレデンシャルの作成
  • 各国の規制データを保存するための推奨モジュール

InCountry 境界

.
  • アプリケーションのフロントエンドとバックエンド間のシームレスな統合
  • 規制されたデータ型の編集および非編集ポリシー
  • オンザフライのデータ編集/非編集
  • ユーザーベースのデータ制御
  • データ形式を保持した規制データのトークン化
  • InCountryプラットフォームでの規制対象データの暗号化

InCountry REST API

.
  • アプリケーションプロトタイプの迅速な作成
  • すべてのCRUD操作はアクセス可能
  • InCountryプラットフォームでの規制対象データの暗号化
  • データ帯域幅経済のためのオンデマンドデータ検索
  • データを処理、検証、集約するためのサーバーレス機能

InCountry SDK

.
  • 3つのプログラミング言語の可用性
  • アプリケーション内での複雑なデータシナリオの実装
  • 好きな方法でのデータ暗号化
  • 堅牢なパフォーマンスと迅速なデータアクセス
  • データ移行とキーローテーション
  • カスタムの暗号化方式
  • バッチでのデータの書き込み/読み取り
  • TLS 1.2による安全なデータ通信。

SaaS統合(Salesforceを含む)

.
  • Salesforceを含むSaaSアプリケーションとの即時統合
  • データ常駐コンプライアンスまでの最小時間
  • JavaScriptを使用したサーバーレス関数
  • データ暗号化
  • 複数のデータ処理モード:編集、制限、レプリケーション

ニーズに最適なソリューションの選択

新しいアプリケーションの開発

.

新しいアプリケーションを開発するには、まず、アプリケーションとInCountryプラットフォーム間の統合レベルとデータ通信フローを特定します。これに加えて、使用するデータ規制モデルと、アプリケーションがサービスを提供する国も特定します。

これらすべてのポイントが特定されたら、次のようにInCountryツールの選択に進むことができます。

1.アプリケーションに135792の単純なデータ通信データフローを備えたフロントエンドとバックエンドしかない場合、最も簡単なソリューションはInCountry Borderとの統合です。データリクエストをフロントエンドからInCountry Proxyにリダイレクトし、さらにバックエンドにルーティングするだけです。

2.より自律的なアプリケーションを実装する場合は、InCountry REST APIを使用する必要があります。アプリケーションのクライアント側で発行された証明書を使用してデータ要求を承認する必要があります。

3.包括的なビジネスロジック、複数の国でのデータローカリゼーション、およびそれらの国からのさらなるデータ集約を備えたアプリケーションを構築する必要がある場合は、InCountry SDKを選択する必要があります。 Python、Java、Node.jsなどの様々なプログラミング言語で提供されます。規制されたデータを処理し、暗号化し、オンデマンドでフェッチするための堅牢で包括的なソリューションを実装するために不可欠なすべての開発ツールを提供します。 InCountry SDKを使用すると、データを必要な方法で管理、処理、および保存するためのより多くの機能を利用できます。

カスタムおよび新しく構築された内部アプリケーションについては、SDKを確認してください。

データ常駐を既存のアプリケーションに統合

.

データ常駐を既存のアプリケーションに統合するには、InCountry Borderを使用します。これは、フロントエンド側とバックエンド側の間のデータ通信フロー内に統合するための迅速な方法を提供します。データリクエストをアプリケーションのフロントエンドからInCountry Borderに再ルーティングする必要があります。これにより、データの編集と編集解除が実行されます。これらのデータ操作を実行した後、InCountry Borderは、規制およびトークン化されたデータを使用してデータ要求をアプリケーションのバックエンドにさらにプロキシします。

この商品は、規制対象のデータフィールドの暗号化や、アプリケーションのバックエンドで確立された既存のデータ形式パターンを保持するためのトークン化など、オンザフライでデータの編集と「編集解除」を実行できます。これには、アプリケーション内での最小限の調整が必要であり、データモデル内での変更はまったく必要ありません。

複数のシステムの統合

.

複数のシステムを統合し、InCountryプラットフォームを介してそれらの間で規制されたデータを処理するには、InCountry REST APIを使用します。このようにして、規制されたデータを1つのシステムからInCountryプラットフォームに書き込み、InCountry REST APIを介して一般的なリクエストを実行することで他のシステムからデータを読み取ることができます。規制対象データのトークン化はInCountry Platformによって実行されるため、記録を区別するための記録キーの保存を除いて、システムで何も調整する必要はありません。

同様に、より多くのシステムを統合できます。たとえば、CRMシステムとERPシステムを使用して、独自のデータレイヤーを格納し、それらを組み合わせて、独自のデータ集約システムで分析することができます。 InCountry REST APIを使用すると、データ通信フローの調整を最小限に抑えてこれを行うことができます。

REST APIは、複数のシステムを統合し、システム間で規制されているデータと規制されていないデータの両方でデータクエリを実行できます。このようにして、データを組み合わせてシステム内に表示できます。

データバックアップの作成

.

規制されたデータを含む記録のデータバックアップを作成するには、InCountry REST APIを使用します。これにより、InCountry Platformへの記録の一括インポートを実行できます。これらの記録は、必要に応じてオンデマンドでアクセスできるため、長期間保存できます。

SaaSソリューションのデータ常駐

.

SalesforceなどのSaaSソリューションを使用する場合は、InCountry Platformでのデータの常駐と安全なデータ保持のためにネイティブのSaaS統合ソリューションを使用してください。迅速な展開と最小限のセットアップは、すぐに使用できるソリューションの主な利点です。サーバーレス機能を使用すると、規制対象データのデータ通信操作を強化および変更できます。

現在、InCountry Platformは、すぐに使用できる次の統合を提供します。

  • Salesforce
  • ServiceNow
  • Mambu
  • Segment
  • Twilio

データ規制モデル

データ ローカリゼーション ルールは、規制されたデータの保管場所、表示、処理に影響を与える可能性があり、これらの 3 つの InCountry モデルに誇張してマッピングされます。

InCountry データ保護モデル
.複製された制限付き還元された
ストレージ出入国国内に国内に
見る出入国出入国国内に
処理出入国国外国内に

InCountryプラットフォームは、規制、コンプライアンス、および顧客のニーズに応じて、3つのモデルで構成できます。

モデル説明

複製された

規制されたデータは最初に発信国に保存され、次にデータを他の国に複製できます。この場合、規制対象のデータは、発信国にあるInCountry Platformに保存されてから、別の国のシステムまたはデータストアに複製されます。

制限付き

規制対象のデータは、発信国のInCountry Platform内に保存されますが、読み取られたときにそのままにしておくことができます。

規制対象データに対するすべてのデータ要求は、発信国以外でこのデータを要求すると、発信国内のInCountryプラットフォームに直接実行されます。規制されたデータはシステム内に保存されません。

還元された

規制対象のデータは原産国に保管されており、出国することはできません。このデータへのアクセスは現在のユーザーの場所に応じて規制されているため、エンドユーザーには規制対象のデータ(アクセス国がデータの発信元の国と一致する場合)または編集されたデータ(アクセス国がデータの発信元の国と異なる場合)のいずれかが表示されます。

規制対象データに対するすべてのデータ要求は、発信元国内のInCountryプラットフォームに対して直接実行されます。規制されたデータはシステム内に保存されません。

次のステップ

以下に、独自にアプリケーションを構築するために使用できる、すぐに使用できるプログラムサンプルへのリンクを示します。

1.
承認

InCountry Platformは、使用している商品に応じて、データ要求を承認するための様々な方法を提供します。

2.
OAuth 2.0認証

InCountry Platformは、認証と承認にOAuth2.0プロトコルを使用します。 InCountry Platformは、Webサーバーやクライアント側のシナリオなどの一般的なOAuth2.0シナリオをサポートします。 OAuth 2.0承認メカニズムは、アカウントのセキュリティを保証し、ユーザーセッションを完全に制御します。
InCountryプラットフォームの使用を開始するには、InCountryポータルで新しいアカウントを作成します。ここでは、新しい環境とクライアントを作成する必要があります。クライアントごとに、クライアントIDとクライアントシークレットを取得します。これらをさらに使用し、InCountryプラットフォームからトークンを取得できます。

3.
クライアントIDとクライアントシークレットの取得

1. InCountryPortalに登録またはサインインします。
2.新しい環境を作成します。
3.新しいクライアントを作成します。
4. InCountry Portalは、環境のクライアントIDとクライアントシークレットを発行します。
5.発行されたクライアントとクライアントシークレットを使用して、SDKでトークンを生成します。

4.
証明書による承認

InCountry REST APIは、証明書を使用してInCountry Platformへのデータ要求を承認しています。 InCountryチームは、リクエストに応じて証明書を提供します。

クイックレシピ

以下に、InCountryPlatformでこのソリューションまたはそのソリューションを実装する方法に関するクイックコードレシピのリストを示します。

Create Storage

ストレージインスタンスの作成

.

InCountry Platform の使用を開始するには、ストレージ インスタンスを作成する必要があります。

  • Python SDK
  • Node.js SDK
  • Java SDK
Python SDK

To access your data in InCountry Platform with Python SDK, you need to create an instance of the Storage class.

class Storage:
    def __init__(
        self,
        environment_id: Optional[str] = None,   # Required to be passed in, or as the INC_API_KEY environment variable
        api_key: Optional[str] = None,          # Required when using API key authorization, or as an environment variable
        client_id: Optional[str] = None,        # Required when using oAuth authorization, can be also set through the INC_CLIENT_ID variable
        client_secret: Optional[str] = None,    # Required when using oAuth authorization, can be also set through the INC_CLIENT_SECRET variable
        endpoint: Optional[str] = None,         # Optional. Defines API URL. Can also be set up through the INC_ENDPOINT environment variable
        encrypt: Optional[bool] = True,         # Optional. If False, encryption is not applied
        debug: Optional[bool] = False,          # Optional. If True enables the additional debug logging
        options: Optional[Dict[str, Any]] = {}, # Optional. It is used to fine-tune some configurations
        custom_encryption_configs: Optional[List[dict]] = None, # Optional. List of custom encryption configurations
        secret_key_accessor: Optional[SecretKeyAccessor] = None, # Instance of the SecretKeyAccessor class. It is used to fetch the encryption secret
    ):
        ...

WARNING

API Key authorization is being deprecated. The backward compatibility is preserved for the api_key parameter but you no longer can access API keys (neither old nor new) from your dashboard.


The client_id, client_secret, and environment_id variables can be fetched from your dashboard on Incountry Portal.

The endpoint variable defines API URL and is used to override the default one.

You can disable the encryption (not recommended). Set the encrypt property to false if you want to disable it. Avoid using it when using for production data.

The options variable allows you to tweak some SDK configurations

{
    "http_options": {
        "timeout": int,         # In seconds. Should be greater than 0
    },
    "auth_endpoints": dict,     # A custom endpoints regional map that should be used for fetching oAuth tokens

    "countries_endpoint": str,  # If your PoPAPI configuration relies on a custom PoPAPI server
                                # (rather than the default one) use the `countriesEndpoint` option
                                # to specify the endpoint responsible for fetching the list of supported countries

    "endpoint_mask": str,       # Defines API base hostname part to use.
                                # If set, all requests will be sent to https://${country}${endpointMask} host
                                # instead of the default one (https://${country}-mt-01.api.incountry.io)
}

Below you can find the example of how to create a storage instance

from incountry import Storage, SecretKeyAccessor

storage = Storage(
    api_key="<api_key>",
    environment_id="<env_id>",
    debug=True,
    secret_key_accessor=SecretKeyAccessor(lambda: "password"),
    options={
        "http_options": {
            "timeout": 5
        },
        "countries_endpoint": "https://private-pop.incountry.io/countries",
        "endpoint_mask" ".private-pop.incountry.io",
    }
)

oAuth Authentication

The SDK also supports oAuth authentication credentials instead of plain API key authorization. oAuth authentication flow is mutually exclusive with API key authentication - you need to provide either an API key or oAuth credentials.

Below you can find the example of how to create a storage instance with oAuth credentials (and also provide a custom oAuth endpoint):

from incountry import Storage, SecretKeyAccessor

storage = Storage(
    client_id="<client_id>",
    client_secret="<client_secret>",
    environment_id="<env_id>",
    debug=True,
    secret_key_accessor=SecretKeyAccessor(lambda: "password"),
    options={
        "auth_endpoints": {
            "default": "https://auth-server-default.com",
            "emea": "https://auth-server-emea.com",
            "apac": "https://auth-server-apac.com",
            "amer": "https://auth-server-amer.com",
        }
    }
)

Encryption key/secret

The secret_key_accessor variable is used to pass a key or secret used for data encryption.

Note: even though SDK uses PBKDF2 to generate a cryptographically strong encryption key, you must ensure that you provide a secret/password which follows the modern security best practices and standards.

The SecretKeyAccessor class constructor allows you to pass a function that should return either a string representing your secret or a dictionary (we call it secrets_data object):

{
  "secrets": [{
       "secret": str,
       "version": int, # Should be an integer greater than or equal to 0
       "isKey": bool,  # Should be True only for user-defined encryption keys
    }
  }, ....],
  "currentVersion": int,
}

The secrets_data variable allows you to specify multiple keys/secrets which the SDK will use for data decryption based on the version of the key or secret used for encryption. Meanwhile SDK will encrypt data by using only the key/secret pair which matches the currentVersion parameter provided in the secrets_data object.

This enables the flexibility required to support Key Rotation policies when secret/key must change with time. The SDK will encrypt data by using the current secret/key while maintaining the ability to decrypt data records that were encrypted with old key/secret. The SDK also provides a method for data migration which allows you to re-encrypt data with the newest key/secret. For details please see the migrate method.

The SDK allows you to use custom encryption keys, instead of secrets. Please note that a user-defined encryption key should be a 32-character 'utf8' encoded string as required by AES-256 cryptographic algorithm.

Below you can find several examples of how you can use SecretKeyAccessor module.

# Get a secret from a variable
from incountry import SecretKeyAccessor

password = "password"
secret_key_accessor = SecretKeyAccessor(lambda: password)

# Get secrets via an HTTP request
from incountry import SecretKeyAccessor
import requests as req

def get_secrets_data():
    url = "<your_secret_url>"
    r = req.get(url)
    return r.json() # assuming response is a `secrets_data` object

secret_key_accessor = SecretKeyAccessor(get_secrets_data)
Node.js SDK

To access your data in InCountry Platform by using Node.js SDK, you need to create an instance of the Storage class using the createStorage async factory method.

type StorageOptions = {
  apiKey?: string;          // Required when using API key authorization, or as the INC_API_KEY environment variable 
  environmentId?: string;   // Required to be passed in, or as the INC_ENVIRONMENT_ID environment variable 

  oauth?: {
    clientId?: string;      // Required when using oAuth authorization, can be also set through the INC_CLIENT_ID environment variable
    clientSecret?: string;  // Required when using oAuth authorization, can be also set through INC_CLIENT_SECRET environment variable
    authEndpoints?: {       // Custom endpoints regional map to use for fetching oAuth tokens
      default: string;
      [key: string]: string;
    };
  };

  endpoint?: string;        // Defines API URL
  encrypt?: boolean;        // If false, encryption is not used. Defaults to true.

  logger?: Logger;
  getSecrets?: Function;    // Used to fetch an encryption secret
  normalizeKeys?: boolean;
  countriesCache?: CountriesCache;
  hashSearchKeys?: boolean; // Set to false to enable partial match search among record's text fields `key1, key2, ..., key10`. Defaults to true.

  /**
   * Defines API base hostname part to use.
   * If set, all requests will be sent to https://${country}${endpointMask} host instead of the default
   * one (https://${country}-mt-01.api.incountry.io)
   */
  endpointMask?: string;

  /**
   * If your PoPAPI configuration relies on a custom PoPAPI server (rather than the default one)
   * use the `countriesEndpoint` option to specify the endpoint responsible for fetching the list of supported countries.
   */
  countriesEndpoint?: string;

  httpOptions?: {
    timeout?: NonNegativeInt; // Timeout in milliseconds.
  };
};

async function createStorage(
  options: StorageOptions,
  customEncryptionConfigs?: CustomEncryptionConfig[]
): Promise<Storage> {
  /* ... */
}

const { createStorage } = require('incountry');
const storage = await createStorage({
  apiKey: 'API_KEY',
  environmentId: 'ENVIRONMENT_ID',
  oauth: {
    clientId: '',
    clientSecret: '',
    authEndpoints: {
      default: 'https://auth',
    },
  },
  endpoint: 'INC_URL',
  encrypt: true,
  getSecrets: () => '',
  endpointMask: '',
  countriesEndpoint: '',
  httpOptions: {
    timeout: 5000,
  },
});

WARNING

API Key authorization is being deprecated. The backward compatibility is preserved for the apiKey parameter but you no longer can access API keys (neither old nor new) from your dashboard.


The oauth.clientId, oauth.clientSecret and environmentId variables can be fetched from your dashboard on InCountry Portal.

Otherwise you can create an instance of the Storage class and run all asynchronous checks by yourself (or skip their running at your own risk!).

const { Storage } = require('incountry');
const storage = new Storage({
  apiKey: 'API_KEY',
  environmentId: 'ENVIRONMENT_ID',
  endpoint: 'INC_URL',
  encrypt: true,
  getSecrets: () => '',
});

await storage.validate();

The validate method fetches the secret using GetSecretsCallback and validates it. If custom encryption configurations were provided they would also be checked with all the matching secrets.

oAuth Authentication

The SDK also supports oAuth authentication credentials instead of plain API key authorization. oAuth authentication flow is mutually exclusive with API key authentication - you will need to provide either API key or oAuth credentials.

Below you can find the example of how to create a storage instance with oAuth credentials (and also provide a custom oAuth endpoint):

const { Storage } = require('incountry');
const storage = new Storage({
  environmentId: 'ENVIRONMENT_ID',
  endpoint: 'INC_URL',
  encrypt: true,
  getSecrets: () => '',
  oauth: {
    clientId: 'CLIENT_ID',
    clientSecret: 'CLIENT_SECRET',
    authEndpoints: {
      "default": "https://auth-server-default.com",
      "emea": "https://auth-server-emea.com",
      "apac": "https://auth-server-apac.com",
      "amer": "https://auth-server-amer.com",
    },
  },
});

Encryption key/secret

The GetSecretsCallback function is used to pass a key or secret used for encryption.

Note: even though SDK uses PBKDF2 to generate a cryptographically strong encryption key, you must ensure that you provide a secret/password which follows the modern security best practices and standards.

The GetSecretsCallback function returns either a string representing your secret or an object (we call it SecretsData) or a Promise which is resolved to that string or object:

type SecretOrKey = {
  secret: string;
  version: NonNegativeInt;
  isKey?: boolean;
  isForCustomEncryption?: boolean;
};

type SecretsData = {
  currentVersion: NonNegativeInt;
  secrets: Array<SecretOrKey>;
};

/// SecretsData example
{
  secrets: [
    {
      secret: 'aaa',
      version: 0
    },
    {
      secret: 'base64...IHN0cmluZw==', // Should be a base64-encoded key (32 byte key)
      version: 1,
      isKey: true
    },
    {
      secret: 'ccc',
      version: 2,
      isForCustomEncryption: true
    }
  ],
  currentVersion: 1
};

The GetSecretsCallback function allows you to specify multiple keys/secrets which the SDK will use for decryption based on the version of the key or secret used for encryption. Meanwhile SDK will encrypt data only by using a key/secret pair which matches the currentVersion parameter provided in the SecretsData object.

This enables the flexibility required to support Key Rotation policies when secret/key pairs must be changed with time. The SDK will encrypt data by using the current secret/key pair while maintaining the ability to decrypt data records that were encrypted with old key/secret pairs. The SDK also provides a method for data migration which allows to re-encrypt data with the newest key/secret. For details please see the migrate method.

The SDK allows you to use custom encryption keys, instead of secrets. Please note that a user-defined encryption key should be a 32-character 'utf8' encoded string as required by AES-256 cryptographic algorithm.

Below you can find several examples of how you can use the GetSecretsCallback function.

type GetSecretsCallback = () => string | SecretsData | Promise<string> | Promise<SecretsData>;

// Synchronous
const getSecretsSync = () => 'longAndStrongPassword';

// Asynchronous
const getSecretsAsync = async () => {
  const secretsData = await getSecretsDataFromSomewhere();
  return secretsData;
};

// Using promises syntax
const getSecretsPromise = () =>
  new Promise(resolve => {
    getPasswordFromSomewhere(secretsData => {
      resolve(secretsData);
    });
  });
Java SDK

Use the StorageImpl class to access your data in InCountry Platofrm using Java SDK.

public class StorageImpl implements Storage {
  /**
   * creating Storage instance
   *
   * @param environmentID     Required to be passed in, or as the INC_API_KEY environment variable
                              with {@link #getInstance()}
   * @param apiKey            Required to be passed in, or as the INC_ENVIRONMENT_ID environment variable
                              with {@link #getInstance()}
   * @param endpoint          Optional. Defines API URL.
   *                          Default endpoint will be used if this parameter is null
   * @param secretKeyAccessor Instance of SecretKeyAccessor class. Used to fetch encryption secret
   * @return instance of Storage
   * @throws StorageClientException if configuration validation finishes with errors
   * @throws StorageServerException if server connection fails or server response error is returned
   */
  public static Storage getInstance(String environmentID, String apiKey, String endpoint,
                            SecretKeyAccessor secretKeyAccessor) throws StorageServerException {...}
//...
}

The environmentID and apiKey parameters (or clientId and clientSecret instead of apiKey) can be fetched from your dashboard on the Incountry Portal.

You can disable encryption (not recommended) by passing the null value to the secretKeyAccessor parameter.

Below you can find the example of how to create a new storage instance:

SecretKeyAccessor secretKeyAccessor = () -> SecretsDataGenerator.fromPassword("<password>");
String endPoint = "https://us-mt-01.api.incountry.io";
String envId = "<env_id>";
String apiKey = "<api_key>";
Storage storage = StorageImpl.getInstance(envId, apiKey, endPoint, secretKeyAccessor);

oAuth Authentication

SDK also supports oAuth authentication credentials instead of plain API key authorization. oAuth authentication flow is mutually exclusive with API key authentication - you will need to provide either API key or oAuth credentials.

Below you can find the example of how to create a new storage instance with oAuth credentials (and also provide a custom oAuth endpoint):

Map<String, String> authEndpointsMap = new HashMap<>();
authEndpointsMap.put("emea", "https://auth-server-emea.com");
authEndpointsMap.put("apac", "https://auth-server-apac.com");
authEndpointsMap.put("amer", "https://auth-server-amer.com");

StorageConfig config = new StorageConfig()
   //can be also set via environment variable INC_CLIENT_ID with {@link #getInstance()}
   .setClientId(CLIENT_ID)
   //can be also set via environment variable INC_CLIENT_SECRET with {@link #getInstance()}
   .setClientSecret(SECRET)
   .setAuthEndpoints(authEndpointsMap)
   .setDefaultAuthEndpoint("https://auth-server-default.com")
   .setEndpointMask(ENDPOINT_MASK)
   .setEnvId(ENV_ID);
Storage storage = StorageImpl.getInstance(config);

Note: the endpointMask parameter is used for switching from the default InCountry host list (api.incountry.io) to a different one. For example the endpointMask==-private.incountry.io setting will further redirect requests to https://{COUNTRY_CODE}-private.incountry.io If your PoPAPI configuration relies on a custom PoPAPI server (rather than the default one) use the countriesEndpoint option to specify the endpoint responsible for fetching the list of supported countries.

StorageConfig config = new StorageConfig()
   .setCountriesEndpoint(countriesEndpoint)
   //...
Storage storage = StorageImpl.getInstance(config);

Encryption key/secret

SDK provides the SecretKeyAccessor interface which allows you to pass your own secrets/keys to the SDK.

/**
 * Secrets accessor. Method {@link SecretKeyAccessor#getSecretsData()} is invoked on each encryption/decryption.
 */
public interface SecretKeyAccessor {

    /**
     * get your container with secrets
     *
     * @return SecretsData
     * @throws StorageClientException when something goes wrong during getting secrets
     */
    SecretsData getSecretsData() throws StorageClientException;
}


public class SecretsData {
    /**
     * creates a container with secrets
     *
     * @param secrets non-empty list of secrets. One of the secrets must have
     *        same version as currentVersion in SecretsData
     * @param currentVersion Should be a non-negative integer
     * @throws StorageClientException when parameter validation fails
     */
     public SecretsData(List<SecretKey> secrets, int currentVersion)
                throws StorageClientException {...}
    //...
}


public class SecretKey {
    /**
    * Creates a secret key
    *
    * @param secret  secret/key
    * @param version secret version, should be a non-negative integer
    * @param isKey   should be True only for user-defined encryption keys
    * @throws StorageClientException when parameter validation fails
    */
    public SecretKey(String secret, int version, boolean isKey)
              throws StorageClientException {...}
    //...
}

You can implement SecretKeyAccessor interface and pass secrets/keys in multiple ways:

  1. As a constant SecretsData object:

    SecretsData secretsData = new SecretsData(secretsList, currentVersion);
    SecretKeyAccessor accessor = () -> secretsData;
  2. As a function that dynamically fetches secrets:

    SecretKeyAccessor accessor = () -> loadSecretsData();
    
    private SecretsData loadSecretsData()  {
       String url = "<your_secret_url>";
       String responseJson = loadFromUrl(url).asJson();
       return SecretsDataGenerator.fromJson(responseJson);
    }

You can also use the SecretsDataGenerator class for creating SecretsData instances:

  1. from a String password:

    SecretsData secretsData = SecretsDataGenerator.fromPassword("<password>");
  2. from a JSON string representing SecretsData object:

    SecretsData secretsData = SecretsDataGenerator.fromJson(jsonString);
    {
    "secrets": [
        {
        "secret": "secret0",
        "version": 0,
        "isKey": false
        },
        {
        "secret": "secret1",
        "version": 1,
        "isKey": false
        }
    ],
    "currentVersion": 1
    }

The SecretsData object allows you to specify multiple keys/secrets which SDK will use for data decryption based on the version of the key or secret used for data encryption.

Writing data to storage

ストレージへのデータの書き込み

.

  • Python SDK
  • Node.js SDK
  • Java SDK
  • REST API
Python SDK

Use the write method to create/replace a record (by record_key).

def write(self, country: str, record_key: str, **record_data: Union[str, int]) -> Dict[str, TRecord]:
    ...


# write returns created record dict on success
{
    "record": Dict
}

Below you can find the example of how you can use the write method.

write_result = storage.write(
    country="us",
    record_key="user_1",
    body="some PII data",
    profile_key="customer",
    range_key1=10000,
    key1="english",
    key2="rolls-royce",
)

# write_result would be as follows
write_result = {
    "record": {
        "record_key": "user_1",
        "body": "some PII data",
        "profile_key": "customer",
        "range_key1": 10000,
        "key1": "english",
        "key2": "rolls-royce",
    }
}

For the list of possible record_data kwargs, see the section below.

List of available record fields

v3.0.0 release introduced a series of new fields available for data storage. Below you can find the full list of all the fields available for storage in InCountry Platform along with their types and storage methods. Each field is either encrypted, hashed or stored as follows:

# String fields, hashed
record_key
key1
key2
key3
key4
key5
key6
key7
key8
key9
key10
profile_key
service_key1
service_key2

# String fields, encrypted
body
precommit_body

# Int fields, plain
range_key1
range_key2
range_key3
range_key4
range_key5
range_key6
range_key7
range_key8
range_key9
range_key10

Batches

Use the batch_write method to create/replace multiple records at once.

def batch_write(self, country: str, records: List[TRecord]) -> Dict[str, List[TRecord]]:
    ...


# batch_write returns the following dict of created records
{
    "records": List
}

Below you can find the example of how to use this method.

batch_result = storage.batch_write(
    country="us",
    records=[
        {"record_key": "key1", "body": "body1", ...},
        {"record_key": "key2", "body": "body2", ...},
    ],
)

# batch_result would be as follows
batch_result = {
    "records": [
        {"record_key": "key1", "body": "body1", ...},
        {"record_key": "key2", "body": "body2", ...},
    ]
}
Node.js SDK

Use the write method create/replace a record (by recordKey) .

List of available record fields

v3.0.0 release introduced a series of new fields available for data storage. Below you can find the full list of all the fields available for storage in InCountry Platform along with their types and storage methods. Each field is either encrypted, hashed or stored as follows:

String fields, hashed:
recordKey
profileKey
serviceKey1
serviceKey2
String fields, hashed if Storage options "hashSearchKeys" is set to true (by default it is):

WARNING If the hashSearchKeys option is set to false the following string fields will have length limitation of 256 characters at most.

key1
key2
key3
key4
key5
key6
key7
key8
key9
key10
String fields, encrypted:
body
precommitBody
Int fields, plain:
rangeKey1
rangeKey2
rangeKey3
rangeKey4
rangeKey5
rangeKey6
rangeKey7
rangeKey8
rangeKey9
rangeKey10
type StorageRecordData = {
  recordKey: string;
  profileKey?: string | null;
  key1?: string | null;  // If `hashSearchKeys` is set to `false` key1 has length limit 256
  key2?: string | null;  // If `hashSearchKeys` is set to `false` key2 has length limit 256
  key3?: string | null;  // If `hashSearchKeys` is set to `false` key3 has length limit 256
  key4?: string | null;  // If `hashSearchKeys` is set to `false` key4 has length limit 256
  key5?: string | null;  // If `hashSearchKeys` is set to `false` key5 has length limit 256
  key6?: string | null;  // If `hashSearchKeys` is set to `false` key6 has length limit 256
  key7?: string | null;  // If `hashSearchKeys` is set to `false` key7 has length limit 256
  key8?: string | null;  // If `hashSearchKeys` is set to `false` key8 has length limit 256
  key9?: string | null;  // If `hashSearchKeys` is set to `false` key9 has length limit 256
  key10?: string | null; // If `hashSearchKeys` is set to `false` key10 has length limit 256
  serviceKey1?: string | null;
  serviceKey2?: string | null;
  body?: string | null;
  precommitBody?: string | null;
  rangeKey1?: t.Int | null;
  rangeKey2?: t.Int | null;
  rangeKey3?: t.Int | null;
  rangeKey4?: t.Int | null;
  rangeKey5?: t.Int | null;
  rangeKey6?: t.Int | null;
  rangeKey7?: t.Int | null;
  rangeKey8?: t.Int | null;
  rangeKey9?: t.Int | null;
  rangeKey10?: t.Int | null;
};

type WriteResult = {
  record: StorageRecordData;
};

async write(
  countryCode: string,
  recordData: StorageRecordData,
  requestOptions: RequestOptions = {},
): Promise<WriteResult> {
  /* ... */
}

Below you can find the example of how you can use the write method.

const recordData = {
  recordKey: '<key>',
  body: '<body>',
  profileKey: '<profile_key>',
  rangeKey1: 0,
  key2: '<key2>',
  key3: '<key3>'
}

const writeResult = await storage.write(countryCode, recordData);

Batches

You can use the batchWrite method to create/replace multiple records at once.

type BatchWriteResult = {
  records: Array<StorageRecordData>;
};

async batchWrite(
  countryCode: string,
  records: Array<StorageRecordData>,
  requestOptions: RequestOptions = {},
): Promise<BatchWriteResult> {
  /* ... */
}

Example of usage:

batchResult = await storage.batchWrite(countryCode, recordDataArr);
Java SDK

Use the write method to create a new record.

public interface Storage {
    /**
     * Write data to a remote storage
     *
     * @param country country identifier
     * @param record  object which encapsulates data which must is written to the storage
     * @return recorded record
     * @throws StorageClientException if validation finishes with errors
     * @throws StorageServerException if server connection fails or a server response error occurs
     * @throws StorageCryptoException if encryption fails
     */
    Record write(String country, Record record)
          throws StorageClientException, StorageServerException, StorageCryptoException;
    //...
}

Below you can find the example of how to initialize a record object:

public class Record {
    /**
     * Full constructor
     *
     * @param key        Required, record key
     * @param body       Optional, data to be stored and encrypted
     * @param profileKey Optional, profile key
     * @param rangeKey   Optional, range key
     * @param key2       Optional, key2
     * @param key3       Optional, key3
     */
    public Record(String key, String body, String profileKey, Long rangeKey, String key2, String key3)
    //...
}

Below you can find the example of how to use the write method:

key = "user_1";
body = "some PII data";
profileKey = "customer";
rangeKey = 10000l;
key2 = "english";
key3 = "insurance";
Record record = new Record(key, body, profileKey, rangeKey, key2, key3);
storage.write("us", record);

Encryption

InCountry Platform uses the client-side encryption for your data. Note that only body is encrypted. Some other fields are hashed. Below you can find the example of how data is transformed and stored in InCountry Platform:

public class Record {
    private String key;          // hashed
    private String body;         // encrypted
    private String profileKey;   // hashed
    private Long rangeKey;       // plain
    private String key2;         // hashed
    private String key3;         // hashed
    //...
}

Batches

Use the batchWrite method to write multiple records to the storage within a single request.

public interface Storage {
     /**
      * Write multiple records at once in remote storage
      *
      * @param country country identifier
      * @param records record list
      * @return BatchRecord object which contains a list of recorded records
      * @throws StorageClientException if validation finishes with errors
      * @throws StorageServerException if server connection fails or a server response error occurs
      * @throws StorageCryptoException if record encryption fails
      */
     BatchRecord batchWrite(String country, List<Record> records)
          throws StorageClientException, StorageServerException, StorageCryptoException;
     //...
}

Below you can find the example of how you can use the batchWrite method:

List<Record> list = new ArrayList<>();
list.add(new Record(firstKey, firstBody, firstProfileKey, firstRangeKey, firstKey2, firstKey3));
list.add(new Record(secondKey, secondBody, secondProfileKey, secondRangeKey, secondKey2, secondKey3));
storage.batchWrite("us", list);
REST API

Create a record

POST /api/records

:::note Alternatively, this request can be used to update the record. Be careful, as it rewrites the data record with new values you provide. :::

Headers:

HeaderValue
Content-Typeapplication/json
Content-LengthCalculated automatically when a request is sent
HostCalculated automatically when a request is sent
User-AgentSpecify the user agent used
Accept/
Accept-Encodinggzip, deflate, br
Connectionkeep-alive

Request Body:

ParametersTypeDescription
record_keystringIt is used for storing the primary key of the record (identifier). The value from this field is also propagated to the key field when REST API creates a new record.
keyNstringCan be used for storing regulated and non-regulated data. Your record can have up to 10 keys.
profile_keystringAdditional data value
service_key1 service_key2stringService data value.
range_key1integerNumerical value. Your record can have up to 10 range keys.
bodystringAny text string that you want to store.
precommit_bodystringAny text string that you want to store. It can be used for storing the intermediate value of the body field.
countryISO country code (lowercase)Country which a new record is written to

Example:

{
  "record_key": "a98787ss87",
  "profile_key": "pk1237",
  "key1": "a1237",
  "key2": "John",
  "key3": "Doe",
  ...
  "key10": "Visa",
  "service_key1": "478",
  "service_key2": "57",
  "body": "This is a contact from Tradeshow",
  "precommit_body": "Consent received on 12/1/2020",
  "range_key1": 42,
  "range_key2": 50,
  ...
  "range_key10": 980,
  "country": "sg"
}

Responses:

STATUS 201 - application/json Returns information and a record key for the newly created record.

Example:

{
    "key": "a98787ss87",
    "profile_key": "pk1237",
    "range_key": 42,
    "record_key": "a98787ss87",
    "body": "This is a contact from Tradeshow",
    "precommit_body": "Consent received on 12/1/2020",
    "key1": "a1237",
    "key2": "John",
    "key3": "Doe",
    ...
    "key10": "Visa",
    "service_key1": "478",
    "service_key2": "57",
    "range_key1": 42,
    "range_key2": 50,
    ...
    "range_key10": 980,
    "country": "sg"
}

STATUS 400 - application/json Bad request when it has not passed validation. The error details will be in the response.

STATUS 401 - This error status is returned when the request is unauthorized.

Reading stored data

保存されたデータの読み取り

.

  • Python SDK
  • Node.js SDK
  • Java SDK
  • REST API
Python SDK

You can read a stored record by its record_key by using the read method. It accepts an object with two fields - country and record_key.

def read(self, country: str, record_key: str) -> Dict[str, TRecord]:
    ...


# The read method returns the record dictionary if the record is found
{
    "record": Dict
}

Date fields

You can use the created_at and updated_at fields to access date-related information about records. The created_at field stores a date when a record was initially created in the target country. The updated_at field stores a date of the latest write operation for the given recordKey.

You can use the read method, as follows:

read_result = storage.read(country="us", record_key="user1")

# read_result would be as follows
read_result = {
    "record": {
        "record_key": "user_1",
        "body": "some PII data",
        "profile_key": "customer",
        "range_key1": 10000,
        "key1": "english",
        "key2": "rolls-royce",
        "created_at": datetime.datetime(...),
        "updated_at": datetime.datetime(...),
    }
}
Node.js SDK

You can read the stored data records by its recordKey by using the read method. It accepts an object with the two fields - country and recordKey. It returns a Promise which is resolved to { record } or is rejected if there are no records for the passed recordKey.

Date fields

You can use the createdAt and updatedAt fields to access date-related information about records. The createdAt field stores the date when the record was initially created in the target country. The updatedAt field stores the date of the latest write operation for the given recordKey.

type StorageRecord = {
  recordKey: string;
  body: string | null;
  profileKey: string | null;
  precommitBody: string | null;
  key1?: string | null;  // If `hashSearchKeys` is set to `false` key1 has length limit 256
  key2?: string | null;  // If `hashSearchKeys` is set to `false` key2 has length limit 256
  key3?: string | null;  // If `hashSearchKeys` is set to `false` key3 has length limit 256
  key4?: string | null;  // If `hashSearchKeys` is set to `false` key4 has length limit 256
  key5?: string | null;  // If `hashSearchKeys` is set to `false` key5 has length limit 256
  key6?: string | null;  // If `hashSearchKeys` is set to `false` key6 has length limit 256
  key7?: string | null;  // If `hashSearchKeys` is set to `false` key7 has length limit 256
  key8?: string | null;  // If `hashSearchKeys` is set to `false` key8 has length limit 256
  key9?: string | null;  // If `hashSearchKeys` is set to `false` key9 has length limit 256
  key10?: string | null; // If `hashSearchKeys` is set to `false` key10 has length limit 256
  serviceKey1: string | null;
  serviceKey2: string | null;
  rangeKey1: t.Int | null;
  rangeKey2: t.Int | null;
  rangeKey3: t.Int | null;
  rangeKey4: t.Int | null;
  rangeKey5: t.Int | null;
  rangeKey6: t.Int | null;
  rangeKey7: t.Int | null;
  rangeKey8: t.Int | null;
  rangeKey9: t.Int | null;
  rangeKey10: t.Int | null;
  createdAt: Date;
  updatedAt: Date;
}

type ReadResult = {
  record: StorageRecord;
};

async read(
  countryCode: string,
  recordKey: string,
  requestOptions: RequestOptions = {},
): Promise<ReadResult> {
  /* ... */
}

Example of usage:

const readResult = await storage.read(countryCode, recordKey);
Java SDK

You can read the stored records by key by using the read method.

public interface Storage {
   /**
    * Read data from remote storage
    *
    * @param country   country identifier
    * @param key record unique identifier
    * @return Record object which contains required data
    * @throws StorageClientException if validation finishes with errors
    * @throws StorageServerException if server connection fails or a server response error occurs
    * @throws StorageCryptoException if decryption fails
    */
    Record read(String country, String key)
        throws StorageClientException, StorageServerException, StorageCryptoException;
    //...
}

Below you can find the example of how you may use read method:

String key = "user_1";
Record record = storage.read("us", key);
String decryptedBody = record.getBody();

The Record object includes the following properties: key, body, key2, key3, profileKey, rangeKey.

These properties can be accessed using getters, for example:

String key2 = record.getKey2();
String body = record.getBody();
REST API

Find a record

POST /api/records/find

Headers:

HeaderValue
Content-Typeapplication/json
Content-LengthCalculated automatically when a request is sent
HostCalculated automatically when a request is sent
User-AgentSpecify the user agent used
Accept/
Accept-Encodinggzip, deflate, br
Connectionkeep-alive

Request Body:

ParametersTypeExampleDescription
countryISO country code (lowercase)sg (equals to Singapore)Country which a new record is looked up in. You can use different countries.
filterobjectSee the example.Specify the criteria for filtration within the filter parameter. You can look up for records by any of record fields. For the full list of supported fields, please see the Available record fields section.
keyNstringSee the example.Value from the key fields of your record.
search_keysstringabcDefines the search query string for lookup. The search keys should be provided within the filter object.
range_keyNarray[1, 5]Defines the range of keys for lookup. The range keys should be provided within the filter object.
optionsobjectSee the example.Specify the criteria for returning the search results:limit, offset
limitinteger10Sets the maximal number of search results.
offsetinteger50Specifies the offset (pagination) of records from which the record lookup is performed.

Example:

{
  "country": "sg",
  "filter": {
    "key1": ["k1234", "k1235"],
    "key2": ["John", "Jane"]
  },
  "options": {
    "limit": 10,
    "offset": 50
  }
}

or

{
  "country": "sg",
  "filter": {
    "search_keys": "abc"
  }
}

or

{
  "country": "sg",
  "filter": {
    "search_keys": "abc",
    "range_key1": [1, 2]
  }
}

Responses:

STATUS 200 - application/json Returns information about the found record or records.

This status is also returned when no records matching the search criteria are found.

Example:

{
    "data": [
        {
            "key": "k1235",
            "profile_key": "pk1235",
            "range_key": 42,
            "record_key": "k1235",
            "body": null,
            "precommit_body": null,
            "key1": null,
            "key2": "Jane",
            "key3": "Doe",
            "key4": null,
            "key5": null,
            "key6": null,
            "key7": null,
            "key8": null,
            "key9": null,
            "key10": null,
            "service_key1": null,
            "service_key2": null,
            "range_key1": 42,
            "range_key2": null,
            "range_key3": null,
            "range_key4": null,
            "range_key5": null,
            "range_key6": null,
            "range_key7": null,
            "range_key8": null,
            "range_key9": null,
            "range_key10": null,
            "version": 0,
            "created_at": "2020-11-21T12:07:43.000Z",
            "updated_at": "2020-11-21T12:07:43.000Z"
        },
        {
            "key": "k1234",
            "profile_key": "pk1234",
            "range_key": 42,
            "record_key": "k1234",
            "body": null,
            "precommit_body": null,
            "key1": null,
            "key2": "John",
            "key3": "Doe",
            "key4": null,
            "key5": null,
            "key6": null,
            "key7": null,
            "key8": null,
            "key9": null,
            "key10": null,
            "service_key1": null,
            "service_key2": null,
            "range_key1": 42,
            "range_key2": null,
            "range_key3": null,
            "range_key4": null,
            "range_key5": null,
            "range_key6": null,
            "range_key7": null,
            "range_key8": null,
            "range_key9": null,
            "range_key10": null,
            "version": 0,
            "created_at": "2020-11-21T11:55:49.000Z",
            "updated_at": "2020-11-21T12:05:28.000Z"
        }
    ],
    "meta": {
        "count": 2,
        "limit": 100,
        "offset": 0,
        "total": 2
    }
}

STATUS 400 - application/json Bad request when it has not passed validation. The error details will be in the response.

STATUS 401 - This error is returned when the request is unauthorized.

Looking up for records

記録の検索

.

  • Python SDK
  • Node.js SDK
  • Java SDK
  • REST API
Python SDK

You can look up for records by keys or version using the find method.

def find(
        self,
        country: str,
        limit: Optional[int] = FIND_LIMIT,
        offset: Optional[int] = 0,
        **filters: Union[TIntFilter, TStringFilter],
    ) -> Dict[str, Any]:
    ...

Note: SDK returns 100 records at most.

The returned object looks like the following:

{
    "data": List,
    "errors": List, # optional
    "meta": {
        "limit": int,
        "offset": int,
        "total": int,  # total records matching filter, ignoring limit
    }
}

You can use the following options to look up for records by hashed string keys from the list above:

# single value
key1="value1" # records with key1 equal to "value1"

# list of values
key2=["value1", "value2"] # records with key2 equal to "value1" or "value2"

# dict with $not operator
key3={"$not": "value1"} # records with key3 not equal "value1"
key4={"$not": ["value1", "value2"]} # records with key4 equal to neither "value1" or "value2"

You can use the following options to look up for records by int keys from the list above:

# single value
range_key1=1 # records with range_key1 equal to 1

# list of values
range_key2=[1, 2] # records with range_key2 equal to 1 or 2

# dictionary with comparison operators
range_key3={"$gt": 1} # records with range_key3 greater than 1
range_key4={"$gte": 1} # records with range_key4 greater than or equal to 1
range_key5={"$lt": 1} # records with range_key5 less than 1
range_key6={"$lte": 1} # records with range_key6 less than or equal to 1

# you can combine different comparison operators
range_key7={"$gt": 1, "$lte": 10} # records with range_key7 greater than 1 and less than or equal to 10

# you cannot combine similar comparison operators - e.g. $gt and $gte, $lt and $lte

You can use the following option to look up for records by version (encryption key version):

# single value
version=1 # records with version equal to 1

# list of values
version=[1, 2] # records with version equal to 1 or 2

# dictionary with $not operator
version={"$not": 1} # records with version not equal 1
version={"$not": [1, 2]} # records with version equal to neither 1 or 2

Below you can find the example of how you can use the find method:

find_result = storage.find(country="us", limit=10, offset=10, key1="value1", key2=["value2", "value3"])

# find_result would be as follows
find_result = {
    "data": [
        {
            "record_key": "<record_key>",
            "body": "<body>",
            "key1": "value1",
            "key2": "value2",
            "created_at": datetime.datetime(...),
            "updated_at": datetime.datetime(...),
            ...
        }
    ],
    "meta": {
        "limit": 10,
        "offset": 10,
        "total": 100,
    }
}

Find one record matching a filter

If you need to find only one of the records matching a specific filter, you can use the find_one method.

def find_one(
        self, country: str, offset: Optional[int] = 0, **filters: Union[TIntFilter, TStringFilter],
    ) -> Union[None, Dict[str, Dict]]:
    ...


# If a record is not found, the find_one method returns `None`. Otherwise it returns a record dictionary.
{
    "record": Dict
}

Below you can find the example of how to use the find_one method:

find_one_result = storage.find_one(country="us", key1="english", key2=["rolls-royce", "bmw"])

# find_one_result would be as follows
find_one_result = {
    "record": {
        "record_key": "user_1",
        "body": "some PII data",
        "profile_key": "customer",
        "range_key1": 10000,
        "key1": "english",
        "key2": "rolls-royce",
    }
}
Node.js SDK

You can look up for data records either by using exact match search operators or partial text match operators in almost any combinations.

Exact match search

The following exact match search criteria are available:

  • single value:
// Matches all records where record.key1 = 'abc' AND record.rangeKey = 1
{ key1: 'abc', rangeKey1: 1 }
  • multiple values as an array:
// Matches all records where (record.key2 = 'def' OR record.key2 = 'jkl') AND (record.rangeKey = 1 OR record.rangeKey = 2)
{ key2: ['def', 'jkl'], rangeKey1: [1, 2] }
// Matches all records where record.key3 <> 'abc'
{ key3: { $not: 'abc' } }

// Matches all records where record.key3 <> 'abc' AND record.key3 <> 'def'
{ key3: { $not: ['abc', 'def'] } }

// Matches all records where record.version <> 1
{ version: { $not: 1 }}
// Matches all records where record.rangeKey >= 5 AND record.rangeKey <= 100
{ rangeKey1: { $gte: 5, $lte: 100 } }
Partial text match search

You can also look up for data records by partial match using the searchKeys operator which performs partial match search (similar to the LIKE SQL operator) within records text fields key1, key2, ..., key10.

// Matches all records where record.key1 LIKE 'abc' OR record.key2 LIKE 'abc' OR ... OR record.key10 LIKE 'abc'
{ searchKeys: 'abc' }

Please note: The searchKeys operator cannot be used in combination with any of key1, key2, ..., key10 keys and works only in combination with the non-hashing Storage mode (hashSearchKeys param for Storage).

// Matches all records where (record.key1 LIKE 'abc' OR record.key2 LIKE 'abc' OR ... OR record.key10 LIKE 'abc') AND (record.rangeKey = 1 OR record.rangeKey = 2)
{ searchKeys: 'abc', rangeKey1: [1, 2] }

// Causes validation error (StorageClientError)
{ searchKeys: 'abc', key1: 'def' }

Search options

The options parameter defines the limit - number of records that are returned, and the offset- the starting index used for record pagination. You can use these parameters to implement pagination. Note: The SDK returns 100 records at most.

type FilterStringValue = string | string[];
type FilterStringQuery = FilterStringValue | { $not?: FilterStringValue };

type FilterNumberValue = number | number[];
type FilterNumberQuery =
  FilterNumberValue |
  {
    $not?: FilterNumberValue;
    $gt?: number;
    $gte?: number;
    $lt?: number;
    $lte?: number;
  };

type FindFilter = Record<string, FilterStringQuery | FilterNumberQuery>;

type FindOptions = {
  limit?: number;
  offset?: number;
};

type FindResult = {
  meta: {
    total: number;
    count: number;
    limit: number;
    offset: number;
  };
  records: Array<StorageRecord>;
  errors?: Array<{ error: StorageCryptoError; rawData: ApiRecord }>;
};

async find(
  countryCode: string,
  filter: FindFilter = {},
  options: FindOptions = {},
  requestOptions: RequestOptions = {},
): Promise<FindResult> {
  /* ... */
}

Example of usage

const filter = {
  key1: 'abc',
  key2: ['def', 'jkl'],
  profileKey: 'test2',
  rangeKey1: { $gte: 5, $lte: 100 },
  rangeKey2: { $not: [0, 1] },
}

const options = {
  limit: 100,
  offset: 0,
};

const findResult = await storage.find(countryCode, filter, options);

The returned findResult object looks like the following:

{
  records: [{/* StorageRecord */}],
  errors: [],
  meta: {
    limit: 100,
    offset: 0,
    total: 24
  }
}

Find one record matching a filter

If you need to find only one of the records matching a specific filter, you can use the find_one method. If a record is not found, it returns null.

type FindOneResult = {
  record: StorageRecord | null;
};

async findOne(
  countryCode: string,
  filter: FindFilter = {},
  options: FindOptions = {},
  requestOptions: RequestOptions = {},
): Promise<FindOneResult> {
  /* ... */
}

Example of usage:

const findOneResult = await storage.findOne(countryCode, filter);
Java SDK

You can search for data records by random keys using the find method.

public interface Storage {
   /**
    * Find records in remote storage according to filters
    *
    * @param country country identifier
    * @param builder object representing find filters and search options
    * @return BatchRecord object which contains required records
    * @throws StorageClientException if validation finishes with errors
    * @throws StorageServerException if server connection fails or a server response error occurs
    * @throws StorageCryptoException if decryption fails
    */
    BatchRecord find(String country, FindFilterBuilder builder)
         throws StorageClientException, StorageServerException, StorageCryptoException;
    //...
}

You can use the FindFilterBuilder class to refine your find request.

Below you can find the example of how to use the find method along with the FindFilterBuilder class:

FindFilterBuilder builder = FindFilterBuilder.create()
                  .key2Eq("someKey")
                  .key3Eq("firstValue","secondValue")
                  .rangeKeyBetween(123l, 456l);

BatchRecord findResult = storage.find("us", builder);
if (findResult.getCount() > 0) {
    Record record = findResult.getRecords().get(0);
    //...
}

The request will return records that are filtered according to the following pseudo-sql:

key2 = 'someKey' AND key3 in ('firstValue' , 'secondValue') AND (123 < = `rangeKey` < = 456)

All conditions added with the FindFilterBuilder class are joined using the logical operator AND. You cannot add multiple conditions for the same key, elsewise only the last condition will be used.

The SDK returns 100 records at most. Use the limit and offset parameters to iterate through the list of records.

FindFilterBuilder builder = FindFilterBuilder.create()
                  //...
                  .limitAndOffset(20, 80);
BatchRecord records = storage.find("us", builder);

Next predicate types are available for each string key field of the Record class through individual methods of the FindFilterBuilder class:

EQUALS         (FindFilterBuilder::keyEq)
               (FindFilterBuilder::key2Eq)
               (FindFilterBuilder::key3Eq)
               (FindFilterBuilder::profileKeyEq)
NOT_EQUALS     (FindFilterBuilder::keyNotEq)
               (FindFilterBuilder::key2NotEq)
               (FindFilterBuilder::key3NotEq)
               (FindFilterBuilder::profileKeyNotEq)

You can use the following builder methods for record filtering by the numerical rangeKey field:

EQUALS              (FindFilterBuilder::rangeKeyEq)
IN                  (FindFilterBuilder::rangeKeyIn)
GREATER             (FindFilterBuilder::rangeKeyGT)
GREATER OR EQUALS   (FindFilterBuilder::rangeKeyGTE)
LESS                (FindFilterBuilder::rangeKeyLT)
LESS OR EQUALS      (FindFilterBuilder::rangeKeyLTE)
BETWEEN             (FindFilterBuilder::rangeKeyBetween)

The find method returns the BatchRecord object which contains a list of Record and some metadata:

class BatchRecord {
    private int count;
    private int limit;
    private int offset;
    private int total;
    private List<Record> records;
    //...
}

These fields can be accessed using getters, for example:

int limit = records.getTotal();

The BatchRecord.getErrors() method allows you to get a list of the RecordException objects with detailed information about records that were not correctly processed during execution of the find request.

Find one record matching a filter

If you need to find the first record matching filter, you can use findOne method:

public interface Storage {
   /**
    * Find only one first record in remote storage according to filters
    *
    * @param country country identifier
    * @param builder object representing find filters
    * @return founded record or null
    * @throws StorageClientException if validation finishes with errors
    * @throws StorageServerException if server connection fails or a server response error occurs
    * @throws StorageCryptoException if decryption fails
    */
    Record findOne(String country, FindFilterBuilder builder)
           throws StorageClientException, StorageServerException, StorageCryptoException;
    //...
}

It works the same way as the find method but returns the first record or null if no records found.

Below you can find the example of how the findOne method can be used:

FindFilterBuilder builder = FindFilterBuilder.create()
                .key2Eq("someKey")
                .key3Eq("firstValue", "secondValue")
                .rangeKeyBetween(123l, 456l);

Record record = storage.findOne("us", builder);
//...
REST API

Find a record

POST /api/records/find

Headers:

HeaderValue
Content-Typeapplication/json
Content-LengthCalculated automatically when a request is sent
HostCalculated automatically when a request is sent
User-AgentSpecify the user agent used
Accept/
Accept-Encodinggzip, deflate, br
Connectionkeep-alive

Request Body:

ParametersTypeExampleDescription
countryISO country code (lowercase)sg (equals to Singapore)Country which a new record is looked up in. You can use different countries.
filterobjectSee the example.Specify the criteria for filtration within the filter parameter. You can look up for records by any of record fields. For the full list of supported fields, please see the Available record fields section.
keyNstringSee the example.Value from the key fields of your record.
search_keysstringabcDefines the search query string for lookup. The search keys should be provided within the filter object.
range_keyNarray[1, 5]Defines the range of keys for lookup. The range keys should be provided within the filter object.
optionsobjectSee the example.Specify the criteria for returning the search results:limit, offset
limitinteger10Sets the maximal number of search results.
offsetinteger50Specifies the offset (pagination) of records from which the record lookup is performed.

Example:

{
  "country": "sg",
  "filter": {
    "key1": ["k1234", "k1235"],
    "key2": ["John", "Jane"]
  },
  "options": {
    "limit": 10,
    "offset": 50
  }
}

or

{
  "country": "sg",
  "filter": {
    "search_keys": "abc"
  }
}

or

{
  "country": "sg",
  "filter": {
    "search_keys": "abc",
    "range_key1": [1, 2]
  }
}

Responses:

STATUS 200 - application/json Returns information about the found record or records.

This status is also returned when no records matching the search criteria are found.

Example:

{
    "data": [
        {
            "key": "k1235",
            "profile_key": "pk1235",
            "range_key": 42,
            "record_key": "k1235",
            "body": null,
            "precommit_body": null,
            "key1": null,
            "key2": "Jane",
            "key3": "Doe",
            "key4": null,
            "key5": null,
            "key6": null,
            "key7": null,
            "key8": null,
            "key9": null,
            "key10": null,
            "service_key1": null,
            "service_key2": null,
            "range_key1": 42,
            "range_key2": null,
            "range_key3": null,
            "range_key4": null,
            "range_key5": null,
            "range_key6": null,
            "range_key7": null,
            "range_key8": null,
            "range_key9": null,
            "range_key10": null,
            "version": 0,
            "created_at": "2020-11-21T12:07:43.000Z",
            "updated_at": "2020-11-21T12:07:43.000Z"
        },
        {
            "key": "k1234",
            "profile_key": "pk1234",
            "range_key": 42,
            "record_key": "k1234",
            "body": null,
            "precommit_body": null,
            "key1": null,
            "key2": "John",
            "key3": "Doe",
            "key4": null,
            "key5": null,
            "key6": null,
            "key7": null,
            "key8": null,
            "key9": null,
            "key10": null,
            "service_key1": null,
            "service_key2": null,
            "range_key1": 42,
            "range_key2": null,
            "range_key3": null,
            "range_key4": null,
            "range_key5": null,
            "range_key6": null,
            "range_key7": null,
            "range_key8": null,
            "range_key9": null,
            "range_key10": null,
            "version": 0,
            "created_at": "2020-11-21T11:55:49.000Z",
            "updated_at": "2020-11-21T12:05:28.000Z"
        }
    ],
    "meta": {
        "count": 2,
        "limit": 100,
        "offset": 0,
        "total": 2
    }
}

STATUS 400 - application/json Bad request when it has not passed validation. The error details will be in the response.

STATUS 401 - This error is returned when the request is unauthorized.

Deleting data records

データ記録の削除

.

  • Python SDK
  • Node.js SDK
  • Java SDK
  • REST API
Python SDK

You can use the delete method to delete a record from InCountry Platform. It is possible by using the record_key field only.

def delete(self, country: str, record_key: str) -> Dict[str, bool]:
    ...


# the delete method returns the following dictionary upon success
{
    "success": True
}

Below you can find the example of how to use the delete method:

delete_result = storage.delete(country="us", record_key="<record_key>")

# delete_result would be as follows
delete_result = {
    "success": True
}
Node.js SDK

You can use the delete method to delete a record from InCountry Platform. It is possible by using the record_key field only.

type DeleteResult = {
  success: true;
};

async delete(
  countryCode: string,
  recordKey: string,
  requestOptions: RequestOptions = {},
): Promise<DeleteResult> {
  /* ... */
}

Example of usage:

const deleteResult = await storage.delete(countryCode, recordKey);
Java SDK

Use the delete method to delete records from InCountry storage. It is only possible by using the key field.

public interface Storage {
    /**
    * Delete a record from remote storage
    *
    * @param country   country code of the record
    * @param key the record's key
    * @return true when record was deleted
    * @throws StorageClientException if validation finishes with errors
    * @throws StorageServerException if server connection fails
    */
    boolean delete(String country, String key)
            throws StorageClientException, StorageServerException;
    //...
}

Below you can find the the example of how you may use the delete method:

String key = "user_1";
storage.delete("us", key);
REST API

Delete a record

DELETE /api/records/:country/:key

Headers:

HeaderValue
HostCalculated automatically when a request is sent
User-AgentSpecify the user agent used
Accept/
Accept-Encodinggzip, deflate, br
Connectionkeep-alive

Request parameters:

ParametersTypeValueDescription
countryISO country code (lowercase)sgCountry which a new record is removed from
keystring/arrayk1234Unique primary key for record identification

Responses:

STATUS 204 - plain This response is returned when the record has been successfully deleted.

STATUS 401- This response is returned when the request is unauthorized.

STATUS 404 - plain This response is returned when the record is not found.

質問がありますか?

開発者向けFAQページにアクセスし、質問に対する回答を見つけることができます。