Skip to content

Commit ae3b79c

Browse files
improve API for YAML custom tags
1 parent 0dcae0d commit ae3b79c

File tree

5 files changed

+65
-66
lines changed

5 files changed

+65
-66
lines changed

.changeset/bright-gifts-turn.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,20 @@
44

55
- Added `ServiceContextMode` to `ServiceContext`. This conveys the mode in which the PowerSync service was started in.
66
- `RouterEngine` is now always present on `ServiceContext`. The router will only configure actual servers, when started, if routes have been registered.
7-
- Added `!env_boolean` and `!env_number` YAML tag functions to `CompoundConfigCollector`.
7+
- Added typecasting to `!env` YAML custom tag function. YAML config environment variable substitution now support casting string environment variables to `number` and `boolean` types.
8+
9+
```yaml
10+
replication:
11+
connections: []
12+
13+
storage:
14+
type: mongodb
15+
16+
api:
17+
parameters:
18+
max_buckets_per_connection: !env PS_MAX_BUCKETS::number
19+
20+
healthcheck:
21+
probes:
22+
use_http: !env PS_MONGO_HEALTHCHECK::boolean
23+
```

.changeset/khaki-cows-thank.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,22 @@
22
'@powersync/service-image': minor
33
---
44

5-
- Added type casting for YAML tag functions. YAML config can now substitute environment variables as boolean and number types.
5+
- Added typecasting to `!env` YAML custom tag function. YAML config environment variable substitution now support casting string environment variables to `number` and `boolean` types.
66

77
```yaml
8-
# PowerSync config
8+
replication:
9+
connections: []
10+
11+
storage:
12+
type: mongodb
13+
914
api:
1015
parameters:
11-
max_buckets_per_connection: !env_number PS_MAX_BUCKETS
16+
max_buckets_per_connection: !env PS_MAX_BUCKETS::number
17+
1218
healthcheck:
1319
probes:
14-
http: !env_boolean PS_MONGO_HEALTHCHECK
20+
use_http: !env PS_MONGO_HEALTHCHECK::boolean
1521
```
1622
1723
- Added the ability to customize healthcheck probe exposure in the configuration. Backwards compatibility is maintained if no `healthcheck->probes` config is provided.
@@ -20,7 +26,7 @@ healthcheck:
2026
healthcheck:
2127
probes:
2228
# Health status can be accessed by reading files (previously always enabled)
23-
filesystem: true
29+
use_filesystem: true
2430
# Health status can be accessed via HTTP requests (previously enabled for API and UNIFIED service modes)
25-
http: true
31+
use_http: true
2632
```

packages/service-core/src/util/config/collectors/config-collector.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { configFile } from '@powersync/service-types';
44

55
import { schema } from '@powersync/lib-services-framework';
66
import { RunnerConfig } from '../types.js';
7-
import { YamlEnvTag, YamlEnvTagBoolean, YamlEnvTagNumber } from './impl/yaml-env.js';
7+
import { YamlEnvTag } from './impl/yaml-env.js';
88

99
export enum ConfigFileFormat {
1010
YAML = 'yaml',
@@ -89,7 +89,7 @@ export abstract class ConfigCollector {
8989
schema: 'core',
9090
keepSourceTokens: true,
9191
lineCounter,
92-
customTags: [YamlEnvTag, YamlEnvTagBoolean, YamlEnvTagNumber]
92+
customTags: [YamlEnvTag]
9393
});
9494

9595
if (parsed.errors.length) {

packages/service-core/src/util/config/collectors/impl/yaml-env.ts

Lines changed: 32 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -14,77 +14,54 @@ const YAML_ENV_PREFIX = 'PS_';
1414

1515
/**
1616
* Custom YAML tag which performs string environment variable substitution
17+
* Allows for type casting string environment variables to boolean or number
18+
* by using the syntax !env PS_MONGO_PORT::number or !env PS_USE_SUPABASE::boolean
1719
*/
1820
export const YamlEnvTag: yaml.ScalarTag = {
1921
tag: '!env',
20-
resolve(envName: string, onError: (error: string) => void): string {
22+
resolve(envName: string, onError: (error: string) => void) {
2123
if (!envName.startsWith(YAML_ENV_PREFIX)) {
2224
onError(
2325
`Attempting to substitute environment variable ${envName} is not allowed. Variables must start with "${YAML_ENV_PREFIX}"`
2426
);
2527
return envName;
2628
}
27-
const value = process.env[envName];
29+
30+
// allow type casting if the envName contains a type suffix
31+
// e.g. PS_MONGO_PORT::number or PS_USE_SUPABASE::boolean
32+
const [name, type = 'string'] = envName.split('::');
33+
34+
let value = process.env[name];
35+
2836
if (typeof value == 'undefined') {
2937
onError(
3038
`Attempted to substitute environment variable "${envName}" which is undefined. Set this variable on the environment.`
3139
);
3240
return envName;
3341
}
34-
return value;
35-
}
36-
};
3742

38-
/**
39-
* Casts the environment variable to a boolean.
40-
*/
41-
export const YamlEnvTagBoolean: yaml.ScalarTag = {
42-
tag: '!env_boolean',
43-
resolve(envName: string, onError: (error: string) => void) {
44-
const stringValue = YamlEnvTag.resolve(envName, onError, {});
45-
if (stringValue == envName) {
46-
// This is returned for error conditions
47-
return stringValue;
43+
switch (type) {
44+
case 'string':
45+
return value;
46+
case 'number':
47+
const numberValue = Number(value);
48+
if (Number.isNaN(numberValue)) {
49+
onError(`Environment variable "${envName}" is not a valid number. Got: "${value}".`);
50+
return envName;
51+
}
52+
return numberValue;
53+
case 'boolean':
54+
if (value?.toLowerCase() == 'true') {
55+
return true;
56+
} else if (value?.toLowerCase() == 'false') {
57+
return false;
58+
} else {
59+
onError(`Environment variable "${envName}" is not a boolean. Expected "true" or "false", got "${value}".`);
60+
return envName;
61+
}
62+
default:
63+
onError(`Environment variable "${envName}" has an invalid type suffix "${type}".`);
64+
return envName;
4865
}
49-
if (typeof stringValue !== 'string') {
50-
// should not reach this point
51-
onError(`Environment variable "${envName}" is not a string.`);
52-
return stringValue;
53-
}
54-
if (stringValue.toLowerCase() == 'true') {
55-
return true;
56-
} else if (stringValue.toLowerCase() == 'false') {
57-
return false;
58-
} else {
59-
onError(`Environment variable "${envName}" is not a boolean. Expected "true" or "false", got "${stringValue}".`);
60-
return stringValue;
61-
}
62-
}
63-
};
64-
65-
/**
66-
* Casts the environment variable to a number.
67-
*/
68-
export const YamlEnvTagNumber: yaml.ScalarTag = {
69-
tag: '!env_number',
70-
resolve(envName: string, onError: (error: string) => void) {
71-
const stringValue = YamlEnvTag.resolve(envName, onError, {});
72-
if (stringValue == envName) {
73-
// This is returned for error conditions from YamlEnvTag
74-
return stringValue;
75-
}
76-
77-
if (typeof stringValue !== 'string') {
78-
onError(`Environment variable "${envName}" is not a string.`);
79-
return stringValue;
80-
}
81-
82-
const numberValue = Number(stringValue);
83-
if (Number.isNaN(numberValue)) {
84-
onError(`Environment variable "${envName}" is not a valid number. Got: "${stringValue}".`);
85-
return stringValue;
86-
}
87-
88-
return numberValue;
8966
}
9067
};

packages/service-core/test/src/config.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ describe('Config', () => {
3535
type: mongodb
3636
healthcheck:
3737
probes:
38-
http: !env_boolean PS_MONGO_HEALTHCHECK
38+
use_http: !env PS_MONGO_HEALTHCHECK::boolean
3939
`;
4040

4141
const collector = new CompoundConfigCollector();
@@ -58,7 +58,7 @@ describe('Config', () => {
5858
type: mongodb
5959
api:
6060
parameters:
61-
max_buckets_per_connection: !env_number PS_MAX_BUCKETS
61+
max_buckets_per_connection: !env PS_MAX_BUCKETS::number
6262
`;
6363

6464
const collector = new CompoundConfigCollector();

0 commit comments

Comments
 (0)