基于 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
英文地址,請注明出處:http://en.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 自回歸分解 將 “序列的聯合…