generated from coulomb/repo-seed
160 lines
5.1 KiB
Python
160 lines
5.1 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from decimal import Decimal
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from ._repo_root import ensure_repo_root_on_syspath
|
|
from .models import PricingModel, Product
|
|
|
|
ensure_repo_root_on_syspath()
|
|
|
|
from adaptive_pricing_core.provider_publication import ( # noqa: E402
|
|
CatalogProduct,
|
|
ProviderPublicationState,
|
|
apply_publication,
|
|
build_publication_bundle,
|
|
plan_publication,
|
|
provider_state_from_dict,
|
|
provider_state_to_dict,
|
|
rollback_publication,
|
|
)
|
|
from adaptive_pricing_core.stripe_provider import map_bundle_to_stripe # noqa: E402
|
|
|
|
|
|
def _serialize(value: Any) -> Any:
|
|
if isinstance(value, Decimal):
|
|
return str(value)
|
|
if hasattr(value, "__dataclass_fields__"):
|
|
return {key: _serialize(getattr(value, key)) for key in value.__dataclass_fields__}
|
|
if isinstance(value, tuple):
|
|
return [_serialize(item) for item in value]
|
|
if isinstance(value, list):
|
|
return [_serialize(item) for item in value]
|
|
if isinstance(value, dict):
|
|
return {key: _serialize(item) for key, item in value.items()}
|
|
return value
|
|
|
|
|
|
def default_stripe_state_path(data_dir: Path) -> Path:
|
|
return data_dir / "provider_state" / "stripe-publication.json"
|
|
|
|
|
|
def load_stripe_publication_state(path: Path) -> ProviderPublicationState:
|
|
if not path.exists():
|
|
return ProviderPublicationState(provider="stripe")
|
|
return provider_state_from_dict(json.loads(path.read_text(encoding="utf-8")))
|
|
|
|
|
|
def save_stripe_publication_state(path: Path, state: ProviderPublicationState) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(
|
|
json.dumps(provider_state_to_dict(state), indent=2),
|
|
encoding="utf-8",
|
|
)
|
|
|
|
|
|
def _catalog_product(product: Product) -> CatalogProduct:
|
|
return CatalogProduct(
|
|
id=product.id,
|
|
name=product.name,
|
|
description=product.description,
|
|
currency=product.currency,
|
|
lifecycle_phase=product.lifecycle_phase,
|
|
active_pricing_model_id=product.active_pricing_model_id,
|
|
metadata={"product_channel": "membership"},
|
|
)
|
|
|
|
|
|
def _target_model(
|
|
models: list[PricingModel],
|
|
product: Product,
|
|
model_id: str | None = None,
|
|
) -> PricingModel:
|
|
requested_id = model_id or product.active_pricing_model_id
|
|
return next(item for item in models if item.id == requested_id)
|
|
|
|
|
|
def build_stripe_publication_preview(
|
|
product: Product,
|
|
models: list[PricingModel],
|
|
data_dir: Path,
|
|
*,
|
|
model_id: str | None = None,
|
|
state_path: Path | None = None,
|
|
) -> dict[str, Any]:
|
|
model = _target_model(models, product, model_id)
|
|
bundle = build_publication_bundle(_catalog_product(product), model)
|
|
package = map_bundle_to_stripe(bundle)
|
|
state = load_stripe_publication_state(state_path or default_stripe_state_path(data_dir))
|
|
plan = plan_publication(package, state)
|
|
|
|
return _serialize(
|
|
{
|
|
"provider": "stripe",
|
|
"state_path": str(state_path or default_stripe_state_path(data_dir)),
|
|
"model_id": model.id,
|
|
"model_name": model.name,
|
|
"current_state": {
|
|
"active_revision_id": state.active_revision_id,
|
|
"active_model_id": state.active_model_id,
|
|
"artifact_count": len(state.artifacts),
|
|
"revision_count": len(state.revisions),
|
|
},
|
|
"artifact_counts": {
|
|
"exact": sum(item.mapping_status == "exact" for item in package.artifacts),
|
|
"approximate": sum(item.mapping_status == "approximate" for item in package.artifacts),
|
|
"unsupported": sum(item.mapping_status == "unsupported" for item in package.artifacts),
|
|
},
|
|
"plan": plan,
|
|
"notes": package.notes,
|
|
}
|
|
)
|
|
|
|
|
|
def publish_to_stripe_shadow(
|
|
product: Product,
|
|
models: list[PricingModel],
|
|
data_dir: Path,
|
|
*,
|
|
model_id: str | None = None,
|
|
state_path: Path | None = None,
|
|
) -> dict[str, Any]:
|
|
path = state_path or default_stripe_state_path(data_dir)
|
|
model = _target_model(models, product, model_id)
|
|
bundle = build_publication_bundle(_catalog_product(product), model)
|
|
package = map_bundle_to_stripe(bundle)
|
|
current_state = load_stripe_publication_state(path)
|
|
result = apply_publication(package, current_state)
|
|
save_stripe_publication_state(path, result.state)
|
|
|
|
return _serialize(
|
|
{
|
|
"provider": "stripe",
|
|
"state_path": str(path),
|
|
"model_id": model.id,
|
|
"model_name": model.name,
|
|
"result": result,
|
|
}
|
|
)
|
|
|
|
|
|
def rollback_stripe_shadow(
|
|
data_dir: Path,
|
|
revision_id: str,
|
|
*,
|
|
state_path: Path | None = None,
|
|
) -> dict[str, Any]:
|
|
path = state_path or default_stripe_state_path(data_dir)
|
|
current_state = load_stripe_publication_state(path)
|
|
result = rollback_publication(current_state, revision_id)
|
|
save_stripe_publication_state(path, result.state)
|
|
return _serialize(
|
|
{
|
|
"provider": "stripe",
|
|
"state_path": str(path),
|
|
"result": result,
|
|
}
|
|
)
|