Skip to content
This repository was archived by the owner on Apr 13, 2025. It is now read-only.

Commit 70ad490

Browse files
authored
Merge pull request #211 from codeoverflow-org/fix/auto-set-instances-with-no-service
Automatically set instances with no required config
2 parents 09d5e76 + 9d9fc70 commit 70ad490

File tree

7 files changed

+84
-36
lines changed

7 files changed

+84
-36
lines changed

nodecg-io-core/dashboard/serviceInstance.ts

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { config, sendAuthenticatedMessage } from "./crypto";
1010

1111
const editorDefaultText = "<---- Select a service instance to start editing it in here";
1212
const editorCreateText = "<---- Create a new service instance on the left and then you can edit it in here";
13+
const editorInvalidServiceText = "!!!!! Service of this instance couldn't be found.";
14+
const editorNotConfigurableText = "----- This service cannot be configured.";
1315

1416
document.addEventListener("DOMContentLoaded", () => {
1517
config.onChange(() => {
@@ -59,18 +61,12 @@ export function onInstanceSelectChange(value: string): void {
5961
showNotice(undefined);
6062
switch (value) {
6163
case "new":
62-
editor?.updateOptions({
63-
readOnly: true,
64-
});
65-
editor?.setModel(monaco.editor.createModel(editorCreateText, "text"));
64+
showInMonaco("text", true, editorCreateText);
6665
setCreateInputs(true, false);
6766
inputInstanceName.value = "";
6867
break;
6968
case "select":
70-
editor?.updateOptions({
71-
readOnly: true,
72-
});
73-
editor?.setModel(monaco.editor.createModel(editorDefaultText, "text"));
69+
showInMonaco("text", true, editorDefaultText);
7470
setCreateInputs(false, false);
7571
break;
7672
default:
@@ -82,30 +78,15 @@ function showConfig(value: string) {
8278
const inst = config.data?.instances[value];
8379
const service = config.data?.services.find((svc) => svc.serviceType === inst?.serviceType);
8480

85-
editor?.updateOptions({
86-
readOnly: false,
87-
});
88-
89-
// Get rid of old models, as they have to be unique and we may add the same again
90-
monaco.editor.getModels().forEach((m) => m.dispose());
81+
if (!service) {
82+
showInMonaco("text", true, editorInvalidServiceText);
83+
} else if (service.requiresNoConfig) {
84+
showInMonaco("text", true, editorNotConfigurableText);
85+
} else {
86+
const jsonString = JSON.stringify(inst?.config || {}, null, 4);
87+
showInMonaco("json", false, jsonString, service?.schema);
88+
}
9189

92-
// This model uri can be completely made up as long the uri in the schema matches with the one in the language model.
93-
const modelUri = monaco.Uri.parse(`mem://nodecg-io/${inst?.serviceType}.json`);
94-
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
95-
validate: service?.schema !== undefined,
96-
schemas:
97-
service?.schema !== undefined
98-
? [
99-
{
100-
uri: modelUri.toString(),
101-
fileMatch: [modelUri.toString()],
102-
schema: objectDeepCopy(service?.schema),
103-
},
104-
]
105-
: [],
106-
});
107-
const model = monaco.editor.createModel(JSON.stringify(inst?.config || {}, null, 4), "json", modelUri);
108-
editor?.setModel(model);
10990
setCreateInputs(false, true);
11091
}
11192

@@ -246,3 +227,39 @@ export function showNotice(msg: string | undefined): void {
246227
spanInstanceNotice.innerText = msg !== undefined ? msg : "";
247228
}
248229
}
230+
231+
function showInMonaco(
232+
type: "text" | "json",
233+
readOnly: boolean,
234+
content: string,
235+
schema?: Record<string, unknown>,
236+
): void {
237+
editor?.updateOptions({ readOnly });
238+
239+
// JSON Schema stuff
240+
// Get rid of old models, as they have to be unique and we may add the same again
241+
monaco.editor.getModels().forEach((m) => m.dispose());
242+
243+
// This model uri can be completely made up as long the uri in the schema matches with the one in the language model.
244+
const modelUri = monaco.Uri.parse(`mem://nodecg-io/selectedServiceSchema.json`);
245+
246+
monaco.languages.json.jsonDefaults.setDiagnosticsOptions(
247+
schema
248+
? {
249+
validate: true,
250+
schemas: [
251+
{
252+
uri: modelUri.toString(),
253+
fileMatch: [modelUri.toString()],
254+
schema: objectDeepCopy(schema),
255+
},
256+
],
257+
}
258+
: {
259+
validate: false, // if not set we disable validation again.
260+
schemas: [],
261+
},
262+
);
263+
264+
editor?.setModel(monaco.editor.createModel(content, type));
265+
}

nodecg-io-core/extension/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ module.exports = (nodecg: NodeCG): NodeCGIOCore => {
2222
const serviceManager = new ServiceManager(nodecg);
2323
const bundleManager = new BundleManager(nodecg);
2424
const instanceManager = new InstanceManager(nodecg, serviceManager, bundleManager);
25-
const persistenceManager = new PersistenceManager(nodecg, instanceManager, bundleManager);
25+
const persistenceManager = new PersistenceManager(nodecg, serviceManager, instanceManager, bundleManager);
2626

2727
new MessageManager(
2828
nodecg,

nodecg-io-core/extension/instanceManager.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,23 @@ export class InstanceManager extends EventEmitter {
6565
const service = svcResult.result;
6666

6767
// Create actual instance and save it
68-
this.serviceInstances[instanceName] = {
68+
const inst = {
6969
serviceType: service.serviceType,
7070
config: service.defaultConfig,
7171
client: undefined,
7272
};
73+
this.serviceInstances[instanceName] = inst;
7374
this.emit("change");
7475

7576
this.nodecg.log.info(
7677
`Service instance "${instanceName}" of service "${service.serviceType}" has been successfully created.`,
7778
);
79+
80+
// Service requires no config, we can create it right now.
81+
if (service.requiresNoConfig) {
82+
this.updateInstanceClient(inst, instanceName, service);
83+
}
84+
7885
return emptySuccess();
7986
}
8087

@@ -145,7 +152,7 @@ export class InstanceManager extends EventEmitter {
145152
return error("The service of this instance couldn't be found.");
146153
}
147154

148-
if (validation) {
155+
if (validation || !service.result.requiresNoConfig) {
149156
const schemaValid = this.ajv.validate(service.result.schema, config);
150157
if (!schemaValid) {
151158
return error("Config invalid: " + this.ajv.errorsText());

nodecg-io-core/extension/persistenceManager.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { BundleManager } from "./bundleManager";
44
import * as crypto from "crypto-js";
55
import { emptySuccess, error, Result, success } from "./utils/result";
66
import { ObjectMap, ServiceDependency, ServiceInstance } from "./types";
7+
import { ServiceManager } from "./serviceManager";
78

89
/**
910
* Models all the data that needs to be persistent in a plain manner.
@@ -58,6 +59,7 @@ export class PersistenceManager {
5859

5960
constructor(
6061
private readonly nodecg: NodeCG,
62+
private readonly services: ServiceManager,
6163
private readonly instances: InstanceManager,
6264
private readonly bundles: BundleManager,
6365
) {
@@ -158,6 +160,11 @@ export class PersistenceManager {
158160
continue;
159161
}
160162

163+
const svc = this.services.getService(inst.serviceType);
164+
if (!svc.failed && svc.result.requiresNoConfig) {
165+
continue;
166+
}
167+
161168
// Re-set config of this instance.
162169
// We can skip the validation here because the config was already validated when it was initially set,
163170
// before getting saved to disk.

nodecg-io-core/extension/serviceBundle.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,17 @@ export abstract class ServiceBundle<R, C> implements Service<R, C> {
9696
* It gets rid of the handlers by stopping the client and creating a new one, to which then only the
9797
* now wanted handlers get registered (e.g. if a bundle doesn't uses this service anymore but another still does).
9898
* Not ideal, but if your service can't implement removeHandlers for some reason it is still better than
99-
* having dangling handlers that still fire eventho they shouldn't.
99+
* having dangling handlers that still fire events eventho they shouldn't.
100100
*/
101101
reCreateClientToRemoveHandlers = false;
102102

103+
/**
104+
* This flag says that this service cannot be configured and doesn't need any config passed to {@link createClient}.
105+
* If this is set {@link validateConfig} will never be called.
106+
* @default false
107+
*/
108+
requiresNoConfig = false;
109+
103110
private readSchema(pathSegments: string[]): unknown {
104111
const joinedPath = path.resolve(...pathSegments);
105112
try {

nodecg-io-core/extension/types.d.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,22 @@ export interface Service<R, C extends ServiceClient<unknown>> {
7575
readonly removeHandlers?(client: C): void;
7676

7777
/**
78-
* This flag can be enabled by services if they can't implement removeHandlers but also have some handlers that
78+
* This flag can be enabled by services if they can't implement {@link removeHandlers} but also have some handlers that
7979
* should be reset if a bundleDependency has been changed.
8080
* It gets rid of the handlers by stopping the client and creating a new one, to which then only the
8181
* now wanted handlers get registered (e.g. if a bundle doesn't uses this service anymore but another still does).
8282
* Not ideal, but if your service can't implement removeHandlers for some reason it is still better than
8383
* having dangling handlers that still fire eventho they shouldn't.
84+
* @default false
8485
*/
8586
reCreateClientToRemoveHandlers: boolean;
87+
88+
/**
89+
* This flag says that this service cannot be configured and doesn't need any config passed to {@link createClient}.
90+
* If this is set {@link validateConfig} will never be called.
91+
* @default false
92+
*/
93+
requiresNoConfig: boolean;
8694
}
8795

8896
/**

nodecg-io-curseforge/extension/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,6 @@ class CurseforgeService extends ServiceBundle<never, CurseForgeClient> {
5353
stopClient(_: CurseForgeClient): void {
5454
this.nodecg.log.info("Successfully stopped CurseForge client.");
5555
}
56+
57+
requiresNoConfig = true;
5658
}

0 commit comments

Comments
 (0)