3
3
ref. https://github.com/tadata-org/fastapi_mcp/blob/v0.3.4/examples/shared/apps/items.py
4
4
"""
5
5
6
- import random
7
6
import uuid
8
7
from os import getenv
9
8
10
9
from azure .monitor .opentelemetry import configure_azure_monitor
11
- from fastapi import FastAPI , HTTPException , Query
10
+ from fastapi import FastAPI
12
11
from opentelemetry .instrumentation .fastapi import FastAPIInstrumentor
13
12
from opentelemetry .trace import Span
14
- from pydantic import BaseModel
15
13
16
- from template_fastapi .opentelemetry import get_meter , get_tracer
14
+ from template_fastapi .routers import demo , games , items
17
15
18
- tracer = get_tracer (__name__ )
19
- meter = get_meter (__name__ )
20
16
app = FastAPI ()
21
17
22
18
# If APPLICATIONINSIGHTS_CONNECTION_STRING exists, configure Azure Monitor
@@ -39,201 +35,7 @@ def server_request_hook(span: Span, scope: dict):
39
35
)
40
36
FastAPIInstrumentor .instrument_app (app )
41
37
42
-
43
- class Item (BaseModel ):
44
- id : int
45
- name : str
46
- description : str | None = None
47
- price : float
48
- tags : list [str ] = []
49
-
50
-
51
- items_db : dict [int , Item ] = {}
52
-
53
-
54
- @app .get ("/items/" , response_model = list [Item ], tags = ["items" ], operation_id = "list_items" )
55
- async def list_items (skip : int = 0 , limit : int = 10 ):
56
- """
57
- List all items in the database.
58
-
59
- Returns a list of items, with pagination support.
60
- """
61
- return list (items_db .values ())[skip : skip + limit ]
62
-
63
-
64
- @app .get ("/items/{item_id}" , response_model = Item , tags = ["items" ], operation_id = "get_item" )
65
- async def read_item (item_id : int ):
66
- """
67
- Get a specific item by its ID.
68
-
69
- Raises a 404 error if the item does not exist.
70
- """
71
- if item_id not in items_db :
72
- raise HTTPException (status_code = 404 , detail = "Item not found" )
73
- return items_db [item_id ]
74
-
75
-
76
- @app .post ("/items/" , response_model = Item , tags = ["items" ], operation_id = "create_item" )
77
- async def create_item (item : Item ):
78
- """
79
- Create a new item in the database.
80
-
81
- Returns the created item with its assigned ID.
82
- """
83
- items_db [item .id ] = item
84
- return item
85
-
86
-
87
- @app .put ("/items/{item_id}" , response_model = Item , tags = ["items" ], operation_id = "update_item" )
88
- async def update_item (item_id : int , item : Item ):
89
- """
90
- Update an existing item.
91
-
92
- Raises a 404 error if the item does not exist.
93
- """
94
- if item_id not in items_db :
95
- raise HTTPException (status_code = 404 , detail = "Item not found" )
96
-
97
- item .id = item_id
98
- items_db [item_id ] = item
99
- return item
100
-
101
-
102
- @app .delete ("/items/{item_id}" , tags = ["items" ], operation_id = "delete_item" )
103
- async def delete_item (item_id : int ):
104
- """
105
- Delete an item from the database.
106
-
107
- Raises a 404 error if the item does not exist.
108
- """
109
- if item_id not in items_db :
110
- raise HTTPException (status_code = 404 , detail = "Item not found" )
111
-
112
- del items_db [item_id ]
113
- return {"message" : "Item deleted successfully" }
114
-
115
-
116
- @app .get ("/items/search/" , response_model = list [Item ], tags = ["search" ], operation_id = "search_items" )
117
- async def search_items (
118
- q : str | None = Query (None , description = "Search query string" ),
119
- min_price : float | None = Query (None , description = "Minimum price" ),
120
- max_price : float | None = Query (None , description = "Maximum price" ),
121
- tags : list [str ] = Query ([], description = "Filter by tags" ),
122
- ):
123
- """
124
- Search for items with various filters.
125
-
126
- Returns a list of items that match the search criteria.
127
- """
128
- results = list (items_db .values ())
129
-
130
- if q :
131
- q = q .lower ()
132
- results = [
133
- item
134
- for item in results
135
- if q in item .name .lower () or (item .description is not None and q in item .description .lower ())
136
- ]
137
-
138
- if min_price is not None :
139
- results = [item for item in results if item .price >= min_price ]
140
- if max_price is not None :
141
- results = [item for item in results if item .price <= max_price ]
142
-
143
- if tags :
144
- results = [item for item in results if all (tag in item .tags for tag in tags )]
145
-
146
- return results
147
-
148
-
149
- sample_items = [
150
- Item (id = 1 , name = "Hammer" , description = "A tool for hammering nails" , price = 9.99 , tags = ["tool" , "hardware" ]),
151
- Item (id = 2 , name = "Screwdriver" , description = "A tool for driving screws" , price = 7.99 , tags = ["tool" , "hardware" ]),
152
- Item (id = 3 , name = "Wrench" , description = "A tool for tightening bolts" , price = 12.99 , tags = ["tool" , "hardware" ]),
153
- Item (id = 4 , name = "Saw" , description = "A tool for cutting wood" , price = 19.99 , tags = ["tool" , "hardware" , "cutting" ]),
154
- Item (id = 5 , name = "Drill" , description = "A tool for drilling holes" , price = 49.99 , tags = ["tool" , "hardware" , "power" ]),
155
- ]
156
- for item in sample_items :
157
- items_db [item .id ] = item
158
-
159
-
160
- # Add flaky API which receives percentage of failure
161
- @app .get ("/flaky/{failure_rate}" , tags = ["flaky" ], operation_id = "flaky" )
162
- async def flaky (failure_rate : int ):
163
- """
164
- A flaky endpoint that simulates a failure based on the provided failure rate.
165
-
166
- The failure rate is a percentage (0-100) that determines the likelihood of failure.
167
- """
168
- if not (0 <= failure_rate <= 100 ):
169
- raise HTTPException (
170
- status_code = 400 ,
171
- detail = "Failure rate must be between 0 and 100" ,
172
- )
173
-
174
- if random .randint (0 , 100 ) < failure_rate :
175
- raise HTTPException (
176
- status_code = 500 ,
177
- detail = "Simulated failure" ,
178
- )
179
-
180
- return {
181
- "message" : "Request succeeded" ,
182
- }
183
-
184
-
185
- # Add flaky API which raises an exception
186
- @tracer .start_as_current_span ("flaky_exception" )
187
- @app .get ("/flaky/exception" , tags = ["flaky" ], operation_id = "flaky_exception" )
188
- async def flaky_exception ():
189
- """
190
- A flaky endpoint that always raises an exception.
191
- """
192
- raise HTTPException (
193
- status_code = 500 ,
194
- detail = "Simulated exception" ,
195
- )
196
-
197
-
198
- # Add a heavy synchronous endpoint which receives milliseconds to sleep
199
- @app .get ("/heavy_sync/{sleep_ms}" , tags = ["heavy" ], operation_id = "heavy_sync_with_sleep" )
200
- async def heavy_sync_with_sleep (sleep_ms : int ):
201
- """
202
- A heavy synchronous endpoint that sleeps for the specified number of milliseconds.
203
-
204
- This simulates a long-running synchronous operation.
205
- """
206
- if sleep_ms < 0 :
207
- raise HTTPException (
208
- status_code = 400 ,
209
- detail = "Sleep time must be a non-negative integer" ,
210
- )
211
-
212
- import time
213
-
214
- with tracer .start_as_current_span ("parent" ):
215
- print (f"Sleeping for { sleep_ms } milliseconds" )
216
- time .sleep (sleep_ms / 1000.0 )
217
- with tracer .start_as_current_span ("child" ):
218
- print ("Child span" )
219
- return {
220
- "message" : f"Slept for { sleep_ms } milliseconds" ,
221
- }
222
-
223
-
224
- # Add counter for dice rolls
225
- roll_counter = meter .create_counter (
226
- "dice.rolls" ,
227
- description = "The number of rolls by roll value" ,
228
- )
229
-
230
-
231
- @app .get ("/roll_dice" , operation_id = "roll_dice" )
232
- async def roll_dice ():
233
- """
234
- Simulate rolling a dice and record the roll in the meter.
235
- """
236
- with tracer .start_as_current_span ("roll_dice" ):
237
- roll = random .randint (1 , 6 )
238
- roll_counter .add (1 , {"roll.value" : str (roll )})
239
- return roll
38
+ # Include routers
39
+ app .include_router (items .router )
40
+ app .include_router (demo .router )
41
+ app .include_router (games .router )
0 commit comments