iota_graphql_rpc/data/
package_resolver.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    collections::{BTreeSet, HashMap},
7    sync::Arc,
8};
9
10use async_graphql::dataloader::Loader;
11use async_trait::async_trait;
12use diesel::{ExpressionMethods, QueryDsl};
13use iota_indexer::{models::packages::StoredPackage, schema::packages};
14use iota_package_resolver::{
15    Package, PackageStore, PackageStoreWithLruCache, Resolver, Result,
16    error::Error as PackageResolverError,
17};
18use move_core_types::account_address::AccountAddress;
19
20use crate::data::{DataLoader, Db, DbConnection, QueryExecutor};
21
22const STORE: &str = "PostgresDB";
23
24pub(crate) type PackageCache = PackageStoreWithLruCache<DbPackageStore>;
25pub(crate) type PackageResolver = Arc<Resolver<PackageCache>>;
26
27/// Store which fetches package for the given address from the backend db on
28/// every call to `fetch`
29pub struct DbPackageStore(DataLoader);
30
31/// `DataLoader` key for fetching the latest version of a `Package` by its ID.
32#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
33struct PackageKey(AccountAddress);
34
35impl DbPackageStore {
36    pub fn new(loader: DataLoader) -> Self {
37        Self(loader)
38    }
39}
40
41#[async_trait]
42impl PackageStore for DbPackageStore {
43    async fn fetch(&self, id: AccountAddress) -> Result<Arc<Package>> {
44        let Self(DataLoader(loader)) = self;
45        let Some(package) = loader.load_one(PackageKey(id)).await? else {
46            return Err(PackageResolverError::PackageNotFound(id));
47        };
48
49        Ok(package)
50    }
51}
52
53impl Loader<PackageKey> for Db {
54    type Value = Arc<Package>;
55    type Error = PackageResolverError;
56
57    async fn load(&self, keys: &[PackageKey]) -> Result<HashMap<PackageKey, Arc<Package>>> {
58        use packages::dsl;
59
60        let ids: BTreeSet<_> = keys.iter().map(|PackageKey(id)| id.to_vec()).collect();
61        let stored_packages: Vec<StoredPackage> = self
62            .execute(move |conn| {
63                conn.results(move || {
64                    dsl::packages.filter(dsl::package_id.eq_any(ids.iter().cloned()))
65                })
66            })
67            .await
68            .map_err(|e| PackageResolverError::Store {
69                store: STORE,
70                source: Arc::new(e),
71            })?;
72
73        let mut id_to_package = HashMap::new();
74        for stored_package in stored_packages {
75            let move_package = bcs::from_bytes(&stored_package.move_package)?;
76            let package = Package::read_from_package(&move_package)?;
77            id_to_package.insert(PackageKey(*move_package.id()), Arc::new(package));
78        }
79
80        Ok(id_to_package)
81    }
82}