基于 FastAPI 和 OpenFeature 使用 Feature Flag 控制业务功能

模拟业务场景:多租户系统跨域转账,需要控制某租户下某用户是否可以在某域转账

open_feature_util.py

import typing
from abc import abstractmethod, ABCMeta
from typing import Sequencefrom openfeature.evaluation_context import EvaluationContext
from openfeature.flag_evaluation import FlagValueType, FlagResolutionDetails
from openfeature.provider import AbstractProviderclass AsyncProvider(AbstractProvider, metaclass=ABCMeta):def resolve_boolean_details(self,flag_key: str,default_value: bool,evaluation_context: typing.Optional[EvaluationContext] = None,) -> FlagResolutionDetails[bool]:raise RuntimeError("Not allow calling sync method in AsyncProvider")@abstractmethodasync def resolve_boolean_details_async(self,flag_key: str,default_value: bool,evaluation_context: typing.Optional[EvaluationContext] = None,) -> FlagResolutionDetails[bool]:passdef resolve_string_details(self,flag_key: str,default_value: str,evaluation_context: typing.Optional[EvaluationContext] = None,) -> FlagResolutionDetails[str]:raise RuntimeError("Not allow calling sync method in AsyncProvider")@abstractmethodasync def resolve_string_details_async(self,flag_key: str,default_value: str,evaluation_context: typing.Optional[EvaluationContext] = None,) -> FlagResolutionDetails[str]:passdef resolve_integer_details(self,flag_key: str,default_value: int,evaluation_context: typing.Optional[EvaluationContext] = None,) -> FlagResolutionDetails[int]:raise RuntimeError("Not allow calling sync method in AsyncProvider")@abstractmethodasync def resolve_integer_details_async(self,flag_key: str,default_value: int,evaluation_context: typing.Optional[EvaluationContext] = None,) -> FlagResolutionDetails[int]:passdef resolve_float_details(self,flag_key: str,default_value: float,evaluation_context: typing.Optional[EvaluationContext] = None,) -> FlagResolutionDetails[float]:raise RuntimeError("Not allow calling sync method in AsyncProvider")@abstractmethodasync def resolve_float_details_async(self,flag_key: str,default_value: float,evaluation_context: typing.Optional[EvaluationContext] = None,) -> FlagResolutionDetails[float]:passdef resolve_object_details(self,flag_key: str,default_value: typing.Union[Sequence[FlagValueType], typing.Mapping[str, FlagValueType]],evaluation_context: typing.Optional[EvaluationContext] = None,) -> FlagResolutionDetails[typing.Union[Sequence[FlagValueType], typing.Mapping[str, FlagValueType]]]:raise RuntimeError("Not allow calling sync method in AsyncProvider")

provider.py

import typingfrom openfeature.evaluation_context import EvaluationContext
from openfeature.flag_evaluation import FlagResolutionDetails
from openfeature.provider import Metadatafrom open_feature_util import AsyncProviderclass MyProvider(AsyncProvider):def get_metadata(self) -> Metadata:return Metadata(name="my_provider")async def resolve_boolean_details_async(self,flag_key: str,default_value: bool,evaluation_context: typing.Optional[EvaluationContext] = None,) -> FlagResolutionDetails[bool]:tenant_id = evaluation_context.attributes.get("tenant_id")user_id = evaluation_context.attributes.get("user_id")if flag_key.startswith("cross_domain_transfer_domain_"):domain = flag_key.split("_", )[-1]# todo 查询业务规则# 使用规则评估flag,假定目前只开放 租户id 为 1 的用户id 为 1、2、3 的用户可以进行 A、B、C 域的功能,其他均关闭if not tenant_id or not user_id or tenant_id not in (1,) or user_id not in (1, 2, 3):return FlagResolutionDetails(value=False, error_message="该功能暂未开放或您暂未被授权体验~")if domain not in ("A", "B", "C"):return FlagResolutionDetails(value=False,error_message=f"账户域({domain})转账功能暂未开放或您暂未被授权体验~")else:return FlagResolutionDetails(value=False, error_message="无此功能~")return FlagResolutionDetails(value=True)async def resolve_string_details_async(self,flag_key: str,default_value: str,evaluation_context: typing.Optional[EvaluationContext] = None,) -> FlagResolutionDetails[str]:passasync def resolve_integer_details_async(self,flag_key: str,default_value: int,evaluation_context: typing.Optional[EvaluationContext] = None,) -> FlagResolutionDetails[int]:passasync def resolve_float_details_async(self,flag_key: str,default_value: float,evaluation_context: typing.Optional[EvaluationContext] = None,) -> FlagResolutionDetails[float]:pass

main.py

import decimal
from contextlib import asynccontextmanager
from dataclasses import asdict
from typing import Listimport uvicorn
from fastapi import FastAPI, Depends, Header, Body, Request
from openfeature import api
from openfeature.evaluation_context import EvaluationContext
from openfeature.flag_evaluation import FlagEvaluationDetails
from pydantic import BaseModel, Field
from starlette.responses import JSONResponsefrom provider import MyProvider@asynccontextmanager
async def lifespan(application: FastAPI):# 注册 Providerapi.set_provider(MyProvider())yieldapp = FastAPI(lifespan=lifespan)class FlagEvaluationException(Exception):pass@app.exception_handler(FlagEvaluationException)
async def flag_disabled_handler(request: Request, exc: FlagEvaluationException):return JSONResponse(content={"ok": False, "msg": str(exc)})class Transfer(BaseModel):demain: str = Field(..., description="账户域,A、B、C")account_id: str = Field(..., description="账户ID")amount: decimal.Decimal = Field(..., description="转账金额")operation: str = Field(..., description="操作类型,in-转入,out-转出")class Transaction(BaseModel):tx_id: str = Field(..., description="Transaction id")transfers: List[Transfer] = Field(..., description="List of transaction transfers")async def get_of_client():return api.get_client()async def check_by_flag(tenant_id: int = Header(..., alias="X-Tenant"),user_id: int = Header(..., alias="X-User"),raw: Transaction = Body(...),of_client=Depends(get_of_client),
) -> Transaction:ec = EvaluationContext(attributes={"tenant_id": tenant_id, "user_id": user_id})for tr in raw.transfers:flag_key = f"cross_domain_transfer_domain_{tr.demain}"detail: FlagEvaluationDetails[bool] = await of_client.get_boolean_details_async(flag_key, default_value=False, evaluation_context=ec)if not detail.value:raise FlagEvaluationException(detail.error_message)return raw@app.post("/cross_domain_transfer")
async def transfer(tenant_id: int = Header(..., alias="X-Tenant"),user_id: int = Header(..., alias="X-User"),transaction: Transaction = Depends(check_by_flag),
):# TODO: 真正转账逻辑print(tenant_id, user_id, transaction.transfers)return {"ok": True, "msg": "成功"}@app.post("/feature_flags")
async def transfer(tenant_id: int = Header(..., alias="X-Tenant"),user_id: int = Header(..., alias="X-User"),flags: list[str] = Body(...),of_client=Depends(get_of_client),
):flag_res = {}ec = EvaluationContext(attributes={"tenant_id": tenant_id, "user_id": user_id})for flag in flags:detail: FlagEvaluationDetails[bool] = await of_client.get_boolean_details_async(flag, default_value=False, evaluation_context=ec)flag_res[flag] = asdict(detail)return {"ok": True, "msg": "成功", "res": flag_res}if __name__ == '__main__':uvicorn.run(app, host="0.0.0.0", port=8000, log_level="debug", use_colors=True)
POST 127.0.0.1:8000/cross_domain_transfer
X-Tenant 1
X-User 2
{"tx_id": "1234567890","transfers": [{"demain": "A","account_id": "1246","amount": "6666","operation": "out"},{"demain": "B","account_id": "345345","amount": "6600","operation": "in"},{"demain": "D","account_id": "23423","amount": "66","operation": "in"}]
}
POST 127.0.0.1:8000/feature_flags
X-Tenant 1
X-User 2
["sdf"]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/94347.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/94347.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Stm32通过ESP8266 WiFi连接阿里云平台

本文将介绍stm32如何通过WiFi来连接阿里云,上传数据和接收指令。要先与阿里云建立TCP连接,然后再通过MQTT协议交互。 大体流程:1、在阿里云网页上创建产品和设备;2、stm32通过WiFi连接云平台;3、MQTT连接阿里云&#…

北京-测试-入职甲方金融-上班第三天

今日上班时间9-20.18,再加42分钟就可以拿到75块钱了,但我想回家,所以下班今天上午有人事举办的入职培训,下午有业务培训,培训完领导给我安排了两个需求。慌死,吓死,我都不懂,业务和工…

Java基础第2天总结

使用switch时注意事项:表达式类型只能是byte、short、int、char,JDK5开始支持枚举,JDK7开始支持String,不支持double、float、long(精确度问题,小数有点不精确)。case给出的值不允许重复,且只能是字面量,不…

鸿蒙开发中的List组件详解

目录 引言 1.List组件基础 2.List接口参数 1.space 2.initialIndex 3.scroller 3.ListView的属性 1.listDirection 2.lanes 3.divider 4.scrollBar 4.布局与约束 5.ListItem生命周期 1.使用ForEach创建ListItem 2.使用LazyForEach创建ListItem 3…

2026界计算机专业毕业的有福了!(开题报告任务书)

开题报告 我们以基于Java的婚纱店管理系统为案例进行指导。 任务书: 首先是毕设的立题依据,这个主要描写一些简洁大体的大白话,描述一下你为什么要做这个题目的毕设。 那就需要你描述一下现阶段社会面婚纱店的运营情况,写一些…

安全、高效、可靠的物理隔离网络安全专用设备———信刻光盘安全隔离与文件单向导入系统!

着各种数据传输、储存技术、信息技术的快速发展,保护信息安全是重中之重。军工企业、政府、部队及企事业单位等利用A网与B网开展导入/导出相关工作已成为不可逆转的趋势。针对于业务需要与保密规范相关要求,涉及重要秘密信息,需做到完全的物理…

JetPack 与 PyTorch 版本对应及资源详情

下载链接 JetPack 版本适配 PyTorch 版本发布日期可下载资源(.whl 安装包 / 文档)JP 6.1PyTorch 2.5.0a0(构建号:872d972e41.nv24.08.17622132)2024/10/01- torch-2.5.0a0872d972e41.nv24.08.17622132-cp310-cp310-li…

【c++进阶系列】:万字详解多态

🔥 本文专栏:c 🌸作者主页:努力努力再努力wz 💪 今日博客励志语录: 你以为自己在孤独地爬坡吗?看看身后吧——那些被汗水浸湿的脚印,早已连成一道向上的阶梯 ★★★ 本文前置知识&am…

AI+预测3D新模型百十个定位预测+胆码预测+去和尾2025年8月23日第168弹

从今天开始,咱们还是暂时基于旧的模型进行预测,好了,废话不多说,按照老办法,重点8-9码定位,配合三胆下1或下2,杀1-2个和尾,再杀4-5个和值,可以做到100-300注左右。(1)定位…

分布式搜索(Elasticsearch)深入用法

目录 数据聚合 聚合的种类 DSL实现聚合 桶聚合 度量聚合 RestAPI实现聚合 多条件聚合 自动补全 拼音分词器 自定义分词器 自动补全查询 实现搜索框自动补全 数据同步 数据同步思路分析 实现elasticsearch与数据库数据同步 集群 搭建ES集群 集群脑裂问题 集群…

java:接口与实现类

1. Java 基础层面接口(Interface) 只是方法的定义/规范,里面没有真正的逻辑。例如:public interface IBookService {boolean save(Book book); }👉 这里只说明了:我要有一个 save 方法,但“怎么…

Chrome 插件开发实战:从入门到进阶

1.1 Chrome 插件的魅力与应用场景Chrome 插件是增强 Chrome 浏览器功能的得力助手,能实现广告拦截、密码管理、标签管理等实用功能。在日常办公中,我们可以借助插件提升效率,如自动填充表单、快速保存网页内容;在浏览网页时&#…

通过官方文档详解Ultralytics YOLO 开源工程-熟练使用 YOLO11实现分割、分类、旋转框检测和姿势估计(附测试代码)

目录 前言: 1.了解ultralytics工程与yolo模型 1.1 yolo11可以为我们做些什 1.2 yolo11模型的高性能 1.3 对于yolo11一些常见的问题 1.3.1 YOLO11 如何以更少的参数实现更高的精度? 1.3.2 YOLO11 可以部署在边缘设备上吗? 2. 深入了解y…

vue实现小程序oss分片上传

随着小程序越来越普及,小程序上传文件必不可少,那么上传的文件大小就不可控了,小则几mb,大到好几百mb,小文件还可以,但是一到超过200mb或稍微再大些的小程序就很容易上传失败,导致功能不能继续进行。以下我们就来解决这个问题,将大文件实现分片上传 温馨提示,不要看内…

14.Shell脚本修炼手册--玩转循环结构(While 与 Until 的应用技巧与案例)

while 循环和 until 循环的应用实践 文章目录while 循环和 until 循环的应用实践当型和直到型循环:两种 "重复" 的逻辑while 循环:满足条件就继续until 循环:不满足条件就继续基础示例:从简单场景学用法示例 1&#xff…

chromadb使用hugging face模型时利用镜像网站下载注意事项

chromadb默认使用sentence-transformers/all-MiniLM-L6-v2的词嵌入(词向量)模型,如果在程序首次运行时,collection的add或query操作时如果没有指定embeddings或query_embeddings,程序会自动下载相关嵌入向量模型&#…

基于大模型的对话式推荐系统技术架构设计

注:此文章内容均节选自充电了么创始人,CEO兼CTO陈敬雷老师的新书《GPT多模态大模型与AI Agent智能体》(跟我一起学人工智能)【陈敬雷编著】【清华大学出版社】 清华《GPT多模态大模型与AI Agent智能体》书籍配套视频课程【陈敬雷…

第1章 React组件开发基础

在掌握React开发之前,我们需要先建立扎实的组件开发基础。这些基础知识不仅影响你的开发效率,更决定了应用程序的性能、可维护性和团队协作的顺畅程度。 本章将深入探讨React组件开发的核心技巧,从JSX语法优化到组件架构设计,帮你建立正确的React开发思维模式。 🗂️ 本…

【yocto】Yocto Project 配置层(.conf)文件语法详解

【加关注,不迷路,持续输出中...】Yocto Project 是一个开源的嵌入式 Linux 系统构建框架,其核心是通过元数据(Metadata)来定义如何构建系统。这些元数据主要包括配方(.bb / .bbappend)、配置&am…

知识蒸馏 Knowledge Distillation 序列的联合概率 分解成 基于历史的条件概率的连乘序列

知识蒸馏 Knowledge Distillation 序列的联合概率 分解成 基于历史的条件概率的连乘序列 flyfish 代码实践 论文 Generalized Knowledge Distillation (GKD) On-Policy Distillation of Language Models: Learning from Self-Generated Mistakes 自回归分解 将 “序列的联合…