Skip to content

Commit 87e750c

Browse files
committed
fix: Write user in transaction, enforce shop_listed already on add, add content-type header
1 parent 782e3d1 commit 87e750c

File tree

3 files changed

+28
-13
lines changed

3 files changed

+28
-13
lines changed

src/viur/shop/modules/api.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ def article_remove(
9494
parent_cart_key: str | db.Key,
9595
):
9696
"""Remove an article from the cart"""
97-
return self.article_update(article_key, 0, parent_cart_key)
97+
return self.article_update(
98+
article_key=article_key, quantity=0, parent_cart_key=parent_cart_key)
9899

99100
@exposed
100101
@force_post

src/viur/shop/modules/cart.py

+23-12
Original file line numberDiff line numberDiff line change
@@ -57,23 +57,25 @@ def _ensure_current_session_cart(self):
5757
current.session.get().markChanged()
5858
# Store basket at the user skel, it will be shared over multiple sessions / devices
5959
if user := current.user.get():
60-
user_skel = conf.main_app.vi.user.editSkel()
61-
user_skel.fromDB(user["key"])
62-
user_skel.setBoneValue("basket", key)
63-
user_skel.toDB()
60+
db.RunInTransaction(self._set_basket_txn, user_key=user["key"], basket_key=key)
6461
return self.session["session_cart_key"]
6562

6663
def detach_session_cart(self) -> db.Key:
6764
key = self.session["session_cart_key"]
6865
self.session["session_cart_key"] = None
6966
current.session.get().markChanged()
7067
if user := current.user.get():
71-
user_skel = conf.main_app.vi.user.editSkel()
72-
user_skel.fromDB(user["key"])
73-
user_skel["basket"] = None
74-
user_skel.toDB()
68+
db.RunInTransaction(self._set_basket_txn, user_key=user["key"], basket_key=None)
7569
return key
7670

71+
@staticmethod
72+
def _set_basket_txn(user_key: db.Key, basket_key: db.Key | None) -> SkeletonInstance:
73+
user_skel = conf.main_app.vi.user.editSkel()
74+
user_skel.fromDB(user_key)
75+
user_skel.setBoneValue("basket", basket_key)
76+
user_skel.toDB()
77+
return user_skel
78+
7779
def get_available_root_nodes(self, *args, **kwargs) -> list[dict[t.Literal["name", "key"], str]]:
7880
root_nodes = [self.current_session_cart]
7981

@@ -99,6 +101,9 @@ def listRootNodes(self, *args, **kwargs) -> t.Any:
99101
100102
:returns: The rendered representation of the available root-nodes.
101103
"""
104+
if utils.string.is_prefix(self.render.kind, "json"):
105+
# TODO: add in viur-core
106+
current.request.get().response.headers["Content-Type"] = "application/json"
102107
return self.render.listRootNodes([
103108
self.render.renderSkelValues(skel)
104109
for skel in self.getAvailableRootNodes(*args, **kwargs)
@@ -174,6 +179,7 @@ def get_article(
174179
self,
175180
article_key: db.Key,
176181
parent_cart_key: db.Key,
182+
must_be_listed: bool = True,
177183
):
178184
if not isinstance(article_key, db.Key):
179185
raise TypeError(f"article_key must be an instance of db.Key")
@@ -185,7 +191,8 @@ def get_article(
185191
query: db.Query = skel.all()
186192
query.filter("parententry =", parent_cart_key)
187193
query.filter("article.dest.__key__ =", article_key)
188-
query.filter("shop_listed =", True)
194+
if must_be_listed:
195+
query.filter("shop_listed =", True)
189196
skel = query.getSkel()
190197
return skel
191198

@@ -205,7 +212,7 @@ def add_or_update_article(
205212
if not self.is_valid_node(parent_cart_key):
206213
raise e.InvalidArgumentException("parent_cart_key", parent_cart_key)
207214
parent_skel = None
208-
if not (skel := self.get_article(article_key, parent_cart_key)):
215+
if not (skel := self.get_article(article_key, parent_cart_key, must_be_listed=False)):
209216
logger.info("This is an add")
210217
skel = self.addSkel("leaf")
211218
res = skel.setBoneValue("article", article_key)
@@ -217,7 +224,10 @@ def add_or_update_article(
217224
else:
218225
skel["parentrepo"] = parent_skel["parentrepo"]
219226
article_skel: SkeletonInstance = self.shop.article_skel()
220-
assert article_skel.fromDB(article_key)
227+
if not article_skel.fromDB(article_key):
228+
raise errors.NotFound(f"Article with key {article_key=} does not exist!")
229+
if not article_skel["shop_listed"]:
230+
raise errors.UnprocessableEntity(f"Article is not listed for the shop!")
221231
# Copy values from the article
222232
for bone in skel.keys():
223233
if not bone.startswith("shop_"): continue
@@ -279,7 +289,7 @@ def move_article(
279289
raise TypeError(f"parent_cart_key must be an instance of db.Key")
280290
if not isinstance(new_parent_cart_key, db.Key):
281291
raise TypeError(f"parent_cart_key must be an instance of db.Key")
282-
if not (skel := self.get_article(article_key, parent_cart_key)):
292+
if not (skel := self.get_article(article_key, parent_cart_key, must_be_listed=False)):
283293
raise e.InvalidArgumentException(
284294
"article_key",
285295
descr_appendix=f"Article does not exist in cart node {parent_cart_key}."
@@ -449,6 +459,7 @@ def freeze_cart(
449459
) -> None:
450460
# TODO: for node in tree:
451461
# freeze node with values, discount, shipping (JSON dump? bone duplication?)
462+
# ensure each article still exists and shop_listed is True
452463
...
453464

454465
# -------------------------------------------------------------------------

src/viur/shop/skeletons/cart.py

+3
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ def get_vat_rate_for_node(skel: "CartNodeSkel", bone: RelationalBone):
6565
# logger.debug(f"{child = }")
6666
if issubclass(child.skeletonCls, CartNodeSkel):
6767
for rel in child["vat_rate"] or []:
68+
if rel is None:
69+
logger.error(f'Relation vat_rate of {child["key"]} is broken.')
70+
continue
6871
rel_keys.add(rel["dest"]["key"])
6972
elif issubclass(child.skeletonCls, CartItemSkel):
7073
if child["shop_vat"] is not None:

0 commit comments

Comments
 (0)