Skip to content

Latest commit

 

History

History
165 lines (129 loc) · 10 KB

File metadata and controls

165 lines (129 loc) · 10 KB

Examples

This repository is a template: the code under backend/mpt_extension_python_template/ and frontend/src/ is a set of small, working examples of the extension building blocks. This document is a guided tour of those examples and where to find them.

It describes what this repository demonstrates, not the SDK APIs themselves. For the library APIs, see the SDK usage guides linked from README.md.

Trying It With Postman

backend/docs/postman_collection.json is a ready-made Postman collection for a locally running backend (default base_url http://localhost:8080, the port exposed by make run-local). Auth is wired automatically through a collection-level pre-request script. It contains sample requests for the example routes below — /health, the order and agreement events, and the agreement API (including list pagination, get, not-found, and sync) — so you can exercise the template without writing requests by hand.

Application Wiring

backend/mpt_extension_python_template/app.py creates the ExtensionApp and registers every example router:

  • events_orders_router — order event handling
  • events_agreements_router — agreement event handling
  • api_agreements_router — agreement API endpoints
  • plug_portal_router — top-level portal plugs (the learn-extensions navigation group with the examples app and guide nested under it, plus the add showcase)
  • plug_modals_router — socketless modal plugs (dialog and wizard) opened by id from the examples "Modals" view
  • plug_agreements_router — agreement plugs (the agreement tab example plus the agreements add showcase)
  • plug_orders_router / plug_subscriptions_router / plug_assets_router / plug_accounts_router — per-entity add showcase plugs

Each router below is an independent example and can be read on its own.

API Endpoints

backend/mpt_extension_python_template/routers/api/agreement.py exposes an APIRouter under /api/v2/agreements:

Route Name Demonstrates
GET / agreements-list Paginated reads with ctx.request.pagination and PaginatedResult / APIResponse.paginated
GET /{agreement_id} agreements-get Reading a single agreement via ctx.mpt_api_service and returning APIResponse.ok
POST /{agreement_id}/sync agreements-sync A write-style action that re-reads current Marketplace data

These handlers read Marketplace data through ctx.mpt_api_service.agreements. The sync route is the backend half of the frontend "Sync now" example (see Plugs).

Events

The template shows the two event styles supported by the SDK:

  • Task eventrouters/events/order.py registers @orders_router.task("/purchase") for platform.commerce.order.status_changed, filtered by a condition on the configured product.ids. It receives a standard OrderContext and runs the PurchasePipeline.
  • Non-task event with a custom contextrouters/events/agreement.py registers @agreements_router.event("/complete") for platform.commerce.agreement.status_changed, with a richer condition (...,eq(status,Active)) and a context_adapter_type=EventAgreementContext. It runs the CompleteAgreementPipeline.

Both conditions are built from get_extension_settings().product_ids, showing how event registration uses extension settings.

Pipelines And Steps

Event handlers delegate to small pipelines, demonstrating the pipeline/step composition pattern:

A pipeline is a BasePipeline whose steps property returns an ordered list of BaseSteps. The steps here only log, on purpose — they are the place real fulfillment logic would go.

Custom Context

backend/mpt_extension_python_template/context/agreement.py defines EventAgreementContext, an example context adapter that extends the SDK AgreementContext with an extra mock_field and overrides from_context. It is wired into the agreement event (context_adapter_type=...) and consumed by LogAgreementStep, showing how to carry extension-specific data through a pipeline.

Plugs

backend/mpt_extension_python_template/routers/plugs/agreements.py declares a Marketplace Portal plug through a PlugRouter. The plug points at a frontend bundle via href and binds to a Portal socket. The frontend module name matches the bundle path one-to-one:

Plug id / socket Frontend module Demonstrates
agreements-agreementportal.commerce.agreements.agreement modules/agreements-agreement/ A full agreement tab: sync status, useAgreementSync, status chips, and a "Sync now" action calling the sync API

Modal Plugs (Opened By Id)

backend/mpt_extension_python_template/routers/plugs/modals.py declares two ModalPlugs. A modal plug has no socket: the platform never renders it as a page action, and it exists only to be opened programmatically by id via useMPTModal().open('<plug-id>'). The examples app "Modals" view (modules/examples/views/Modals.tsx) opens both plugs by id, passes a context payload to them, and displays the result each modal reports back through close(data) (delivered to the opener's onClose callback):

Plug id Frontend module Demonstrates
dialog modules/dialog/ A confirmation dialog that renders the opener's question from useMPTContext() and returns { confirmed: true/false }
wizard modules/wizard/ A multi-step Wizard that echoes the opener context and returns { completed: true/false }

The result objects above are what these modals pass to close(data). When a modal is dismissed without an explicit close — for example through the platform's modal chrome — onClose receives undefined, so openers must guard against an undefined result before reading fields like confirmed or completed.

Each module's index.tsx follows the same entry-point pattern: import the safe-storage shim, call setup() from @mpt-extension/sdk, and mount the App with createRoot. The shared building blocks the agreement tab reuses (useAgreement, useAgreementId, AgreementDetailsList, model.ts) live under frontend/src/shared/; see docs/architecture.md for the frontend structure.

UI SDK Showcase Plugs

These plugs demonstrate the UI SDK breadth rather than a specific business flow. Like the agreement plugs, they are organised per entity — one PlugRouter file each: portal.py (top-level: the learn-extensions group with examples and guide, add), orders.py, subscriptions.py, assets.py, accounts.py, and the agreements add plug in agreements.py:

Plug id / socket Frontend module Demonstrates
learn-extensionsportal — (no bundle) A NavigationPlug container: a href-less navigation grouping node whose id derives the nested socket portal.learn-extensions for the two plugs below
examplesportal.learn-extensions modules/examples/ A multi-tab React app (react-router) touring the UI SDK: Introduction, Basics, Context (useMPTContext), API calls (the http client feeding a server-driven Grid via useGridWithRql + RQL), UI elements (buttons, inputs, selects, toggles, date pickers, grids, entity references), and Modals (the socketless dialog / wizard round-trip via useMPTModal().open())
guideportal.learn-extensions modules/guide/ Rendering bundled Markdown with InlineMarkdown (esbuild .md text loader)
add-<socket> (one per socket) modules/add-*/ A "plug here" scaffold shown on every remaining Portal socket, so the full set of sockets is covered

The add-* plugs cover many near-identical sockets without duplicating logic: each is a thin module directory whose index.tsx mounts the shared AddPlugShowcase component with its socket passed as a prop, and each maps to one add_plug(...) line in the matching per-entity router (the helper lives in plugs/common.py).

Migrations

backend/migrations/ contains example data and schema migrations. They are intentionally fake placeholders; see docs/migrations.md.