Skip to content

Commit 640dbd5

Browse files
feat: use Datastore for user usage (#1218)
* feat: use Datastore for user usage * feat: remove getter * feat: update did * feat: update datastore * feat: in progress * feat: use a single usage collection * feat: timestamps are in doc * chore: fmt and clippy * feat: in progress * feat: parse key * test: adapt * feat: only controllers can read usage * test: adapt for controllers * chore: fmt * refactor: object param * test: revert to previous stable expected memory * chore: singular * fix: create #user-usage rule on migration * test: migrate collection * chore: fmt
1 parent 8f1675f commit 640dbd5

33 files changed

+433
-647
lines changed

src/declarations/satellite/satellite.did.d.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,6 @@ export interface SetRule {
194194
write: Permission;
195195
max_changes_per_user: [] | [number];
196196
}
197-
export interface SetUserUsage {
198-
changes_count: number;
199-
}
200197
export interface StorageConfig {
201198
iframe: [] | [StorageConfigIFrame];
202199
rewrites: Array<[string, string]>;
@@ -243,12 +240,6 @@ export interface UploadChunk {
243240
export interface UploadChunkResult {
244241
chunk_id: bigint;
245242
}
246-
export interface UserUsage {
247-
updated_at: bigint;
248-
created_at: bigint;
249-
version: [] | [bigint];
250-
changes_count: number;
251-
}
252243
export interface _SERVICE {
253244
build_version: ActorMethod<[], string>;
254245
commit_asset_upload: ActorMethod<[CommitBatch], undefined>;
@@ -277,7 +268,6 @@ export interface _SERVICE {
277268
get_many_docs: ActorMethod<[Array<[string, string]>], Array<[string, [] | [Doc]]>>;
278269
get_rule: ActorMethod<[CollectionType, string], [] | [Rule]>;
279270
get_storage_config: ActorMethod<[], StorageConfig>;
280-
get_user_usage: ActorMethod<[string, CollectionType, [] | [Principal]], [] | [UserUsage]>;
281271
http_request: ActorMethod<[HttpRequest], HttpResponse>;
282272
http_request_streaming_callback: ActorMethod<
283273
[StreamingCallbackToken],
@@ -298,7 +288,6 @@ export interface _SERVICE {
298288
set_many_docs: ActorMethod<[Array<[string, string, SetDoc]>], Array<[string, Doc]>>;
299289
set_rule: ActorMethod<[CollectionType, string, SetRule], Rule>;
300290
set_storage_config: ActorMethod<[StorageConfig], undefined>;
301-
set_user_usage: ActorMethod<[string, CollectionType, Principal, SetUserUsage], UserUsage>;
302291
upload_asset_chunk: ActorMethod<[UploadChunk], UploadChunkResult>;
303292
version: ActorMethod<[], string>;
304293
}

src/declarations/satellite/satellite.factory.certified.did.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,6 @@ export const idlFactory = ({ IDL }) => {
147147
write: Permission,
148148
max_changes_per_user: IDL.Opt(IDL.Nat32)
149149
});
150-
const UserUsage = IDL.Record({
151-
updated_at: IDL.Nat64,
152-
created_at: IDL.Nat64,
153-
version: IDL.Opt(IDL.Nat64),
154-
changes_count: IDL.Nat32
155-
});
156150
const HttpRequest = IDL.Record({
157151
url: IDL.Text,
158152
method: IDL.Text,
@@ -240,7 +234,6 @@ export const idlFactory = ({ IDL }) => {
240234
write: Permission,
241235
max_changes_per_user: IDL.Opt(IDL.Nat32)
242236
});
243-
const SetUserUsage = IDL.Record({ changes_count: IDL.Nat32 });
244237
const UploadChunk = IDL.Record({
245238
content: IDL.Vec(IDL.Nat8),
246239
batch_id: IDL.Nat,
@@ -287,11 +280,6 @@ export const idlFactory = ({ IDL }) => {
287280
),
288281
get_rule: IDL.Func([CollectionType, IDL.Text], [IDL.Opt(Rule)], []),
289282
get_storage_config: IDL.Func([], [StorageConfig], []),
290-
get_user_usage: IDL.Func(
291-
[IDL.Text, CollectionType, IDL.Opt(IDL.Principal)],
292-
[IDL.Opt(UserUsage)],
293-
[]
294-
),
295283
http_request: IDL.Func([HttpRequest], [HttpResponse], []),
296284
http_request_streaming_callback: IDL.Func(
297285
[StreamingCallbackToken],
@@ -321,11 +309,6 @@ export const idlFactory = ({ IDL }) => {
321309
),
322310
set_rule: IDL.Func([CollectionType, IDL.Text, SetRule], [Rule], []),
323311
set_storage_config: IDL.Func([StorageConfig], [], []),
324-
set_user_usage: IDL.Func(
325-
[IDL.Text, CollectionType, IDL.Principal, SetUserUsage],
326-
[UserUsage],
327-
[]
328-
),
329312
upload_asset_chunk: IDL.Func([UploadChunk], [UploadChunkResult], []),
330313
version: IDL.Func([], [IDL.Text], [])
331314
});

src/declarations/satellite/satellite.factory.did.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,6 @@ export const idlFactory = ({ IDL }) => {
147147
write: Permission,
148148
max_changes_per_user: IDL.Opt(IDL.Nat32)
149149
});
150-
const UserUsage = IDL.Record({
151-
updated_at: IDL.Nat64,
152-
created_at: IDL.Nat64,
153-
version: IDL.Opt(IDL.Nat64),
154-
changes_count: IDL.Nat32
155-
});
156150
const HttpRequest = IDL.Record({
157151
url: IDL.Text,
158152
method: IDL.Text,
@@ -240,7 +234,6 @@ export const idlFactory = ({ IDL }) => {
240234
write: Permission,
241235
max_changes_per_user: IDL.Opt(IDL.Nat32)
242236
});
243-
const SetUserUsage = IDL.Record({ changes_count: IDL.Nat32 });
244237
const UploadChunk = IDL.Record({
245238
content: IDL.Vec(IDL.Nat8),
246239
batch_id: IDL.Nat,
@@ -287,11 +280,6 @@ export const idlFactory = ({ IDL }) => {
287280
),
288281
get_rule: IDL.Func([CollectionType, IDL.Text], [IDL.Opt(Rule)], ['query']),
289282
get_storage_config: IDL.Func([], [StorageConfig], ['query']),
290-
get_user_usage: IDL.Func(
291-
[IDL.Text, CollectionType, IDL.Opt(IDL.Principal)],
292-
[IDL.Opt(UserUsage)],
293-
['query']
294-
),
295283
http_request: IDL.Func([HttpRequest], [HttpResponse], ['query']),
296284
http_request_streaming_callback: IDL.Func(
297285
[StreamingCallbackToken],
@@ -321,11 +309,6 @@ export const idlFactory = ({ IDL }) => {
321309
),
322310
set_rule: IDL.Func([CollectionType, IDL.Text, SetRule], [Rule], []),
323311
set_storage_config: IDL.Func([StorageConfig], [], []),
324-
set_user_usage: IDL.Func(
325-
[IDL.Text, CollectionType, IDL.Principal, SetUserUsage],
326-
[UserUsage],
327-
[]
328-
),
329312
upload_asset_chunk: IDL.Func([UploadChunk], [UploadChunkResult], []),
330313
version: IDL.Func([], [IDL.Text], ['query'])
331314
});

src/declarations/satellite/satellite.factory.did.mjs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,6 @@ export const idlFactory = ({ IDL }) => {
147147
write: Permission,
148148
max_changes_per_user: IDL.Opt(IDL.Nat32)
149149
});
150-
const UserUsage = IDL.Record({
151-
updated_at: IDL.Nat64,
152-
created_at: IDL.Nat64,
153-
version: IDL.Opt(IDL.Nat64),
154-
changes_count: IDL.Nat32
155-
});
156150
const HttpRequest = IDL.Record({
157151
url: IDL.Text,
158152
method: IDL.Text,
@@ -240,7 +234,6 @@ export const idlFactory = ({ IDL }) => {
240234
write: Permission,
241235
max_changes_per_user: IDL.Opt(IDL.Nat32)
242236
});
243-
const SetUserUsage = IDL.Record({ changes_count: IDL.Nat32 });
244237
const UploadChunk = IDL.Record({
245238
content: IDL.Vec(IDL.Nat8),
246239
batch_id: IDL.Nat,
@@ -287,11 +280,6 @@ export const idlFactory = ({ IDL }) => {
287280
),
288281
get_rule: IDL.Func([CollectionType, IDL.Text], [IDL.Opt(Rule)], ['query']),
289282
get_storage_config: IDL.Func([], [StorageConfig], ['query']),
290-
get_user_usage: IDL.Func(
291-
[IDL.Text, CollectionType, IDL.Opt(IDL.Principal)],
292-
[IDL.Opt(UserUsage)],
293-
['query']
294-
),
295283
http_request: IDL.Func([HttpRequest], [HttpResponse], ['query']),
296284
http_request_streaming_callback: IDL.Func(
297285
[StreamingCallbackToken],
@@ -321,11 +309,6 @@ export const idlFactory = ({ IDL }) => {
321309
),
322310
set_rule: IDL.Func([CollectionType, IDL.Text, SetRule], [Rule], []),
323311
set_storage_config: IDL.Func([StorageConfig], [], []),
324-
set_user_usage: IDL.Func(
325-
[IDL.Text, CollectionType, IDL.Principal, SetUserUsage],
326-
[UserUsage],
327-
[]
328-
),
329312
upload_asset_chunk: IDL.Func([UploadChunk], [UploadChunkResult], []),
330313
version: IDL.Func([], [IDL.Text], ['query'])
331314
});

src/frontend/src/lib/api/satellites.api.ts

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import type {
77
ListResults as ListAssets,
88
ListResults_1 as ListDocs,
99
Rule,
10-
SetRule,
11-
UserUsage
10+
SetRule
1211
} from '$declarations/satellite/satellite.did';
1312
import { getSatelliteActor } from '$lib/api/actors/actor.juno.api';
1413
import type { CustomDomains } from '$lib/types/custom-domain';
@@ -33,6 +32,21 @@ export const listDocs = async ({
3332
return list_docs(collection, toListParams(params));
3433
};
3534

35+
export const getDoc = async ({
36+
satelliteId,
37+
collection,
38+
key,
39+
identity
40+
}: {
41+
satelliteId: Principal;
42+
collection: string;
43+
key: string;
44+
identity: OptionIdentity;
45+
}): Promise<[] | [Doc]> => {
46+
const { get_doc } = await getSatelliteActor({ satelliteId, identity });
47+
return get_doc(collection, key);
48+
};
49+
3650
export const listAssets = async ({
3751
satelliteId,
3852
collection,
@@ -291,20 +305,3 @@ export const countCollectionAssets = async ({
291305
const { count_collection_assets } = await getSatelliteActor({ satelliteId, identity });
292306
return count_collection_assets(collection);
293307
};
294-
295-
export const getUsageUsage = async ({
296-
satelliteId,
297-
collection,
298-
collectionType,
299-
userId,
300-
identity
301-
}: {
302-
satelliteId: Principal;
303-
collection: string;
304-
collectionType: CollectionType;
305-
userId: Principal;
306-
identity: OptionIdentity;
307-
}): Promise<[] | [UserUsage]> => {
308-
const { get_user_usage } = await getSatelliteActor({ satelliteId, identity });
309-
return get_user_usage(collection, collectionType, toNullable(userId));
310-
};

src/frontend/src/lib/services/user.services.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import type { CollectionType } from '$declarations/satellite/satellite.did';
2-
import { getUsageUsage, listRules } from '$lib/api/satellites.api';
2+
import { getDoc, listRules } from '$lib/api/satellites.api';
33
import { DbCollectionType, StorageCollectionType } from '$lib/constants/rules.constants';
44
import { busy } from '$lib/stores/busy.store';
55
import { i18n } from '$lib/stores/i18n.store';
66
import { toasts } from '$lib/stores/toasts.store';
77
import type { OptionIdentity } from '$lib/types/itentity';
88
import type { User } from '$lib/types/user';
9-
import type { UserUsageCollection } from '$lib/types/user-usage';
9+
import type { UserUsage, UserUsageCollection } from '$lib/types/user-usage';
1010
import { emit } from '$lib/utils/events.utils';
1111
import { waitReady } from '$lib/utils/timeout.utils';
1212
import type { Identity } from '@dfinity/agent';
1313
import type { Principal } from '@dfinity/principal';
1414
import { assertNonNullish, fromNullable, isNullish } from '@dfinity/utils';
15+
import { fromArray } from '@junobuild/utils';
1516
import { get } from 'svelte/store';
1617

1718
interface OpenUserDetailParams {
@@ -88,19 +89,24 @@ const loadUserUsages = async ({
8889
collectionType: CollectionType;
8990
maxChangesPerUser: number | undefined;
9091
}): Promise<UserUsageCollection> => {
91-
const usage = await getUsageUsage({
92+
const key = `${user.owner.toText()}#${'Storage' in collectionType ? 'storage' : 'db'}#${collection}`;
93+
94+
const result = await getDoc({
9295
satelliteId,
93-
collection,
94-
collectionType,
95-
identity,
96-
userId: user.owner
96+
collection: '#user-usage',
97+
key,
98+
identity
9799
});
98100

101+
const doc = fromNullable(result);
102+
103+
const usage = isNullish(doc) ? undefined : await fromArray<UserUsage>(doc.data);
104+
99105
return {
100106
collection,
101107
collectionType,
102108
maxChangesPerUser,
103-
usage: fromNullable(usage)
109+
usage
104110
};
105111
};
106112

src/frontend/src/lib/types/user-usage.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import type { CollectionType, UserUsage } from '$declarations/satellite/satellite.did';
1+
import type { CollectionType } from '$declarations/satellite/satellite.did';
2+
3+
export interface UserUsage {
4+
changes_count: number;
5+
}
26

37
export interface UserUsageCollection {
48
collection: string;

src/libs/collections/src/constants.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,24 @@ pub const COLLECTION_LOG_DEFAULT_RULE: SetRule = SetRule {
3232
rate_config: None,
3333
};
3434

35-
pub const DEFAULT_DB_COLLECTIONS: [(&str, SetRule); 2] = [
35+
pub const USER_USAGE_COLLECTION_KEY: &str = "#user-usage";
36+
37+
pub const DEFAULT_USER_USAGE_RULE: SetRule = SetRule {
38+
read: Controllers,
39+
write: Controllers,
40+
memory: Some(Memory::Stable),
41+
mutable_permissions: Some(false),
42+
max_size: None,
43+
max_capacity: None,
44+
max_changes_per_user: None,
45+
version: None,
46+
rate_config: None,
47+
};
48+
49+
pub const DEFAULT_DB_COLLECTIONS: [(&str, SetRule); 3] = [
3650
(COLLECTION_USER_KEY, COLLECTION_USER_DEFAULT_RULE),
3751
(COLLECTION_LOG_KEY, COLLECTION_LOG_DEFAULT_RULE),
52+
(USER_USAGE_COLLECTION_KEY, DEFAULT_USER_USAGE_RULE),
3853
];
3954

4055
pub const DB_COLLECTIONS_WITHOUT_USER_USAGE: [&str; 1] = [COLLECTION_LOG_KEY];

src/libs/satellite/satellite.did

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,6 @@ type SetRule = record {
158158
write : Permission;
159159
max_changes_per_user : opt nat32;
160160
};
161-
type SetUserUsage = record { changes_count : nat32 };
162161
type StorageConfig = record {
163162
iframe : opt StorageConfigIFrame;
164163
rewrites : vec record { text; text };
@@ -201,12 +200,6 @@ type UploadChunk = record {
201200
order_id : opt nat;
202201
};
203202
type UploadChunkResult = record { chunk_id : nat };
204-
type UserUsage = record {
205-
updated_at : nat64;
206-
created_at : nat64;
207-
version : opt nat64;
208-
changes_count : nat32;
209-
};
210203
service : () -> {
211204
commit_asset_upload : (CommitBatch) -> ();
212205
count_assets : (text, ListParams) -> (nat64) query;
@@ -240,9 +233,6 @@ service : () -> {
240233
) query;
241234
get_rule : (CollectionType, text) -> (opt Rule) query;
242235
get_storage_config : () -> (StorageConfig) query;
243-
get_user_usage : (text, CollectionType, opt principal) -> (
244-
opt UserUsage,
245-
) query;
246236
http_request : (HttpRequest) -> (HttpResponse) query;
247237
http_request_streaming_callback : (StreamingCallbackToken) -> (
248238
StreamingCallbackHttpResponse,
@@ -266,9 +256,6 @@ service : () -> {
266256
);
267257
set_rule : (CollectionType, text, SetRule) -> (Rule);
268258
set_storage_config : (StorageConfig) -> ();
269-
set_user_usage : (text, CollectionType, principal, SetUserUsage) -> (
270-
UserUsage,
271-
);
272259
upload_asset_chunk : (UploadChunk) -> (UploadChunkResult);
273260
version : () -> (text) query;
274261
}

src/libs/satellite/src/impls.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::memory::init_stable_state;
2-
use crate::types::state::{HeapState, RuntimeState, State};
2+
use crate::types::state::{CollectionType, HeapState, RuntimeState, State};
3+
use std::fmt::{Display, Formatter, Result as FmtResult};
34

45
impl Default for State {
56
fn default() -> Self {
@@ -10,3 +11,16 @@ impl Default for State {
1011
}
1112
}
1213
}
14+
15+
impl Display for CollectionType {
16+
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
17+
write!(
18+
f,
19+
"{}",
20+
match self {
21+
CollectionType::Db => "db",
22+
CollectionType::Storage => "storage",
23+
}
24+
)
25+
}
26+
}

0 commit comments

Comments
 (0)