Description
Hello !
I started experimenting with a Godot client (GDScript
), and albeit there's not much to show right now I wanted to write something here in case anyone else is interested in joining forces. I looked here for the keywords Godot
and GDScript
.
Here's the working branch.
Rationale
Being able to quickly draw a pure GDScript
client for any OAS would be amazing for some online games, even though Godot has its own network engine, because in HTML5 exports only the HTTP client is available (for now). Anyway, there's plenty of uses for HTTP connections to a server in games (auth, leaderboards, news, etc.).
I don't know if GDScript will have all the features we need, we'll see along the way.
Y U NO C ?
Having to compile Godot itself and distribute it to all collaborators is no small task.
And I'd rather not have Mono if I can help it.
Design
Some ideas, constraints inherent to Godot, etc. Nothing is frozen yet.
Indent using Tabs
Godot uses tabs by default, although it is configurable.
Java code should be indented like the other generators.
Try not to pollute global space (with class_name
) ?
Godot does not have namespaces. (for now)
What should we add in the global namespace (via class_name
, or perhaps singleton nodes) ?
If we do decide to use class_name
(and pollute global space) for models and api, which I suspect we'll need to get nice type defs, we need to set up for all class names a prefix that will act as a namespace.
We can rather easily do this for models with
--model-name-prefix
, just need to make sure our users know about it. I found--api-name-suffix
but no prefix, although it is used intoApiName()
. Probably because of lack of support across targets ? Should I add my own--api-name-prefix
CLI option or wait ?
snake_case vs camelCase
I'd love some configuration but I won't bother with it until at least I have a working client.
Static typing
Godot does not allow nullable types (we're waiting for them).
This means we either skip types, or try to figure out how to replace null
by the appropriate pseudo-null value for the given type. (""
for String
, etc.)
But it becomes awkward for int
values, where 0
might not be a sensible pseudo-null.
Async
In HTML5 exports, only async is available.
Since it is my personal target, I'll favor it.
Godot 4 has Callable
s and lambda defs, but no Promise
s yet.
I think the best method signature for our generated async API method endpoints would be to return a Promise
.
But if we start waiting on the promises of Godot… ;)
Therefore, for the first major version of the generator, we'll use Callable
s in that fashion, unless you have a better idea :
func createUser(
# … ← generated params from the endpoint schema
on_success: Callable, # func(result: User)
on_failure: Callable # func(error: ApiError)
):
pass # ← method implementation
This function signature is actually very cumbersome to use (see the current state of the demo in
samples
), because we'll often need to chain requests. We need to find an implementation ofPromise
in pure GDScript, and fast.
To Singleton or not to Singleton
I tried to avoid using Singletons (aka. Autoloads), so XxxxApi
classes are simple RefCounted
(plain ol' Godot Object with garbage collection), but that means each XxxxApi
holds its own (host, port) configuration to lazily create its client when none was provided. Same goes for extra headers the user wants to send.
You can create the configuration once and pass it to each API class.
State of Things
I managed to bootstrap the gdscript
target, compile and generate the petstore sample.
Now we need to fill the templates with working code, one feature at a time.
- Models template basic bones
- Apis template basic bones
- Basic Java CodeGen
- Enums
- Datetimes ← using String, shifting timezone responsibility on user
- Model Collections ← using
Array
s with dubious recursion support - Reserved Words ← mostly done, never done
- Configure enabled/disabled features
- Demo / Integration Tests
- Normalization
- Serialization (JSON)
- Model validation of
required
- Model validation of constraints (
length
, etc.) - Deserialization (JSON) ← my focus
-
Deserialization (JSON+LD)← skipped (ouch) -
Serialization (XML)← skipped -
Deserialization (XML)← skipped - RequestBodies Components
- SecuritySchemes ← hmm
- Response Errors ← may skip
- Unit Tests
- Header customization
- Single host configuration from OAS
- Multiple host configuration from OAS
- HTTPClient async constraint
- HTTPClient reuse through Apis
- Override endpoints for easier template overriding (perhaps via template partials?)
- Bunch of configuration (YAML)
- Bunch of configuration (CLI)
Probably won't do everything on this list myself. Will do enough for my own use, hopefully.
I'm looking at the other language targets and throwing darts. Wish me luck !
Burning questions
-
Is there somewhere a docker image (or whatever) for a server with a working petstore I can use for my demo during dev ?
Yes → https://github.com/OpenAPITools/openapi-generator/wiki/Integration-Tests (thanks @wing328)
-
Should I use
handlebars
straight away or keep usingmustache
, as mildly recommended ? I'm used to (and fond of)jinja2
andTwig
(Pebble
, for Java).Moved to handlebars because the truthy value of mustache includes
""
and"null"
and that does not play well with{{#example}}
. -
I made a small python script to grab the reserved keywords from Godot's XML. Since it might be useful again when updating the generator to account for changes in Godot, I'd like to keep it close. Where can I store it ? (right now it's in
modules/openapi-generator/src/main/resources/gdscript
, schmoozing with the templates)