Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote sсhemas #107

Merged
merged 12 commits into from
Oct 14, 2023
10 changes: 8 additions & 2 deletions swagger_parser/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
## 1.10.0
- Support for generating schemas by url (see [example](https://github.com/Carapacik/swagger_parser/blob/main/swagger_parser/example/swagger_parser.yaml))
- Add new config parameter `schema_url`
- Add new config parameter `schema_from_url_to_file`
- Add new config parameter `prefer_schema_source`

## 1.9.2
- Fix error with `required` in clients ([#101](https://github.com/Carapacik/swagger_parser/issues/103))

## 1.9.1
- Handling incorrect names for classes, enums and methods.
- Additional name for unnamed models [#98](https://github.com/Carapacik/swagger_parser/issues/98)
- Additional name for unnamed models ([#98](https://github.com/Carapacik/swagger_parser/issues/98))
- Support for `deprecated` annotations for methods

## 1.9.0
Expand All @@ -13,7 +19,7 @@
- Fix error with missing File import ([#101](https://github.com/Carapacik/swagger_parser/issues/101))

## 1.8.0
- Multiple schemas support(see ([example](https://github.com/Carapacik/swagger_parser/blob/main/swagger_parser/example/swagger_parser.yaml)))
- Multiple schemas support (see [example](https://github.com/Carapacik/swagger_parser/blob/main/swagger_parser/example/swagger_parser.yaml))
- Support for specifying nullable types via anyOf
- Edit root client template
- Add new config parameter `root_client_name`
Expand Down
25 changes: 19 additions & 6 deletions swagger_parser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
[![Tests](https://github.com/Carapacik/swagger_parser/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/Carapacik/swagger_parser/actions/workflows/tests.yml)
<a href="https://omega-r.com/"><img src="https://raw.githubusercontent.com/Carapacik/swagger_parser/main/.github/readme/omega_logo.png" width="200" align="right"/></a>

## Dart package that generates REST clients and data classes from OpenApi definition file
## Dart package that generates REST clients and data classes from OpenApi definition files or links

## Features

- Supports OpenApi v2, v3.0 and v3.1
- Support JSON and YAML format
- Support for generation by link
- Support for multiple schemes
- Generate REST client files based on Retrofit
- Generate data classes (also on [freezed](https://pub.dev/packages/freezed))
- Support for multiple languages (Dart, Kotlin)
Expand Down Expand Up @@ -46,8 +48,12 @@ An example of YAML is shown below. A default value is specified for each of the

```yaml
swagger_parser:
# Required. Sets the OpenApi schema path directory for api definition.
# You must provide the file path and/or url to the OpenApi schema.

# Sets the OpenApi schema path directory for api definition.
schema_path: schemas/openapi.json
# Sets the url of the OpenApi schema
schema_url: https://petstore.swagger.io/v2/swagger.json

# Required. Sets output directory for generated files (Clients and DTOs).
output_directory: lib/api
Expand All @@ -56,6 +62,10 @@ swagger_parser:
# Current available languages are: dart, kotlin
language: dart

# Optional. If 'schema_path' and 'schema_url' are specified, what will be used.
# Current available options are: path, url.
prefer_schema_source: url

# Optional (dart only). Set 'true' to generate data classes using freezed package.
freezed: false

Expand All @@ -79,6 +89,9 @@ swagger_parser:
# Optional. Set to 'true' to squash all clients in one client.
squash_clients: false

# Optional. Set to 'false' to not write the schema from the url to the schema file.
schema_from_url_to_file: true

# Optional. Set postfix for Client class and file.
client_postfix: Client

Expand Down Expand Up @@ -123,14 +136,14 @@ swagger_parser:
put_in_folder: true
replacement_rules: []

- schema_path: schemas/openapi.json
- schema_url: https://petstore.swagger.io/v2/swagger.json
name: pet_service
client_postfix: DataSource
client_postfix: Service
put_clients_in_folder: true
enums_to_json: true
put_in_folder: true

- schema_path: schemas/openapi.json
- schema_path: schemas/pet_store.json
schema_url: https://petstore.swagger.io/v2/swagger.json
output_directory: lib/api/kotlin
language: kotlin
```
Expand Down
23 changes: 17 additions & 6 deletions swagger_parser/example/swagger_parser.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
swagger_parser:
# Required. Sets the OpenApi schema path directory for api definition.
# You must provide the file path and/or url to the OpenApi schema.

# Sets the OpenApi schema path directory for api definition.
# schema_path: schemas/openapi.json
# Sets the url of the OpenApi schema
# schema_url: https://petstore.swagger.io/v2/swagger.json

# Required. Sets output directory for generated files (Clients and DTOs).
output_directory: lib/api

# Optional. Sets the programming language.
# Current available languages are: dart, kotlin
# Current available languages are: dart, kotlin.
language: dart

# Optional. If 'schema_path' and 'schema_url' are specified, what will be used.
# Current available options are: path, url.
prefer_schema_source: url

# Optional (dart only). Set 'true' to generate data classes using freezed package.
freezed: false

Expand All @@ -32,6 +40,9 @@ swagger_parser:
# Optional. Set to 'true' to squash all clients in one client.
squash_clients: false

# Optional. Set to 'false' to not write the schema from the url to the schema file.
schema_from_url_to_file: true

# Optional. Set postfix for Client class and file.
client_postfix: Client

Expand Down Expand Up @@ -67,13 +78,13 @@ swagger_parser:
put_in_folder: true
replacement_rules: []

- schema_path: schemas/openapi.json
- schema_url: https://petstore.swagger.io/v2/swagger.json
name: pet_service
client_postfix: DataSource
client_postfix: Service
put_clients_in_folder: true
enums_to_json: true
put_in_folder: true

- schema_path: schemas/openapi.json
- schema_path: schemas/pet_store.json
schema_url: https://petstore.swagger.io/v2/swagger.json
output_directory: lib/api/kotlin
language: kotlin
110 changes: 79 additions & 31 deletions swagger_parser/lib/src/config/yaml_config.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:args/args.dart';
import 'package:collection/collection.dart';
import 'package:yaml/yaml.dart';

import '../generator/models/prefer_schema_source.dart';
import '../generator/models/programming_language.dart';
import '../generator/models/replacement_rule.dart';
import '../utils/file_utils.dart';
Expand All @@ -17,9 +19,12 @@ import 'config_exception.dart';
final class YamlConfig {
/// Applies parameters directly from constructor
const YamlConfig({
required this.schemaPath,
required this.outputDirectory,
required this.name,
required this.outputDirectory,
this.schemaPath,
this.schemaUrl,
this.schemaFromUrlToFile,
this.preferSchemaSource,
this.language,
this.freezed,
this.rootClient,
Expand Down Expand Up @@ -48,9 +53,19 @@ final class YamlConfig {
schemaPath = '';
}

if (schemaPath == null) {
final schemaUrl = yamlConfig['schema_url']?.toString();
if (schemaUrl != null) {
final uri = Uri.tryParse(schemaUrl);
if (uri == null) {
throw const ConfigException(
"Config parameter 'schema_url' must be valid URL.",
);
}
}

if (schemaPath == null && schemaUrl == null) {
throw const ConfigException(
"Config parameter 'schema_path' is required.",
"Config parameters 'schema_path' or 'schema_url' are required.",
);
}

Expand All @@ -73,13 +88,48 @@ final class YamlConfig {
outputDirectory = rootConfig.outputDirectory;
}

final rawName = yamlConfig['name'];
if (rawName is! String?) {
throw const ConfigException(
"Config parameter 'name' must be String.",
);
}

final name = rawName == null || rawName.isEmpty
? (schemaPath ?? schemaUrl)!
.split('/')
.lastOrNull
?.split('.')
.firstOrNull ??
'unknown'
: rawName;

final schemaFromUrlToFile = yamlConfig['schema_from_url_to_file'];
if (schemaFromUrlToFile is! bool?) {
throw const ConfigException(
"Config parameter 'schema_from_url_to_file' must be bool.",
);
}

PreferSchemaSource? preferSchemaSource;
final rawPreferSchemeSource =
yamlConfig['prefer_schema_source']?.toString();
if (rawPreferSchemeSource != null) {
preferSchemaSource = PreferSchemaSource.fromString(rawPreferSchemeSource);
if (preferSchemaSource == null) {
throw ConfigException(
"'prefer_schema_source' field must be contained in ${PreferSchemaSource.values.map((e) => e.name)}.",
);
}
}

ProgrammingLanguage? language;
final rawLanguage = yamlConfig['language']?.toString();
if (rawLanguage != null) {
language = ProgrammingLanguage.fromString(rawLanguage);
if (language == null) {
throw ConfigException(
"'language' field must be contained in ${ProgrammingLanguage.values}.",
"'language' field must be contained in ${ProgrammingLanguage.values.map((e) => e.name)}.",
);
}
}
Expand Down Expand Up @@ -117,6 +167,13 @@ final class YamlConfig {
);
}

final putInFolder = yamlConfig['put_in_folder'];
if (putInFolder is! bool?) {
throw const ConfigException(
"Config parameter 'put_in_folder' must be bool.",
);
}

final squashClients = yamlConfig['squash_clients'];
if (squashClients is! bool?) {
throw const ConfigException(
Expand Down Expand Up @@ -180,37 +237,23 @@ final class YamlConfig {
}
}

final putInFolder = yamlConfig['put_in_folder'];
if (putInFolder is! bool?) {
throw const ConfigException(
"Config parameter 'put_in_folder' must be bool.",
);
}

final rawName = yamlConfig['name'];
if (rawName is! String?) {
throw const ConfigException(
"Config parameter 'name' must be String.",
);
}

final name = rawName == null || rawName.isEmpty
? schemaPath.split('/').last.split('.').first
: rawName;

return YamlConfig(
name: name,
schemaPath: schemaPath,
outputDirectory: outputDirectory,
name: name,
schemaUrl: schemaUrl,
schemaFromUrlToFile:
schemaFromUrlToFile ?? rootConfig?.schemaFromUrlToFile,
preferSchemaSource: preferSchemaSource ?? rootConfig?.preferSchemaSource,
language: language ?? rootConfig?.language,
freezed: freezed ?? rootConfig?.freezed,
rootClient: rootClient ?? rootConfig?.rootClient,
rootClientName: rootClientName ?? rootConfig?.rootClientName,
clientPostfix: clientPostfix ?? rootConfig?.clientPostfix,
putInFolder: putInFolder ?? rootConfig?.putInFolder,
putClientsInFolder: putClientsInFolder ?? rootConfig?.putClientsInFolder,
squashClients: squashClients ?? rootConfig?.squashClients,
pathMethodName: pathMethodName ?? rootConfig?.pathMethodName,
putInFolder: putInFolder ?? rootConfig?.putInFolder,
enumsToJson: enumsToJson ?? rootConfig?.enumsToJson,
enumsPrefix: enumsPrefix ?? rootConfig?.enumsPrefix,
markFilesAsGenerated:
Expand Down Expand Up @@ -249,17 +292,19 @@ final class YamlConfig {
final configs = <YamlConfig>[];

final schemaPath = yamlMap['schema_path'] as String?;
final schemaUrl = yamlMap['schema_url'] as String?;
final schemas = yamlMap['schemas'] as YamlList?;

if (schemas == null && schemaPath == null) {
if (schemas == null && schemaUrl == null && schemaPath == null) {
throw const ConfigException(
"Config parameter 'schema_path' or 'schemas' is required.",
"Config parameter 'schema_path', 'schema_url' or 'schemas' is required.",
);
}

if (schemas != null && schemaPath != null) {
if (schemas != null && schemaPath != null ||
schemas != null && schemaUrl != null) {
throw const ConfigException(
"Config parameter 'schema_path' and 'schemas' can't be used together.",
"Config parameter 'schema_path' or 'schema_url' can't be used with 'schemas'.",
);
}

Expand Down Expand Up @@ -290,16 +335,19 @@ final class YamlConfig {
}

final String name;
final String schemaPath;
final String outputDirectory;
final String? schemaPath;
final String? schemaUrl;
final bool? schemaFromUrlToFile;
final PreferSchemaSource? preferSchemaSource;
final ProgrammingLanguage? language;
final bool? freezed;
final String? clientPostfix;
final bool? rootClient;
final String? rootClientName;
final bool? putClientsInFolder;
final bool? squashClients;
final bool? pathMethodName;
final bool? putClientsInFolder;
final bool? putInFolder;
final bool? enumsToJson;
final bool? enumsPrefix;
Expand Down
Loading