Skip to content

Commit e6e8882

Browse files
committed
feat: modify field name notifications -> notification
Signed-off-by: ImMin5 <[email protected]>
1 parent 6fd4588 commit e6e8882

File tree

8 files changed

+76
-70
lines changed

8 files changed

+76
-70
lines changed

src/spaceone/cost_analysis/error/budget.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,21 @@ class ERROR_DATE_IS_WRONG(ERROR_INVALID_ARGUMENT):
3030

3131

3232
class ERROR_UNIT_IS_REQUIRED(ERROR_INVALID_ARGUMENT):
33-
_message = (
34-
"Unit is required for notifications (key = notifications, value = {value})"
35-
)
33+
_message = "Unit is required for notification (key = notification, value = {value})"
3634

3735

3836
class ERROR_NOTIFICATION_TYPE_IS_REQUIRED(ERROR_INVALID_ARGUMENT):
39-
_message = "Notification type is required for notifications (key = notifications, value = {value})"
37+
_message = "Notification type is required for notification (key = notification, value = {value})"
4038

4139

4240
class ERROR_THRESHOLD_IS_WRONG(ERROR_INVALID_ARGUMENT):
4341
_message = (
44-
"Threshold must be greater than zero. (key = notifications, value = {value})"
42+
"Threshold must be greater than zero. (key = notification, value = {value})"
4543
)
4644

4745

4846
class ERROR_THRESHOLD_IS_WRONG_IN_PERCENT_TYPE(ERROR_INVALID_ARGUMENT):
49-
_message = "In percentage type, the threshold must be less than 100. (key = notifications, value = {value})"
47+
_message = "In percentage type, the threshold must be less than 100. (key = notification, value = {value})"
5048

5149

5250
class ERROR_PROVIDER_FILTER_IS_EMPTY(ERROR_INVALID_ARGUMENT):

src/spaceone/cost_analysis/manager/budget_usage_manager.py

+12-6
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,12 @@ def notify_budget_usage(self, budget_vo: Budget) -> None:
114114
current_month = datetime.now(timezone.utc).strftime("%Y-%m")
115115
updated_plans = []
116116
is_changed = False
117-
notifications = budget_vo.notifications
117+
notification = budget_vo.notification
118118

119-
plans = notifications.plans or []
119+
plans = notification.plans or []
120120

121121
for plan in plans:
122-
if current_month not in plan.notified_months:
122+
if current_month not in budget_vo.notified_months:
123123
unit = plan.unit
124124
threshold = plan.threshold
125125
is_notify = False
@@ -206,8 +206,14 @@ def notify_budget_usage(self, budget_vo: Budget) -> None:
206206
updated_plans.append(plan.to_dict())
207207

208208
if is_changed:
209-
notifications.plans = updated_plans
210-
budget_vo.update({"notifications": notifications.to_dict()})
209+
notification.plans = updated_plans
210+
budget_vo.update({"notifications": notification.to_dict()})
211+
212+
def delete_budget_usage_by_budget_vo(self, budget_vo: Budget) -> None:
213+
budget_usage_vos = self.filter_budget_usages(
214+
budget_id=budget_vo.budget_id, domain_id=budget_vo.domain_id
215+
)
216+
budget_usage_vos.delete()
211217

212218
def _notify_message(
213219
self,
@@ -246,7 +252,7 @@ def _notify_message(
246252
budget_vo.domain_id,
247253
budget_vo.workspace_id,
248254
budget_vo.project_id,
249-
budget_vo.notifications.recipients.to_dict(),
255+
budget_vo.notification.recipients.to_dict(),
250256
service_account_id=budget_vo.service_account_id,
251257
)
252258

src/spaceone/cost_analysis/model/budget/database.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,13 @@ class PlannedLimit(EmbeddedDocument):
1111
class Plan(EmbeddedDocument):
1212
threshold = FloatField(required=True)
1313
unit = StringField(max_length=20, required=True, choices=["PERCENT", "ACTUAL_COST"])
14-
notified_months = ListField(StringField(max_length=10))
1514

1615
def to_dict(self):
1716
return dict(self.to_mongo())
1817

1918

2019
class Recipients(EmbeddedDocument):
2120
users = ListField(StringField(), default=[])
22-
role_types = ListField(StringField(), default=[])
23-
service_account_manager = StringField(
24-
max_length=40, choices=["ENABLED", "DISABLED"], null=True, default=None
25-
)
2621

2722
def to_dict(self):
2823
return dict(self.to_mongo())
@@ -46,7 +41,9 @@ class Budget(MongoModel):
4641
time_unit = StringField(max_length=20, choices=["TOTAL", "MONTHLY"])
4742
start = StringField(required=True, max_length=7)
4843
end = StringField(required=True, max_length=7)
49-
notifications = EmbeddedDocumentField(Notification)
44+
notification = EmbeddedDocumentField(Notification)
45+
notified_months = ListField(StringField(max_length=10))
46+
utilization_rate = FloatField(null=True, default=0)
5047
tags = DictField(default={})
5148
resource_group = StringField(
5249
max_length=40, choices=["WORKSPACE", "PROJECT"]
@@ -65,7 +62,11 @@ class Budget(MongoModel):
6562
"name",
6663
"limit",
6764
"planned_limits",
68-
"notifications",
65+
"start",
66+
"end",
67+
"notification",
68+
"notified_month",
69+
"utilization_rate",
6970
"tags",
7071
],
7172
"minimal_fields": [

src/spaceone/cost_analysis/model/budget/request.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class BudgetCreateRequest(BaseModel):
1414
time_unit: TimeUnit
1515
start: str
1616
end: str
17-
notifications: Union[dict, None] = None
17+
notification: Union[dict, None] = None
1818
tags: Union[dict, None] = None
1919
resource_group: ResourceGroup
2020
service_account_id: Union[str, None] = None
@@ -28,14 +28,16 @@ class BudgetUpdateRequest(BaseModel):
2828
name: Union[str, None] = None
2929
limit: Union[float, None] = None
3030
planned_limits: Union[list, None] = None
31+
start: Union[str, None] = None
32+
end: Union[str, None] = None
3133
tags: Union[dict, None] = None
3234
workspace_id: str
3335
domain_id: str
3436

3537

3638
class BudgetSetNotificationRequest(BaseModel):
3739
budget_id: str
38-
notifications: dict
40+
notification: dict
3941
project_id: Union[str, None] = None
4042
workspace_id: str
4143
domain_id: str

src/spaceone/cost_analysis/model/budget/response.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ class BudgetResponse(BaseModel):
3232
time_unit: Union[str, None] = None
3333
start: Union[str, None] = None
3434
end: Union[str, None] = None
35-
notifications: Union[Notification, dict] = None
35+
notification: Union[Notification, dict] = None
36+
utilization_rate: Union[float, None] = None
3637
tags: Union[dict, None] = None
3738
resource_group: Union[ResourceGroup, None] = None
3839
service_account_id: Union[str, None] = None

src/spaceone/cost_analysis/model/budget_usage/request.py

-25
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,6 @@
66
ResourceGroup = Literal["WORKSPACE", "PROJECT"]
77

88

9-
class BudgetCreateRequest(BaseModel):
10-
data_source_id: str
11-
name: str
12-
limit: Union[float, None] = None
13-
planned_limits: Union[list, None] = None
14-
currency: Union[str, None] = None
15-
time_unit: TimeUnit
16-
start: str
17-
end: str
18-
notifications: Union[dict, None] = None
19-
tags: Union[dict, None] = None
20-
resource_group: ResourceGroup
21-
service_account_id: Union[str, None] = None
22-
project_id: Union[str, None] = None
23-
workspace_id: Union[str, None] = None
24-
domain_id: str
25-
26-
27-
class BudgetDeleteRequest(BaseModel):
28-
budget_id: str
29-
workspace_id: str
30-
domain_id: str
31-
user_projects: Union[str, None] = None
32-
33-
349
class BudgetUsageSearchQueryRequest(BaseModel):
3510
query: dict
3611
workspace_id: Union[str, None] = None

src/spaceone/cost_analysis/service/budget_service.py

+45-20
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def create(self, params: BudgetCreateRequest) -> Union[BudgetResponse, dict]:
4343
'time_unit': 'str', # required
4444
'start': 'str', # required
4545
'end': 'str', # required
46-
'notifications': 'dict',
46+
'notification': 'dict',
4747
'tags': 'dict',
4848
'resource_group': 'str', # required
4949
'project_id': 'str',
@@ -65,14 +65,14 @@ def create(self, params: BudgetCreateRequest) -> Union[BudgetResponse, dict]:
6565
start = params.start
6666
end = params.end
6767

68-
notifications = params.notifications or {}
68+
notification = params.notification or {}
6969
resource_group = params.resource_group
7070

71-
self._check_time_period(start, end)
72-
7371
if resource_group != "PROJECT":
7472
raise ERROR_NOT_IMPLEMENTED()
7573

74+
self._check_time_period(start, end)
75+
7676
identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager")
7777
identity_mgr.check_workspace(workspace_id, domain_id)
7878
identity_mgr.get_project(project_id, domain_id)
@@ -101,8 +101,8 @@ def create(self, params: BudgetCreateRequest) -> Union[BudgetResponse, dict]:
101101
for planned_limit in planned_limits:
102102
params.limit += planned_limit.get("limit", 0)
103103

104-
# Check Notifications
105-
self._check_notifications(notifications, domain_id, workspace_id)
104+
# Check Notification
105+
self._check_notification(notification, domain_id, workspace_id)
106106

107107
# Check Duplicated Budget
108108
budget_vos = self.budget_mgr.filter_budgets(
@@ -144,6 +144,8 @@ def update(self, params: BudgetUpdateRequest) -> Union[BudgetResponse, dict]:
144144
'name': 'str',
145145
'limit': 'float',
146146
'planned_limits': 'list',
147+
'start': 'str',
148+
'end': 'str',
147149
'tags': 'dict'
148150
'workspace_id', 'str', # injected from auth (optional)
149151
'domain_id': 'str' # injected from auth
@@ -164,12 +166,28 @@ def update(self, params: BudgetUpdateRequest) -> Union[BudgetResponse, dict]:
164166
budget_id, domain_id, workspace_id
165167
)
166168

169+
start = budget_vo.start
170+
end = budget_vo.end
171+
172+
if params.start or params.end:
173+
start = params.start or start
174+
end = params.end or end
175+
self._check_time_period(start, end)
176+
planned_limits = planned_limits or budget_vo.planned_limits
177+
167178
# Check limit and Planned Limits
179+
if planned_limits:
180+
self._check_planned_limits(start, end, budget_vo.time_unit, planned_limits)
181+
168182
budget_vo = self.budget_mgr.update_budget_by_vo(
169183
params.dict(exclude_unset=True), budget_vo
170184
)
171185

172-
if "name" in params:
186+
# Update Budget Usages
187+
if planned_limits:
188+
budget_usage_mgr.delete_budget_usage_by_budget_vo(budget_vo)
189+
budget_usage_mgr.create_budget_usages(budget_vo)
190+
elif "name" in params:
173191
budget_usage_vos = budget_usage_mgr.filter_budget_usages(
174192
budget_id=budget_id,
175193
domain_id=domain_id,
@@ -197,7 +215,7 @@ def set_notification(
197215
Args:
198216
params (dict): {
199217
'budget_id': 'str',
200-
'notifications': 'dict',
218+
'notification': 'dict',
201219
'workspace_id': 'str',
202220
'domain_id': 'str'
203221
'user_projects': 'list'
@@ -210,15 +228,15 @@ def set_notification(
210228
project_id = params.project_id
211229
workspace_id = params.workspace_id
212230
domain_id = params.domain_id
213-
notifications = params.notifications or {}
231+
notification = params.notification or {}
214232

215233
budget_vo: Budget = self.budget_mgr.get_budget(
216234
budget_id, domain_id, workspace_id, project_id
217235
)
218236

219-
# Check Notifications
220-
self._check_notifications(notifications, domain_id, workspace_id)
221-
params.notifications = notifications
237+
# Check notification
238+
self._check_notification(notification, domain_id, workspace_id)
239+
params.notification = notification
222240

223241
budget_vo = self.budget_mgr.update_budget_by_vo(
224242
params.dict(exclude_unset=True), budget_vo
@@ -356,7 +374,14 @@ def _check_time_period(start, end):
356374
if start >= end:
357375
raise ERROR_INVALID_TIME_RANGE(start=start, end=end)
358376

359-
def _check_planned_limits(self, start, end, time_unit, planned_limits):
377+
def _check_planned_limits(
378+
self, start: str, end: str, time_unit: str, planned_limits: list
379+
):
380+
if time_unit == "TOTAL":
381+
raise ERROR_INVALID_PARAMETER(
382+
key="time_unit", value=f"Only MONTHLY time_unit is allowed"
383+
)
384+
360385
planned_limits_dict = self._convert_planned_limits_data_type(planned_limits)
361386
date_format = "%Y-%m"
362387

@@ -381,7 +406,7 @@ def _check_planned_limits(self, start, end, time_unit, planned_limits):
381406
raise ERROR_DATE_IS_WRONG(date=list(planned_limits_dict.keys()))
382407

383408
@staticmethod
384-
def _convert_planned_limits_data_type(planned_limits):
409+
def _convert_planned_limits_data_type(planned_limits: list) -> dict:
385410
planned_limits_dict = {}
386411

387412
for planned_limit in planned_limits:
@@ -398,12 +423,12 @@ def _convert_planned_limits_data_type(planned_limits):
398423
return planned_limits_dict
399424

400425
@staticmethod
401-
def _check_notifications(
402-
notifications: dict,
426+
def _check_notification(
427+
notification: dict,
403428
domain_id: str,
404429
workspace_id: str,
405430
) -> dict:
406-
plans = notifications.get("plans", [])
431+
plans = notification.get("plans", [])
407432

408433
for plan in plans:
409434
unit = plan["unit"]
@@ -420,7 +445,7 @@ def _check_notifications(
420445
raise ERROR_THRESHOLD_IS_WRONG_IN_PERCENT_TYPE(value=plan)
421446

422447
# check recipients
423-
recipients = notifications.get("recipients", {})
448+
recipients = notification.get("recipients", {})
424449
users = list(set(recipients.get("users", [])))
425450
role_types = list(set(recipients.get("role_types", [])))
426451

@@ -449,8 +474,8 @@ def _check_notifications(
449474
raise ERROR_NOT_FOUND(key="user_id", value=user_id)
450475
recipients["users"] = users
451476

452-
notifications["recipients"] = recipients
453-
return notifications
477+
notification["recipients"] = recipients
478+
return notification
454479

455480
@staticmethod
456481
def _set_user_project_or_project_group_filter(params: dict) -> dict:

src/spaceone/cost_analysis/service/budget_usage_service.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ class BudgetUsageService(BaseService):
1919

2020
def __init__(self, *args, **kwargs):
2121
super().__init__(*args, **kwargs)
22-
self.budget_usage_mgr: BudgetUsageManager = self.locator.get_manager(
23-
"BudgetUsageManager"
24-
)
22+
self.budget_usage_mgr = BudgetUsageManager()
2523

2624
@transaction(
2725
permission="cost-analysis:BudgetUsage.read",

0 commit comments

Comments
 (0)