Skip to content

Commit 707d185

Browse files
authored
✨ Enhancements for product-owner users and invitations (#4862)
1 parent f2e6292 commit 707d185

File tree

35 files changed

+643
-232
lines changed

35 files changed

+643
-232
lines changed

.env-devel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ S3_SECURE=0
117117
SCICRUNCH_API_BASE_URL=https://scicrunch.org/api/1
118118
SCICRUNCH_API_KEY=REPLACE_ME_with_valid_api_key
119119

120-
SMTP_HOST=mail.speag.com
120+
SMTP_HOST=fake.mail.server.com
121121
SMTP_PORT=25
122122
SMTP_USERNAME=it_doesnt_matter
123123
SMTP_PASSWORD=it_doesnt_matter

api/specs/web-server/_products.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55
# pylint: disable=unused-variable
66

77

8-
from fastapi import APIRouter
8+
from typing import Annotated
9+
10+
from fastapi import APIRouter, Depends
911
from models_library.api_schemas_webserver.product import (
10-
CreditPriceGet,
1112
GenerateInvitation,
13+
GetCreditPrice,
14+
GetProduct,
1215
InvitationGenerated,
1316
)
1417
from models_library.generics import Envelope
1518
from simcore_service_webserver._meta import API_VTAG
19+
from simcore_service_webserver.products._handlers import _ProductsRequestParams
1620

1721
router = APIRouter(
1822
prefix=f"/{API_VTAG}",
@@ -24,12 +28,20 @@
2428

2529
@router.get(
2630
"/credits-price",
27-
response_model=Envelope[CreditPriceGet],
31+
response_model=Envelope[GetCreditPrice],
2832
)
2933
async def get_current_product_price():
3034
...
3135

3236

37+
@router.get(
38+
"/products/{product_name}",
39+
response_model=Envelope[GetProduct],
40+
)
41+
async def get_product(_params: Annotated[_ProductsRequestParams, Depends()]):
42+
...
43+
44+
3345
@router.post(
3446
"/invitation:generate",
3547
response_model=Envelope[InvitationGenerated],

packages/models-library/src/models_library/api_schemas_webserver/product.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from ._base import InputSchema, OutputSchema
1010

1111

12-
class CreditPriceGet(OutputSchema):
12+
class GetCreditPrice(OutputSchema):
1313
product_name: str
1414
usd_per_credit: NonNegativeDecimal | None = Field(
1515
...,
@@ -26,18 +26,40 @@ class Config(OutputSchema.Config):
2626
}
2727

2828

29+
class GetProduct(OutputSchema):
30+
name: ProductName
31+
display_name: str
32+
short_name: str | None = Field(
33+
default=None, description="Short display name for SMS"
34+
)
35+
36+
vendor: dict | None = Field(default=None, description="vendor attributes")
37+
issues: list[dict] | None = Field(
38+
default=None, description="Reference to issues tracker"
39+
)
40+
manuals: list[dict] | None = Field(default=None, description="List of manuals")
41+
support: list[dict] | None = Field(
42+
default=None, description="List of support resources"
43+
)
44+
45+
login_settings: dict
46+
max_open_studies_per_user: PositiveInt | None
47+
is_payment_enabled: bool
48+
credits_per_usd: NonNegativeDecimal | None
49+
50+
2951
class GenerateInvitation(InputSchema):
3052
guest: LowerCaseEmailStr
3153
trial_account_days: PositiveInt | None = None
32-
extra_credits: PositiveInt | None = None
54+
extra_credits_in_usd: PositiveInt | None = None
3355

3456

3557
class InvitationGenerated(OutputSchema):
3658
product_name: ProductName
3759
issuer: LowerCaseEmailStr
3860
guest: LowerCaseEmailStr
3961
trial_account_days: PositiveInt | None = None
40-
extra_credits: PositiveInt | None = None
62+
extra_credits_in_usd: PositiveInt | None = None
4163
created: datetime
4264
invitation_link: HttpUrl
4365

@@ -49,7 +71,7 @@ class Config(OutputSchema.Config):
4971
"issuer": "john.doe@email.com",
5072
"guest": "guest@example.com",
5173
"trialAccountDays": 7,
52-
"extraCredits": 30,
74+
"extraCreditsInUsd": 30,
5375
"created": "2023-09-27T15:30:00",
5476
"invitationLink": "https://example.com/invitation#1234",
5577
},

packages/models-library/src/models_library/invitations.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ class InvitationInputs(BaseModel):
2323
description="If set, this invitation will activate a trial account."
2424
"Sets the number of days from creation until the account expires",
2525
)
26-
extra_credits: PositiveInt | None = Field(
26+
extra_credits_in_usd: PositiveInt | None = Field(
2727
None,
28-
description="If set, the account's primary wallet will add these extra credits",
28+
description="If set, the account's primary wallet will add extra credits corresponding to this ammount in USD",
2929
)
3030

3131

packages/postgres-database/src/simcore_postgres_database/models/products_prices.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,6 @@
4949
"usd_per_credit >= 0", name="non_negative_usd_per_credit_constraint"
5050
),
5151
)
52+
53+
54+
__all__: tuple[str, ...] = ("NUMERIC_KWARGS",)

packages/postgres-database/src/simcore_postgres_database/utils_products_prices.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
from decimal import Decimal
2+
from typing import Final
23

34
import sqlalchemy as sa
45
from aiopg.sa.connection import SAConnection
5-
from simcore_postgres_database.models.products_prices import products_prices
6+
from simcore_postgres_database.models.products_prices import (
7+
NUMERIC_KWARGS,
8+
products_prices,
9+
)
10+
11+
QUANTIZE_EXP_ARG: Final = Decimal(f"1e-{NUMERIC_KWARGS['scale']}")
612

713

814
async def get_product_latest_credit_price_or_none(
@@ -16,7 +22,7 @@ async def get_product_latest_credit_price_or_none(
1622
.limit(1)
1723
)
1824
if usd_per_credit is not None:
19-
return Decimal(usd_per_credit)
25+
return Decimal(usd_per_credit).quantize(QUANTIZE_EXP_ARG)
2026
return None
2127

2228

packages/pytest-simcore/src/pytest_simcore/helpers/utils_assert.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
33
"""
44
from pprint import pformat
5-
from typing import Optional
65

76
from aiohttp import ClientResponse
87
from aiohttp.web import HTTPError, HTTPException, HTTPInternalServerError, HTTPNoContent
@@ -12,10 +11,10 @@
1211
async def assert_status(
1312
response: ClientResponse,
1413
expected_cls: type[HTTPException],
15-
expected_msg: Optional[str] = None,
16-
expected_error_code: Optional[str] = None,
17-
include_meta: Optional[bool] = False,
18-
include_links: Optional[bool] = False,
14+
expected_msg: str | None = None,
15+
expected_error_code: str | None = None,
16+
include_meta: bool | None = False,
17+
include_links: bool | None = False,
1918
) -> tuple[dict, ...]:
2019
"""
2120
Asserts for enveloped responses
@@ -34,9 +33,8 @@ async def assert_status(
3433
assert not data, pformat(data)
3534
assert not error, pformat(error)
3635
else:
37-
# with a 200, data may still be empty see
38-
# https://medium.com/@santhoshkumarkrishna/http-get-rest-api-no-content-404-vs-204-vs-200-6dd869e3af1d
39-
# assert data is not None, pformat(data)
36+
# with a 200, data may still be empty so we cannot 'assert data is not None'
37+
# SEE https://medium.com/@santhoshkumarkrishna/http-get-rest-api-no-content-404-vs-204-vs-200-6dd869e3af1d
4038
assert not error, pformat(error)
4139

4240
if expected_msg:
@@ -56,7 +54,7 @@ async def assert_status(
5654
async def assert_error(
5755
response: ClientResponse,
5856
expected_cls: type[HTTPException],
59-
expected_msg: Optional[str] = None,
57+
expected_msg: str | None = None,
6058
):
6159
data, error = unwrap_envelope(await response.json())
6260
return do_assert_error(data, error, expected_cls, expected_msg)
@@ -66,8 +64,8 @@ def do_assert_error(
6664
data,
6765
error,
6866
expected_cls: type[HTTPException],
69-
expected_msg: Optional[str] = None,
70-
expected_error_code: Optional[str] = None,
67+
expected_msg: str | None = None,
68+
expected_error_code: str | None = None,
7169
):
7270
assert not data, pformat(data)
7371
assert error, pformat(error)

packages/pytest-simcore/src/pytest_simcore/helpers/utils_login.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,15 @@ def __init__(
126126
guest_email: str | None = None,
127127
host: dict | None = None,
128128
trial_days: int | None = None,
129-
extra_credits: int | None = None,
129+
extra_credits_in_usd: int | None = None,
130130
):
131131
assert client.app
132132
super().__init__(params=host, app=client.app)
133133
self.client = client
134134
self.tag = f"Created by {guest_email or FAKE.email()}"
135135
self.confirmation = None
136136
self.trial_days = trial_days
137-
self.extra_credits = extra_credits
137+
self.extra_credits_in_usd = extra_credits_in_usd
138138

139139
async def __aenter__(self) -> "NewInvitation":
140140
# creates host user
@@ -148,7 +148,7 @@ async def __aenter__(self) -> "NewInvitation":
148148
user_email=self.user["email"],
149149
tag=self.tag,
150150
trial_days=self.trial_days,
151-
extra_credits=self.extra_credits,
151+
extra_credits_in_usd=self.extra_credits_in_usd,
152152
)
153153
return self
154154

services/invitations/openapi.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,11 @@
188188
"description": "If set, this invitation will activate a trial account.Sets the number of days from creation until the account expires",
189189
"minimum": 0
190190
},
191-
"extra_credits": {
192-
"title": "Extra Credits",
191+
"extra_credits_in_usd": {
192+
"title": "Extra Credits In Usd",
193193
"exclusiveMinimum": true,
194194
"type": "integer",
195-
"description": "If set, the account's primary wallet will add these extra credits",
195+
"description": "If set, the account's primary wallet will add extra credits corresponding to this ammount in USD",
196196
"minimum": 0
197197
},
198198
"created": {
@@ -240,11 +240,11 @@
240240
"description": "If set, this invitation will activate a trial account.Sets the number of days from creation until the account expires",
241241
"minimum": 0
242242
},
243-
"extra_credits": {
244-
"title": "Extra Credits",
243+
"extra_credits_in_usd": {
244+
"title": "Extra Credits In Usd",
245245
"exclusiveMinimum": true,
246246
"type": "integer",
247-
"description": "If set, the account's primary wallet will add these extra credits",
247+
"description": "If set, the account's primary wallet will add extra credits corresponding to this ammount in USD",
248248
"minimum": 0
249249
},
250250
"created": {
@@ -299,11 +299,11 @@
299299
"description": "If set, this invitation will activate a trial account.Sets the number of days from creation until the account expires",
300300
"minimum": 0
301301
},
302-
"extra_credits": {
303-
"title": "Extra Credits",
302+
"extra_credits_in_usd": {
303+
"title": "Extra Credits In Usd",
304304
"exclusiveMinimum": true,
305305
"type": "integer",
306-
"description": "If set, the account's primary wallet will add these extra credits",
306+
"description": "If set, the account's primary wallet will add extra credits corresponding to this ammount in USD",
307307
"minimum": 0
308308
}
309309
},

services/invitations/src/simcore_service_invitations/invitations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class Config:
6060
"trial_account_days": {
6161
"alias": "t",
6262
},
63-
"extra_credits": {
63+
"extra_credits_in_usd": {
6464
"alias": "e",
6565
},
6666
"created": {

0 commit comments

Comments
 (0)