Git Product home page Git Product logo

swagger_parser's Introduction

Swagger Parser

pub version pub likes dart style Star on Github Last commit on Github Tests

Swagger Parser is a Dart package that takes an OpenApi definition file and generates REST clients based on retrofit and data classes for your project.

  • Supports OpenApi v2, v3.0 and v3.1
  • Can generate files from JSON and YAML format
  • Generation by definition link or from file
  • Support for multiple definitions
  • Supports Dart and Kotlin

We also have a web interface so you can try out swagger parser: https://carapacik.github.io/swagger_parser

swagger_parser's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

swagger_parser's Issues

Empty return type should be Object or Map<String, dynamic> not void

 "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "username": {
                                        "error": "Username is required",
                                        "type": "string"
                                    }
                                },
                                "required": [
                                    "username"
                                ],
                                "additionalProperties": false
                            }
                        },
  @MultiPart()
  @POST('/login')
  Future<void> postLogin({
    @Part(name: 'username') String? username,
  });

Incorrect method name generation

Hi, thanks for developing a great package, I am getting some errors when generating.

When generating method names in the rest client, a summary is used, which may not be valid for the name.

Result:

import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';

import '../shared_models/email_form.dart';
import '../shared_models/verify_email_response.dart';

part 'email_client.g.dart';

@RestApi()
abstract class EmailClient {
  factory EmailClient(Dio dio, {String baseUrl}) = _EmailClient;

  @POST('/auth/email/verify')
  Future<VerifyEmailResponse> проверитьEmailНаСуществованиеAuthEmailVerifyPost({  // <- error
    @Body() required EmailForm body,
  });
}

Expected result:

import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';

import '../shared_models/email_form.dart';
import '../shared_models/verify_email_response.dart';

part 'email_client.g.dart';

@RestApi()
abstract class EmailClient {
  factory EmailClient(Dio dio, {String baseUrl}) = _EmailClient;

  /// Проверить Email на существование
  @POST('/auth/email/verify')
  Future<VerifyEmailResponse> authEmailVerifyPost({
    @Body() required EmailForm body,
  });
}

OpenApi:

{
  "openapi": "3.1.0",
  "info": {
    "title": "Microservice Auth API methods",
    "version": "0.2.2"
  },
  "paths": {
    "/auth/email/verify": {
      "post": {
        "tags": [
          "Email"
        ],
        "summary": "Проверить Email На Существование",
        "description": "Вернет True, если пользователь с указанным Email уже зарегистрирован, иначе False.",
        "operationId": "Проверить_Email_на_существование_auth_email_verify_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmailForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifyEmailResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "EmailForm": {
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "title": "Email",
            "description": "Адрес электронной почты"
          }
        },
        "type": "object",
        "required": [
          "email"
        ],
        "title": "EmailForm"
      },
      "VerifyEmailResponse": {
        "properties": {
          "exists": {
            "type": "boolean",
            "title": "Exists",
            "description": "Возвращает true, если сущность существует, иначе false"
          }
        },
        "type": "object",
        "required": [
          "exists"
        ],
        "title": "VerifyEmailResponse"
      }
    }
  },
  "tags": [
    {
      "name": "Auth",
      "description": "Auth Management"
    }
  ]
}

Question: required, nullable combinations

All all gereated fields should be optional not required

Schema
components->schema

"FindAllStudentsDto": {
                "type": "object",
                "properties": {
                    "page": {
                        "type": "number",
                        "default": 1
                    },
                    "from": {
                        "type": "string"
                    },
                    "to": {
                        "type": "string"
                    },
                    "limit": {
                        "type": "number",
                        "default": 50
                    },
                    "name": {
                        "type": "string"
                    },
                    "departments": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/IdDto"
                        }
                    },
                    "subjects": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/IdDto"
                        }
                    },
                    "classGroups": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/IdDto"
                        }
                    },
                    "yearGroups": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/IdDto"
                        }
                    },
                    "formGroups": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/IdDto"
                        }
                    },
                    "ids": {
                        "type": "array",
                        "items": {
                            "type": "number"
                        }
                    }
                }
            },

Generated Code

import 'package:json_annotation/json_annotation.dart';

import 'id_dto.dart';

part 'find_all_students_dto.g.dart';

@JsonSerializable()
class FindAllStudentsDto {
  const FindAllStudentsDto({
    required this.from,
    required this.to,
    required this.name,
    required this.departments,
    required this.subjects,
    required this.classGroups,
    required this.yearGroups,
    required this.formGroups,
    required this.ids,
    required this.withExamAndHomeworkStats,
    this.page = 1,
    this.limit = 50,
  });
  
  factory FindAllStudentsDto.fromJson(Map<String, Object?> json) => _$FindAllStudentsDtoFromJson(json);
  
  final String from;
  final String to;
  final num page;
  final num limit;
  final String name;
  final List<IdDto> departments;
  final List<IdDto> subjects;
  final List<IdDto> classGroups;
  final List<IdDto> yearGroups;
  final List<IdDto> formGroups;
  final List<num> ids;
  final bool withExamAndHomeworkStats;

  Map<String, Object?> toJson() => _$FindAllStudentsDtoToJson(this);
}

Generated code does not work with JSON request format

I get the following error: cannot be used to imply a default content-type, please set a proper content-type in the request. Even though, in the openapi docs the request type is explicitly marked as application/json.

API method name should use operationId

Typically, the operationId is used as the method name in the generated client. Right now, swagger_parser does not use this property at all. Instead, the method name is generated as follows:

final request = UniversalRequest(
name: (key + path).toCamel,

This is using the API path as the method name which is not necessarily indicative of the operation. Also, this arbitrary name definition (HTTP method (key) + path) is essentially defining a new specification for how method names should be defined... which is kind of the opposite of the OpenAPI spec which is meant to define a common standard.

Further, if you reference the source documentation of the API you will likely notice that the operationId is what is listed in the docs for that operation. So using that for consistency makes it easier to map the client generated by swagger_parser to the source API and any accompanying docs.

Options

This is a breaking change to anyone already using swagger_parser because it will change the API client definition method names. However, this is how most OpenAPI spec parsers work and it should at least be implemented in the package. There are two options here:

  1. Make operationId the new method name - will break users
  2. Make operationId the default and allow it to be opt-in via configuration file option - will not break users

I am partial to option 1 because this is how I have seen nearly every other parser, for every language, work. operationId is used as the method name. Although it is not a requirement of the OpenAPI spec, it is a common assumption that most parsers make.

I can push up a PR with option 1 as I have this working right now.

Missing import for File

With the schema:

"paths": {
      "/api/example": {
        "post": {
          "tags": [
            "Instances"
          ],
          "summary": "Creates one or more instances from a file or archive, respectively",
          "requestBody": {
            "content": {
              "multipart/form-data": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "file": {
                      "type": "string",
                      "format": "binary"
                    }
                  }
                },
                "encoding": {
                  "file": {
                    "style": "form"
                  }
                }
              }
            }
          },

The following is generated:

  /// Creates one or more instances from a file or archive, respectively
  @MultiPart()
  @POST('/api/example')
  Future<CreatedInstancesResponse> postExample({
    @Part(name: 'file') File? file,
  });

However, the type File is missing the import to import 'dart:io';:

Undefined class 'File'.
Try changing the name to the name of an existing class, or creating a class with the name 'File'.

It can be added manually, but is there a way to define this beforehand?

Codegen issues with object response

// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint

import 'package:json_annotation/json_annotation.dart';

part 'object0_value.g.dart';

@JsonSerializable()
class Object0Value {
  const Object0Value({
    this.id,
    this.name,
  });

  factory Object0Value.fromJson(Map<String, Object?> json) =>
      _$Object0ValueFromJson(json);

  final String? id;
  final String? name;

  Map<String, Object?> toJson() => _$Object0ValueToJson(this);
}

Issue with code generation

  • Object0Value should be PostLoginResponse, as its unnamed, provide name from the path.
  • id and name are required in the open api but is generating optional properties

OpenAPI

"/login": {
            "post": {
                "parameters": [],
                "responses": {
                    "200": {
                        "additionalProperties": false,
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "id": {
                                            "error": "id not found",
                                            "type": "string"
                                        },
                                        "name": {
                                            "error": "name not found",
                                            "type": "string"
                                        }
                                    },
                                    "required": [
                                        "id",
                                        "name"
                                    ]
                                }
                            },
                            "multipart/form-data": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "id": {
                                            "error": "id not found",
                                            "type": "string"
                                        },
                                        "name": {
                                            "error": "name not found",
                                            "type": "string"
                                        }
                                    },
                                    "required": [
                                        "id",
                                        "name"
                                    ]
                                }
                            },
                            "text/plain": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "id": {
                                            "error": "id not found",
                                            "type": "string"
                                        },
                                        "name": {
                                            "error": "name not found",
                                            "type": "string"
                                        }
                                    },
                                    "required": [
                                        "id",
                                        "name"
                                    ]
                                }
                            }
                        }
                    }
                },
                "operationId": "postLogin",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "username": {
                                        "error": "Username is required",
                                        "type": "string"
                                    }
                                },
                                "required": [
                                    "username"
                                ],
                                "additionalProperties": false
                            }
                        },
                        "multipart/form-data": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "username": {
                                        "error": "Username is required",
                                        "type": "string"
                                    }
                                },
                                "required": [
                                    "username"
                                ],
                                "additionalProperties": false
                            }
                        },
                        "text/plain": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "username": {
                                        "error": "Username is required",
                                        "type": "string"
                                    }
                                },
                                "required": [
                                    "username"
                                ],
                                "additionalProperties": false
                            }
                        }
                    }
                }
            }
        },

Fix incorrect parsing of component spec

@Carapacik this is an example of a parsing result that I believe is incorrect. Using version 0.10.0.

Here is the OpenAPI spec of a sample component. The notable item is that this component does not have any properties defined.

"UpdateResponse": {
  "type": "object",
  "description": "The response for the `Update` operation."
},

Current Result

Due to the fact that this component does not have any properties, swagger_parser is returning the following result:

typedef UpdateResponse = Object;

This is then problematic in the generated serialization code, as seen below. Of course, the native dart Object type does not have a fromJson defined.

final value = Object.fromJson(_result.data!);

The above will error and the only workaround right now is to simply overwrite the swagger_parser result, after the fact, with the solution provided below.

Desired Result

Changing the typedef parsed result to an empty serializable object (i.e. no parameters) resolves the issue. This should be the appropriate parsed result for that OpenAPI spec definition.

import 'package:json_annotation/json_annotation.dart';
part 'update_response.g.dart';

@JsonSerializable()
class UpdateResponse {
  const UpdateResponse();

  factory UpdateResponse.fromJson(Map<String, dynamic> json) =>
      _$UpdateResponseFromJson(json);

  Map<String, dynamic> toJson() => _$UpdateResponseToJson(this);
}

unknownEnumValue support for json_serializable

Could you please add an option that enums are generated with unkown value? This enables enums to be more safe for future additions.

json_serializable does unfortunately not support null values for unkown cases, but you can define a fallback value like unkown.
The configuration in json_serializable for this looks like this:

@JsonEnum()
enum MenuItemType {
...
  @JsonValue('unkown')
  unkown;
}


@JsonKey(
   name: 'type',
   unknownEnumValue: MenuItemType.unkown,
)
 final MenuItemType type;

Relationship DTO not imported

Relationship DTO are not imported, I have to manually do it each time

"components": {
        "schemas": {
            "Channel": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer",
                        "description": "integer",
                        "nullable": false,
                        "example": 1566687616
                    },
                    "name": {
                        "type": "string",
                        "description": "string(255): Whatsapp, Telegram, etc",
                        "nullable": false,
                        "example": "string"
                    },
                    "slug": {
                        "type": "string",
                        "description": "string(255)",
                        "nullable": false,
                        "example": "string"
                    },
                    "description": {
                        "type": "string",
                        "description": "string(255)",
                        "nullable": true,
                        "example": "string"
                    }
                },
                "required": [
                    "id",
                    "name",
                    "slug"
                ]
            },
            "Church": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer",
                        "description": "integer",
                        "nullable": false,
                        "example": 1232951869
                    },
                    "name": {
                        "type": "string",
                        "description": "string(255)",
                        "nullable": false,
                        "example": "string"
                    },
                    "path": {
                        "type": "string",
                        "description": "string(255): parent-id path ex: '-1-2-6-' for a church of id 6, having church parent id = 2, and church-2 belong its self to 1",
                        "nullable": false,
                        "example": "string"
                    },
                    "address": {
                        "type": "string",
                        "description": "string(255): Describe church location/address",
                        "nullable": false,
                        "example": "string"
                    },
                    "reported_by_user_id": {
                        "type": "integer",
                        "format": "int64",
                        "description": "bigint",
                        "nullable": true,
                        "example": 1725148829
                    },
                    "main_channel_id": {
                        "type": "integer",
                        "description": "integer",
                        "nullable": false,
                        "example": 1641752488
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "description": "datetime",
                        "nullable": true,
                        "example": "string"
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "description": "datetime",
                        "nullable": true,
                        "example": "string"
                    },
                    "deleted_at": {
                        "type": "string",
                        "format": "date-time",
                        "description": "datetime",
                        "nullable": true,
                        "example": "string"
                    },
                    "mainChannel": {
                        "type": "object",
                        "$ref": "#/components/schemas/Channel"
                    }
                },
                "required": [
                    "id",
                    "name",
                    "path",
                    "address",
                    "main_channel_id"
                ]
            },
           ....

Here is the generated Church model, see like Channel is used (last attribute) but not imported

import 'package:json_annotation/json_annotation.dart';

part 'church.g.dart';

@JsonSerializable()
class Church {
 const Church({
   required this.id,
   required this.name,
   required this.path,
   required this.address,
   required this.mainChannelId,
   this.reportedByUserId,
   this.createdAt,
   this.updatedAt,
   this.deletedAt,
   this.mainChannel,
 });
 
 factory Church.fromJson(Map<String, Object?> json) => _$ChurchFromJson(json);
 
 /// integer
 final int id;
 /// string(255)
 final String name;
 /// string(255): parent-id path ex: '-1-2-6-' for a church of id 6, having church parent id = 2, and church-2 belong its self to 1
 final String path;
 /// string(255): Describe church location/address
 final String address;
 /// bigint
 @JsonKey(name: 'reported_by_user_id')
 final int? reportedByUserId;
 /// integer
 @JsonKey(name: 'main_channel_id')
 final int mainChannelId;
 /// datetime
 @JsonKey(name: 'created_at')
 final DateTime? createdAt;
 /// datetime
 @JsonKey(name: 'updated_at')
 final DateTime? updatedAt;
 /// datetime
 @JsonKey(name: 'deleted_at')
 final DateTime? deletedAt;
 final Channel? mainChannel;

 Map<String, Object?> toJson() => _$ChurchToJson(this);
}

You can test it with your own openapi json file and you get this error, I got this error each times and for now, I have to import every time myself.

swagger_parser: ^1.5.2

here is my swagger_parser.json

# dart run swagger_parser:generate
# dart run build_runner build -d

swagger_parser:
  schema_path: assets/openapi.json # Required. Sets the OpenApi schema path directory for api definition
  output_directory: lib/api # Required. Sets output directory for generated files (Clients and Dtos)
  language: dart # Optional. Sets the programming language. Current available languages are: dart, kotlin. Default: dart
  root_interface: true # Optional (dart only). Set 'true' to generate interface with all clients instances. Default: true
  squish_clients: false # Optional. Set 'true' to put all clients in one folder. Default: false
  client_postfix: ApiCall # Optional. Set postfix for Client class and file. Default: Client
  freezed: false # Optional (dart only). Set 'true' to generate data classes using freezed package. Default: false
  enums_to_json: true # Optional. Set 'true' to include toJson() in enums. If set to false, serialization will use .name instead. Default: false
  enums_prefix: false # Optional. Set 'true' to set enum prefix from parent component. Default: false
  replacement_rules: # Optional. Set regex replacement rules for the names of the generated classes/enums. All rules are applied in order.
    # Example of rule
    - pattern: "[0-9]+"
      replacement: ""

Feature: Root repository/interface

Hey @Carapacik

Right now I m doing the follow

class RootRepo {
  final AuthRepo auth;
  const AppService({required this.auth});
}

final repositoryProvider = Provider<AppService>((ref) {
  final dio = ref.watch(dioProvider);
  return RootRepo(
    auth: AuthRepo(dio),
  );
});

I will have probably 20 repos like this as I migrate. And this is fine for most part. But it can be great if there is way to generate Root repo where are the other repo are initialised and automated as API changes.

What do you think?

How to make a list of nullable items?

I want to return an array of a type, that can also contain null values. But even if I set "nullable": true in the items the generated list always contains non-null type.

E.g. In the example Pets api if I change findPets response from:

 "items": {
    "$ref": "#/components/schemas/Pet"
 }

to:

"items": {
    "nullable": true,
    "$ref": "#/components/schemas/Pet"
}

Even then the resulting code is the same List<Pet> instead of List<Pet?>

Classes that are used as body parameters are not resolved and always result in a parameter of type "Object"

The JSON below produces the following dart method:

  @PUT('/users/credentials')
  Future<void> changeUserCredentials({
    @Body() required Object credentials,
  });

while the expected content is

  @PUT('/users/credentials')
  Future<void> changeUserCredentials({
    @Body() required Credentials credentials,
  });

JSON:

{
  "swagger": "2.0",
  "info": {
    "version": "1.0",
    "title": "User Service"
  },
  "host": "",
  "basePath": "/api",
  "tags": [
    {
      "name": "Users",
      "description": "User Controller"
    }
  ],
  "paths": {
    "/users/credentials": {
      "put": {
        "tags": [
          "Users"
        ],
        "summary": "Changes the user's credentials",
        "operationId": "changeUserCredentials",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "*/*"
        ],
        "parameters": [
          {
            "in": "body",
            "name": "credentials",
            "description": "credentials",
            "required": true,
            "schema": {
              "$ref": "#/definitions/Credentials"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Credentials changed correctly"
          }
        },
        "deprecated": false
      }
    }
  },
  "definitions": {
    "Credentials": {
      "type": "object",
      "required": [
        "email",
        "password"
      ],
      "properties": {
        "email": {
          "type": "string",
          "description": "The new email"
        },
        "password": {
          "type": "string"
        }
      },
      "title": "Credentials"
    }
  }
}

Fix client_postfix option when not set

According to the documentation:

Optional. Set postfix for client folder and Class. Works if there is only a single class or squish_clients is true. Default: ApiClient

However, when attempting to set an empty postfix in swagger_parser.yaml:

client_postfix: '' 

Results in an incorrect output (assume the client file name is api_operations):

part 'api_operations_.g.dart';

There is a trailing underscore when there should not be. Removing the underscore resolves the issue.

Expected Result:

part 'api_operations.g.dart';

Not supported specify nullable types via anyOf

The new pydantic 2.1 on backend (Python, FastApi) describes nullable types in the anyOf specification. It would be nice to support this way of specifying a nullable type.

Small example:

 "last_name": {
    "anyOf": [
      {
        "type": "string"
      },
      {
        "type": "null"
      }
    ],
    "title": "Last Name"
 }

Result:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@Freezed()
class User with _$User {
  const factory User({
    /// Адрес электронной почты
    required String email,
    /// Имя пользователя
    @JsonKey(name: 'first_name')
    required String firstName,
    /// Фамилия пользователя
    @JsonKey(name: 'last_name')
    required Object lastName,
    /// Никнейм пользователя
    required String username,
    /// Уникальный id пользователя в базе данных
    required int id,
    /// Уникальный uuid пользователя в базе данных
    required String uuid,
    /// Время создания аккаунта пользователя
    @JsonKey(name: 'created_at')
    required DateTime createdAt,
    /// Время удаления аккаунта пользователя
    @JsonKey(name: 'deleted_at')
    required Object deletedAt,
    /// Время изменения аккаунта пользователя, либо обновления refresh токена
    @JsonKey(name: 'updated_at')
    required Object updatedAt,
  }) = _User;
  
  factory User.fromJson(Map<String, Object?> json) => _$UserFromJson(json);
}

Expected result:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@Freezed()
class User with _$User {
  const factory User({
    /// Адрес электронной почты
    required String email,
    /// Имя пользователя
    @JsonKey(name: 'first_name')
    required String firstName,
    /// Фамилия пользователя
    @JsonKey(name: 'last_name')
    required String? lastName,
    /// Никнейм пользователя
    required String username,
    /// Уникальный id пользователя в базе данных
    required int id,
    /// Уникальный uuid пользователя в базе данных
    required String uuid,
    /// Время создания аккаунта пользователя
    @JsonKey(name: 'created_at')
    required DateTime createdAt,
    /// Время удаления аккаунта пользователя
    @JsonKey(name: 'deleted_at')
    required DateTime? deletedAt,
    /// Время изменения аккаунта пользователя, либо обновления refresh токена
    @JsonKey(name: 'updated_at')
    required DateTime? updatedAt,
  }) = _User;
  
  factory User.fromJson(Map<String, Object?> json) => _$UserFromJson(json);
}

OpenApi:

{
  "openapi": "3.1.0",
  "info": {
    "title": "Microservice Auth API methods",
    "version": "0.2.2"
  },
  "paths": {},
  "components": {
    "schemas": {
      "User": {
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "title": "Email",
            "description": "Адрес электронной почты"
          },
          "first_name": {
            "type": "string",
            "title": "First Name",
            "description": "Имя пользователя"
          },
          "last_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Name",
            "description": "Фамилия пользователя"
          },
          "username": {
            "type": "string",
            "title": "Username",
            "description": "Никнейм пользователя"
          },
          "id": {
            "type": "integer",
            "title": "Id",
            "description": "Уникальный id пользователя в базе данных"
          },
          "uuid": {
            "type": "string",
            "format": "uuid",
            "title": "Uuid",
            "description": "Уникальный uuid пользователя в базе данных"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At",
            "description": "Время создания аккаунта пользователя"
          },
          "deleted_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Deleted At",
            "description": "Время удаления аккаунта пользователя"
          },
          "updated_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Updated At",
            "description": "Время изменения аккаунта пользователя, либо обновления refresh токена"
          }
        },
        "type": "object",
        "required": [
          "email",
          "first_name",
          "last_name",
          "username",
          "id",
          "uuid",
          "created_at",
          "deleted_at",
          "updated_at"
        ],
        "title": "User"
      }
    },
    "tags": [
      {
        "name": "Auth",
        "description": "Auth Management"
      }
    ]
  }
}

[dart] "format date" generate class without properties instead of using native DateTime

while using the generator on a swagger generated by Java, Date is not recognize as dart DateTime

swagger

    RangeDate:
      type: object
      properties:
        startDate:
          $ref: '#/components/schemas/Date'
        endDate:
          $ref: '#/components/schemas/Date'
    Date:
      format: date
      type: string
      example: 2022-03-10

generated

import 'package:json_annotation/json_annotation.dart';

import 'date.dart';

part 'range_date.g.dart';

@JsonSerializable()
class RangeDate {
  const RangeDate({
    required this.startDate,
    required this.endDate,
  });
  
  factory RangeDate.fromJson(Map<String, dynamic> json) => _$RangeDateFromJson(json);
  
  final Date startDate;
  final Date endDate;

  Map<String, dynamic> toJson() => _$RangeDateToJson(this);
}
import 'package:json_annotation/json_annotation.dart';

part 'date.g.dart';

@JsonSerializable()
class Date {
  const Date();
  
  factory Date.fromJson(Map<String, dynamic> json) => _$DateFromJson(json);
  
  Map<String, dynamic> toJson() => _$DateToJson(this);
}

expected

import 'package:json_annotation/json_annotation.dart';

part 'range_date.g.dart';

@JsonSerializable()
class RangeDate {
  const RangeDate({
    required this.startDate,
    required this.endDate,
  });
  
  factory RangeDate.fromJson(Map<String, dynamic> json) => _$RangeDateFromJson(json);
  
  final DateTime startDate;
  final DateTime endDate;

  Map<String, dynamic> toJson() => _$RangeDateToJson(this);
}

not checking if `rawParameter[_schemaConst]` is null when resolving parameter type

In the OpenApiParser class, when resolving the type of the parameter, it is not checked whether rawParameter[_schemaConst] is null

allOfObject: (rawParameter[_schemaConst] as Map<String, dynamic>)
    .containsKey(_allOfConst)

it throws an exception as follows:

Unhandled exception:
type 'Null' is not a subtype of type 'Map<String, dynamic>' in type cast
#0      OpenApiParser.parseRestClients.parametersV2 (package:swagger_parser/src/parser/parser.dart:338:43)
#1      OpenApiParser.parseRestClients.<anonymous closure>.<anonymous closure> (package:swagger_parser/src/parser/parser.dart:374:15)
#2      _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:625:13)
#3      OpenApiParser.parseRestClients.<anonymous closure> (package:swagger_parser/src/parser/parser.dart:362:43)
#4      _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:625:13)
#5      OpenApiParser.parseRestClients (package:swagger_parser/src/parser/parser.dart:361:10)
#6      Generator._parseOpenApiDefinitionFile (package:swagger_parser/src/generator/generator.dart:127:27)
#7      Generator.generateFiles (package:swagger_parser/src/generator/generator.dart:112:5)
#8      main (file:///home/elena/docs/dev/flutter/swagger_parser/swagger_parser/bin/generate.dart:11:21)

Enum default values when used in query parameters

When enums have default values and are used as query parameters the default value is generated as follows:

@Query('unit') Unit unit = CELSIUS

which is invalid. It should be generated as:

@Query('unit') Unit unit = Unit.celsius

Handling binary

Hey mate @Carapacik

Allot of great work, was checking out the fixes in commit history.

This is last issue pending with my project's openapi. Will be migrating to swagger_parser soon :)

 "CreateUploadDto": {
                "type": "object",
                "properties": {
                    "assets": {
                        "type": "string",
                        "format": "binary"
                    },
                    "type": {
                        "type": "string",
                        "enum": [
                            "RESOURCES",
                            "MESSAGES",
                            "COVER_IMAGE"
                        ]
                    }
                },
                "required": [
                    "assets",
                    "type"
                ]
            },

produces

import 'dart:io';

import 'package:json_annotation/json_annotation.dart';

import 'type.dart';

part 'create_upload_dto.g.dart';

@JsonSerializable()
class CreateUploadDto {
  const CreateUploadDto({
    required this.assets,
    required this.type,
  });
  
  factory CreateUploadDto.fromJson(Map<String, dynamic> json) => _$CreateUploadDtoFromJson(json);
  
  final File assets;
  final Type type;

  Map<String, dynamic> toJson() => _$CreateUploadDtoToJson(this);
}

And I can't serialize FIle by default.


  ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
  ┃                               ┃
  ┃   Welcome to swagger_parser   ┃
  ┃                               ┃
  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Generate...
The generation was completed successfully. You can run the generation using build_runner.
[INFO] Generating build script...
[WARNING] Invalid builder key `freezed:freezed` found in global_options config of build.yaml. This configuration will have no effect.
[WARNING] Invalid builder key `retrofit_generator:retrofit_generator` found in global_options config of build.yaml. This configuration will have no effect.
[INFO] Generating build script completed, took 160ms

[WARNING] Configuring `freezed:freezed` in global options but this is not a known Builder
[INFO] Initializing inputs
[INFO] Reading cached asset graph...
[INFO] Reading cached asset graph completed, took 70ms

[INFO] Checking for updates since last build...
[INFO] Checking for updates since last build completed, took 333ms

[INFO] Running build...
[INFO] 1.2s elapsed, 0/16 actions completed.
[INFO] 2.2s elapsed, 147/163 actions completed.
[INFO] Running build completed, took 2.5s

[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 32ms

[SEVERE] json_serializable on lib/shared_models/create_upload_dto.dart (cached):

Could not generate `fromJson` code for `assets`.
To support the type `File` you can:
* Use `JsonConverter`
  https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonConverter-class.html
* Use `JsonKey` fields `fromJson` and `toJson`
  https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/fromJson.html
  https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/toJson.html
package:glu_client_flutter_repo/shared_models/create_upload_dto.dart:18:14
   ╷
18 │   final File assets;
   │              ^^^^^^
   ╵
[SEVERE] Failed after 2.6s

Content type for generated client

Swagger Parser does not seem to respect content type for generated client of the following example. I have to add contentType: 'application/x-www-form-urlencoded' and data: _data.toJson() to the generated client or am i missing something?

# https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md
openapi: 3.0.3
info:
  title: Keycloak Token
  version: 1.0.0
servers:
  - url: 'http://localhost:8009/auth'
tags:
  - name: KeycloakToken
paths:
  /realms/{realm}/protocol/openid-connect/token:
    post:
      tags:
        - KeycloakToken
      requestBody:
        required: true
        content:
          # Bug: Content-Type missing after code generation
          # _KeycloakTokenClient
          # contentType: 'application/x-www-form-urlencoded',
          # data: _data.toJson(),
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/KeycloakAuthenticationIdentifier'
      operationId: getKeycloakToken
      parameters:
        - name: realm
          in: path
          required: true
          example: testing
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KeycloakOpenIdentityConnect'
components:
  schemas:
    KeycloakOpenIdentityConnect:
      type: object
      properties:
        access_token:
          type: string
        expires_in:
          type: integer
          example: 300
        refresh_expires_in:
          type: integer
          example: 1800
        refresh_token:
          type: string
        token_type:
          type: string
          example: Bearer
        scope:
          type: string
          example: 'profile email'
      required:
        - access_token
        - expires_in
        - refresh_expires_in
        - refresh_token
        - token_type
        - scope
    KeycloakAuthenticationIdentifier:
      type: object
      properties:
        client_id:
          type: string
          description: The ID of the client.
          example: 'testing-client'
        client_secret:
          type: string
          description: The secret of the client.
          example: '**********'
        username:
          type: string
          description: The username of the user.
          example: 'alice'
        password:
          type: string
          description: The password of the user.
          example: '123456'
        grant_type:
          type: string
          description: The grant type.
          example: 'password'
      required:
        - client_id
        - client_secret
        - username
        - password
        - grant_type

Reference responses not properly handled

Take the following example. It looks like the parser is incorrectly using "type": "object" and ignoring the $ref property if they are both defined.

In this case "type": "object" should be ignored and $ref should be used as the response type.

"/collections/{collectionName}": {
  "get": {
    "summary": "describe_collection",
    "operationId": "describe_collection",
    "description": "Get a description of a collection.",
    "parameters": [
      {
        "name": "collectionName",
        "required": true,
        "in": "path",
        "description": "The name of the collection",
        "schema": {
          "type": "string"
        }
      }
    ],
    "responses": {
      "200": {
        "description": "Configuration information and deployment status of the index",
        "content": {
          "application/json": {
            "schema": {
              "type": "object", <---- Problem Here
              "$ref": "#/components/schemas/collectionMeta"
            }
          }
        }
      },
      "404": {
        "description": "Index not found."
      },
      "500": {
        "description": "Internal error. Can be caused by invalid parameters."
      }
    },
  },
}

During the parsing process the result for the API definition is:

@GET('/collections/{collectionName}')
Future<Object> getCollectionsCollectionName({
  @Path('collectionName') required String collectionName,
});

Expected Result

If I manually remove "type": "object", from the schema (indicated above), I get the correct response type:

@GET('/collections/{collectionName}')
Future<CollectionMeta> getCollectionsCollectionName({
  @Path('collectionName') required String collectionName,
});

Flag to use the path for the generated method name

This feature request is related to #83.

Proposal

Add a flag to the swagger_parser configuration that leaves only path in the method name.

swagger_parser:
  path_for_method_name: false # Optional. Set to 'true' to use only the endpoint path for the method name. If set to false, the operationId will be used. Default: false

Motivation

Sometimes the summary in OpenAPI can be irrelevant (invalid, too long or short, in a different language), which can be confusing when searching for the right method in the api client.

The optional flag will leave only path in the name, which will allow better navigation through the code.

Suggested behavior

Current method generation:

  @POST('/auth/email/verify')
  Future<void> emailUsersDbAuthEmailVerifyPost({
    @Body() required Object body,
  });

Proposed method generation:

  @POST('/auth/email/verify')
  Future<void> authEmailVerifyPost({
    @Body() required Object body,
  });

OpenAPI:

{
  "openapi": "3.1.0",
  "info": {
    "title": "Microservice Auth API methods",
    "version": "0.2.2"
  },
  "paths": {
    "/auth/email/verify": {
      "post": {
        "tags": [
          "Email"
        ],
        "summary": "Проверить Email на cуществование в users_db",
        "description": "Вернет True, если пользователь с указанным Email уже зарегистрирован, иначе False.",
        "operationId": "Проверить_Email_на_существование_в_users_db_auth_email_verify_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "type": "object"
              }
            }
          }
        }
      }
    }
  }
}

Enum default generated wrongly

should get the right one, instead of the left
image

{
    "openapi": "3.0.1",
    "info": {
        "title": "OpenAPI definition",
        "version": "v0"
    },
    "servers": [
        {
            "url": "http://example.com",
            "description": "Generated server url"
        }
    ],
    "security": [
        {
            "bearerAuth": []
        }
    ],
    "paths": {},
    "components": {
        "schemas": {
            "GenerateTransactionReq": {
                "type": "object",
                "properties": {
                    "status": {
                        "type": "string",
                        "default": "SUCCESS",
                        "enum": [
                            "success",
                            "failed"
                        ]
                    },
                    "accountType": {
                        "type": "string",
                        "default": "invoice",
                        "enum": [
                            "invoice",
                            "salesorder"
                        ]
                    },
                    "keyword": {
                        "type": "string"
                    }
                }
            }
        },
        "securitySchemes": {
            "bearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "JWT"
            }
        }
    }
}

Bug: AllOf not processed correctly

Hi! Thanks again for your great work on this tool :) I've run into another issue, with objects that use/inherit from other objects. They are translated as using 'allOf' in swagger.json. I've copied a small sample of this below.

"CreateUserRequest": {
        "required": [
          "friendlyName",
          "userId"
        ],
        "type": "object",
        "properties": {
          "userId": {
            "type": "integer",
            "format": "int32"
          },
          "friendlyName": {
            "minLength": 1,
            "type": "string"
          }
        },
        "additionalProperties": false
      },

 "CreateUserWithPhoneRequest": {
        "required": [
          "simCardNumber"
        ],
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/CreateUserRequest"
          }
        ],
        "properties": {
          "simCardNumber": {
            "minLength": 1,
            "type": "string"
          }
        },
        "additionalProperties": false
      },


The created file for the CreateUserWithPhoneRequest looks like:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'create_user_with_phone_request.freezed.dart';
part 'create_user_with_phone_request.g.dart';

@Freezed()
class CreateUserWithPhoneRequest with _$CreateUserWithPhoneRequest {
  const factory CreateUserWithPhoneRequest({
    required String simCardNumber,
  }) = _CreateUserWithPhoneRequest;

  factory CreateUserWithPhoneRequest.fromJson(Map<String, dynamic> json) =>
      _$CreateUserWithPhoneRequestFromJson(json);
}

In short, the inherited class is missing the fields from the base class.

Default value for required fields

Hey thanks for the plugin it's really nice done !

I just have a problem with the default values

@JsonSerializable()
class _$_Affiliateclick implements _Affiliateclick {
  const _$_Affiliateclick(
      {required this.book,
      required this.user,
      required this.partner,
      @JsonKey(name: 'deleted_at')
          required this.deletedAt,
      @JsonKey(name: 'created_at')
          required this.createdAt = '2023-05-21T19:09:37.812Z',
      @JsonKey(name: 'is_deleted')
          required this.isDeleted = false});

The two last are generated with a default value and still have a required prefix which raise an error.
I think it's shouldn't have any required prefix.

nullable Array not generated correctly

Example

 {
   "orderItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderItem"
            },
            "nullable": true
          }
}

Expect

MyModel({
    required List<OrderItem>? orderItems,
  })

Actual

MyModel({
    required List<OrderItem> orderItems, // it lost the '?' operator
  })

Feature: Enum prefix

Most time enum name are like 'type', 'status'. Which is not very good enum from global POV. I am currently using 'swagger_dart_code_generator'. The way they are handling is by prefixing enum with return type. And turns out practically very good solution. This can be a non-default flag if you disagree about it as default behaviour.

allOf results in the schema becoming an Object alias

This is a simplified version of a swagger json I'm working with:

{
    "openapi": "3.0.1",
    "tags": [
        {
            "name": "test-controller"
        }
    ],
    "paths": {
        "/api/test": {
            "get": {
                "tags": [
                    "test-controller"
                ],
                "operationId": "getTest",
                "parameters": [
                    {
                        "name": "testId",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/TestResponse"
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "bearer": []
                    }
                ]
            }
        }
    },
    "components": {
        "schemas": {
            "TestResponse": {
                "type": "object",
                "properties": {
                    "testInheritingField": {
                        "oneOf": [
                            {
                                "$ref": "#/components/schemas/OtherInheritingClass"
                            },
                            {
                                "$ref": "#/components/schemas/FirstInheritingClass"
                            }
                        ]
                    }
                }
            },
            "TestParentClass": {
                "required": [
                    "version",
                    "type"
                ],
                "type": "object",
                "properties": {
                    "version": {
                        "type": "integer",
                        "format": "int64"
                    },
                    "type": {
                        "type": "string"
                    }
                },
                "discriminator": {
                    "propertyName": "type"
                }
            },
            "OtherInheritingClass": {
                "required": [
                    "otherField",
                    "version"
                ],
                "type": "object",
                "allOf": [
                    {
                        "$ref": "#/components/schemas/TestParentClass"
                    },
                    {
                        "properties": {
                            "otherField": {
                                "type": "string"
                            }
                        }
                    }
                ]
            },
            "FirstInheritingClass": {
                "required": [
                    "version",
                    "firstField"
                ],
                "type": "object",
                "allOf": [
                    {
                        "$ref": "#/components/schemas/TestParentClass"
                    },
                    {
                        "type": "object",
                        "properties": {
                            "firstField": {
                                "type": "string"
                            }
                        }
                    }
                ]
            }
        },
        "securitySchemes": {
            "bearer": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "JWT"
            }
        }
    }
}

As you can see there is a TestResponse object with a field which can be of FirstInheritingClass or OtherInheritingClass type. Both of those classes inherit TestParentClass as reflected in "allOf" which combines parent class with the inheriting class' fields.

Unfortunately the way FirstInheritingClass and OtherInheritingClass are generated is as follows:

typedef FirstInheritingClass = Object?;
typedef OtherInheritingClass = Object?;

I'm pretty sure this used to work in the previous versions of the library.

[Bug]: Created enums are not renamed according to replacementRules

Hi!

First off, great tool! Has saved me many hours of work, so thank you!

I have run into a bug, when renaming Enums through swagger_parser.yaml. The classes that use the enums, are referring to the renamed file, however, the Enums themselves don't get renamed. It seems to be an oversight in parser.dart, specifically, I've added, to line472: key = replacementRule.apply(key)!;
This fixes the issue for me.

Thanks and have a great day!

[Question] Can I have multiple schemas?

Like below

swagger_parser:
  - schema_path: assets/openapis/app_api.json
     output_directory: lib/data/sources/app_api
     client_postfix: AppApi
  
  - schema_path: assets/openapis/common_api.json
     output_directory: lib/data/sources/common_api
     client_postfix: CommonApi  

OpenAPI spec `servers` property is not accounted for

The servers property can be defined at the root level of the spec, or at each endpoint. The latter case is not being accounted for in the parser. In turn there is a parsing error.

Unhandled exception:
type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>' in type cast
#0      OpenApiParser.parseRestClients.<anonymous closure>.<anonymous closure> (package:swagger_parser/src/parser/parser.dart:326:13)

Sample spec that fails parsing (saved as text file because Github does not allow JSON file uploads): sample_spec.txt

servers specification: https://swagger.io/docs/specification/api-host-and-base-path/

Solution

It does not look like swagger_parser utilizes or has a need for any of the servers properties. This can be ignored during the parsing process.

@Body is generated with the name as a string parameter

In swagger v2 generation, the @Body is generated with the name as string parameter which is not accepted by the annotation.
Example:

  @PUT('/v7/test')
  Future<ABC> test({
    @Body('obj') required Object object, // the body annotation does not accept the 'obj' string 
  });

Output is invalid

Hi,

The output we had with your code became a bit invalid.

It's missing a .g.dart file and also missing the a private class:

image

Also the dependencies are not totally clear, could you map them? The output however, seems quite nice.

Json request body should add appropriate content-type header

Hello!
I noticed that despite the following part in the swagger json:

"requestBody":{
    "content":{
      "application/json":{
        "schema":{
          "$ref":"#/components/schemas/RequestBodyObject"
        }
      }
    }
  }

a content-type header with application/json value is not added to the endpoint which causes issues in my app.

Could this be added?

Query Params with omitted "required" attribute should be treated as "required": false

According to the OpenApi spec (2.0, 3.0 and 3.1) the required attribute default value is false.
"[...]the property MAY be included and its default value is false."

That is not the case for generated query parameters now.

For instance, this:

{
  "/api/v1/{id}": {
    "get": {
      "tags": [
        "..."
      ],
      "summary": "....",
      "operationId": "...",
      "parameters": [
        {
          "name": "idd",
          "in": "path",
          "description": "...",
          "required": true,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "pageOffset",
          "in": "query",
          "description": "",
          "schema": {
            "type": "integer",
            "format": "int32"
          }
        },
        {
          "name": "pageSize",
          "in": "query",
          "description": "",
          "schema": {
            "type": "integer",
            "format": "int32"
          }
        }
      ]
    }
  }
}

Will produce this:

  @GET('/api/v1/{id}')
  Future<List<ResponseType>> get({
    @Path('id') required String id,
    @Query('pageOffset') required int pageOffset,
    @Query('pageSize') required int pageSize,
  });

But should produce this:

  @GET('/api/v1/{id}')
  Future<List<ResponseType>> get({
    @Path('id') required String id,
    @Query('pageOffset') int? pageOffset,
    @Query('pageSize') int? pageSize,
  });

I think the problem is this line in each of the parametersV* methods in parser.dart:
isRequired: isRequired ?? true,

should be

isRequired: isRequired ?? false,

Utilize the description field in API spec to populate doc strings

Request

Add description data to generated serialization models as doc strings.

Example

"Collection": {
  "type": "object",
  "description": "Collection of elements",
  "required": [
    "name",
    "dimension",
    "status"
  ],
  "properties": {
    "name": {
      "type": "string",
      "description": "Namespace location in storage"
    },
    "dimension": {
      "type": "integer",
      "format": "int64",
      "description": "Vector dimension"
    },
    "size": {
      "type": "integer",
      "format": "int64",
      "description": "Size of the file on disk"
    },
    "status": {
      "type": "string",
    }
  }
}

Expected Output

/// Collection of elements
@JsonSerializable()
class Collection {
  const Collection({
    required this.name,
    required this.dimension,
    required this.status,
    this.size,
  });

  factory Collection.fromJson(Map<String, dynamic> json) =>
      _$CollectionFromJson(json);

  /// Namespace location in storage
  final String name;

  /// Vector dimension
  final int dimension;

  /// Size of the file on disk
  final int? size;

  /// No description provided
  final String status;

  Map<String, dynamic> toJson() => _$CollectionToJson(this);
}

For documentation coverage purposes, it would be nice for properties without a doc string to denote /// No description provided or something similar.... Else if it is empty, Dart docs will complain that the item is not documented. This may serve as a hint to the user to update the schema definition with descriptions.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.