Skip to content
This repository has been archived by the owner on Aug 28, 2023. It is now read-only.

Add tags to SafeApp #851

Merged
merged 1 commit into from
Apr 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx/
FEATURE_FLAG_NESTED_DECODING=true
FEATURE_FLAG_BALANCES_RATE_IMPLEMENTATION=false

# Enables the tags feature for Safe Apps
SAFE_APPS_TAGS_FEATURE_ENABLED=false

# Random string (generated with openssl rand -base64 32)
# [string] a 256-bit base64 encoded string (44 characters) to use as the secret key
ROCKET_SECRET_KEY=Qt6DPFUU8qO4BKTCQnKAgt9FBBJxIWAYUGyHuruVfpE=
Expand Down
5 changes: 5 additions & 0 deletions src/common/converters/tests/safe_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ fn safe_apps_several_apps() {
chain_ids: vec!["1".to_string(), "137".to_string()],
provider: None,
access_control: SafeAppAccessControlPolicies::NoRestrictions,
tags: vec![]
},
SafeApp {
id: 24,
Expand All @@ -49,6 +50,7 @@ fn safe_apps_several_apps() {
chain_ids: vec!["1".to_string(), "4".to_string(),"10".to_string(),"56".to_string(),"100".to_string(),"137".to_string(),"246".to_string(), "42161".to_string(), "43114".to_string(), "73799".to_string()],
provider: None,
access_control: SafeAppAccessControlPolicies::NoRestrictions,
tags: vec![]
},
SafeApp {
id: 11,
Expand All @@ -64,6 +66,7 @@ fn safe_apps_several_apps() {
access_control: SafeAppAccessControlPolicies::DomainAllowlist(SafeAppDomainAllowlistPolicy {
value: vec!["https://gnosis-safe.io".to_string(), "https://dev.gnosis-safe.io".to_string()],
}),
tags: vec![]
},
SafeApp {
id: 30,
Expand All @@ -74,6 +77,7 @@ fn safe_apps_several_apps() {
chain_ids: vec!["1".to_string(),"56".to_string(),"137".to_string()],
provider: None,
access_control: SafeAppAccessControlPolicies::NoRestrictions,
tags: vec![]
},
SafeApp {
id: 25,
Expand All @@ -84,6 +88,7 @@ fn safe_apps_several_apps() {
chain_ids: vec!["1".to_string(), "4".to_string(), "10".to_string(),"56".to_string(),"100".to_string(),"137".to_string(),"246".to_string(), "73799".to_string(), "42161".to_string(), "43114".to_string()],
provider: None,
access_control: SafeAppAccessControlPolicies::NoRestrictions,
tags: vec![]
},
];

Expand Down
3 changes: 3 additions & 0 deletions src/common/models/backend/safe_apps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ pub struct SafeApp {
pub chain_ids: Vec<u64>,
pub provider: Option<SafeAppProvider>,
pub access_control: SafeAppAccessControlPolicies,
// We set this value as a default since this feature might not be enabled yet. See SAFE_APPS_TAGS_FEATURE_ENABLED
#[serde(default)]
pub tags: Vec<String>,
}

#[derive(Deserialize, Debug, PartialEq, Clone)]
Expand Down
4 changes: 4 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ pub fn feature_flag_balances_rate_implementation() -> bool {
env_with_default("FEATURE_FLAG_BALANCES_RATE_IMPLEMENTATION", false)
}

pub fn is_safe_apps_tags_feature_enabled() -> bool {
env_with_default("SAFE_APPS_TAGS_FEATURE_ENABLED", false)
}

pub fn vpc_transaction_service_uri() -> bool {
env_with_default("VPC_TRANSACTION_SERVICE_URI", true)
}
Expand Down
1 change: 1 addition & 0 deletions src/routes/safe_apps/converters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ impl From<BackendSafeApp> for SafeApp {
}
_ => SafeAppAccessControlPolicies::Unknown,
},
tags: safe_app.tags,
}
}
}
10 changes: 10 additions & 0 deletions src/routes/safe_apps/models.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use serde::Serialize;

use crate::config::is_safe_apps_tags_feature_enabled;

#[derive(Serialize, Debug, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, derive(serde::Deserialize))]
Expand All @@ -12,6 +14,14 @@ pub struct SafeApp {
pub chain_ids: Vec<String>,
pub provider: Option<SafeAppProvider>,
pub access_control: SafeAppAccessControlPolicies,
#[serde(skip_serializing_if = "should_skip_serializing_tags")]
// We deserialize this for testing so it would break since the value wouldn't be present
#[serde(default)]
pub tags: Vec<String>,
}

pub fn should_skip_serializing_tags(_tags: &Vec<String>) -> bool {
return !is_safe_apps_tags_feature_enabled();
}

#[derive(Serialize, Debug, PartialEq, Clone)]
Expand Down
91 changes: 91 additions & 0 deletions src/routes/safe_apps/tests/json/response_safe_apps_with_tags.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
[
{
"id": 26,
"url": "https://curve.fi",
"name": "Curve",
"iconUrl": "https://curve.fi/logo-square.svg",
"description": "Decentralized exchange liquidity pool designed for extremely efficient stablecoin trading and low-risk income for liquidity providers",
"chainIds": ["1", "137"],
"provider": null,
"accessControl": {
"type": "NO_RESTRICTIONS"
},
"tags": ["tag1"]
},
{
"id": 24,
"url": "https://safe-apps.dev.gnosisdev.com/tx-builder",
"name": "Transaction Builder",
"iconUrl": "https://safe-apps.dev.gnosisdev.com/tx-builder/tx-builder.png",
"description": "A Safe app to compose custom transactions",
"chainIds": [
"1",
"4",
"10",
"56",
"100",
"137",
"246",
"42161",
"43114",
"73799"
],
"provider": null,
"accessControl": {
"type": "NO_RESTRICTIONS"
},
"tags": ["tag2"]
},
{
"id": 11,
"url": "https://app.1inch.io",
"name": "1inch.exchange",
"iconUrl": "https://app.1inch.io/assets/images/1inch.svg",
"description": "The most efficient defi aggregator",
"chainIds": ["1", "56", "137"],
"provider": {
"url": "https://1inch.exchange",
"name": "1inch corporation"
},
"accessControl": {
"type": "DOMAIN_ALLOWLIST",
"value": ["https://gnosis-safe.io", "https://dev.gnosis-safe.io"]
}
},
{
"id": 30,
"url": "https://paraswap.io",
"name": "ParaSwap",
"iconUrl": "https://paraswap.io/paraswap.svg",
"description": "ParaSwap allows dApps and traders to get the best DEX liquidity by aggregating multiple markets and offering the best rates",
"chainIds": ["1", "56", "137"],
"provider": null,
"accessControl": {
"type": "NO_RESTRICTIONS"
}
},
{
"id": 25,
"url": "https://safe-apps.dev.gnosisdev.com/wallet-connect",
"name": "WalletConnect",
"iconUrl": "https://safe-apps.dev.gnosisdev.com/wallet-connect/wallet-connect.svg",
"description": "Connect your Safe to any dApp that supports WalletConnect",
"chainIds": [
"1",
"4",
"10",
"56",
"100",
"137",
"246",
"73799",
"42161",
"43114"
],
"provider": null,
"accessControl": {
"type": "NO_RESTRICTIONS"
},
"tags": ["tag1", "tag2"]
}
]
2 changes: 2 additions & 0 deletions src/routes/safe_apps/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod routes;

pub(crate) const RESPONSE_SAFE_APPS: &str = include_str!("json/response_safe_apps.json");
pub(crate) const RESPONSE_SAFE_APPS_WITH_TAGS: &str =
include_str!("json/response_safe_apps_with_tags.json");
48 changes: 48 additions & 0 deletions src/routes/safe_apps/tests/routes.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
use crate::routes::safe_apps::models::SafeApp;
use crate::routes::safe_apps::tests::RESPONSE_SAFE_APPS_WITH_TAGS;
use crate::tests::main::setup_rocket;
use crate::utils::errors::{ApiError, ErrorDetails};
use crate::utils::http_client::{MockHttpClient, Request, Response};
use mockall::predicate::eq;
use rocket::http::{Header, Status};
use rocket::local::asynchronous::Client;
use serde_json::json;
use std::env;

use super::RESPONSE_SAFE_APPS;

#[rocket::async_test]
async fn safe_apps() {
env::set_var("SAFE_APPS_TAGS_FEATURE_ENABLED", "false");
let chain_id = "137";
let client_url = "https://gnosis-safe.io";

Expand Down Expand Up @@ -58,6 +61,7 @@ async fn safe_apps() {

#[rocket::async_test]
async fn safe_apps_not_found() {
env::set_var("SAFE_APPS_TAGS_FEATURE_ENABLED", "false");
let chain_id = "4";
let backend_error_json = json!({"details": "Not found"}).to_string();
let error = ErrorDetails {
Expand Down Expand Up @@ -105,3 +109,47 @@ async fn safe_apps_not_found() {
serde_json::to_string(&error).unwrap()
);
}

#[rocket::async_test]
async fn safe_apps_tags() {
env::set_var("SAFE_APPS_TAGS_FEATURE_ENABLED", "true");
let chain_id = "137";
let client_url = "https://gnosis-safe.io";
let mut mock_http_client = MockHttpClient::new();
let safe_apps_request = Request::new(config_uri!(
"/v1/safe-apps/?chainId={}&clientUrl={}",
chain_id,
client_url
));
mock_http_client
.expect_get()
.times(1)
.with(eq(safe_apps_request))
.return_once(move |_| {
Ok(Response {
status_code: 200,
body: String::from(crate::tests::json::POLYGON_SAFE_APPS_WITH_TAGS),
})
});
let client = Client::tracked(
setup_rocket(
mock_http_client,
routes![super::super::routes::get_safe_apps],
)
.await,
)
.await
.expect("valid rocket instance");
let response = {
let mut response = client.get("/v1/chains/137/safe-apps?client_url=https://gnosis-safe.io");
response.add_header(Header::new("Host", "test.gnosis.io"));
response.dispatch().await
};
let actual_status = response.status();
let actual_body = response.into_string().await.unwrap();
let actual: Vec<SafeApp> = serde_json::from_str(&actual_body).unwrap();
let expected: Vec<SafeApp> = serde_json::from_str(RESPONSE_SAFE_APPS_WITH_TAGS).unwrap();

assert_eq!(actual_status, Status::Ok);
assert_eq!(actual, expected);
}
2 changes: 2 additions & 0 deletions src/tests/json/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ pub const CHAIN_INFO_RINKEBY_ENABLED_FEATURES: &str =
include_str!("chains/rinkeby_enabled_features.json");

pub const POLYGON_SAFE_APPS: &str = include_str!("safe_apps/polygon_safe_apps.json");
pub const POLYGON_SAFE_APPS_WITH_TAGS: &str =
include_str!("safe_apps/polygon_safe_apps_with_tags.json");
pub const UNISWAP_SAFE_APP_MANIFEST: &str = include_str!("safe_apps/uniswap_manifest.json");

pub const POLYGON_MASTER_COPIES: &str = include_str!("master_copies/polygon_master_copies.json");
Expand Down
69 changes: 69 additions & 0 deletions src/tests/json/safe_apps/polygon_safe_apps_with_tags.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
[
{
"id": 26,
"url": "https://curve.fi",
"name": "Curve",
"iconUrl": "https://curve.fi/logo-square.svg",
"description": "Decentralized exchange liquidity pool designed for extremely efficient stablecoin trading and low-risk income for liquidity providers",
"chainIds": [1, 137],
"provider": null,
"accessControl": {
"type": "NO_RESTRICTIONS"
},
"tags": ["tag1"]
},
{
"id": 24,
"url": "https://safe-apps.dev.gnosisdev.com/tx-builder",
"name": "Transaction Builder",
"iconUrl": "https://safe-apps.dev.gnosisdev.com/tx-builder/tx-builder.png",
"description": "A Safe app to compose custom transactions",
"chainIds": [1, 4, 10, 56, 100, 137, 246, 42161, 43114, 73799],
"provider": null,
"accessControl": {
"type": "NO_RESTRICTIONS"
},
"tags": ["tag2"]
},
{
"id": 11,
"url": "https://app.1inch.io",
"name": "1inch.exchange",
"iconUrl": "https://app.1inch.io/assets/images/1inch.svg",
"description": "The most efficient defi aggregator",
"chainIds": [1, 56, 137],
"provider": {
"url": "https://1inch.exchange",
"name": "1inch corporation"
},
"accessControl": {
"type": "DOMAIN_ALLOWLIST",
"value": ["https://gnosis-safe.io", "https://dev.gnosis-safe.io"]
}
},
{
"id": 30,
"url": "https://paraswap.io",
"name": "ParaSwap",
"iconUrl": "https://paraswap.io/paraswap.svg",
"description": "ParaSwap allows dApps and traders to get the best DEX liquidity by aggregating multiple markets and offering the best rates",
"chainIds": [1, 56, 137],
"provider": null,
"accessControl": {
"type": "NO_RESTRICTIONS"
}
},
{
"id": 25,
"url": "https://safe-apps.dev.gnosisdev.com/wallet-connect",
"name": "WalletConnect",
"iconUrl": "https://safe-apps.dev.gnosisdev.com/wallet-connect/wallet-connect.svg",
"description": "Connect your Safe to any dApp that supports WalletConnect",
"chainIds": [1, 4, 10, 56, 100, 137, 246, 73799, 42161, 43114],
"provider": null,
"accessControl": {
"type": "NO_RESTRICTIONS"
},
"tags": ["tag1", "tag2"]
}
]