Skip to content

Commit e7b89d7

Browse files
author
Eric Swanson
authored
feat: generate extension manifest schema (#3806)
1 parent 9123924 commit e7b89d7

6 files changed

Lines changed: 250 additions & 7 deletions

File tree

.github/workflows/update-docs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ jobs:
2828
cargo run -- schema --outfile docs/dfx-json-schema.json
2929
cargo run -- schema --for networks --outfile docs/networks-json-schema.json
3030
cargo run -- schema --for dfx-metadata --outfile docs/dfx-metadata-schema.json
31+
cargo run -- schema --for extension-manifest --outfile docs/extension-manifest-schema.json
3132
3233
echo "JSON Schema changes:"
3334
if git diff --exit-code ; then

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
# UNRELEASED
44

5+
### feat: add `dfx schema --for extension-manifest`
6+
7+
The schema command can now output the schema for extension.json files.
8+
59
# 0.21.0
610

711
### feat: dfx killall

docs/cli-reference/dfx-schema.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ You can use the following option with the `dfx schema` command.
1818

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

2424
## Examples
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "ExtensionManifest",
4+
"type": "object",
5+
"required": [
6+
"categories",
7+
"homepage",
8+
"name",
9+
"summary",
10+
"version"
11+
],
12+
"properties": {
13+
"authors": {
14+
"type": [
15+
"string",
16+
"null"
17+
]
18+
},
19+
"canister_type": {
20+
"anyOf": [
21+
{
22+
"$ref": "#/definitions/ExtensionCanisterType"
23+
},
24+
{
25+
"type": "null"
26+
}
27+
]
28+
},
29+
"categories": {
30+
"type": "array",
31+
"items": {
32+
"type": "string"
33+
}
34+
},
35+
"dependencies": {
36+
"type": [
37+
"object",
38+
"null"
39+
],
40+
"additionalProperties": {
41+
"type": "string"
42+
}
43+
},
44+
"description": {
45+
"type": [
46+
"string",
47+
"null"
48+
]
49+
},
50+
"homepage": {
51+
"type": "string"
52+
},
53+
"keywords": {
54+
"type": [
55+
"array",
56+
"null"
57+
],
58+
"items": {
59+
"type": "string"
60+
}
61+
},
62+
"name": {
63+
"type": "string"
64+
},
65+
"subcommands": {
66+
"anyOf": [
67+
{
68+
"$ref": "#/definitions/ExtensionSubcommandsOpts"
69+
},
70+
{
71+
"type": "null"
72+
}
73+
]
74+
},
75+
"summary": {
76+
"type": "string"
77+
},
78+
"version": {
79+
"type": "string"
80+
}
81+
},
82+
"additionalProperties": false,
83+
"definitions": {
84+
"ArgNumberOfValues": {
85+
"oneOf": [
86+
{
87+
"description": "zero or more values",
88+
"type": "object",
89+
"required": [
90+
"Number"
91+
],
92+
"properties": {
93+
"Number": {
94+
"type": "integer",
95+
"format": "uint",
96+
"minimum": 0.0
97+
}
98+
},
99+
"additionalProperties": false
100+
},
101+
{
102+
"description": "non-inclusive range",
103+
"type": "object",
104+
"required": [
105+
"Range"
106+
],
107+
"properties": {
108+
"Range": {
109+
"$ref": "#/definitions/Range_of_uint"
110+
}
111+
},
112+
"additionalProperties": false
113+
},
114+
{
115+
"description": "unlimited values",
116+
"type": "string",
117+
"enum": [
118+
"Unlimited"
119+
]
120+
}
121+
]
122+
},
123+
"ExtensionCanisterType": {
124+
"type": "object",
125+
"properties": {
126+
"defaults": {
127+
"description": "Default values for the canister type. These values are used when the user does not provide values in dfx.json. The \"metadata\" field, if present, is appended to the metadata field from dfx.json, which has the effect of providing defaults. The \"tech_stack field, if present, it merged with the tech_stack field from dfx.json, which also has the effect of providing defaults.",
128+
"default": {},
129+
"type": "object",
130+
"additionalProperties": true
131+
},
132+
"evaluation_order": {
133+
"description": "If one field depends on another and both specify a handlebars expression, list the fields in the order that they should be evaluated.",
134+
"default": [],
135+
"type": "array",
136+
"items": {
137+
"type": "string"
138+
}
139+
}
140+
}
141+
},
142+
"ExtensionSubcommandArgOpts": {
143+
"type": "object",
144+
"properties": {
145+
"about": {
146+
"type": [
147+
"string",
148+
"null"
149+
]
150+
},
151+
"long": {
152+
"type": [
153+
"string",
154+
"null"
155+
]
156+
},
157+
"multiple": {
158+
"default": false,
159+
"deprecated": true,
160+
"type": "boolean"
161+
},
162+
"short": {
163+
"type": [
164+
"string",
165+
"null"
166+
],
167+
"maxLength": 1,
168+
"minLength": 1
169+
},
170+
"values": {
171+
"$ref": "#/definitions/ArgNumberOfValues"
172+
}
173+
},
174+
"additionalProperties": false
175+
},
176+
"ExtensionSubcommandOpts": {
177+
"type": "object",
178+
"properties": {
179+
"about": {
180+
"type": [
181+
"string",
182+
"null"
183+
]
184+
},
185+
"args": {
186+
"type": [
187+
"object",
188+
"null"
189+
],
190+
"additionalProperties": {
191+
"$ref": "#/definitions/ExtensionSubcommandArgOpts"
192+
}
193+
},
194+
"subcommands": {
195+
"anyOf": [
196+
{
197+
"$ref": "#/definitions/ExtensionSubcommandsOpts"
198+
},
199+
{
200+
"type": "null"
201+
}
202+
]
203+
}
204+
},
205+
"additionalProperties": false
206+
},
207+
"ExtensionSubcommandsOpts": {
208+
"type": "object",
209+
"additionalProperties": {
210+
"$ref": "#/definitions/ExtensionSubcommandOpts"
211+
}
212+
},
213+
"Range_of_uint": {
214+
"type": "object",
215+
"required": [
216+
"end",
217+
"start"
218+
],
219+
"properties": {
220+
"end": {
221+
"type": "integer",
222+
"format": "uint",
223+
"minimum": 0.0
224+
},
225+
"start": {
226+
"type": "integer",
227+
"format": "uint",
228+
"minimum": 0.0
229+
}
230+
}
231+
}
232+
}
233+
}

src/dfx-core/src/extension/manifest/extension.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::error::extension::{
22
ConvertExtensionSubcommandIntoClapArgError, ConvertExtensionSubcommandIntoClapCommandError,
33
LoadExtensionManifestError,
44
};
5+
use schemars::JsonSchema;
56
use serde::{Deserialize, Deserializer};
67
use serde_json::Value;
78
use std::path::PathBuf;
@@ -15,7 +16,7 @@ pub static MANIFEST_FILE_NAME: &str = "extension.json";
1516
type SubcmdName = String;
1617
type ArgName = String;
1718

18-
#[derive(Debug, Deserialize)]
19+
#[derive(Debug, Deserialize, JsonSchema)]
1920
#[serde(deny_unknown_fields)]
2021
pub struct ExtensionManifest {
2122
pub name: String,
@@ -62,7 +63,7 @@ impl ExtensionManifest {
6263
}
6364
}
6465

65-
#[derive(Debug, Deserialize)]
66+
#[derive(Debug, Deserialize, JsonSchema)]
6667
pub struct ExtensionCanisterType {
6768
/// If one field depends on another and both specify a handlebars expression,
6869
/// list the fields in the order that they should be evaluated.
@@ -79,18 +80,18 @@ pub struct ExtensionCanisterType {
7980
pub defaults: BTreeMap<String, Value>,
8081
}
8182

82-
#[derive(Debug, Deserialize, Default)]
83+
#[derive(Debug, Deserialize, Default, JsonSchema)]
8384
pub struct ExtensionSubcommandsOpts(BTreeMap<SubcmdName, ExtensionSubcommandOpts>);
8485

85-
#[derive(Debug, Deserialize)]
86+
#[derive(Debug, Deserialize, JsonSchema)]
8687
#[serde(deny_unknown_fields)]
8788
pub struct ExtensionSubcommandOpts {
8889
pub about: Option<String>,
8990
pub args: Option<BTreeMap<ArgName, ExtensionSubcommandArgOpts>>,
9091
pub subcommands: Option<ExtensionSubcommandsOpts>,
9192
}
9293

93-
#[derive(Debug, Deserialize)]
94+
#[derive(Debug, Deserialize, JsonSchema)]
9495
#[serde(deny_unknown_fields)]
9596
pub struct ExtensionSubcommandArgOpts {
9697
pub about: Option<String>,
@@ -103,7 +104,7 @@ pub struct ExtensionSubcommandArgOpts {
103104
pub values: ArgNumberOfValues,
104105
}
105106

106-
#[derive(Debug)]
107+
#[derive(Debug, JsonSchema)]
107108
pub enum ArgNumberOfValues {
108109
/// zero or more values
109110
Number(usize),

src/dfx/src/commands/schema.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ enum ForFile {
1010
Dfx,
1111
Networks,
1212
DfxMetadata,
13+
ExtensionManifest,
1314
}
1415

1516
/// Prints the schema for dfx.json.
@@ -27,6 +28,9 @@ pub fn exec(opts: SchemaOpts) -> DfxResult {
2728
let schema = match opts.r#for {
2829
Some(ForFile::Networks) => schema_for!(TopLevelConfigNetworks),
2930
Some(ForFile::DfxMetadata) => schema_for!(DfxMetadata),
31+
Some(ForFile::ExtensionManifest) => {
32+
schema_for!(dfx_core::extension::manifest::ExtensionManifest)
33+
}
3034
_ => schema_for!(ConfigInterface),
3135
};
3236
let nice_schema =

0 commit comments

Comments
 (0)