gdbots/pbjx-bundle

Symfony 扩展包,集成了 gdbots/pbjx 消息工具。

安装次数: 14,541

依赖项: 1

建议者: 0

安全性: 0

星级: 0

关注者: 9

分支: 1

开放问题: 0

类型:symfony-bundle

v3.2.0 2024-01-24 05:08 UTC

README

Symfony 扩展包,集成了 gdbots/pbjx 库。

配置

按照标准 bundle install 使用 gdbots/pbjx-bundle 作为 composer 包名。默认配置为所有发送、发布、请求操作提供内存处理。EventStore 和 EventSearch 默认未配置。

以下示例假设您正在运行 DynamoDb EventStore 和 Elastica EventSearch。这些是可选配置。

config/packages/pbjx.yml

# many of the configurations below are defaults so you can remove them
# from your configuration, added here for reference

parameters:
  # optionally defined as parameter here to allow reuse
  es_clusters:
    default:
      debug: '%kernel.debug%'
      timeout: 300 # default
      persistent: true # default
      round_robin: true # default
      servers:
        - {host: '127.0.0.1', port: 9200} # default

gdbots_pbjx:
  # ensure tamper proof messaging by signing all pbjx messages
  # e.g. AWS Lambda to your API servers (signed with shared secret)
  # or by using bearer tokens in SPA to sign messages
  pbjx_token_signer:
    default_kid: '%env(PBJX_CURRENT_SIGNING_KID)%'
    # multiple keys allow for seamless key rotation
    keys:
      - {kid: '%env(PBJX_NEXT_SIGNING_KID)%', secret: '%env(PBJX_NEXT_SIGNING_SECRET)%'}
      - {kid: '%env(PBJX_CURRENT_SIGNING_KID)%', secret: '%env(PBJX_CURRENT_SIGNING_SECRET)%'}
  pbjx_controller:
    # if accepting commands from web trackers (analytics, click tracking, etc.)
    # then you may want to enable "GET" requests
    allow_get_request: false # default
    # array of curies or regex patterns that will not require x-pbjx-token validation
    bypass_token_validation: ['gdbots:pbjx:request:echo-request'] # default
  # the receive controller accepts transport messages.  it is ideally secured
  # in a VPC and only called by internal services.
  pbjx_receive_controller:
    enabled: false # default, ensure pbjx_token_signer.keys are set prior to enabling
  command_bus:
    transport: ~ # in_memory, firehose, kinesis
  event_bus:
    transport: ~ # in_memory, firehose, kinesis
  request_bus:
    # requests must return a value, so firehose and kinesis simply run
    # the request in memory as they don't support request/response.
    transport: ~ # in_memory
  event_store:
    provider: dynamodb
    dynamodb:
      table_name: acme-event-store
  event_search:
    provider: elastica
    elastica:
      # your app will at some point need to customize the queries
      # override the class so you can provide these customizations.
      class: Acme\Pbjx\EventSearch\Elastica\ElasticaEventSearch
      query_timeout: '500ms' # default
      clusters: '%es_clusters%'
      index_manager:
        # to customize index mapping
        class: Acme\Pbjx\EventSearch\Elastica\IndexManager
  scheduler:
    provider: dynamodb
    dynamodb:
      table_name: acme-scheduler
      state_machine_arn: 'arn:aws:states:%cloud_region%:%aws_account_id%:stateMachine:acme-%app_env%-pbjx-scheduler'

# typically these would be in services.yml file.
services:
  # unless you're starting with dynamodb streams publishing events you will need this.
  gdbots_pbjx.event_store.dynamodb_2pc:
    class: Gdbots\Pbjx\EventStore\TwoPhaseCommitEventStore
    decorates: gdbots_pbjx.event_store.dynamodb
    arguments:
      - '@pbjx'
      - '@gdbots_pbjx.event_store.dynamodb_2pc.inner'
      - '%env(PBJX_DISABLE_2PC_EVENT_STORE)%'
    public: false

  # If you are using AWS ElasticSearch service, use AwsAuthV4ClientManager
  # UPDATE as of November 2017, AWS supports ES in a VPC; this is preferable
  # to using IP rules and/or AwsAuthV4ClientManager.
  gdbots_pbjx.event_search.elastica.client_manager:
    class: Gdbots\Pbjx\EventSearch\Elastica\AwsAuthV4ClientManager
    public: true
    arguments:
      - '@aws_credentials'
      - '%cloud_region%'
      - '%es_clusters%'
      - '@logger'
    tags:
      - {name: monolog.logger, channel: pbjx.event_search}

在您的本地环境中,强烈建议配置 PbjxDebugger。

config/services_local.yml

services:
  monolog_json_formatter:
    class: Monolog\Formatter\JsonFormatter
    arguments: ['!php/const:Monolog\Formatter\JsonFormatter::BATCH_MODE_NEWLINES']

monolog:
  handlers:
    pbjx_debugger:
      type: stream
      path: '%kernel.logs_dir%/pbjx-debugger.log'
      level: debug
      formatter: monolog_json_formatter
      channels: ['pbjx.debugger']

Pbjx HTTP 端点

Pbjx 已准备好在您的应用程序和控制台命令中使用,但尚未通过 HTTP 提供。 提供 HTTP 功能非常强大,但如果不正确地安全,可能会非常危险。

在保护您的应用程序时,所有常规规则都适用。身份验证和授权由您负责,但是,Symfony 使用 security components 使得这相对简单。

示例安全配置

# see https://symfony.ac.cn/doc/current/security/voters.html
pbjx_permission_voter:
  class: App\Security\PbjxPermissionVoter
  public: false
  arguments: ['@security.access.decision_manager']
  tags:
    - {name: security.voter}

# see https://symfony.ac.cn/doc/current/components/security/authorization.html#access-decision-manager
# use the Gdbots\Bundle\PbjxBundle\Validator\PermissionValidatorTrait to provide some boilerplate.
gdbots_pbjx.pbjx_permission_validator:
  class: AppBundle\Security\PbjxPermissionValidator
  public: false
  arguments: ['@request_stack', '@security.authorization_checker']
  tags:
    - {name: pbjx.event_subscriber}

要启用 Pbjx http 端点,您必须包含路由。在 config/routes.yml

pbjx:
  resource: '@GdbotsPbjxBundle/Resources/config/routes.xml'
  prefix: /pbjx

一旦设置完成,就可以将 ANY pbjx 消息发送到端点 /pbjx/vendor/package/category/message。此 URL 是配置的前缀,然后是 SchemaCurie 解析为 URL。

为什么不直接使用 /pbjx?对于日志记录、授权、负载均衡、调试等,有完整的路径到 SchemaCurie 是一个巨大的好处。

示例 curl 请求

curl -X POST -s -H "Content-Type: application/json" "https://yourdomain.com/pbjx/gdbots/pbjx/request/echo-request" -d '{"msg":"test"}'

# with PbjxToken signature (use composer package gdbots/pbjx or npm package @gdbots/pbjx to create tokens)
curl -X POST -s -H "Content-Type: application/json" -H "x-pbjx-token: TOKENHERE" "https://yourdomain.com/pbjx/acme/blog/command/publish-article" -d '{"id":"123"}'

示例 ajax 请求

$.ajax({
  url: '/pbjx/gdbots/pbjx/request/echo-request',
  type: 'post',
  contentType: 'application/json; charset=utf-8',
  dataType: 'json',
  data: JSON.stringify({msg: 'hello'}),
  complete: function (xhr) {
    console.log(xhr.responseJSON);
  }
});

如果您的 SchemaCurie 包含一个空的类别段,请使用 URL 中的 "_" 替换。

控制器

在控制器中使用 Pbjx 的推荐方法是使用 Symfony 依赖注入所需的 pbjx 服务,并在必要时导入 PbjxControllerTrait 以获取将 pbj 渲染到 twig 模板中的辅助方法。

final class ArticleController extends Controller
{
    use PbjxControllerTrait;

    /** @var Pbjx */
    private $pbjx;

    /**
     * @param Pbjx $pbjx
     */
    public function __construct(Pbjx $pbjx)
    {
        $this->pbjx = $pbjx;
    }

    /**
     * @Route("/articles/{article_id}", requirements={"article_id": "^[0-9A-Fa-f]+$"})
     * @Method("GET")
     * @Security("is_granted('acme:blog:request:get-article-request')")
     *
     * @param Request $request
     *
     * @return Response
     */
    public function getAction(Request $request): Response
    {
        $getArticleRequest = GetArticleRequestV1::create()->set('article_id', $request->attributes->get('article_id'));
        $getArticleResponse = $this->pbjx->request($getArticleRequest);
        return $this->renderPbj($getArticleResponse);
    }
}

PbjxControllerTrait::renderPbj

这是一个方便的方法,它接受一个 pbj 消息并使用 pbjTemplate 推导模板名称,然后调用 Symfony 的 render 方法(来自框架包 Controller)。

模板将具有 pbj 作为变量,这是消息对象本身。

提示: {{ pbj }} 将将消息以 yaml 格式输出,以便在 twig 中轻松调试,或 {{ pbj|json_encode(constant('JSON_PRETTY_PRINT')) }}

PbjxControllerTrait::pbjTemplate

根据提供的消息的架构(pbj 架构)返回一个对模板的引用。这允许为 pbj 消息进行组件样式开发。您正在请求可以渲染您的消息(例如 Article)为“卡片”、“模态框”、“页面”等的模板。

这可以与 gdbots/app-bundleDeviceViewRendererTrait::renderUsingDeviceView 结合使用(renderPbj 方法会自动执行此操作)。

最终结果是模板的命名空间路径引用,该模板符合 namespaced pathSymfony 模板命名最佳实践。示例

Twig 扩展

提供了一些 twig 函数来将控制器可以执行的大部分功能暴露给您的 twig 模板。

Twig 函数:pbj_template

根据提供消息(pbj模式)的架构返回一个twig模板的引用。这允许为pbj消息进行组件样式开发。您正在请求一个可以将消息(例如,文章)渲染为“卡片”、“模态框”、“slack_post”等的模板,并且该模板可以是特定于设备的视图(card.smartphone.html.twig)

示例

{% include pbj_template(pbj, 'card', 'html', device_view) with {'pbj': pbj} %}

Twig函数:pbj_url

返回一个pbj实例的命名URL。这取决于gdbots/uri-template包,该包提供了一种注册uri模板的方式。这些预期格式为vendor:label.template_name,例如acme:article.canonical

示例

{{ pbj_url(pbj, 'canonical') }}

Twig函数:uri_template_expand

当您需要直接使用自己的变量扩展URI模板时,请使用此函数。

示例

{{ uri_template_expand('acme:article.canonical', {slug: 'some-slug'}) }}

Twig函数:pbjx_request

与您可以在 Twig中嵌入Symfony控制器的方式相同,您也可以在Twig中嵌入pbjx请求。此函数执行$pbjx->request($request);并返回响应。如果启用调试,则会抛出异常(通常在开发环境中),否则将其记录并返回null。

示例

{% set get_comments_response = pbjx_request('acme:blog:request:get-comments-request', {'article_id': id}) %}
{% if get_comments_response %}
  {% include pbj_template(get_comments_response, 'list', device_view) with {'pbj': get_comments_response} %}
{% endif %}

控制台命令

此库提供了一些命令,使管理Pbjx服务变得简单。运行Symfony控制台并查找pbjx命令。

pbjx                                [pbjx:message] Handles pbjx messages (command, event, request) and returns an envelope with the result.
pbjx:batch                          [pbjx:lines] Reads messages from a newline-delimited JSON file and processes them.
pbjx:create-event-search-storage    Creates the EventSearch storage.
pbjx:create-event-store-storage     Creates the EventStore storage.
pbjx:create-scheduler-storage       Creates the Scheduler storage.
pbjx:describe-event-search-storage  Describes the EventSearch storage.
pbjx:describe-event-store-storage   Describes the EventStore storage.
pbjx:describe-scheduler-storage     Describes the Scheduler storage.
pbjx:export-events                  Pipes events from the EventStore to STDOUT.
pbjx:reindex-events                 Pipes events from the EventStore and reindexes them.
pbjx:replay-events                  Pipes events from the EventStore and replays them through pbjx->publish.
pbjx:tail-events                    Tails events from the EventStore for a given stream id and writes them to STDOUT.

最有用的可能是pbjxpbjx:batch命令。这些命令运行pbjx的方式与您在应用程序代码中运行的方式相同,并返回生成的pbj。

Pbjx设计为可以通过cli、应用程序代码和http以相同的方式运行。

console pbjx --pretty 'gdbots:pbjx:request:echo-request' '{"msg":"hello"}'

示例响应

{
    "_schema": "pbj:gdbots:pbjx::envelope:1-0-0",
    "envelope_id": "5d87da8b-b3b5-4e2f-9f60-843e79b678dc",
    "ok": true,
    "code": 0,
    "http_code": 200,
    "etag": null,
    "message_ref": {
        "curie": "gdbots:pbjx:request:echo-response",
        "id": "aacc60a0-92a5-4ee6-9aec-63b149abcf1d"
    },
    "message": {
        "_schema": "pbj:gdbots:pbjx:request:echo-response:1-0-0",
        "response_id": "aacc60a0-92a5-4ee6-9aec-63b149abcf1d",
        "created_at": "1488149039527239",
        "ctx_request_ref": {
            "curie": "gdbots:pbjx:request:echo-request",
            "id": "c6867d0c-0c97-4e27-903f-30bbd69da79c"
        },
        "ctx_request": {
            "_schema": "pbj:gdbots:pbjx:request:echo-request:1-0-0",
            "request_id": "c6867d0c-0c97-4e27-903f-30bbd69da79c",
            "occurred_at": "1488149039229879",
            "ctx_retries": 0,
            "ctx_correlator_ref": {
                "curie": "gdbots:pbjx::envelope",
                "id": "5d87da8b-b3b5-4e2f-9f60-843e79b678dc"
            },
            "ctx_app": {
                "_schema": "pbj:gdbots:contexts::app:1-0-0",
                "vendor": "acme",
                "name": "blog-php.console",
                "version": "v0.1.0",
                "build": "1488148956"
            },
            "ctx_cloud": {
                "_schema": "pbj:gdbots:contexts::cloud:1-0-0",
                "provider": "private",
                "region": "us-west-2",
                "zone": "us-west-2a",
                "instance_id": "123456",
                "instance_type": "vbox"
            },
            "ctx_ip": "127.0.0.1",
            "ctx_ua": "pbjx-console\/0.x",
            "msg": "hello"
        },
        "ctx_correlator_ref": {
            "curie": "gdbots:pbjx::envelope",
            "id": "5d87da8b-b3b5-4e2f-9f60-843e79b678dc"
        },
        "msg": "hello"
    }
}

有关pbjx命令的更多详细信息,请参阅--help

库开发

Pbj有一个名为混入的概念,它只是一个可以添加到其他架构的模式。这种策略对于创建一致的数据结构以及允许在混入而不是具体架构上执行库开发非常有用。

混入不能单独使用来创建消息,它必须添加到不是混入的架构中。

此捆绑包提供了一个编译器遍历,可以自动注册pbjx命令和请求的处理程序。

示例

虚构的WidgetCo通过创建混入和库来为网站创建小部件,以提供这些混入的实现。

  • WidgetCo有一个名为widgetco:blog:mixin:add-comment的混入
  • WidgetCo有一个名为WidgetCo\Blog\AddCommentHandler的处理程序,它使用标记接口Gdbots\Pbjx\DependencyInjection\PbjxHandler
  • WidgetCo的处理程序使用findOnefindAll方法来返回它可以处理的全部SchemaCurie对象。

您的公司Acme现在有一个博客,并希望使用WidgetCo混入以及WidgetCoBlogBundle提供的实现。

  • Acme创建了一个名为acme:blog:command:add-comment的具体架构,该架构使用混入widgetco:blog:mixin:add-comment
  • 当pbjx发送命令时,它将查找处理acme:blog:command:add-comment curie的服务。
  • 该服务可以被symfony自动配置

如果您想扩展或替换widgetco提供的功能,可以实施自己的处理程序,并使用PbjxHandler接口。

您可以在库中提供具体架构和实现。这两种策略都有其优点和缺点,最大的问题是,如果库不是使用混入开发的,则在应用程序级别对架构的定制将不那么容易。

Pbjx 本身是一个基于混入(mixins)构建的库,用于处理 CommandRequestEvent 消息。