Skip to content

Commit

Permalink
feat: add AGENT_ISSUANCE_CREDENTIAL_LOGO_URL and test feature flag (
Browse files Browse the repository at this point in the history
#11)

* feat: make startup commands segregated

* refactor: reuse `startup_commands` in `agent_api_rest` unit tests

* feat: accept host url in `startup_commands`

* feat: support credential logo url

* fix: remove temp test

* feat: add `test` feature to `agent_shared` to allow for testing across agents

* test: use the `agent_shared` `test` feature

* fix: fix test feature settings for `fn config`

* fix: remove

* fix: make optional

* fix: typo
  • Loading branch information
nanderstabel authored Dec 21, 2023
1 parent c090a5b commit 0015888
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 40 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
AGENT_CONFIG_LOG_FORMAT=json
AGENT_CONFIG_EVENT_STORE=postgres
AGENT_APPLICATION_HOST=my-domain.example.org
AGENT_ISSUANCE_CREDENTIAL_NAME="Demo Credential"
AGENT_ISSUANCE_CREDENTIAL_LOGO_URL=https://my-domain.example.org/credential_logo.png
AGENT_STORE_DB_CONNECTION_STRING=postgresql://demo_user:demo_pass@localhost:5432/demo
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions agent_api_rest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ serde_json.workspace = true
tracing.workspace = true

[dev-dependencies]
agent_shared = { path = "../agent_shared", features = ["test"] }
agent_store = { path = "../agent_store" }

lazy_static.workspace = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,23 @@ mod tests {
startup_commands::{create_credentials_supported, load_credential_issuer_metadata},
state::{initialize, CQRS},
};
use agent_shared::config;
use agent_store::in_memory;
use axum::{
body::Body,
http::{self, Request},
};
use serde_json::{json, Value};
use oid4vci::{
credential_format_profiles::{
w3c_verifiable_credentials::jwt_vc_json::{CredentialDefinition, JwtVcJson},
CredentialFormats, Parameters,
},
credential_issuer::{
credential_issuer_metadata::CredentialIssuerMetadata, credentials_supported::CredentialsSupportedObject,
},
ProofType,
};
use serde_json::json;
use tower::ServiceExt;

#[tokio::test]
Expand Down Expand Up @@ -73,31 +84,41 @@ mod tests {
assert_eq!(response.status(), StatusCode::OK);

let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
let body: Value = serde_json::from_slice(&body).unwrap();
let credential_issuer_metadata: CredentialIssuerMetadata = serde_json::from_slice(&body).unwrap();
assert_eq!(
body,
json!({
"credential_issuer": "https://example.com/",
"credential_endpoint": "https://example.com/openid4vci/credential",
"credentials_supported": [{
"format": "jwt_vc_json",
"cryptographic_binding_methods_supported": [
"did:key"
],
"cryptographic_suites_supported": [
"EdDSA"
],
"credential_definition":{
"type": [
"VerifiableCredential",
"OpenBadgeCredential"
]
},
"proof_types_supported": [
"jwt"
]
}]
})
credential_issuer_metadata,
CredentialIssuerMetadata {
credential_issuer: BASE_URL.clone(),
authorization_server: None,
credential_endpoint: BASE_URL.join("openid4vci/credential").unwrap(),
batch_credential_endpoint: None,
deferred_credential_endpoint: None,
credentials_supported: vec![CredentialsSupportedObject {
id: None,
credential_format: CredentialFormats::JwtVcJson(Parameters {
format: JwtVcJson,
parameters: (
CredentialDefinition {
type_: vec!["VerifiableCredential".to_string(), "OpenBadgeCredential".to_string()],
credential_subject: None,
},
None,
)
.into(),
}),
scope: None,
cryptographic_binding_methods_supported: Some(vec!["did:key".to_string()]),
cryptographic_suites_supported: Some(vec!["EdDSA".to_string()]),
proof_types_supported: Some(vec![ProofType::Jwt]),
display: Some(vec![json!({
"name": config!("credential_name").unwrap(),
"logo": {
"url": config!("credential_logo_url").unwrap()
}
})]),
}],
display: None,
}
);
}
}
6 changes: 3 additions & 3 deletions agent_issuance/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ impl View<IssuanceData> for IssuanceDataView {
.replace(credential_offer.clone());
}
UnsignedCredentialCreated { subject_id, credential } => {
if let Some(subject) = self.subjects
.iter_mut()
.find(|subject| subject.id == *subject_id) { subject.credentials.replace(credential.clone()); }
if let Some(subject) = self.subjects.iter_mut().find(|subject| subject.id == *subject_id) {
subject.credentials.replace(credential.clone());
}
}
PreAuthorizedCodeUpdated {
subject_id,
Expand Down
12 changes: 11 additions & 1 deletion agent_issuance/src/startup_commands.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use agent_shared::config;
use oid4vci::{
credential_format_profiles::{
w3c_verifiable_credentials::jwt_vc_json::{CredentialDefinition, JwtVcJson},
Expand All @@ -9,6 +10,7 @@ use oid4vci::{
},
ProofType,
};
use serde_json::json;

use crate::command::IssuanceCommand;

Expand Down Expand Up @@ -74,7 +76,15 @@ pub fn create_credentials_supported() -> IssuanceCommand {
cryptographic_binding_methods_supported: Some(vec!["did:key".to_string()]),
cryptographic_suites_supported: Some(vec!["EdDSA".to_string()]),
proof_types_supported: Some(vec![ProofType::Jwt]),
display: None,
display: match (config!("credential_name"), config!("credential_logo_url")) {
(Ok(name), Ok(logo_url)) => Some(vec![json!({
"name": name,
"logo": {
"url": logo_url
}
})]),
_ => None,
},
}],
}
}
3 changes: 3 additions & 0 deletions agent_shared/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ edition = "2021"
config = { version = "0.13" }
dotenvy = { version = "0.15" }
tracing.workspace = true

[features]
test = []
33 changes: 25 additions & 8 deletions agent_shared/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
use tracing::info;

/// Read environment variables
#[allow(unused)]
pub fn config(package_name: &str) -> config::Config {
// Load global .env file
dotenvy::dotenv().ok();
#[cfg(feature = "test")]
let config = test_config();

// Build configuration
let config = config::Config::builder()
.add_source(config::Environment::with_prefix(package_name))
.add_source(config::Environment::with_prefix("AGENT_CONFIG"))
.build()
.unwrap();
#[cfg(not(feature = "test"))]
let config = {
dotenvy::dotenv().ok();

config::Config::builder()
.add_source(config::Environment::with_prefix(package_name))
.add_source(config::Environment::with_prefix("AGENT_CONFIG"))
.build()
.unwrap()
};

info!("{:?}", config);

config
}

/// Read environment variables for tests that can be used across packages
#[cfg(feature = "test")]
fn test_config() -> config::Config {
dotenvy::from_filename("agent_shared/tests/.env.test").ok();

config::Config::builder()
.add_source(config::Environment::with_prefix("TEST"))
.add_source(config::Environment::with_prefix("AGENT_CONFIG"))
.build()
.unwrap()
}
4 changes: 3 additions & 1 deletion agent_shared/tests/.env.test
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
AGENT_SHARED_VARIABLE=env_value
TEST_VARIABLE=env_value
AGENT_CONFIG_GLOBAL_VARIABLE=global_env_value
AGENT_OTHER_OTHER_VARIABLE=other_env_value
TEST_CREDENTIAL_NAME="Demo Credential"
TEST_CREDENTIAL_LOGO_URL=https://my-domain.example.org/credential_logo.png
3 changes: 1 addition & 2 deletions agent_shared/tests/config.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use agent_shared::config;

#[cfg(feature = "test")]
#[test]
fn test_config() {
dotenvy::from_filename("tests/.env.test").ok();

assert_eq!(config!("variable").unwrap(), "env_value");
assert_eq!(config!("global_variable").unwrap(), "global_env_value");
// Reading from an environment variable that belongs to another package should fail.
Expand Down

0 comments on commit 0015888

Please sign in to comment.