Giter Club home page Giter Club logo

fastapi-code-generator's Introduction

Hi there πŸ‘‹,

I am a software developer who enjoys contributing to Open Source Software (OSS) with a goal to make the development environment friendlier πŸ˜„.

Here are some of the projects I've worked on:

If any of my tools have been helpful in your projects, would you consider sponsoring me? Your support would really make a difference and help me continue to maintain and improve these tools for everyone. Thank you! πŸš€

fastapi-code-generator's People

Contributors

akuma5157 avatar allen-munsch avatar baophamtd avatar bpow avatar david-westreicher avatar dependabot-preview[bot] avatar dependabot[bot] avatar dmille avatar ioggstream avatar jcarlosgalvezm avatar koxudaxi avatar longbeachhxc avatar mmzeynalli avatar n0nvme avatar notpushkin avatar pre-commit-ci[bot] avatar sofianhnaide avatar sondrelg avatar v1kan0v avatar ytoml avatar yyamano avatar

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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

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

fastapi-code-generator's Issues

Not 200 response generated wrong

Example path:

"/api/v1/smtg/{smtg_id}"
delete:
  operationId: delete_smtg
  parameters:
    - required: true
      schema:
        title: Id
        type: integer
      name: smtg_id
      in: path
  responses:
    "204":
      description: Successful Response

The code generated:

@app.delete("/api/v1/smtg/{smtg_id}", response_model=None)
def delete_smtg(
    smtg_id: int,
) -> None:
    pass

This code tells nothing about status code, so fastapi response status code is 200 instead of 204.

The code need to be generated:

@app.delete("/api/v1/smtg/{smtg_id}", status_code=204)
def delete_smtg(
    smtg_id: int,
) -> None:
    pass

This code tells fastapi to respond status code 204 not 200.

https://fastapi.tiangolo.com/tutorial/response-status-code/?h=status

Unable to install fastapi-code-generator with python 3.7

I cannot install fastapi-code-generator as described in the readme on debian 10 with python 3.7.3:

$ docker run -i -t --rm python:3.7.3 pip install fastapi-code-generator
ERROR: Could not find a version that satisfies the requirement fastapi-code-generator (from versions: none)
ERROR: No matching distribution found for fastapi-code-generator

Unbound local error when not defining '--model-file'

seems like there could be a bug that requires the use of --model-file rather than it be Optional.

When I pass in --model-file the program runs.

fastapi_code_generator/__main__.py", line 33, in main
    return generate_code(input_name, input_text, output_dir, template_dir, model_path)
UnboundLocalError: local variable 'model_path' referenced before assignment

[suggestion] Explicitly stating the required python version

I tried to install this library for quite a while, but pip couldn't find an appropriate version which confused me quite a lot. After a while, I realized I had python 3.7 and maybe that was the issue. After seeing the badge with python 3.8, I realized I had to use 3.8 and it worked. However, this requirement is not explicitly stated anywhere as far as I could find, apart from the button.

I think it would be clearer if this was added somewhere for clarity. I also don't know if the 3.8 is a hard requirement or that is only has been tested for that version, but I'll leave that open.

Test_generate fails on master branch

I'd expect tests to pass cleanly on the master branch. Did I do something wrong?

sholden@overhead /tmp % git clone [email protected]:koxudaxi/fastapi-code-generator.git
Cloning into 'fastapi-code-generator'...
remote: Enumerating objects: 2344, done.
remote: Counting objects: 100% (732/732), done.
remote: Compressing objects: 100% (244/244), done.
remote: Total 2344 (delta 525), reused 640 (delta 469), pack-reused 1612
Receiving objects: 100% (2344/2344), 2.72 MiB | 2.07 MiB/s, done.
Resolving deltas: 100% (1248/1248), done.
sholden@overhead /tmp % cd fastapi-code-generator
sholden@overhead fastapi-code-generator % poetry install
Creating virtualenv fastapi-code-generator-4IuS1XPG-py3.10 in /Users/sholden/Library/Caches/pypoetry/virtualenvs
Installing dependencies from lock file

Package operations: 61 installs, 0 updates, 0 removals

  β€’ Installing six (1.16.0)
  β€’ Installing attrs (21.4.0)
  β€’ Installing idna (3.3)
  β€’ Installing pyrsistent (0.16.1)
  β€’ Installing sniffio (1.2.0)
  β€’ Installing anyio (3.5.0)
  β€’ Installing certifi (2021.10.8)
  β€’ Installing charset-normalizer (2.0.11)
  β€’ Installing dnspython (2.2.0)
  β€’ Installing h11 (0.12.0)
  β€’ Installing isodate (0.6.1)
  β€’ Installing jsonschema (3.2.0)
  β€’ Installing pyparsing (3.0.7)
  β€’ Installing ruamel.yaml.clib (0.2.6)
  β€’ Installing urllib3 (1.26.8)
  β€’ Installing appdirs (1.4.4)
  β€’ Installing chardet (4.0.0)
  β€’ Installing click (8.0.3)
  β€’ Installing email-validator (1.1.3)
  β€’ Installing httpcore (0.14.7)
  β€’ Installing iniconfig (1.1.1)
  β€’ Installing markupsafe (2.0.1)
  β€’ Installing openapi-schema-validator (0.1.6)
  β€’ Installing packaging (21.3)
  β€’ Installing pathspec (0.9.0)
  β€’ Installing pluggy (1.0.0)
  β€’ Installing py (1.11.0)
  β€’ Installing pyyaml (6.0)
  β€’ Installing regex (2022.1.18)
  β€’ Installing requests (2.27.1)
  β€’ Installing rfc3986 (1.5.0)
  β€’ Installing ruamel.yaml (0.17.20)
  β€’ Installing semver (2.13.0)
  β€’ Installing toml (0.10.2)
  β€’ Installing tomli (2.0.1)
  β€’ Installing typed-ast (1.5.2)
  β€’ Installing typing-extensions (4.0.1)
  β€’ Installing argcomplete (2.0.0)
  β€’ Installing black (19.10b0)
  β€’ Installing colorama (0.4.4)
  β€’ Installing coverage (6.3.1)
  β€’ Installing genson (1.2.2)
  β€’ Installing httpx (0.22.0)
  β€’ Installing inflect (5.4.0)
  β€’ Installing isort (4.3.21)
  β€’ Installing jinja2 (3.0.3)
  β€’ Installing mypy-extensions (0.4.3)
  β€’ Installing openapi-spec-validator (0.3.3)
  β€’ Installing prance (0.21.8.0)
  β€’ Installing pydantic (1.9.0)
  β€’ Installing pysnooper (0.5.0)
  β€’ Installing pytest (6.2.5)
  β€’ Installing python-dateutil (2.8.2)
  β€’ Installing shellingham (1.4.0)
  β€’ Installing datamodel-code-generator (0.11.19)
  β€’ Installing freezegun (1.1.0)
  β€’ Installing mypy (0.931)
  β€’ Installing pytest-cov (3.0.0)
  β€’ Installing pytest-mock (3.7.0)
  β€’ Installing stringcase (1.2.0)
  β€’ Installing typer (0.4.0)

Installing the current project: fastapi-code-generator (0.0.1)
sholden@overhead fastapi-code-generator % pytest
zsh: command not found: pytest
sholden@overhead fastapi-code-generator % ls
LICENSE			docs/			mkdocs.yml		poetry.lock		scripts/
README.md		fastapi_code_generator/	mypy.ini		pyproject.toml		tests/
sholden@overhead fastapi-code-generator % poetry run pytest
================================================================ test session starts ================================================================
platform darwin -- Python 3.10.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /private/tmp/fastapi-code-generator
plugins: anyio-3.5.0, mock-3.7.0, cov-3.0.0
collected 7 items

tests/test_generate.py ......F                                                                                                                [100%]

===================================================================== FAILURES ======================================================================
_____________________________________________________________ test_generate_remote_ref ______________________________________________________________

__wrapped_mock_method__ = <function NonCallableMock.assert_has_calls at 0x103886200>
args = (<MagicMock name='get' id='4386748592'>, [call('https://schema.example', headers=None)]), kwargs = {}, __tracebackhide__ = True
msg = "Calls not found.\nExpected: [call('https://schema.example', headers=None)]\nActual: [call('https://schema.example', h...erify': True} == {}\n  Left contains 2 more items:\n  {'headers': None, 'verify': True}\n  Use -v to get the full diff"
__mock_self = <MagicMock name='get' id='4386748592'>, actual_args = ('https://schema.example',), actual_kwargs = {'headers': None, 'verify': True}
introspection = "\nArgs:\nassert ('https://schema.example',) == ([call('https...aders=None)],)\n  At index 0 diff: 'https://schema.exa...erify': True} == {}\n  Left contains 2 more items:\n  {'headers': None, 'verify': True}\n  Use -v to get the full diff"
@py_assert2 = ([call('https://schema.example', headers=None)],), @py_assert1 = False
@py_format4 = "('https://schema.example',) == ([call('https...aders=None)],)\n~At index 0 diff: 'https://schema.example' != [call('https://schema.example', headers=None)]\n~Use -v to get the full diff"

    def assert_wrapper(
        __wrapped_mock_method__: Callable[..., Any], *args: Any, **kwargs: Any
    ) -> None:
        __tracebackhide__ = True
        try:
>           __wrapped_mock_method__(*args, **kwargs)

/Users/sholden/Library/Caches/pypoetry/virtualenvs/fastapi-code-generator-4IuS1XPG-py3.10/lib/python3.10/site-packages/pytest_mock/plugin.py:414:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <MagicMock name='get' id='4386748592'>, calls = [call('https://schema.example', headers=None)], any_order = False

    def assert_has_calls(self, calls, any_order=False):
        """assert the mock has been called with the specified calls.
        The `mock_calls` list is checked for the calls.

        If `any_order` is False (the default) then the calls must be
        sequential. There can be extra calls before or after the
        specified calls.

        If `any_order` is True then the calls can be in any order, but
        they must all appear in `mock_calls`."""
        expected = [self._call_matcher(c) for c in calls]
        cause = next((e for e in expected if isinstance(e, Exception)), None)
        all_calls = _CallList(self._call_matcher(c) for c in self.mock_calls)
        if not any_order:
            if expected not in all_calls:
                if cause is None:
                    problem = 'Calls not found.'
                else:
                    problem = ('Error processing expected calls.\n'
                               'Errors: {}').format(
                                   [e if isinstance(e, Exception) else None
                                    for e in expected])
>               raise AssertionError(
                    f'{problem}\n'
                    f'Expected: {_CallList(calls)}'
                    f'{self._calls_repr(prefix="Actual").rstrip(".")}'
                ) from cause
E               AssertionError: Calls not found.
E               Expected: [call('https://schema.example', headers=None)]
E               Actual: [call('https://schema.example', headers=None, verify=True)]

/opt/homebrew/Cellar/[email protected]/3.10.4/Frameworks/Python.framework/Versions/3.10/lib/python3.10/unittest/mock.py:956: AssertionError

During handling of the above exception, another exception occurred:

mocker = <pytest_mock.plugin.MockerFixture object at 0x1059302b0>

    @freeze_time("2020-06-19")
    def test_generate_remote_ref(mocker):
        oas_file = DATA_DIR / OPEN_API_REMOTE_REF_DIR_NAME / 'body_and_parameters.yaml'
        person_response = mocker.Mock()
        person_response.text = oas_file.read_text()
        httpx_get_mock = mocker.patch('httpx.get', side_effect=[person_response])

        with TemporaryDirectory() as tmp_dir:
            output_dir = Path(tmp_dir) / oas_file.stem
            generate_code(
                input_name=oas_file.name,
                input_text=oas_file.read_text(),
                output_dir=output_dir,
                template_dir=None,
            )
>           httpx_get_mock.assert_has_calls(
                [call('https://schema.example', headers=None),]
            )
E           AssertionError: Calls not found.
E           Expected: [call('https://schema.example', headers=None)]
E           Actual: [call('https://schema.example', headers=None, verify=True)]
E
E           pytest introspection follows:
E
E           Args:
E           assert ('https://schema.example',) == ([call('https...aders=None)],)
E             At index 0 diff: 'https://schema.example' != [call('https://schema.example', headers=None)]
E             Use -v to get the full diff
E           Kwargs:
E           assert {'headers': N...verify': True} == {}
E             Left contains 2 more items:
E             {'headers': None, 'verify': True}
E             Use -v to get the full diff

tests/test_generate.py:78: AssertionError
================================================================= warnings summary ==================================================================
tests/test_generate.py::test_generate_default_template[oas_file0]
tests/test_generate.py::test_generate_default_template[oas_file1]
tests/test_generate.py::test_generate_default_template[oas_file2]
tests/test_generate.py::test_generate_default_template[oas_file3]
tests/test_generate.py::test_generate_default_template[oas_file4]
tests/test_generate.py::test_generate_custom_security_template[oas_file0]
tests/test_generate.py::test_generate_remote_ref
  /Users/sholden/Library/Caches/pypoetry/virtualenvs/fastapi-code-generator-4IuS1XPG-py3.10/lib/python3.10/site-packages/datamodel_code_generator/format.py:87: UserWarning: black doesn't support `experimental-string-processing` option for wrapping string literal in 19.10b0
    warn(

-- Docs: https://docs.pytest.org/en/stable/warnings.html
============================================================== short test summary info ==============================================================
FAILED tests/test_generate.py::test_generate_remote_ref - AssertionError: Calls not found.
====================================================== 1 failed, 6 passed, 7 warnings in 4.09s ======================================================

Support remote $ref-s

I expect

Remote $refs in parameters to be processed, eg

paths:
  /foo:
    parameters:
    - $ref: 'https://schema.example#/components/parameters/MyParam'

and in schema.example

components:
  parameters:
    MyParam:
      name: foo
      schema:
        type: string

Instead

I get NotImplementedError ;)

Schema with enums optional fields are not generated correctly

When the schema includes optional string properties with enum format, the generated model crashes with the following exception:
TypeError: Optional[t] requires a single type. Got FieldInfo(description='', extra={}).

The reason is the generated Enum class has the same name as the Field:
from enum import Enum
from typing import Optional

from pydantic import BaseModel, Field

class Prop(Enum):
    A = 'A'
    B = 'B'
    C = 'C'

class Request(BaseModel):
    Prop: Optional[Prop] = Field(None, description='') # <-- the problem is here`

When run, Optional.__getitem__()' gets Prop - the FieldInfo, not the Enum class. Changing class Prop(Enum) to class PropEnum(Enum) solved the probelm.

To reproduce, run with the following schema:
openapi: 3.0.0 servers: - description: SwaggerHub API Auto Mocking url: 'https://virtserver.swaggerhub.com/cropmodel/1.0.0' info: description: Demo version: 1.0.0 title: Demo paths: /: post: requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Request' responses: '200': description: OK '400': description: bad input parameter components: schemas: Request: type: object properties: Prop: type: string enum: [A, B, C] description: ""

No Request parameter generated for `requestBody` with `multipart/form-data`

I am describing a schema for uploading files, and hence using multipart/form-data content type for requestBody. The generated function have no argument provided. Note that the request: Request is correctly generated for application/x-www-form-urlencoded content type.

paths:
  /foo:
    put:
      summary: Bar
        requestBody:
        content:
          "multipart/form-data":
            schema:
              type: object
              properties:
                metadata:
                  type: string
                  format: binary
                payload:
                  type: string
                  format: binary

Request body assumed always ref

Seems like codegen (0.0.13) expects:

      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Something"

and does not accept:

      requestBody:
        content:
          application/json:
            schema:
              type: string

Log:

Traceback (most recent call last):
  File "/Users/victor/megad-heated-floor-thermostat/.venv/bin/fastapi-codegen", line 8, in <module>
    sys.exit(app())
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/typer/main.py", line 213, in __call__
    return get_command(self)()
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/typer/main.py", line 496, in wrapper
    return callback(**use_params)  # type: ignore
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/fastapi_code_generator/__main__.py", line 26, in main
    return generate_code(input_name, input_text, output_dir, template_dir)
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/fastapi_code_generator/__main__.py", line 37, in generate_code
    parsed_object: ParsedObject = parser.parse()
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 391, in parse
    return self.parse_paths(openapi)
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 400, in parse_paths
    return ParsedObject(
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 372, in __init__
    operation.arguments
  File "/usr/local/opt/[email protected]/Frameworks/Python.framework/Versions/3.8/lib/python3.8/functools.py", line 967, in __get__
    val = self.func(instance)
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 175, in arguments
    return self.get_arguments(snake_case=False)
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 183, in get_arguments
    argument.argument for argument in self.get_argument_list(snake_case)
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 197, in get_argument_list
    if self.request:
  File "/usr/local/opt/[email protected]/Frameworks/Python.framework/Versions/3.8/lib/python3.8/functools.py", line 967, in __get__
    val = self.func(instance)
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 113, in request
    type_hint=schema.ref_object_name,
  File "/Users/victor/megad-heated-floor-thermostat/.venv/lib/python3.8/site-packages/datamodel_code_generator/parser/jsonschema.py", line 115, in ref_object_name
    return self.ref.rsplit('/', 1)[-1]  # type: ignore
AttributeError: 'NoneType' object has no attribute 'rsplit'

Confused how to set up an API key auth using this generator

Hello! This looks very cool.

I can create the server fine, and load the jinja templates, but I'm not sure how to assign the correct Security to operations.

I want to add these:

  "BearerAuth": {
        "type": "http",
        "scheme": "bearer"
      },

or 

    API-key-header:
      in: header
      name: Authorization
      type: apiKey

How do I add this into the jinja template properly?

Feature request: $ref from local file

First of - very useful library/tool - thanks for making it!

I have a set of OpenAPI files (from these 3GPP specification) which I want to use.

In these files, many $refs point to components from local files.

For example:

'400':
    $ref: 'TS29571_CommonData.yaml#/components/responses/400'

or

dnais:
    type: array
    items:
        $ref: 'TS29571_CommonData.yaml#/components/schemas/Dnai'
    minItems: 1

I see that the generator supports remote references but it does not support local files. It won't be too hard to search-and-replace these references - assuming the files are hosted somewhere, or you want to make the effort to host them yourself, which is not always the case... so maybe it would be good to support such local references.

I've look into the code and it seems a few changes are required.

In this library, in get_ref_body (fastapi_code_generator/parser.py, in line 40) - need to add another case to identify references to a file. I manually tested the following code:

RE_FILE_REF: Pattern[str] = re.compile(r"^.*\.ya?ml#") # at the top

    elif RE_FILE_REF.match(ref):   
        # a new case - get ref body from local file
        filename, path = ref.rsplit('#/', 1)
        ref_body = openapi_model_parser._get_ref_body(filename)
        return get_model_by_path(ref_body, path.split('/'))

I thought this might be enough, and it does work if your cwd is where the files are located. Otherwise, _get_ref_body_from_remote fails because of the way it is currently implemented.

I tried to understand what would be the right approach from here but I am not sure - I couldn't find (yet) a reference to the original file available in parsing another - with the original file name, the $ref-ed file could either be in cwd or relative to the original file.

I sort of got the project to run locally on my machine but I run into issues. I am not sure how quickly I can get everything to run (tests etc).

I'd be happy to help and I will try to setup my computer for development...

Thanks!

Tomer

Need to bump datamodel-code-generator to 0.11.18

Following error is thrown with the current version using datamodel-code-generator==0.11.15:

Traceback (most recent call last):
  File "/a/path/bin/fastapi-codegen", line 8, in <module>
    sys.exit(app())
  File "/a/path/lib/python3.8/site-packages/typer/main.py", line 214, in __call__
    return get_command(self)(*args, **kwargs)
  File "/a/path/lib/python3.8/site-packages/click/core.py", line 1128, in __call__
    return self.main(*args, **kwargs)
  File "/a/path/lib/python3.8/site-packages/click/core.py", line 1053, in main
    rv = self.invoke(ctx)
  File "/a/path/lib/python3.8/site-packages/click/core.py", line 1395, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/a/path/lib/python3.8/site-packages/click/core.py", line 754, in invoke
    return __callback(*args, **kwargs)
  File "/a/path/lib/python3.8/site-packages/typer/main.py", line 500, in wrapper
    return callback(**use_params)  # type: ignore
  File "/a/path/lib/python3.8/site-packages/fastapi_code_generator/__main__.py", line 47, in main
    return generate_code(input_name, input_text, output_dir, template_dir, model_path)
  File "/a/path/lib/python3.8/site-packages/fastapi_code_generator/__main__.py", line 79, in generate_code
    models = parser.parse()
  File "/a/path/lib/python3.8/site-packages/datamodel_code_generator/parser/base.py", line 433, in parse
    code_formatter: Optional[CodeFormatter] = CodeFormatter(
  File "/a/path/lib/python3.8/site-packages/datamodel_code_generator/format.py", line 44, in __init__
    path = root / "pyproject.toml"
TypeError: unsupported operand type(s) for /: 'tuple' and 'str'

Simple workaround until it is bumped:
pip install -U datamodel-code-generator==0.11.18

AttributeError raised with MEF-LSO-Legato generated OpenAPI

I get an AttributeError when I run fastapi-codegen with some OpenAPI files generated from MEF-LSO-Legato Billie Release. The exception is raised inside jsonschema parser library. Here is the error:

File "<...>\.venv\lib\site-packages\datamodel_code_generator\parser\jsonschema.py", line 1052, in root_id_context
    root_id: Optional[str] = root_raw.get('$id')
AttributeError: 'str' object has no attribute 'get'

I left a "frozen" version of MEF-LSO-Legato in this public repo and just changed the $ref for using repo's raw files. I'm trying to generate code from <...>/serviceApi/inventory/serviceInventoryApi.openapi.yaml file when I get the error.

How to update api.yaml and keep main.py route logic?

  1. Let's say I currently have the api.yaml and added route logic to main.py (no longer just "pass"):
# generated by fastapi-codegen:
#   filename:  api.yaml
#   timestamp: 2020-06-14T10:45:22+00:00

from __future__ import annotations

from typing import Optional

from fastapi import FastAPI, Query

from .models import Pets

app = FastAPI(version="1.0.0", title="Swagger Petstore", license="{'name': 'MIT'}",)


@app.get('/pets', response_model=Pets)
def list_pets(limit: Optional[int] = None) -> Pets:
    """
    List all pets
    """
    # Added logic
    return db.pets.get().limit(limit)
  1. I now realize I want to add another search parameter offset so I make the necessary changes in api.yaml and run fastapi-codegen --input api.yaml --output app

The output I receive:

@app.get('/pets', response_model=Pets)
def list_pets(limit: Optional[int] = None, offset: Optional[int] = None) -> Pets:
    """
    List all pets
    """
    pass

Versus the output I would like:

@app.get('/pets', response_model=Pets)
def list_pets(limit: Optional[int] = None, offset: Optional[int] = None) -> Pets:
    """
    List all pets
    """
    # Added logic
    return db.pets.get().limit(limit)

Is it possible to get this behavior?

Please move black to dev-dependencies

You are listing black as a runtime dependency with a fixed version of 19.1b0. Unfortunately that conflicts with the version I want to use 20.8b1.

Afaics black is only used as a development dependency in this project, so it should be marked as such and the conflict would be resolved.

Scoped imports

this code generator has imports for rendering import statements.
But, The variable has all import statements.
I want to create imports each scope. (example: request, response, query parameter)

TypeError: unsupported operand type(s) for /: 'tuple' and 'str'

I have just discovered this library. But the example is not even running and there seems to be a problem which may be related to the issue reported here. What is the work-around just to get the library going - any older versions which would work?

We use poetry for package management

[tool.poetry.dependencies]
python = "^3.10"
fastapi-code-generator = "^0.3.4"
fastapi = "^0.74.1"
path = root / "pyproject.toml"

TypeError: unsupported operand type(s) for /: 'tuple' and 'str'

Handle all request parameter types

Hi,

Thanks for this tool !
I'm trying to infer request parameter type, based on what's inside "in" field in operation's parameters.
It appears that only Query parameter type is handled for the moment, is it a known issue ? Willing to help and PR if needed.

Empty `response_model` gets typehinted to `Any`, which is not imported

How to reproduce:

  • Omit the response_model in the router decorator
  • Run this on the openapi.json file
  • The result's main.py would have used Any for the empty response model and type hint without importing it.

Why does this happen?

My take is that the datamodel-code-generator you use to create schemas and resolve imports won't create this schema as its empty. Therefore its imports won't have Any thus creating an unresolved import.

Resolve $refs

I expect

the generator to resolve refs

Instead

It doesn't

Sort generated required body and query/path parameters

Bug report

Given OAS3 like this:

paths:
  /{ueId}/sdm-subscriptions:
    post:
      summary: subscribe to notifications
      operationId: Subscribe
      tags:
        - Subscription Creation
      parameters:
        - name: ueId
          in: path
          description: Identity of the user
          required: true
          schema:
            $ref: 'http://localhost:8081/29.571/16.7.0/TS29571_CommonData.yaml#/components/schemas/VarUeId'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SdmSubscription'
        required: true

Expected behaviour:
The required body is generated BEFORE the optional parameters in the Python code.

image

Actual behaviour:
The required body is generated AFTER the optional parameters in the Python code. I have to bring it into the right order by hand to solve the issue.

image

Solution:
Put the body function parameter before the other function parameters at the beginning of the parameter list.

Absolutely minor bug, but I thought I report it. Thanks!

Arrays always assumed to contain a $ref

The following spec currently results in a generation error:

openapi: 3.0.0
info:
  title: pet-finder
  version: '1.0'
servers:
  - url: 'http://localhost:3000'
paths:
  /find/:
    get:
      summary: find
      tags: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  type: integer
              examples:
                example-1:
                  value:
                    - 0
                    - 1
                    - 3
      operationId: get-find
components:
  schemas: {}

Error message:

❯ poetry run fastapi-codegen -i ../petfinder/reference/pet-finder.v1.yaml -o /tmp/petfinder
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/languitar/src/fastapi-code-generator/.venv/lib/python3.8/site-packages/typer/main.py", line 213, in __call__
    return get_command(self)()
  File "/home/languitar/src/fastapi-code-generator/.venv/lib/python3.8/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/home/languitar/src/fastapi-code-generator/.venv/lib/python3.8/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/home/languitar/src/fastapi-code-generator/.venv/lib/python3.8/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/languitar/src/fastapi-code-generator/.venv/lib/python3.8/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/home/languitar/src/fastapi-code-generator/.venv/lib/python3.8/site-packages/typer/main.py", line 496, in wrapper
    return callback(**use_params)  # type: ignore
  File "/home/languitar/src/fastapi-code-generator/fastapi_code_generator/__main__.py", line 26, in main
    return generate_code(input_name, input_text, output_dir, template_dir)
  File "/home/languitar/src/fastapi-code-generator/fastapi_code_generator/__main__.py", line 37, in generate_code
    parsed_object: ParsedObject = parser.parse()
  File "/home/languitar/src/fastapi-code-generator/fastapi_code_generator/parser.py", line 391, in parse
    return self.parse_paths(openapi)
  File "/home/languitar/src/fastapi-code-generator/fastapi_code_generator/parser.py", line 400, in parse_paths
    return ParsedObject(
  File "/home/languitar/src/fastapi-code-generator/fastapi_code_generator/parser.py", line 375, in __init__
    operation.response
  File "/usr/lib/python3.8/functools.py", line 967, in __get__
    val = self.func(instance)
  File "/home/languitar/src/fastapi-code-generator/fastapi_code_generator/parser.py", line 252, in response
    type_ = f'List[{schema.items.ref_object_name}]'
  File "/home/languitar/src/fastapi-code-generator/.venv/lib/python3.8/site-packages/datamodel_code_generator/parser/jsonschema.py", line 115, in ref_object_name
    return self.ref.rsplit('/', 1)[-1]  # type: ignore
AttributeError: 'NoneType' object has no attribute 'rsplit'

Problem generating stub for get method with array parameter

Trying fastapi-codegen --input maas-err.swagger.yaml --output app_fastapi with this yaml:

openapi: 3.0.0
info:
  title: maas.proto
  version: version not set
paths:
  /bertee:
    post:
      operationId: ModelService_getEvent
      responses:
        "200":
          description: A successful response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Response"
        default:
          description: An unexpected error response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/runtimeError"
      requestBody:
        $ref: "#/components/requestBodies/Request"
      tags:
        - ModelService
  /isalive:
    get:
      operationId: ModelService_isAliveGet
      responses:
        "200":
          description: A successful response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Response"
        default:
          description: An unexpected error response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/runtimeError"
      parameters:
        - name: message_texts
          in: query
          required: false
          explode: true
          schema:
            type: array
            items:
              type: string
      tags:
        - ModelService
    post:
      operationId: ModelService_isAlivePost
      responses:
        "200":
          description: A successful response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Response"
        default:
          description: An unexpected error response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/runtimeError"
      requestBody:
        $ref: "#/components/requestBodies/Request"
      tags:
        - ModelService
components:
  requestBodies:
    Request:
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Request"
      required: true
  schemas:
    Request:
      type: object
      properties:
        message_texts:
          type: array
          items:
            type: string
    Response:
      type: object
      properties:
        tx_id:
          type: string
        events:
          type: array
          items:
            type: string
    protobufAny:
      type: object
      properties:
        type_url:
          type: string
        value:
          type: string
          format: byte
    runtimeError:
      type: object
      properties:
        error:
          type: string
        code:
          type: integer
          format: int32
        message:
          type: string
        details:
          type: array
          items:
            $ref: "#/components/schemas/protobufAny"

I get this error:

Traceback (most recent call last):
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/bin/fastapi-codegen", line 8, in <module>
    sys.exit(app())
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/typer/main.py", line 213, in __call__
    return get_command(self)()
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/typer/main.py", line 496, in wrapper
    return callback(**use_params)  # type: ignore
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/fastapi_code_generator/__main__.py", line 26, in main
    return generate_code(input_name, input_text, output_dir, template_dir)
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/fastapi_code_generator/__main__.py", line 37, in generate_code
    parsed_object: ParsedObject = parser.parse()
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 391, in parse
    return self.parse_paths(openapi)
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 400, in parse_paths
    return ParsedObject(
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 372, in __init__
    operation.arguments
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/functools.py", line 967, in __get__
    val = self.func(instance)
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 175, in arguments
    return self.get_arguments(snake_case=False)
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 183, in get_arguments
    argument.argument for argument in self.get_argument_list(snake_case)
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 195, in get_argument_list
    arguments.append(self.get_parameter_type(parameter, snake_case))
  File "/Users/mhill/opt/anaconda3/envs/bert-ee/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 206, in get_parameter_type
    type_ = json_schema_data_formats[schema.type][format_]
KeyError: 'array'

If I remove the parameters: section of isalive/ get: then it seems to work fine. Are arrays supported here?

ValueError: 'template/main.jinja2' is not in the subpath

I'm trying to make my project compatible with poetry so I have to add a start method where I invoke uvicorn so that I can have the following in the poetry run script:

[tool.poetry.scripts]
run = "skeleton_python_api.main:start"

I have the following structure of a project:

β”œβ”€β”€ README.md
β”œβ”€β”€ openapi.yaml
β”œβ”€β”€ poetry.lock
β”œβ”€β”€ pyproject.toml
β”œβ”€β”€ skeleton_python_api
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ main.py
β”‚   └── models.py
β”œβ”€β”€ template
β”‚   └── main.jinja2
└── tests
    β”œβ”€β”€ __init__.py
    └── test_skeleton_python_api.py

While running the following command:

(skeleton-python-api-PB31_aPS-py3.9) ➜  skeleton-python-api git:(master) βœ— fastapi-codegen --input openapi.yaml --output skeleton_python_api -t template 

I'm getting an error:

Traceback (most recent call last):
  File "/Users/bartosz.nadworny/Library/Caches/pypoetry/virtualenvs/skeleton-python-api-PB31_aPS-py3.9/bin/fastapi-codegen", line 8, in <module>
    sys.exit(app())
  File "/Users/bartosz.nadworny/Library/Caches/pypoetry/virtualenvs/skeleton-python-api-PB31_aPS-py3.9/lib/python3.9/site-packages/typer/main.py", line 214, in __call__
    return get_command(self)(*args, **kwargs)
  File "/Users/bartosz.nadworny/Library/Caches/pypoetry/virtualenvs/skeleton-python-api-PB31_aPS-py3.9/lib/python3.9/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/Users/bartosz.nadworny/Library/Caches/pypoetry/virtualenvs/skeleton-python-api-PB31_aPS-py3.9/lib/python3.9/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/Users/bartosz.nadworny/Library/Caches/pypoetry/virtualenvs/skeleton-python-api-PB31_aPS-py3.9/lib/python3.9/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/bartosz.nadworny/Library/Caches/pypoetry/virtualenvs/skeleton-python-api-PB31_aPS-py3.9/lib/python3.9/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/Users/bartosz.nadworny/Library/Caches/pypoetry/virtualenvs/skeleton-python-api-PB31_aPS-py3.9/lib/python3.9/site-packages/typer/main.py", line 497, in wrapper
    return callback(**use_params)  # type: ignore
  File "/Users/bartosz.nadworny/Library/Caches/pypoetry/virtualenvs/skeleton-python-api-PB31_aPS-py3.9/lib/python3.9/site-packages/fastapi_code_generator/__main__.py", line 28, in main
    return generate_code(input_name, input_text, output_dir, template_dir)
  File "/Users/bartosz.nadworny/Library/Caches/pypoetry/virtualenvs/skeleton-python-api-PB31_aPS-py3.9/lib/python3.9/site-packages/fastapi_code_generator/__main__.py", line 50, in generate_code
    relative_path = target.relative_to(template_dir.absolute())
  File "/usr/local/Cellar/[email protected]/3.9.0_2/Frameworks/Python.framework/Versions/3.9/lib/python3.9/pathlib.py", line 928, in relative_to
    raise ValueError("{!r} is not in the subpath of {!r}"
ValueError: 'template/main.jinja2' is not in the subpath of '/Users/bartosz.nadworny/workspace/space/skeleton-python-api/template' OR one path is relative and the other is absolute.

Template:

from __future__ import annotations

import uvicorn
from fastapi import FastAPI

{{imports}}

app = FastAPI(
    {% if info %}
    {% for key,value in info.items() %}
    {{ key }} = "{{ value }}",
    {% endfor %}
    {% endif %}
    )


{% for operation in operations %}
@app.{{operation.type}}('{{operation.snake_case_path}}', response_model={{operation.response}})
def {{operation.function_name}}({{operation.snake_case_arguments}}) -> {{operation.response}}:
    {%- if operation.summary %}
    """
    {{ operation.summary }}
    """
    {%- endif %}
    pass
{% endfor %}


def start():
    uvicorn.run(app, host="0.0.0.0", port=8000)

Env

macos
python 3.9.0
fastapi-code-generator 0.1.0

Illegal FastAPI constructor generated (AttributeError: 'str' object has no attribute 'get': server_data.get("url"))

Using default main template:

app = FastAPI(
    {% if info %}
    {% for key,value in info.items() %}
    {{ key }} = "{{ value }}",
    {% endfor %}
    {% endif %}
    )

Generates invalid FastAPI constructor when servers are used:

app = FastAPI(
    title="App title",
    description="App desc",
    version="0.1",
    servers="[{'url': 'http://api.example.com/v1', 'description': 'App desc'}, {'url': 'http://staging-api.example.com', 'description': 'App desc'}]",
)

(note the servers key value is actually a string).

Full stacktrace:

  File "/CODE/repo/openapi/main.py", line 23, in <module>
    app = FastAPI(
  File "/CODE/.repo-venv/lib/python3.10/site-packages/fastapi/applications.py", line 146, in __init__
    self.setup()
  File "/CODE/.repo-venv/lib/python3.10/site-packages/fastapi/applications.py", line 216, in setup
    server_urls = {url for url in urls if url}
  File "/CODE/.repo-venv/lib/python3.10/site-packages/fastapi/applications.py", line 216, in <setcomp>
    server_urls = {url for url in urls if url}
  File "/CODE/.repo-venv/lib/python3.10/site-packages/fastapi/applications.py", line 215, in <genexpr>
    urls = (server_data.get("url") for server_data in self.servers)
AttributeError: 'str' object has no attribute 'get'

Removing the " from the template in {{ key }} = "{{ value }}" does not help as it breaks the scalar keys. What do help is removing servers from OpenApi spec.

Version 0.3.5.

Generation break if parameter name contains illegal character like :

The following schema will break creating this function
def list_pets(rs:query: Optional[int] = None) -> Pets:

schema.yml

openapi: "3.0.0"
info:
  version: 1.0.0
  title: Swagger Petstore
  license:
    name: MIT
servers:
  - url: http://petstore.swagger.io/v1
paths:
  /pets:
    get:
      summary: List all pets
      operationId: listPets
      tags:
        - pets
      parameters:
        - name: "rs:query"
          in: query
          description: How many items to return at one time (max 100)
          required: false
          schema:
            type: integer
            format: int32
      responses:
        '200':
          description: A paged array of pets
          headers:
            x-next:
              description: A link to the next page of responses
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Pets"
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
                x-amazon-apigateway-integration:
                  uri:
                    Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PythonVersionFunction.Arn}/invocations
                  passthroughBehavior: when_no_templates
                  httpMethod: POST
                  type: aws_proxy
components:
  schemas:
    Pet:
      required:
        - id
        - name
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
        tag:
          type: string

Solution is to replace : but another legal character like _

Is there any way to generate api client from yaml?

Hey, @koxudaxi
First, Thank for this great library. Now, I'm trying to create microservice with fastapi and here is the my scenario.
I've build one api server with fastapi. Now i've to use this apis on another two microservices. So i'm looking for is there a any way to generate api class from first api server's yaml ?

Code generation completes silently, generating nothing for schemas with 0 content sections

I run into an issue that some schema I was working on, caused no files to be generated (and old files not overwritten, as one would expect). It appears to happen when there are no content sections in the yaml.

Reproducible as fastapi-codegen --input spec.yaml --output generated/, with both 0.3.0 and 0.3.2. The command prints nothing and completes with 0 exit code.

Working:

openapi: 3.0.0

paths:
  /foo:
    put:
      summary: Bar
      responses:
        '400':
          description: Baz
          content:
            application/json:
              schema:
                type: object

Broken:

openapi: 3.0.0

paths:
  /foo:
    put:
      summary: Bar
      responses:
        '400':
          description: Baz

$refs in parameter is not processed

I expect

$refs in parameters to be processed, eg

paths:
  /foo:
    parameters:
    - $ref: '#/components/parameters/MyParam'
components:
  parameters:
    MyParam:
      name: foo
      schema:
        type: string

Instead

I get KeyError

  File "/usr/local/lib/python3.8/site-packages/fastapi_code_generator/parser.py", line 207, in get_parameter_type
    schema: JsonSchemaObject = JsonSchemaObject.parse_obj(parameter["schema"])
KeyError: 'schema'

Unclear problem to solve "Exception: Modular references are not supported in this version"

It is not clear for me how to solve this problem - where is problem and why is problem?

Traceback (most recent call last):
File "C:\root\Python\Python39\lib\runpy.py", line 197, in run_module_as_main
return run_code(code, main_globals, None,
File "C:\root\Python\Python39\lib\runpy.py", line 87, in run_code
exec(code, run_globals)
File "C:\root\Python\Python39\Scripts\fastapi-codegen.exe_main
.py", line 7, in
File "C:\root\Python\Python39\lib\site-packages\typer\main.py", line 214, in call
return get_command(self)(*args, **kwargs)
File "C:\root\Python\Python39\lib\site-packages\click\core.py", line 829, in call
return self.main(*args, **kwargs)
File "C:\root\Python\Python39\lib\site-packages\click\core.py", line 782, in main
rv = self.invoke(ctx)
File "C:\root\Python\Python39\lib\site-packages\click\core.py", line 1066, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "C:\root\Python\Python39\lib\site-packages\click\core.py", line 610, in invoke
return callback(*args, **kwargs)
File "C:\root\Python\Python39\lib\site-packages\typer\main.py", line 500, in wrapper
return callback(**use_params) # type: ignore
File "C:\root\Python\Python39\lib\site-packages\fastapi_code_generator_main
.py", line 47, in main
return generate_code(input_name, input_text, output_dir, template_dir, model_path)
File "C:\root\Python\Python39\lib\site-packages\fastapi_code_generator_main
.py", line 86, in generate_code
raise Exception('Modular references are not supported in this version')
Exception: Modular references are not supported in this version

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.