Skip to content

Commit 4f49e0b

Browse files
meskilltusharmath
authored andcommitted
feat: add support for separate runtime config in json/yaml formats
1 parent 316254c commit 4f49e0b

File tree

8 files changed

+312
-216
lines changed

8 files changed

+312
-216
lines changed

examples/jsonplaceholder.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# yaml-language-server: $schema=../generated/.tailcallrc.schema.json
2+
3+
server:
4+
port: 8000
5+
6+
upstream:
7+
batch:
8+
delay: 100
9+
httpCache: 42
10+
11+
links:
12+
- src: ./jsonplaceholder.graphql

generated/.tailcallrc.schema.json

Lines changed: 117 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
{
22
"$schema": "http://json-schema.org/draft-07/schema#",
3-
"title": "Config",
3+
"title": "RuntimeConfig",
44
"type": "object",
5+
"required": [
6+
"links"
7+
],
58
"properties": {
9+
"links": {
10+
"description": "A list of all links in the schema.",
11+
"type": "array",
12+
"items": {
13+
"$ref": "#/definitions/Link"
14+
}
15+
},
616
"server": {
717
"description": "Dictates how the server behaves and helps tune tailcall for all ingress requests. Features such as request batching, SSL, HTTP2 etc. can be configured here.",
818
"default": {},
@@ -96,10 +106,6 @@
96106
}
97107
}
98108
},
99-
"Bytes": {
100-
"title": "Bytes",
101-
"description": "Field whose value is a sequence of bytes."
102-
},
103109
"Cors": {
104110
"description": "Type to configure Cross-Origin Resource Sharing (CORS) for a server.",
105111
"type": "object",
@@ -169,22 +175,6 @@
169175
}
170176
}
171177
},
172-
"Date": {
173-
"title": "Date",
174-
"description": "Field whose value conforms to the standard date format as specified in RFC 3339 (https://datatracker.ietf.org/doc/html/rfc3339)."
175-
},
176-
"DateTime": {
177-
"title": "DateTime",
178-
"description": "Field whose value conforms to the standard datetime format as specified in RFC 3339 (https://datatracker.ietf.org/doc/html/rfc3339\")."
179-
},
180-
"Email": {
181-
"title": "Email",
182-
"description": "Field whose value conforms to the standard internet email address format as specified in HTML Spec: https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address."
183-
},
184-
"Empty": {
185-
"title": "Empty",
186-
"description": "Empty scalar type represents an empty value."
187-
},
188178
"Headers": {
189179
"type": "object",
190180
"properties": {
@@ -240,30 +230,6 @@
240230
"HTTP2"
241231
]
242232
},
243-
"Int128": {
244-
"title": "Int128",
245-
"description": "Field whose value is a 128-bit signed integer."
246-
},
247-
"Int16": {
248-
"title": "Int16",
249-
"description": "Field whose value is a 16-bit signed integer."
250-
},
251-
"Int32": {
252-
"title": "Int32",
253-
"description": "Field whose value is a 32-bit signed integer."
254-
},
255-
"Int64": {
256-
"title": "Int64",
257-
"description": "Field whose value is a 64-bit signed integer."
258-
},
259-
"Int8": {
260-
"title": "Int8",
261-
"description": "Field whose value is an 8-bit signed integer."
262-
},
263-
"JSON": {
264-
"title": "JSON",
265-
"description": "Field whose value conforms to the standard JSON format as specified in RFC 8259 (https://datatracker.ietf.org/doc/html/rfc8259)."
266-
},
267233
"KeyValue": {
268234
"type": "object",
269235
"required": [
@@ -279,6 +245,112 @@
279245
}
280246
}
281247
},
248+
"Link": {
249+
"description": "The @link directive allows you to import external resources, such as configuration – which will be merged into the config importing it –, or a .proto file – which will be later used by `@grpc` directive –.",
250+
"type": "object",
251+
"properties": {
252+
"headers": {
253+
"description": "Custom headers for gRPC reflection server.",
254+
"type": [
255+
"array",
256+
"null"
257+
],
258+
"items": {
259+
"$ref": "#/definitions/KeyValue"
260+
}
261+
},
262+
"id": {
263+
"description": "The id of the link. It is used to reference the link in the schema.",
264+
"type": [
265+
"string",
266+
"null"
267+
]
268+
},
269+
"meta": {
270+
"description": "Additional metadata pertaining to the linked resource."
271+
},
272+
"src": {
273+
"description": "The source of the link. It can be a URL or a path to a file. If a path is provided, it is relative to the file that imports the link.",
274+
"type": "string"
275+
},
276+
"type": {
277+
"description": "The type of the link. It can be `Config`, or `Protobuf`.",
278+
"allOf": [
279+
{
280+
"$ref": "#/definitions/LinkType"
281+
}
282+
]
283+
}
284+
},
285+
"additionalProperties": false
286+
},
287+
"LinkType": {
288+
"oneOf": [
289+
{
290+
"description": "Points to another Tailcall Configuration file. The imported configuration will be merged into the importing configuration.",
291+
"type": "string",
292+
"enum": [
293+
"Config"
294+
]
295+
},
296+
{
297+
"description": "Points to a Protobuf file. The imported Protobuf file will be used by the `@grpc` directive. If your API exposes a reflection endpoint, you should set the type to `Grpc` instead.",
298+
"type": "string",
299+
"enum": [
300+
"Protobuf"
301+
]
302+
},
303+
{
304+
"description": "Points to a JS file. The imported JS file will be used by the `@js` directive.",
305+
"type": "string",
306+
"enum": [
307+
"Script"
308+
]
309+
},
310+
{
311+
"description": "Points to a Cert file. The imported Cert file will be used by the server to serve over HTTPS.",
312+
"type": "string",
313+
"enum": [
314+
"Cert"
315+
]
316+
},
317+
{
318+
"description": "Points to a Key file. The imported Key file will be used by the server to serve over HTTPS.",
319+
"type": "string",
320+
"enum": [
321+
"Key"
322+
]
323+
},
324+
{
325+
"description": "A trusted document that contains GraphQL operations (queries, mutations) that can be exposed a REST API using the `@rest` directive.",
326+
"type": "string",
327+
"enum": [
328+
"Operation"
329+
]
330+
},
331+
{
332+
"description": "Points to a Htpasswd file. The imported Htpasswd file will be used by the server to authenticate users.",
333+
"type": "string",
334+
"enum": [
335+
"Htpasswd"
336+
]
337+
},
338+
{
339+
"description": "Points to a Jwks file. The imported Jwks file will be used by the server to authenticate users.",
340+
"type": "string",
341+
"enum": [
342+
"Jwks"
343+
]
344+
},
345+
{
346+
"description": "Points to a reflection endpoint. The imported reflection endpoint will be used by the `@grpc` directive to resolve data from gRPC services.",
347+
"type": "string",
348+
"enum": [
349+
"Grpc"
350+
]
351+
}
352+
]
353+
},
282354
"Method": {
283355
"type": "string",
284356
"enum": [
@@ -311,10 +383,6 @@
311383
}
312384
}
313385
},
314-
"PhoneNumber": {
315-
"title": "PhoneNumber",
316-
"description": "Field whose value conforms to the standard E.164 format as specified in E.164 specification (https://en.wikipedia.org/wiki/E.164)."
317-
},
318386
"PrometheusExporter": {
319387
"description": "Output the telemetry metrics data to prometheus server",
320388
"type": "object",
@@ -612,26 +680,6 @@
612680
}
613681
]
614682
},
615-
"UInt128": {
616-
"title": "UInt128",
617-
"description": "Field whose value is a 128-bit unsigned integer."
618-
},
619-
"UInt16": {
620-
"title": "UInt16",
621-
"description": "Field whose value is a 16-bit unsigned integer."
622-
},
623-
"UInt32": {
624-
"title": "UInt32",
625-
"description": "Field whose value is a 32-bit unsigned integer."
626-
},
627-
"UInt64": {
628-
"title": "UInt64",
629-
"description": "Field whose value is a 64-bit unsigned integer."
630-
},
631-
"UInt8": {
632-
"title": "UInt8",
633-
"description": "Field whose value is an 8-bit unsigned integer."
634-
},
635683
"Upstream": {
636684
"description": "The `upstream` directive allows you to control various aspects of the upstream server connection. This includes settings like connection timeouts, keep-alive intervals, and more. If not specified, default values are used.",
637685
"type": "object",
@@ -778,10 +826,6 @@
778826
}
779827
},
780828
"additionalProperties": false
781-
},
782-
"Url": {
783-
"title": "Url",
784-
"description": "Field whose value conforms to the standard URL format as specified in RFC 3986 (https://datatracker.ietf.org/doc/html/rfc3986)."
785829
}
786830
}
787831
}

src/cli/generator/source.rs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,31 @@
1-
use thiserror::Error;
1+
use crate::core::config;
2+
use crate::core::config::SourceError;
23

34
#[derive(Clone, Copy, PartialEq, Debug)]
45
pub enum ConfigSource {
56
Json,
67
Yml,
78
}
89

9-
impl ConfigSource {
10-
fn ext(&self) -> &str {
11-
match self {
12-
Self::Json => "json",
13-
Self::Yml => "yml",
14-
}
15-
}
10+
impl TryFrom<config::Source> for ConfigSource {
11+
type Error = SourceError;
1612

17-
fn ends_with(&self, file: &str) -> bool {
18-
file.ends_with(&format!(".{}", self.ext()))
13+
fn try_from(value: config::Source) -> Result<Self, Self::Error> {
14+
match value {
15+
config::Source::Json => Ok(Self::Json),
16+
config::Source::Yml => Ok(Self::Yml),
17+
config::Source::GraphQL => {
18+
Err(SourceError::UnsupportedFileFormat(value.ext().to_string()))
19+
}
20+
}
1921
}
22+
}
2023

24+
impl ConfigSource {
2125
/// Detect the config format from the file name
22-
pub fn detect(name: &str) -> Result<Self, UnsupportedFileFormat> {
23-
const ALL: &[ConfigSource] = &[ConfigSource::Json, ConfigSource::Yml];
26+
pub fn detect(name: &str) -> Result<Self, SourceError> {
27+
let source = config::Source::detect(name)?;
2428

25-
ALL.iter()
26-
.find(|format| format.ends_with(name))
27-
.copied()
28-
.ok_or(UnsupportedFileFormat(name.to_string()))
29+
ConfigSource::try_from(source)
2930
}
3031
}
31-
32-
#[derive(Debug, Error, PartialEq)]
33-
#[error("Unsupported config extension: {0}")]
34-
pub struct UnsupportedFileFormat(String);

0 commit comments

Comments
 (0)