Skip to content

Commit a0d3bac

Browse files
authored
Add plugins JSON schema and VSCode configuration to use it (#284)
* Add plugins JSON schema and VSCode configuration to use it * Add VSCode YAML extension to workspace recommendations * Fix formatting issues * Update VSCode settings to use Prettier for JSON and YAML formatting
1 parent bdf1f04 commit a0d3bac

3 files changed

Lines changed: 358 additions & 0 deletions

File tree

.vscode/extensions.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"recommendations": ["redhat.vscode-yaml", "esbenp.prettier-vscode"]
3+
}

.vscode/settings.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"yaml.validate": true,
3+
"yaml.disableAdditionalProperties": true,
4+
"yaml.completion": true,
5+
"yaml.extension.recommendations": true,
6+
"yaml.hover": true,
7+
"yaml.format.singleQuote": false,
8+
"yaml.format.printWidth": 120,
9+
"yaml.format.proseWrap": "always",
10+
"yaml.schemas": {
11+
"schema/plugin.schema.json": ["/plugins/**", "/themes/**"]
12+
},
13+
"[json]": {
14+
"editor.defaultFormatter": "esbenp.prettier-vscode"
15+
},
16+
"[jsonc]": {
17+
"editor.defaultFormatter": "esbenp.prettier-vscode"
18+
},
19+
"[yaml]": {
20+
"editor.defaultFormatter": "esbenp.prettier-vscode"
21+
}
22+
}

schema/plugin.schema.json

Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema",
3+
"$id": "plugin",
4+
"title": "Stash Plugin",
5+
"description": "A stash plugin config",
6+
"type": "object",
7+
"additionalProperties": false,
8+
"required": ["name"],
9+
"properties": {
10+
"name": {
11+
"title": "Plugin name",
12+
"description": "The name of the plugin.",
13+
"type": "string"
14+
},
15+
"description": {
16+
"title": "Plugin description",
17+
"description": "Short description of the plugin.",
18+
"type": ["string", "null"]
19+
},
20+
"version": {
21+
"title": "Plugin version",
22+
"description": "Format: x.y.z where x y and z are integers.",
23+
"type": ["string", "integer", "number"],
24+
"pattern": "^\\d+(\\.\\d+)?(\\.\\d+)?$"
25+
},
26+
"url": {
27+
"title": "Url",
28+
"description": "Optional url",
29+
"type": ["string", "null"]
30+
},
31+
"ui": {
32+
"title": "Plugin UI",
33+
"description": "Optional files needed to render plugin specific UI",
34+
"$ref": "#/definitions/UIConfig"
35+
},
36+
"exec": {
37+
"title": "Command to run the plugin",
38+
"description": "For external plugin tasks, the exec field is a list with the first element being the binary that will be executed, and the subsequent elements are the arguments passed. The execution process will search the path for the binary, then will attempt to find the program in the same directory as the plugin configuration file. The exe extension is not necessary on Windows systems.\n\nFor embedded plugins, the exec field is a list with the first element being the path to the Javascript file that will be executed. It is expected that the path to the Javascript file is relative to the directory of the plugin configuration file.",
39+
"type": "array",
40+
"items": {
41+
"type": "string"
42+
}
43+
},
44+
"interface": {
45+
"title": "Plugin interface",
46+
"description": "For external plugin tasks, the interface field must be set to one of the following values: rpc, raw\n\nFor embedded plugins, the interface field must be set to one of the following values: js\n\nThe interface field defaults to raw if not provided.",
47+
"type": "string",
48+
"enum": ["js", "raw", "rpc"],
49+
"default": "raw"
50+
},
51+
"hooks": {
52+
"title": "Hooks configuration",
53+
"description": "Array of individual hook configurations.",
54+
"type": "array",
55+
"items": {
56+
"$ref": "#/definitions/HookConfig"
57+
}
58+
},
59+
"tasks": {
60+
"title": "Tasks configuration",
61+
"description": "Array of individual tasks configurations.",
62+
"type": "array",
63+
"items": {
64+
"$ref": "#/definitions/TaskConfig"
65+
}
66+
},
67+
"settings": {
68+
"title": "Plugin settings",
69+
"description": "The settings defined for this plugin.",
70+
"$ref": "#/definitions/SettingConfig"
71+
}
72+
},
73+
//
74+
//
75+
"definitions": {
76+
"UIConfig": {
77+
"type": "object",
78+
"additionalProperties": false,
79+
"anyOf": [
80+
{
81+
"required": ["css"]
82+
},
83+
{
84+
"required": ["javascript"]
85+
}
86+
],
87+
"properties": {
88+
// # optional list of css files to include in the UI
89+
// css:
90+
// - <path to css file>
91+
"css": {
92+
"title": "CSS files",
93+
"description": "Optional list of CSS files to include in the UI",
94+
"items": {
95+
"description": "Path to CSS file"
96+
},
97+
"$ref": "#/definitions/StringList"
98+
},
99+
// # optional list of js files to include in the UI
100+
// javascript:
101+
// - <path to javascript file>
102+
"javascript": {
103+
"title": "Javascript files",
104+
"description": "Optional list of Javascript files to include in the UI",
105+
"items": {
106+
"description": "Path to Javascript file"
107+
},
108+
"$ref": "#/definitions/StringList"
109+
},
110+
// # optional list of plugin IDs to load prior to this plugin
111+
// requires:
112+
// - <plugin ID>
113+
"requires": {
114+
"title": "Required plugins",
115+
"description": "Optional list of plugin IDs to load prior to this plugin.",
116+
"items": {
117+
"description": "Required plugin ID"
118+
},
119+
"$ref": "#/definitions/StringList"
120+
},
121+
// # optional list of assets
122+
// assets:
123+
// urlPrefix: fsLocation
124+
// ...
125+
"assets": {
126+
"title": "Assets",
127+
"description": "Optional map of assets.",
128+
"type": "object",
129+
"patternProperties": {
130+
"": {
131+
"type": "string",
132+
"title": "Asset location",
133+
"description": "Asset location on the file system as pair of values urlPrefix: fsLocation."
134+
}
135+
}
136+
},
137+
// # content-security policy overrides
138+
// csp:
139+
// script-src:
140+
// - http://alloweddomain.com
141+
// style-src:
142+
// - http://alloweddomain.com
143+
// connect-src:
144+
// - http://alloweddomain.com
145+
"csp": {
146+
"title": "Content-security policy overrides",
147+
"description": "Optional map of policy directives",
148+
"type": "object",
149+
"additionalProperties": true,
150+
"properties": {
151+
"script-src": {
152+
"$ref": "#/definitions/CspDirective"
153+
},
154+
"style-src": {
155+
"$ref": "#/definitions/CspDirective"
156+
},
157+
"connect-src": {
158+
"$ref": "#/definitions/CspDirective"
159+
}
160+
},
161+
"patternProperties": {
162+
"": {
163+
"$ref": "#/definitions/CspDirective"
164+
}
165+
}
166+
},
167+
// # map of setting names to be displayed in the plugins page in the UI
168+
// settings:
169+
// # internal name
170+
// foo:
171+
// # name to display in the UI
172+
// displayName: Foo
173+
// # type of the attribute to show in the UI
174+
// # can be BOOLEAN, NUMBER, or STRING
175+
// type: BOOLEAN
176+
"settings": {
177+
"title": "UI plugin settings",
178+
"description": "Map of setting names to be displayed in the plugins page in the UI.",
179+
"$ref": "#/definitions/SettingConfig"
180+
}
181+
}
182+
},
183+
"HookConfig": {
184+
"type": "object",
185+
"required": ["name", "triggeredBy"],
186+
"properties": {
187+
"name": {
188+
"type": "string",
189+
"description": "Hook name"
190+
},
191+
"description": {
192+
"type": ["string", "null"],
193+
"description": "Optional description for this hook"
194+
},
195+
"triggeredBy": {
196+
"type": "array",
197+
"items": {
198+
"$ref": "#/definitions/TriggerType"
199+
}
200+
},
201+
"defaultArgs": {
202+
"description": "Default arguments",
203+
"type": "object",
204+
"items": {
205+
"type": "string"
206+
}
207+
}
208+
}
209+
},
210+
"TriggerType": {
211+
"type": "string",
212+
"pattern": "^(Scene|SceneMarker|Image|Gallery|GalleryChapter|Movie|Performer|Studio|Tag)\\.(Create|Update|Destroy|Merge)\\.Post$",
213+
"enum": [
214+
"Scene.Create.Post",
215+
"Scene.Update.Post",
216+
"Scene.Destroy.Post",
217+
"Scene.Merge.Post",
218+
//
219+
"SceneMarker.Create.Post",
220+
"SceneMarker.Update.Post",
221+
"SceneMarker.Destroy.Post",
222+
"SceneMarker.Merge.Post",
223+
//
224+
"Image.Create.Post",
225+
"Image.Update.Post",
226+
"Image.Destroy.Post",
227+
"Image.Merge.Post",
228+
//
229+
"Gallery.Create.Post",
230+
"Gallery.Update.Post",
231+
"Gallery.Destroy.Post",
232+
"Gallery.Merge.Post",
233+
//
234+
"GalleryChapter.Create.Post",
235+
"GalleryChapter.Update.Post",
236+
"GalleryChapter.Destroy.Post",
237+
//
238+
"Movie.Create.Post",
239+
"Movie.Update.Post",
240+
"Movie.Destroy.Post",
241+
"Movie.Merge.Post",
242+
//
243+
"Performer.Create.Post",
244+
"Performer.Update.Post",
245+
"Performer.Destroy.Post",
246+
"Performer.Merge.Post",
247+
//
248+
"Studio.Create.Post",
249+
"Studio.Update.Post",
250+
"Studio.Destroy.Post",
251+
"Studio.Merge.Post",
252+
//
253+
"Tag.Create.Post",
254+
"Tag.Update.Post",
255+
"Tag.Destroy.Post",
256+
"Tag.Merge.Post"
257+
]
258+
},
259+
"TaskConfig": {
260+
"type": "object",
261+
"required": ["name"],
262+
"properties": {
263+
"name": {
264+
"type": "string",
265+
"description": "Task name"
266+
},
267+
"description": {
268+
"type": "string",
269+
"description": "Optional description for this task"
270+
},
271+
"defaultArgs": {
272+
"description": "Default arguments",
273+
"type": "object",
274+
"additionalProperties": {
275+
"type": "string"
276+
}
277+
},
278+
"execArgs": {
279+
"description": "Default arguments",
280+
"type": "object",
281+
"additionalProperties": {
282+
"type": "string"
283+
}
284+
}
285+
}
286+
},
287+
"SettingConfig": {
288+
"type": "object",
289+
"patternProperties": {
290+
"": {
291+
"type": "object",
292+
"description": "Internal name",
293+
"required": ["displayName", "type"],
294+
"properties": {
295+
"displayName": {
296+
"type": "string",
297+
"description": "Name to display in the UI"
298+
},
299+
"description": {
300+
"type": ["string", "null"],
301+
"description": "Optional description for this setting"
302+
},
303+
"type": {
304+
"type": "string",
305+
"description": "Type of the attribute to show in the UI. It can be one of BOOLEAN, NUMBER, STRING",
306+
"enum": ["BOOLEAN", "NUMBER", "STRING"]
307+
}
308+
}
309+
}
310+
}
311+
},
312+
"StringList": {
313+
"type": ["array", "null"],
314+
"items": {
315+
"type": "string"
316+
}
317+
},
318+
"CspDirective": {
319+
"description": "Policy directive",
320+
"type": ["array", "null"],
321+
"items": {
322+
"type": "string",
323+
"description": "Allowed domain",
324+
"examples": [
325+
"self",
326+
"http://alloweddomain.com",
327+
"example.com",
328+
"*.example.com"
329+
]
330+
}
331+
}
332+
}
333+
}

0 commit comments

Comments
 (0)