Документация Системы хранилищ

Store Systems в Mockarty

Содержание

  1. Обзор Store систем
  2. Mock Store (MStore)
  3. Chain Store (CStore)
  4. Global Store (GStore)
  5. API для управления Store
  6. Примеры использования
  7. Лучшие практики

Совет: Примеры кода доступны для cURL, CLI и SDK-клиентов (Go, Python, Java). Смотрите Руководство по SDK для установки и настройки. Смотрите Руководство по CLI для командной утилиты.

URL-адреса в примерах: Во всех примерах используется localhost:5770 как адрес Mockarty по умолчанию. Если ваш экземпляр работает на удалённом сервере, замените localhost:5770 на реальный адрес (например, https://mockarty.company.com или http://192.168.1.50:5770). Подробнее — в разделе Полезные функции и советы.

Обзор Store систем

Ключевые понятия для начинающих

Прежде чем углубляться в Store-системы, вот несколько ключевых терминов:

  • Мок – имитация API-эндпоинта, которая возвращает заранее заданные ответы вместо обращения к реальному сервису.
  • Namespace (пространство имён) – логическая группировка моков (как папка). Моки в разных пространствах имён изолированы друг от друга.
  • Chain (цепочка) – последовательность связанных моков, которые разделяют состояние. Например, «создать заказ» -> «оплатить заказ» -> «отправить заказ» образуют цепочку.
  • Extract (извлечение) – процесс извлечения данных из запроса (или генерации данных) и сохранения их в хранилище для последующего использования.
  • JsonPath – язык запросов (например, $.mS.userId) для обращения к сохранённым значениям в ответах моков.

Какой Store выбрать

Не уверены, какое хранилище использовать? Вот простое руководство:

  • Mock Store (mS) – Используйте, когда нужны временные данные в рамках одного вызова мока. Пример: сгенерировать UUID и использовать его в нескольких местах одного ответа. Данные исчезают после обработки запроса.
  • Chain Store (cS) – Используйте, когда нужно передавать данные между связанными моками, образующими рабочий процесс. Пример: процесс регистрации пользователя, где мок регистрации сохраняет userId, а мок профиля его считывает. Данные сохраняются между вызовами в рамках одной цепочки.
  • Global Store (gS) – Используйте, когда нужно общее состояние для всех моков в пространстве имён. Пример: счётчики запросов, feature-флаги или системные настройки. Данные сохраняются бессрочно до явной очистки.

Концепция Store

Store - это системы хранения данных в Mockarty, которые позволяют:

  • Сохранять данные между вызовами моков
  • Обмениваться данными между связанными моками
  • Аккумулировать состояние для сложных сценариев
  • Создавать динамические ответы на основе накопленных данных с помощью JsonPath и Faker

Архитектура Store

graph TB
    subgraph "Store Hierarchy"
        MSTORE["Mock Store (mS)"]
        CSTORE["Chain Store (cS)"]
        GSTORE["Global Store (gS)"]
    end

    subgraph "Scope"
        MOCK_SCOPE["Single Mock"]
        CHAIN_SCOPE["Chain of Mocks"]
        GLOBAL_SCOPE["All Mocks in Namespace"]
    end

    subgraph "Persistence"
        SESSION["Ephemeral (request only)"]
        PERMANENT["Persistent (database)"]
        NAMESPACE_ISOLATED["Persistent + Namespace Isolated"]
    end

    MSTORE --> MOCK_SCOPE
    CSTORE --> CHAIN_SCOPE
    GSTORE --> GLOBAL_SCOPE

    MSTORE --> SESSION
    CSTORE --> PERMANENT
    GSTORE --> NAMESPACE_ISOLATED

    classDef store fill:#4a90d9,stroke:#2c5f8a,color:#fff
    classDef scope fill:#8e6fbf,stroke:#5e4590,color:#fff
    classDef persistence fill:#5aad6e,stroke:#3a7d4e,color:#fff

    class MSTORE,CSTORE,GSTORE store
    class MOCK_SCOPE,CHAIN_SCOPE,GLOBAL_SCOPE scope
    class SESSION,PERMANENT,NAMESPACE_ISOLATED persistence

Сравнение типов Store

Store Type Область видимости Время жизни Namespace JsonPath доступ
MStore Локальный мок Во время обработки мока N/A $.mS.key
CStore Все моки в Chain Постоянный $.cS.key
GStore Все моки в Namespace Постоянный $.gS.key

Интерфейс управления хранилищами

Страница Stores в веб-интерфейсе Mockarty предоставляет визуальный интерфейс для просмотра и управления всеми тремя типами хранилищ. Используйте вкладки вверху для переключения между Global Store, Chain Store и Mock Store.

Страница Stores – вкладка Global Store

Примечание: Запись в хранилища из extract-операций выполняется асинхронно. Значения могут быть недоступны мгновенно при одновременных запросах.


Mock Store (MStore)

Что такое Mock Store?

Mock Store (MStore) - это локальное хранилище данных для конкретного мока, доступное только во время обработки этого мока.

Вкладка Mock Store – эфемерное хранилище

Особенности:

  • Изоляция – данные доступны только внутри конкретного мока
  • Временность – данные живут только во время обработки запроса
  • Быстрота – хранится в памяти мока
  • Простота – не требует управления namespace или chainId

Использование MStore

Извлечение в MStore

{
  "id": "user-create",
  "http": {
    "route": "/api/users",
    "httpMethod": "POST"
  },
  "response": {
    "payload": {
      "id": "$.mS.generatedId",
      "name": "$.req.name", 
      "email": "$.req.email",
      "clientIP": "$.mS.clientIP",
      "userAgent": "$.mS.userAgent"
    }
  },
  "extract": {
    "mStore": {
      "generatedId": "$.fake.UUIDDigit",
      "requestTime": "$.fake.RFC3339",
      "clientIP": "$.reqHeader['X-Forwarded-For'][0]",
      "userAgent": "$.reqHeader['User-Agent'][0]",
      "requestCount": "$.increment($.gS.requestCount || 0)"
    }
  }
}

Доступ к MStore в ответе

{
  "response": {
    "payload": {
      "userId": "$.mS.generatedId",
      "processedAt": "$.mS.requestTime",
      "metadata": {
        "clientIP": "$.mS.clientIP",
        "userAgent": "$.mS.userAgent",
        "requestCount": "$.mS.requestCount"
      }
    }
  }
}

Типичные случаи использования MStore

  1. Генерация связанных ID:

    "extract": {
      "mStore": {
        "userId": "$.fake.UUIDDigit",
        "profileId": "$.fake.UUIDDigit",
        "sessionId": "$.fake.UUID"
      }
    }
    
  2. Обработка метаданных запроса:

    "extract": {
      "mStore": {
        "timestamp": "$.fake.RFC3339",
        "requestId": "$.fake.UUID",
        "traceId": "$.reqHeader['X-Trace-ID'][0]"
      }
    }
    
  3. Математические вычисления:

    "extract": {
      "mStore": {
        "basePrice": "$.req.price",
        "tax": "$.multiply($.req.price, 0.2)",
        "totalPrice": "$.sum($.req.price, $.mS.tax)"
      }
    }
    

Chain Store (CStore)

Что такое Chain Store?

Chain Store (CStore) - это разделяемое хранилище данных между всеми моками с одинаковым chainId в рамках одного namespace.

Вкладка Chain Store – общее состояние между связанными моками

Особенности:

  • Разделяемость – данные доступны всем мокам в цепочке
  • Персистентность – данные сохраняются между вызовами
  • Namespace изоляция – данные изолированы по namespace
  • Аккумуляция – данные накапливаются по мере выполнения цепочки

Жизненный цикл CStore

sequenceDiagram
    participant M1 as Mock 1
    participant CS as Chain Store
    participant M2 as Mock 2
    participant M3 as Mock 3

    M1->>CS: Extract data (step 1)
    CS-->>M1: Store updated
    
    M2->>CS: Read data from step 1
    CS-->>M2: Return step 1 data
    M2->>CS: Extract additional data (step 2)
    CS-->>M2: Store updated
    
    M3->>CS: Read data from steps 1 & 2
    CS-->>M3: Return accumulated data
    M3->>CS: Extract final data (step 3)
    CS-->>M3: Store updated (complete)

Использование CStore

Начальный мок в цепочке

{
  "id": "order-start",
  "chainId": "order-processing",
  "namespace": "e-commerce",
  "http": {
    "route": "/api/orders/create",
    "httpMethod": "POST"
  },
  "response": {
    "payload": {
      "orderId": "$.fake.UUIDDigit",
      "status": "created",
      "items": "$.req.items"
    }
  },
  "extract": {
    "cStore": {
      "orderId": "$.fake.UUIDDigit",
      "customerId": "$.req.customerId",
      "items": "$.req.items",
      "totalAmount": "$.req.totalAmount",
      "createdAt": "$.fake.RFC3339",
      "currentStep": "created"
    }
  }
}

Промежуточный мок в цепочке

{
  "id": "order-payment",
  "chainId": "order-processing",
  "namespace": "e-commerce",
  "http": {
    "route": "/api/orders/payment",
    "httpMethod": "POST"
  },
  "response": {
    "payload": {
      "orderId": "$.cS.orderId",
      "customerId": "$.cS.customerId",
      "amount": "$.cS.totalAmount",
      "paymentId": "$.fake.UUIDDigit",
      "status": "paid"
    }
  },
  "extract": {
    "cStore": {
      "paymentId": "$.fake.UUIDDigit",
      "paidAt": "$.fake.RFC3339",
      "paymentMethod": "$.req.paymentMethod",
      "currentStep": "paid"
    }
  }
}

Финальный мок в цепочке

{
  "id": "order-fulfill",
  "chainId": "order-processing", 
  "namespace": "e-commerce",
  "http": {
    "route": "/api/orders/fulfill",
    "httpMethod": "POST"
  },
  "response": {
    "payload": {
      "orderId": "$.cS.orderId",
      "customerId": "$.cS.customerId",
      "paymentId": "$.cS.paymentId",
      "trackingNumber": "$.fake.ZipCode",
      "estimatedDelivery": "$.fake.Date",
      "orderSummary": {
        "items": "$.cS.items",
        "totalAmount": "$.cS.totalAmount",
        "createdAt": "$.cS.createdAt",
        "paidAt": "$.cS.paidAt",
        "fulfilledAt": "$.fake.RFC3339"
      }
    }
  },
  "extract": {
    "cStore": {
      "trackingNumber": "$.fake.ZipCode",
      "fulfilledAt": "$.fake.RFC3339",
      "currentStep": "fulfilled",
      "isComplete": true
    }
  }
}

API для Chain Store

Получение данных Chain Store

GET /api/v1/stores/chain/{chainId}?namespace=e-commerce

Добавление данных в Chain Store

Каждый запрос добавляет одну пару ключ-значение. Для добавления неск��льких записей выполните отдельные запросы для каждого ключа.

POST /api/v1/stores/chain/order-processing
Content-Type: application/json

{
  "key": "manualStep",
  "value": "admin-review",
  "namespace": "e-commerce"
}

Удаление данных из Chain Store

Удаление одного ключа через URL-путь:

DELETE /api/v1/stores/chain/order-processing/temporaryData?namespace=e-commerce

Global Store (GStore)

Что такое Global Store?

Global Store (GStore) - это глобальное хранилище данных, доступное всем мокам в рамках одного namespace.

Global Store с данными – пары ключ-значение в интерфейсе

Особенности:

  • Глобальность – данные доступны всем мокам в namespace
  • Персистентность – данные сохраняются между всеми вызовами
  • Namespace изоляция – данные изолированы по namespace
  • Аккумуляция – идеально для счетчиков и статистики

Использование GStore

Счетчики и статистика

{
  "id": "api-request-counter",
  "namespace": "monitoring",
  "http": {
    "route": "/api/*"
  },
  "response": {
    "payload": {
      "requestId": "$.fake.UUID",
      "timestamp": "$.fake.RFC3339",
      "stats": {
        "totalRequests": "$.increment($.gS.totalRequests || 0)",
        "todayRequests": "$.increment($.gS.todayRequests || 0)",
        "uniqueUsers": "$.gS.uniqueUsers"
      }
    }
  },
  "extract": {
    "gStore": {
      "totalRequests": "$.increment($.gS.totalRequests || 0)",
      "todayRequests": "$.increment($.gS.todayRequests || 0)",
      "lastRequestAt": "$.fake.RFC3339",
      "lastUserID": "$.reqHeader['X-User-ID'][0]"
    }
  }
}

Системные настройки

{
  "id": "system-config",
  "namespace": "system",
  "http": {
    "route": "/api/config"
  },
  "response": {
    "payload": {
      "config": {
        "version": "$.gS.systemVersion",
        "maintenanceMode": "$.gS.maintenanceMode",
        "features": "$.gS.enabledFeatures",
        "lastUpdated": "$.gS.configLastUpdated"
      }
    }
  },
  "extract": {
    "gStore": {
      "configAccessed": "$.increment($.gS.configAccessed || 0)",
      "lastConfigAccess": "$.fake.RFC3339"
    }
  }
}

Глобальные состояния

{
  "id": "feature-toggle",
  "namespace": "features",
  "http": {
    "route": "/api/features/status"
  },
  "response": {
    "payload": {
      "feature": "$.req.featureName",
      "enabled": "$.gS.features.newDashboard.enabled",
      "config": "$.gS.features.newDashboard.config",
      "rolloutPercentage": "$.gS.features.newDashboard.rollout"
    }
  },
  "extract": {
    "gStore": {
      "featureAccessCount": "$.increment($.gS.featureAccessCount || 0)",
      "lastFeatureAccess": "$.fake.RFC3339"
    }
  }
}

API для Global Store

Получение данных Global Store

GET /api/v1/stores/global?namespace=monitoring

Добавление данных в Global Store

Каждый запрос добавляет одну пару ключ-значение. Для добавления нескольких записей выполните отдельные запросы для каждого ключа.

POST /api/v1/stores/global
Content-Type: application/json

{
  "key": "systemVersion",
  "value": "1.2.3",
  "namespace": "monitoring"
}

Удаление данных из Global Store

Удаление одного ключа через URL-путь:

DELETE /api/v1/stores/global/oldConfig?namespace=monitoring

API для управления Store

Полная справка API

Полную документацию API см. в Справочнике API.

Global Store Endpoints

Method Endpoint Описание
GET /api/v1/stores/global Получить Global Store
POST /api/v1/stores/global Добавить пару ключ-значение в Global Store
DELETE /api/v1/stores/global/{key} Удалить ключ из Global Store

Query Parameters:

  • namespace (optional) - пространство (default: “sandbox”)

POST Body: {"key": "...", "value": "...", "namespace": "..."}

Chain Store Endpoints

Method Endpoint Описание
GET /api/v1/stores/chain/{chainId} Получить Chain Store
POST /api/v1/stores/chain/{chainId} Добавить пару ключ-значение в Chain Store
DELETE /api/v1/stores/chain/{chainId}/{key} Удалить ключ из Chain Store

Path Parameters:

  • chainId - ID цепочки
  • key - удаляемый ключ (только для DELETE)

Query Parameters:

  • namespace (optional) - пространство (default: “sandbox”)

POST Body: {"key": "...", "value": "...", "namespace": "..."}

Примеры API вызовов

Добавление в Global Store

Каждый вызов API добавляет одну пару ключ-значение. Для нескольких ключей выполните отдельные запросы.

cURL

# Добавить пару ключ-значение в Global Store
curl -X POST http://localhost:5770/api/v1/stores/global \
  -H "Content-Type: application/json" \
  -d '{"key": "appVersion", "value": "1.0.0", "namespace": "testing"}'

# Добавить ещё один ключ
curl -X POST http://localhost:5770/api/v1/stores/global \
  -H "Content-Type: application/json" \
  -d '{"key": "environment", "value": "testing", "namespace": "testing"}'

CLI

mockarty-cli store global set --namespace testing --key appVersion --value "1.0.0"
mockarty-cli store global set --namespace testing --key environment --value "testing"

Go

import mockarty "github.com/mockarty/mockarty-go"

client := mockarty.NewClient("http://localhost:5770", "YOUR_TOKEN")

// Namespace задаётся при создании клиента (mockarty.WithNamespace("testing"))
err := client.Stores().GlobalSet(ctx, "appVersion", "1.0.0")
err = client.Stores().GlobalSet(ctx, "environment", "testing")

Python

from mockarty import MockartyClient

client = MockartyClient("http://localhost:5770", token="YOUR_TOKEN")

# Namespace задаётся при создании клиента (MockartyClient(..., namespace="testing"))
client.stores.global_set(key="appVersion", value="1.0.0")
client.stores.global_set(key="environment", value="testing")

Java

import ru.mockarty.MockartyClient;

MockartyClient client = new MockartyClient("http://localhost:5770", "YOUR_TOKEN");

client.stores().globalSet("testing", "appVersion", "1.0.0");
client.stores().globalSet("testing", "environment", "testing");

Добавление в Chain Store

cURL

curl -X POST http://localhost:5770/api/v1/stores/chain/user-journey \
  -H "Content-Type: application/json" \
  -d '{"key": "journeyStarted", "value": "true", "namespace": "testing"}'

curl -X POST http://localhost:5770/api/v1/stores/chain/user-journey \
  -H "Content-Type: application/json" \
  -d '{"key": "startTime", "value": "2024-01-01T10:00:00Z", "namespace": "testing"}'

CLI

mockarty-cli store chain set user-journey --namespace testing --key journeyStarted --value "true"
mockarty-cli store chain set user-journey --namespace testing --key startTime --value "2024-01-01T10:00:00Z"

Go

err := client.Stores().ChainSet(ctx, "user-journey", "journeyStarted", "true")
err = client.Stores().ChainSet(ctx, "user-journey", "startTime", "2024-01-01T10:00:00Z")

Python

client.stores.chain_set(chain_id="user-journey", key="journeyStarted", value="true")
client.stores.chain_set(chain_id="user-journey", key="startTime", value="2024-01-01T10:00:00Z")

Java

client.stores().chainSet("user-journey", "testing", "journeyStarted", "true");
client.stores().chainSet("user-journey", "testing", "startTime", "2024-01-01T10:00:00Z");

Удаление данных из Store

Удаление одного ключа за запрос через URL-путь.

cURL

# Удалить ключ из Global Store
curl -X DELETE http://localhost:5770/api/v1/stores/global/tempData?namespace=testing

# Удалить ключ из Chain Store
curl -X DELETE http://localhost:5770/api/v1/stores/chain/user-journey/debugData?namespace=testing

CLI

# Удалить ключ из Global Store
mockarty-cli store global delete --namespace testing --key tempData

# Удалить ключ из Chain Store
mockarty-cli store chain delete user-journey --namespace testing --key debugData

Go

// Удалить ключ из Global Store
err := client.Stores().GlobalDelete(ctx, "testing", "tempData")

// Удалить ключ из Chain Store
err = client.Stores().ChainDelete(ctx, "user-journey", "testing", "debugData")

Python

# Удалить ключ из Global Store
client.stores.global_delete(namespace="testing", key="tempData")

# Удалить ключ из Chain Store
client.stores.chain_delete(chain_id="user-journey", namespace="testing", key="debugData")

Java

// Удалить ключ из Global Store
client.stores().globalDelete("testing", "tempData");

// Удалить ключ из Chain Store
client.stores().chainDelete("user-journey", "testing", "debugData");

Примеры использования

В предыдущих разделах уже показаны базовые паттерны для каждого типа хранилища – счётчики, конфигурация и цепочки моков. Примеры ниже демонстрируют более сложные сценарии, которые комбинируют несколько типов Store или показывают межхранилищное взаимодействие.

Сложные Chain сценарии

Multi-step Workflow

{
  "id": "workflow-step-1",
  "chainId": "approval-workflow",
  "namespace": "workflows",
  "http": {
    "route": "/api/workflow/submit",
    "httpMethod": "POST"
  },
  "response": {
    "payload": {
      "workflowId": "$.fake.UUIDDigit",
      "status": "submitted",
      "submittedBy": "$.req.userId",
      "nextStep": "manager-review"
    }
  },
  "extract": {
    "cStore": {
      "workflowId": "$.fake.UUIDDigit",
      "submittedBy": "$.req.userId",
      "submittedAt": "$.fake.RFC3339",
      "currentStep": "submitted",
      "steps": ["submitted"],
      "approvalChain": ["manager", "director", "ceo"]
    },
    "gStore": {
      "totalWorkflows": "$.increment($.gS.totalWorkflows || 0)"
    }
  }
}
{
  "id": "workflow-step-2", 
  "chainId": "approval-workflow",
  "namespace": "workflows",
  "http": {
    "route": "/api/workflow/approve",
    "httpMethod": "POST"
  },
  "response": {
    "payload": {
      "workflowId": "$.cS.workflowId",
      "approvedBy": "$.req.approverId",
      "currentStep": "manager-approved",
      "nextStep": "director-review",
      "progress": {
        "submittedBy": "$.cS.submittedBy",
        "submittedAt": "$.cS.submittedAt",
        "percentage": 33
      }
    }
  },
  "extract": {
    "cStore": {
      "managerApprovedBy": "$.req.approverId",
      "managerApprovedAt": "$.fake.RFC3339",
      "currentStep": "manager-approved"
    }
  }
}

Отслеживание экспериментов с Store

Отслеживание назначений экспериментов

{
  "id": "experiment-tracker",
  "namespace": "experiments",
  "http": {
    "route": "/api/experiment/assign",
    "httpMethod": "POST"
  },
  "response": {
    "payload": {
      "userId": "$.req.userId",
      "experiment": "new-checkout-flow",
      "variant": "$.req.variant",
      "assignedAt": "$.fake.RFC3339",
      "totalAssignments": "$.gS.totalAssignments"
    }
  },
  "extract": {
    "gStore": {
      "totalAssignments": "$.increment($.gS.totalAssignments || 0)",
      "lastAssignedUser": "$.req.userId",
      "lastAssignedVariant": "$.req.variant",
      "lastAssignedAt": "$.fake.RFC3339"
    }
  }
}

Лучшие практики

Рекомендации

  1. Используйте правильный тип Store для задач:

    • MStore - для временных вычислений внутри мока
    • CStore - для передачи данных между этапами сценария
    • GStore - для глобальных счетчиков и настроек
  2. Структурируйте данные в Store:

    "extract": {
      "gStore": {
        "users": {
          "total": "$.increment($.gS.users.total || 0)",
          "active": "$.gS.users.active || 0",
          "lastRegistered": "$.fake.RFC3339"
        },
        "stats": {
          "requests": "$.increment($.gS.stats.requests || 0)",
          "errors": "$.gS.stats.errors || 0"
        }
      }
    }
    
  3. Используйте осмысленные ключи:

    Хорошо – описательное имя ключа, объясняющее что считается:

    "userRegistrationCount": "$.increment($.gS.userRegistrationCount || 0)"
    

    Плохо – общее имя ключа, не несущее смысла:

    "count": "$.increment($.gS.count || 0)"
    
  4. Инициализируйте значения по умолчанию:

    С default (рекомендуется) – использует || 0 для старта с нуля, если ключ не существует:

    "totalUsers": "$.increment($.gS.totalUsers || 0)"
    

    Без default (рискованно) – может завершиться ошибкой, если ключ ещё не был установлен:

    "totalUsers": "$.increment($.gS.totalUsers)"
    
  5. Группируйте связанные данные:

    "extract": {
      "cStore": {
        "order": {
          "id": "$.fake.UUIDDigit",
          "createdAt": "$.fake.RFC3339",
          "status": "created"
        },
        "customer": {
          "id": "$.req.customerId",
          "email": "$.req.email"
        }
      }
    }
    

Что избегать

  1. Не храните чувствительные данные в Store (пароли, токены)
  2. Не создавайте слишком глубокие вложенности (>3 уровней)
  3. Не забывайте про namespace изоляцию
  4. Не используйте Store для простых статических ответов
  5. Не переполняйте Store большими объектами данных

Управление данными

Очистка устаревших данных

{
  "id": "cleanup-stores",
  "namespace": "maintenance",
  "http": {
    "route": "/api/maintenance/cleanup"
  },
  "extract": {
    "gStore": {
      "tempData": null,
      "debugInfo": null,
      "lastCleanup": "$.fake.RFC3339",
      "cleanupCount": "$.increment($.gS.cleanupCount || 0)"
    }
  }
}

Архивация данных

{
  "id": "archive-data",
  "extract": {
    "gStore": {
      "archived": {
        "data": "$.gS.currentData",
        "archivedAt": "$.fake.RFC3339"
      },
      "currentData": null
    }
  }
}

Используйте Store системы для создания мощных динамических сценариев с сохранением состояния!


Смотрите также