1use crate::auth::store::get_config as get_auth_config;
2use crate::controllers::store::get_controllers;
3use crate::db::assert::{
4 assert_delete_doc, assert_get_doc, assert_get_docs, assert_set_config, assert_set_doc,
5};
6use crate::db::state::{
7 count_docs_heap, count_docs_stable, delete_collection as delete_state_collection,
8 delete_doc as delete_state_doc, get_config, get_doc as get_state_doc, get_docs_heap,
9 get_docs_stable, get_rule as get_state_rule, init_collection as init_state_collection,
10 insert_config, insert_doc as insert_state_doc,
11 is_collection_empty as is_state_collection_empty,
12};
13use crate::db::types::config::DbConfig;
14use crate::db::types::interface::{DelDoc, SetDbConfig, SetDoc};
15use crate::db::types::state::{Doc, DocContext, DocUpsert};
16use crate::db::types::store::AssertSetDocOptions;
17use crate::db::utils::filter_values;
18use crate::memory::state::STATE;
19use crate::types::store::{AssertContext, StoreContext};
20use candid::Principal;
21use junobuild_collections::msg::msg_db_collection_not_empty;
22use junobuild_collections::types::core::CollectionKey;
23use junobuild_collections::types::rules::{Memory, Rule};
24use junobuild_shared::list::list_values;
25use junobuild_shared::types::core::Key;
26use junobuild_shared::types::list::{ListParams, ListResults};
27use junobuild_shared::types::state::{Controllers, UserId};
28pub fn init_collection_store(collection: &CollectionKey, memory: &Memory) {
33 init_state_collection(collection, memory);
34}
35
36pub fn delete_collection_store(collection: &CollectionKey) -> Result<(), String> {
37 secure_delete_collection(collection)
38}
39
40fn secure_delete_collection(collection: &CollectionKey) -> Result<(), String> {
41 let rule = get_state_rule(collection)?;
42 delete_collection_impl(collection, &rule.memory)
43}
44
45fn delete_collection_impl(
46 collection: &CollectionKey,
47 memory: &Option<Memory>,
48) -> Result<(), String> {
49 let empty = is_state_collection_empty(collection, memory)?;
50
51 if !empty {
52 return Err(msg_db_collection_not_empty(collection));
53 }
54
55 delete_state_collection(collection, memory)?;
56
57 Ok(())
58}
59
60pub fn get_doc_store(
83 caller: UserId,
84 collection: CollectionKey,
85 key: Key,
86) -> Result<Option<Doc>, String> {
87 let controllers: Controllers = get_controllers();
88
89 let context = StoreContext {
90 caller,
91 controllers: &controllers,
92 collection: &collection,
93 };
94
95 secure_get_doc(&context, key)
96}
97
98fn secure_get_doc(context: &StoreContext, key: Key) -> Result<Option<Doc>, String> {
99 let rule = get_state_rule(context.collection)?;
100 let auth_config = get_auth_config();
101
102 let assert_context = AssertContext {
103 rule: &rule,
104 auth_config: &auth_config,
105 };
106
107 get_doc_impl(context, &assert_context, key)
108}
109
110fn get_doc_impl(
111 context: &StoreContext,
112 assert_context: &AssertContext,
113 key: Key,
114) -> Result<Option<Doc>, String> {
115 let value = get_state_doc(context.collection, &key, assert_context.rule)?;
116
117 match value {
118 None => Ok(None),
119 Some(value) => {
120 if assert_get_doc(context, assert_context, &value).is_err() {
121 return Ok(None);
122 }
123
124 Ok(Some(value))
125 }
126 }
127}
128
129pub fn set_doc_store(
152 caller: UserId,
153 collection: CollectionKey,
154 key: Key,
155 value: SetDoc,
156) -> Result<DocContext<DocUpsert>, String> {
157 let controllers: Controllers = get_controllers();
158 let config = get_config();
159
160 let context = StoreContext {
161 caller,
162 controllers: &controllers,
163 collection: &collection,
164 };
165
166 let assert_options = AssertSetDocOptions {
167 with_assert_rate: true,
168 };
169
170 let data = secure_set_doc(&context, &config, &assert_options, key.clone(), value)?;
171
172 Ok(DocContext {
173 key,
174 collection,
175 data,
176 })
177}
178
179pub fn internal_set_doc_store(
186 caller: UserId,
187 collection: CollectionKey,
188 key: Key,
189 value: SetDoc,
190 assert_options: &AssertSetDocOptions,
191) -> Result<DocContext<DocUpsert>, String> {
192 let controllers: Controllers = get_controllers();
193 let config = get_config();
194
195 let context = StoreContext {
196 caller,
197 controllers: &controllers,
198 collection: &collection,
199 };
200
201 let data = secure_set_doc(&context, &config, assert_options, key.clone(), value)?;
202
203 Ok(DocContext {
204 key,
205 collection,
206 data,
207 })
208}
209
210fn secure_set_doc(
211 context: &StoreContext,
212 config: &Option<DbConfig>,
213 assert_options: &AssertSetDocOptions,
214 key: Key,
215 value: SetDoc,
216) -> Result<DocUpsert, String> {
217 let rule = get_state_rule(context.collection)?;
218 let auth_config = get_auth_config();
219
220 let assert_context = AssertContext {
221 rule: &rule,
222 auth_config: &auth_config,
223 };
224
225 set_doc_impl(context, &assert_context, config, assert_options, key, value)
226}
227
228fn set_doc_impl(
229 context: &StoreContext,
230 assert_context: &AssertContext,
231 config: &Option<DbConfig>,
232 assert_options: &AssertSetDocOptions,
233 key: Key,
234 value: SetDoc,
235) -> Result<DocUpsert, String> {
236 let current_doc = get_state_doc(context.collection, &key, assert_context.rule)?;
237
238 assert_set_doc(
239 context,
240 assert_context,
241 config,
242 assert_options,
243 &key,
244 &value,
245 ¤t_doc,
246 )?;
247
248 let doc: Doc = Doc::prepare(context.caller, ¤t_doc, value);
249
250 let (_evicted_doc, after) =
251 insert_state_doc(context.collection, &key, &doc, assert_context.rule)?;
252
253 Ok(DocUpsert {
254 before: current_doc,
255 after,
256 })
257}
258
259pub fn list_docs_store(
280 caller: Principal,
281 collection: CollectionKey,
282 filter: &ListParams,
283) -> Result<ListResults<Doc>, String> {
284 let controllers: Controllers = get_controllers();
285
286 secure_get_docs(caller, &controllers, collection, filter)
287}
288
289pub fn count_docs_store(
309 caller: Principal,
310 collection: CollectionKey,
311 filter: &ListParams,
312) -> Result<usize, String> {
313 let results = list_docs_store(caller, collection, filter)?;
314 Ok(results.items_length)
315}
316
317fn secure_get_docs(
318 caller: Principal,
319 controllers: &Controllers,
320 collection: CollectionKey,
321 filter: &ListParams,
322) -> Result<ListResults<Doc>, String> {
323 let context: StoreContext = StoreContext {
324 caller,
325 collection: &collection,
326 controllers,
327 };
328
329 let rule = get_state_rule(&collection)?;
330 let auth_config = get_auth_config();
331
332 let assert_context = AssertContext {
333 rule: &rule,
334 auth_config: &auth_config,
335 };
336
337 assert_get_docs(&context, &assert_context)?;
338
339 match rule.mem() {
340 Memory::Heap => STATE.with(|state| {
341 let state_ref = state.borrow();
342 let docs = get_docs_heap(&collection, &state_ref.heap.db.db)?;
343 get_docs_impl(&docs, caller, controllers, filter, &rule)
344 }),
345 Memory::Stable => STATE.with(|state| {
346 let stable = get_docs_stable(&collection, &state.borrow().stable.db)?;
347 let docs: Vec<(&Key, &Doc)> = stable.iter().map(|(key, doc)| (&key.key, doc)).collect();
348 get_docs_impl(&docs, caller, controllers, filter, &rule)
349 }),
350 }
351}
352
353fn get_docs_impl<'a>(
354 docs: &[(&'a Key, &'a Doc)],
355 caller: Principal,
356 controllers: &Controllers,
357 filters: &ListParams,
358 rule: &Rule,
359) -> Result<ListResults<Doc>, String> {
360 let matches = filter_values(caller, controllers, &rule.read, docs, filters)?;
361
362 let results = list_values(&matches, filters);
363
364 Ok(results)
365}
366
367pub fn delete_doc_store(
393 caller: UserId,
394 collection: CollectionKey,
395 key: Key,
396 value: DelDoc,
397) -> Result<DocContext<Option<Doc>>, String> {
398 let controllers: Controllers = get_controllers();
399
400 let context = StoreContext {
401 caller,
402 controllers: &controllers,
403 collection: &collection,
404 };
405
406 let doc = secure_delete_doc(&context, key.clone(), value)?;
407
408 Ok(DocContext {
409 key,
410 collection,
411 data: doc,
412 })
413}
414
415fn secure_delete_doc(
416 context: &StoreContext,
417 key: Key,
418 value: DelDoc,
419) -> Result<Option<Doc>, String> {
420 let rule = get_state_rule(context.collection)?;
421 let auth_config = get_auth_config();
422
423 let assert_context = AssertContext {
424 rule: &rule,
425 auth_config: &auth_config,
426 };
427
428 delete_doc_impl(context, &assert_context, key, value)
429}
430
431fn delete_doc_impl(
432 context: &StoreContext,
433 assert_context: &AssertContext,
434 key: Key,
435 value: DelDoc,
436) -> Result<Option<Doc>, String> {
437 let current_doc = get_state_doc(context.collection, &key, assert_context.rule)?;
438
439 assert_delete_doc(context, assert_context, &key, &value, ¤t_doc)?;
440
441 delete_state_doc(context.collection, &key, assert_context.rule)
442}
443
444pub fn delete_docs_store(collection: &CollectionKey) -> Result<(), String> {
459 let rule = get_state_rule(collection)?;
460
461 let keys = match rule.mem() {
462 Memory::Heap => STATE.with(|state| {
463 get_docs_heap(collection, &state.borrow().heap.db.db)
464 .map(|docs| docs.into_iter().map(|(key, _)| key.clone()).collect())
465 }),
466 Memory::Stable => STATE.with(|state| {
467 get_docs_stable(collection, &state.borrow().stable.db)
468 .map(|docs| docs.iter().map(|(key, _)| key.key.clone()).collect())
469 }),
470 }?;
471
472 delete_docs_impl(&keys, collection, &rule)
473}
474
475fn delete_docs_impl(
476 keys: &Vec<Key>,
477 collection: &CollectionKey,
478 rule: &Rule,
479) -> Result<(), String> {
480 for key in keys {
481 delete_state_doc(collection, key, rule)?;
482 }
483
484 Ok(())
485}
486
487pub fn count_collection_docs_store(collection: &CollectionKey) -> Result<usize, String> {
502 let rule = get_state_rule(collection)?;
503
504 match rule.mem() {
505 Memory::Heap => STATE.with(|state| {
506 let state_ref = state.borrow();
507 let length = count_docs_heap(collection, &state_ref.heap.db.db)?;
508 Ok(length)
509 }),
510 Memory::Stable => STATE.with(|state| {
511 let length = count_docs_stable(collection, &state.borrow().stable.db)?;
512 Ok(length)
513 }),
514 }
515}
516
517pub fn delete_filtered_docs_store(
539 caller: Principal,
540 collection: CollectionKey,
541 filter: &ListParams,
542) -> Result<Vec<DocContext<Option<Doc>>>, String> {
543 let controllers: Controllers = get_controllers();
544
545 let docs = secure_get_docs(caller, &controllers, collection.clone(), filter)?;
546
547 let context = StoreContext {
548 caller,
549 controllers: &controllers,
550 collection: &collection,
551 };
552
553 delete_filtered_docs_store_impl(&context, &docs)
554}
555
556fn delete_filtered_docs_store_impl(
557 context: &StoreContext,
558 docs: &ListResults<Doc>,
559) -> Result<Vec<DocContext<Option<Doc>>>, String> {
560 let rule = get_state_rule(context.collection)?;
561 let auth_config = get_auth_config();
562
563 let assert_context = AssertContext {
564 rule: &rule,
565 auth_config: &auth_config,
566 };
567
568 let mut results: Vec<DocContext<Option<Doc>>> = Vec::new();
569
570 for (key, doc) in &docs.items {
571 let value = DelDoc {
572 version: doc.version,
573 };
574
575 let deleted_doc = delete_doc_impl(context, &assert_context, key.clone(), value)?;
576
577 let doc_context = DocContext {
578 key: key.clone(),
579 collection: context.collection.clone(),
580 data: deleted_doc,
581 };
582
583 results.push(doc_context);
584 }
585
586 Ok(results)
587}
588
589pub fn set_config_store(proposed_config: &SetDbConfig) -> Result<DbConfig, String> {
594 let current_config = get_config();
595
596 assert_set_config(proposed_config, ¤t_config)?;
597
598 let config = DbConfig::prepare(¤t_config, proposed_config);
599
600 insert_config(&config);
601
602 Ok(config)
603}
604
605pub fn get_config_store() -> Option<DbConfig> {
606 get_config()
607}