Skip to content
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
1 change: 1 addition & 0 deletions .github/workflows/update-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
cargo run -- schema --for networks --outfile docs/networks-json-schema.json
cargo run -- schema --for dfx-metadata --outfile docs/dfx-metadata-schema.json
cargo run -- schema --for extension-manifest --outfile docs/extension-manifest-schema.json
cargo run -- schema --for extension-dependencies --outfile docs/extension-dependencies-schema.json

echo "JSON Schema changes:"
if git diff --exit-code ; then
Expand Down
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@

To do this, the `@dfinity/agent` version was updated as well.

### feat: add `dfx schema --for extension-manifest`
### feat: add `dfx schema` support for .json files related to extensions

The schema command can now output the schema for extension.json files.
- `dfx schema --for extension-manifest` corresponds to extension.json
- `dfx schema --for extension-dependencies` corresponds to dependencies.json

### chore!: enforce minimum password length of 9 characters

Expand Down
21 changes: 17 additions & 4 deletions docs/cli-reference/dfx-schema.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,20 @@ dfx schema [option] [flag]

You can use the following option with the `dfx schema` command.

| Option | Description |
|------------------------|--------------------------------------------------------------------------------------------------|
| `--for <which>` | Display schema for which JSON file. (default: dfx, possible values: dfx, networks, dfx-metadata, extension-manifest) |
| `--outfile <outfile>` | Specifies a file to output the schema to instead of printing it to stdout. |
| Option | Description |
|------------------------|----------------------------------------------------------------------------|
| `--for <which>` | Display schema for which JSON file. (default: dfx) |
| `--outfile <outfile>` | Specifies a file to output the schema to instead of printing it to stdout. |

The parameter passed to `--for` can be one of the following:

| `<for>` option | Corresponds To |
|------------------------|--------------------------------------------------------|
| dfx | dfx.json |
| networks | networks.json |
| dfx-metadata | dfx-related metadata stored in canister and accessible with `dfx canister metadata <canister> dfx` |
| extension-manifest | extension.json [example][example-extension-json] |
| extension-dependencies | dependencies.json [example][example-dependencies-json] |

## Examples

Expand All @@ -46,3 +56,6 @@ If you want to write the schema for dfx.json to `path/to/file/schema.json`, you
``` bash
dfx schema --outfile path/to/file/schema.json
```

[example-extension-json]: https://raw.githubusercontent.com/dfinity/dfx-extensions/main/extensions/nns/extension.json
[example-dependencies-json]: https://raw.githubusercontent.com/dfinity/dfx-extensions/main/extensions/nns/dependencies.json
30 changes: 30 additions & 0 deletions docs/extension-dependencies-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ExtensionDependencies",
"type": "object",
"additionalProperties": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/DependencyRequirement"
}
},
"definitions": {
"DependencyRequirement": {
"oneOf": [
{
"description": "A SemVer version requirement, for example \">=0.17.0, <0.19.0\".",
"type": "object",
"required": [
"version"
],
"properties": {
"version": {
"type": "string"
}
},
"additionalProperties": false
}
]
}
}
}
10 changes: 9 additions & 1 deletion docs/extension-manifest-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"null"
],
"additionalProperties": {
"type": "string"
"$ref": "#/definitions/ExtensionDependency"
}
},
"description": {
Expand Down Expand Up @@ -139,6 +139,14 @@
}
}
},
"ExtensionDependency": {
"anyOf": [
{
"description": "A SemVer version requirement, for example \">=0.17.0\".",
"type": "string"
}
]
},
"ExtensionSubcommandArgOpts": {
"type": "object",
"properties": {
Expand Down
58 changes: 58 additions & 0 deletions src/dfx-core/src/extension/manifest/dependencies.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use crate::json::structure::VersionReqWithJsonSchema;
use candid::Deserialize;
use schemars::JsonSchema;
use semver::Version;
use std::collections::HashMap;

type ExtensionVersion = Version;
type DependencyName = String;

#[derive(Debug, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum DependencyRequirement {
/// A SemVer version requirement, for example ">=0.17.0, <0.19.0".
Version(VersionReqWithJsonSchema),
}

#[derive(Deserialize, Debug, JsonSchema)]
pub struct ExtensionDependencies(
pub HashMap<ExtensionVersion, HashMap<DependencyName, DependencyRequirement>>,
);

#[test]
fn parse_test_file() {
let f = r#"
{
"0.3.4": {
"dfx": {
"version": ">=0.8, <0.9"
}
},
"0.6.2": {
"dfx": {
"version": ">=0.9.6"
}
}
}
"#;
let m: Result<ExtensionDependencies, serde_json::Error> = dbg!(serde_json::from_str(f));
assert!(m.is_ok());
let manifest = m.unwrap();

let versions = manifest.0.keys().collect::<Vec<_>>();
assert_eq!(versions.len(), 2);
assert!(versions.contains(&&Version::new(0, 3, 4)));
assert!(versions.contains(&&Version::new(0, 6, 2)));

let v_3_4 = manifest.0.get(&Version::new(0, 3, 4)).unwrap();
let dfx = v_3_4.get("dfx").unwrap();
let DependencyRequirement::Version(req) = dfx;
assert!(req.matches(&semver::Version::new(0, 8, 5)));
assert!(!req.matches(&semver::Version::new(0, 9, 0)));

let v_6_2 = manifest.0.get(&Version::new(0, 6, 2)).unwrap();
let dfx = v_6_2.get("dfx").unwrap();
let DependencyRequirement::Version(req) = dfx;
assert!(req.matches(&semver::Version::new(0, 9, 6)));
assert!(!req.matches(&semver::Version::new(0, 9, 5)));
}
22 changes: 20 additions & 2 deletions src/dfx-core/src/extension/manifest/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::error::extension::{
ConvertExtensionSubcommandIntoClapArgError, ConvertExtensionSubcommandIntoClapCommandError,
LoadExtensionManifestError,
};
use crate::json::structure::VersionReqWithJsonSchema;
use schemars::JsonSchema;
use serde::{Deserialize, Deserializer};
use serde_json::Value;
Expand All @@ -28,10 +29,17 @@ pub struct ExtensionManifest {
pub keywords: Option<Vec<String>>,
pub description: Option<String>,
pub subcommands: Option<ExtensionSubcommandsOpts>,
pub dependencies: Option<HashMap<String, String>>,
pub dependencies: Option<HashMap<String, ExtensionDependency>>,
pub canister_type: Option<ExtensionCanisterType>,
}

#[derive(Debug, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum ExtensionDependency {
/// A SemVer version requirement, for example ">=0.17.0".
Version(VersionReqWithJsonSchema),
}

impl ExtensionManifest {
pub fn load(
name: &str,
Expand Down Expand Up @@ -236,6 +244,9 @@ fn parse_test_file() {
"sns",
"nns"
],
"dependencies": {
"dfx": ">=0.8, <0.9"
},
"keywords": [
"sns",
"nns",
Expand Down Expand Up @@ -361,8 +372,15 @@ fn parse_test_file() {

let m: Result<ExtensionManifest, serde_json::Error> = dbg!(serde_json::from_str(f));
assert!(m.is_ok());
let manifest = m.unwrap();

let dependencies = manifest.dependencies.as_ref().unwrap();
let dfx_dep = dependencies.get("dfx").unwrap();
let ExtensionDependency::Version(req) = dfx_dep;
assert!(req.matches(&semver::Version::new(0, 8, 5)));
assert!(!req.matches(&semver::Version::new(0, 9, 0)));

let mut subcmds = dbg!(m.unwrap().into_clap_commands().unwrap());
let mut subcmds = dbg!(manifest.into_clap_commands().unwrap());

use clap::error::ErrorKind::*;
for c in &mut subcmds {
Expand Down
13 changes: 7 additions & 6 deletions src/dfx-core/src/extension/manifest/mod.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
//! Directory contains code that parses the .json files.

pub mod compatibility_matrix;
pub mod dependencies;
pub mod extension;

/// `compatibility.json` is a file describing the compatibility
/// matrix between extensions versions and the dfx version.
pub use compatibility_matrix::ExtensionCompatibilityMatrix;
/// URL to the `compatibility.json` file.
pub use compatibility_matrix::COMMON_EXTENSIONS_MANIFEST_LOCATION;

pub mod extension;
/// `manifest.json` is a file describing the extension.
/// A file that lists the dependencies of all versions of an extension.
pub use dependencies::ExtensionDependencies;

/// A file that describes an extension.
pub use extension::ExtensionManifest;
/// File name for the file describing the extension.
pub use extension::MANIFEST_FILE_NAME;
20 changes: 20 additions & 0 deletions src/dfx-core/src/json/structure.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use candid::Deserialize;
use schemars::JsonSchema;
use semver::VersionReq;
use serde::Serialize;
use std::fmt::Display;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;

#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, PartialEq, Eq)]
Expand Down Expand Up @@ -53,3 +55,21 @@ where
}
}
}

#[derive(Debug, Deserialize, JsonSchema)]
#[serde(transparent)]
pub struct VersionReqWithJsonSchema(#[schemars(with = "String")] pub VersionReq);

impl Deref for VersionReqWithJsonSchema {
type Target = VersionReq;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for VersionReqWithJsonSchema {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
7 changes: 4 additions & 3 deletions src/dfx/src/commands/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::lib::{error::DfxResult, metadata::dfx::DfxMetadata};
use anyhow::Context;
use clap::{Parser, ValueEnum};
use dfx_core::config::model::dfinity::{ConfigInterface, TopLevelConfigNetworks};
use dfx_core::extension::manifest::{ExtensionDependencies, ExtensionManifest};
use schemars::schema_for;
use std::path::PathBuf;

Expand All @@ -10,6 +11,7 @@ enum ForFile {
Dfx,
Networks,
DfxMetadata,
ExtensionDependencies,
ExtensionManifest,
}

Expand All @@ -28,9 +30,8 @@ pub fn exec(opts: SchemaOpts) -> DfxResult {
let schema = match opts.r#for {
Some(ForFile::Networks) => schema_for!(TopLevelConfigNetworks),
Some(ForFile::DfxMetadata) => schema_for!(DfxMetadata),
Some(ForFile::ExtensionManifest) => {
schema_for!(dfx_core::extension::manifest::ExtensionManifest)
}
Some(ForFile::ExtensionDependencies) => schema_for!(ExtensionDependencies),
Some(ForFile::ExtensionManifest) => schema_for!(ExtensionManifest),
_ => schema_for!(ConfigInterface),
};
let nice_schema =
Expand Down