Git Product home page Git Product logo

microsoft / kiota Goto Github PK

View Code? Open in Web Editor NEW
2.3K 40.0 159.0 25.27 MB

OpenAPI based HTTP Client code generator

Home Page: https://aka.ms/kiota/docs

License: MIT License

C# 95.16% Dockerfile 0.05% Ruby 0.14% Swift 0.36% CSS 0.06% JavaScript 0.13% Java 0.84% TypeScript 2.11% PowerShell 0.95% Go 0.15% PHP 0.02% Python 0.03% Kotlin 0.03%
openapi csharp typescript java dotnet golang ruby php http api

kiota's Issues

Add support for alternative allOf inheritance definition

We currently have implemented one case of inheritance definition which is when all types in the inheritance tree are defined in the all of collection.
In the OpenAPI semantics, there's another way of defining inheritance where the child type will be in the schema and allOf collection will contain the base type.
AB#8905

break writers down into a visitor pattern

our language writers are getting bigger and bigger, which makes things hard to maintain, scope, and test. We should break them in a visitor pattern. Each visitor would implement the "write" method of IWriter<T> where T : CodeElement. Visitors would be registered via a method somewhere for each language.
AB#8904

Add additional properties on the using statements

In languages like typescript or java we need additional properties:

  • import symbol (that'd be the class name in the case of TypeScript/java)
  • external import or not (to understand whether it's something we've generated or not)
  • some kind of namespace prefixing information for local imports

add a better client configuration experience

The client root class needs a better configuration experience. We could either go the fluent way like current graph clients or leverage a auto registration approach (singleton/reflection that adds things automatically when importing the module/dependencies).

This configuration experience needs to account for:

  • middleware addition/reorganisation (see #120)
  • serialization/deserialization factories registration
  • core engine registration

Note: we might want to rework the authentication handler as a middleware as part of this work.
AB#9082

getting started documentation

This repository is lacking getting started information so people can try kiota and contribute. It should contain:

  • the list of tools that are required
  • the documentation for the different parameters
  • links to sample OpenAPI descriptions
  • links to the design documentation
    AB#8966

add support for middleware pipeline for request execution

People need to be able to inject behavior on responses before things are returned to the caller to handle things like retry, redirect, etc...
Review the design guidance

We probably need to model:

  • ResponseInfo (abstract reflection of http response)
  • RequestMiddleware (interface for middlewares excuting before the request is sent)
  • ResponseMiddleware (interface for middlewares executing after the response is received)
  • A property bag to tag the request with options for the middleware
  • A way to register (and order?) middlewares
  • A way to set default options for requests
    AB#9081

Request builder generic type parameter with type restriction overload

I suggest that we generate an overload that uses type parameters with constraints instead of concrete types in our final request builder. This could serve the following purposes:

  1. If the CSDL references a base type, the generated derived type serialization instructions can be used instead of the base type serialization instructions. No derivedtypeconverter
  2. In the case that the generated library hasn't been updated and there is a new derived type, a customer can derive from the base type, add the missing properties, and add serialization instructions as a quick fix until the generated library can be updated.
  3. Some customers like to use our models as the model for their applications. They GET the object, change the object, and then PATCH the object. This results in the entire object being sent in the PATCH, instead of a PATCH object containing only the updated properties. This results in serialized properties being sent that are read-only and ignored, or even properties that cause errors in the service when the service doesn't gracefully ignore unexpected properties. We provide guidance for this scenario where we have them derive from the base type, and they can potentially enhance their model with something like a PropertyChanged event to provide instruction to the Serialize(writer) method. We should consider how we would instruct the user to propagate PropertyChanged down to the base.Serialize method so that they could control serialization from their custom derived type into our generated base types without having special instructions in the SerializationWriter. OR, we just tell them to new up an object with just their changes.

The Serialize method in the model is interesting.

MessageRequestBuilder

public class MessagesRequestBuilder<T> where T : Message {
    public MessageRequestBuilder this[string position] { get {
        return new MessageRequestBuilder { HttpCore = HttpCore, CurrentPath = CurrentPath + PathSegment  + "/" + position};
    } }
    public async Task<MessagesResponse<T>> GetAsync(Action<GetQueryParameters> q = default, Action<IDictionary<string, string>> h = default, IResponseHandler responseHandler = default) {
        var requestInfo = new RequestInfo {
            HttpMethod = HttpMethod.GET,
            URI = new Uri(CurrentPath),
        };
        if (q != null) {
            var qParams = new GetQueryParameters();
            q.Invoke(qParams);
            qParams.AddQueryParameters(requestInfo.QueryParameters);
        }
        h?.Invoke(requestInfo.Headers);
        return await HttpCore.SendAsync<MessagesResponse<T>>(requestInfo, responseHandler);
    }
    public async Task<T> PostAsync(Action<IDictionary<string, string>> h = default, IResponseHandler responseHandler = default) {
        var requestInfo = new RequestInfo {
            HttpMethod = HttpMethod.POST,
            URI = new Uri(CurrentPath),
        };
        h?.Invoke(requestInfo.Headers);
        return await HttpCore.SendAsync<T>(requestInfo, responseHandler);
    }
    // ....
    }
}

MessageResponse

public class MessagesResponse<T> where T : Message {
    public List<T> Value { get; set; }
    public string NextLink { get; set; }
}

AB#8908

Question: can we accept "json" OpenApi input files?

The OpenApi files I try playing with are either the common sample PetStore swagger file (ref: https://petstore.swagger.io/ ) or a default dotnet new webapi template with swagger enabled.

Does Kiota accept .json openapi files?

very briefly checking the code, it feels like it's yaml only?

EDIT: i know that the swagger editor can save the json as yml ... but i'm just curious about just json without having to do the manual conversion?

EDIT 2: here's the sample json file i was using: https://petstore.swagger.io/v2/swagger.json
Same error occured when I saved it as yml (via https://editor.swagger.io/ ) and tried that :/

image

AB#9047

Generate RequestBuilders in a hierarchy of folder and namespaces

Using this approach we can remove the need to generate hashes on request builder class names to avoid name clashes.

namespace MicrosoftGraph.Users
  UsersRequestBuilder
  
namespace microsoftgraph.Users.Messages
  MessagesRequestBuilder

namespace microsoftgraph.Users.Messages.Item
  MessageRequestBuilder

namespace microsoftgraph.users.Messages.Item.attachments
  AttachmentsRequestBuilder

namespace microsoftgraph.Users.MailFolder.Item.Messages.Item
  MessageRequestBuilder

Release kiota image on mcr.microsoft.com and dotnet tool on nuget.org

Can we please have the docker image stored in the newer ghcr.io registry? means we don't need auth/PAT's.

Currently there are 2 GH Docker registries, one at docker.pkg.github.com and a new one at ghcr.io. The original registry always requires a PAT with the read:packages scope (even for public packages). The new ghcr.io registry has the option to create true public container packages.

If you publish an image to ghcr.io you can make it public with no auth required. See here for more information:
https://docs.github.com/en/packages/guides/configuring-access-control-and-visibility-for-container-images#configuring-visibility-of-container-images-for-an-organization
AB#9098

Ability to execute the requests on the wire

  • implementation for dotnet auth provider + http core
  • implementation for java auth provider + http core
  • implementation for typescript auth provider + http core
  • abstraction for authentication provider for dotnet
  • abstraction for authentication provider for java
  • abstraction for authentication provider for typescript
    AB#8498

Make OperationId optional

Currently we depend on OperationId but it is not a required field in OpenAPI, so if we can avoid being dependent on it, that would be good.

private string GetNamespaceNameForModelByOperationId(string operationId) {
            if(string.IsNullOrEmpty(operationId)) throw new ArgumentNullException(nameof(operationId));
            var cleanOperationId = operationId.Split('_').First();
            return $"{this.config.ClientNamespaceName}.{cleanOperationId}";
        }

AB#8847

CSharp entrypoint + mapping needs update

there's still a bug in the CSHarp writer because of the recent change of not using namespaces as an entry point. The mapping needs to be updated like Java/Tyepescript, and the usings need to be tested

related #12

Generate methods to get the generic request

We'll need the generic request for batching/parallelization of calls. We need to generate additional methods to be able to get that abstract object without executing the call.
Split the request executors in two halves, the second one calling the first one like this

@javax.annotation.Nullable
    public RequestInfo createGetRequest(@javax.annotation.Nonnull final java.util.function.Consumer<GetQueryParameters> q, @javax.annotation.Nonnull final java.util.function.Consumer<Map<String, String>> h) throws URISyntaxException {
        Objects.requireNonNull(q);
        Objects.requireNonNull(h);
        final RequestInfo requestInfo = new RequestInfo() {{
            uri = new URI(currentPath);
            httpMethod = HttpMethod.GET;
        }};
        final GetQueryParameters qParams = new GetQueryParameters();
        q.accept(qParams);
        qParams.AddQueryParameters(requestInfo.queryParameters);
        h.accept(requestInfo.headers);
        return requestInfo;
    }
    @javax.annotation.Nullable
    public java.util.concurrent.CompletableFuture<MessagesResponse> get(@javax.annotation.Nonnull final java.util.function.Consumer<GetQueryParameters> q, @javax.annotation.Nonnull final java.util.function.Consumer<Map<String, String>> h, @javax.annotation.Nonnull final ResponseHandler responseHandler) {
        Objects.requireNonNull(q);
        Objects.requireNonNull(h);
        Objects.requireNonNull(responseHandler);
        try {
            final RequestInfo requestInfo = this.createGetRequest(q, h);
            return this.httpCore.sendAsync(requestInfo, responseHandler);
        } catch (URISyntaxException ex) {
            return java.util.concurrent.CompletableFuture.failedFuture(ex);
        }
    }

Additionally we might want to add a "addRequestToBatch(batch)" mehtod on the request info to enable fluent style batch building.
AB#8503

Entity called entity breaks the Java code gen

This code fails

       protected void FixReferencesToEntityType(CodeElement currentElement, CodeClass entityClass = null){
            if(entityClass == null)
                entityClass = currentElement.GetImmediateParentOfType<CodeNamespace>()
                            .GetRootNamespace()
                            .GetChildElementOfType<CodeClass>(x => x.Name.Equals("entity", StringComparison.InvariantCultureIgnoreCase));

with this OpenAPI

openapi: 3.0.0
info:
  title: "Todo API"
  version: "1.0.0"
paths:
  /todos: 
    get:
      operationId: todos_ListTodos
      parameters:
          - name: active
            in: query
            schema:
              type: boolean
          - name: keyword
            in: query
            schema:
              type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                title: collectionTodos
                type: object
                properties:
                  value:
                    items: 
                      $ref: "#/components/schemas/todo"

    post:
      operationId: todos_CreateTodo
      responses:
        '201':
          description: OK
  /todos/{todoId}:
    get:
      operationId: todos_GetTodo
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/todo"
    delete:
      operationId: todos_DeleteTodo
      responses:
        '200':
          description: OK
  /tag/{tagId}:
    get:
      operationId: todos_GetTag
      responses:
        '200':
          description: OK
components:
  schemas:
    entity:
      type: object
      properties:
        id: 
          type: string
    tag:
      type: object
      properties:
        id: 
          type: string
        displayName: 
          type: string
    todo:
      allOf:
        - $ref: "#/components/schemas/entity"
        - type: object
          properties:
            subject:
              type: string

AB#8848

Fix java relative imports

There's still a bug in Java for the import for the GraphClient. It needs to do relative calculation like typescript does

related #12

Model class with no name breaks common language refiner

       protected void MoveClassesWithNamespaceNamesUnderNamespace(CodeElement currentElement) {
            if(currentElement is CodeClass currentClass && 
                currentClass.Parent is CodeNamespace parentNamespace) {
                var childNamespaceWithClassName = parentNamespace.InnerChildElements
                                                                .OfType<CodeNamespace>()
                                                                .FirstOrDefault(x => x.Name
                                                                                    .EndsWith(currentClass.Name, StringComparison.InvariantCultureIgnoreCase));
                if(childNamespaceWithClassName != null) {
                    parentNamespace.InnerChildElements.Remove(currentClass);
                    childNamespaceWithClassName.AddClass(currentClass);
                }
            }
            CrawlTree(currentElement, MoveClassesWithNamespaceNamesUnderNamespace);
        }

CrawlTree will fail with elements that have a null name.

openapi: 3.0.0
info:
  title: "Todo API"
  version: "1.0.0"
paths:
  /todos: 
    get:
      operationId: todos_ListTodos
      parameters:
          - name: active
            in: query
            schema:
              type: boolean
          - name: keyword
            in: query
            schema:
              type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                title: collectionTodos
                type: object
                properties:
                  value:
                    items: 
                      $ref: "#/components/schemas/todo"

    post:
      operationId: todos_CreateTodo
      responses:
        '201':
          description: OK
  /todos/{todoId}:
    get:
      operationId: todos_GetTodo
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/todo"
    delete:
      operationId: todos_DeleteTodo
      responses:
        '200':
          description: OK
  /tag/{tagId}:
    get:
      operationId: todos_GetTag
      responses:
        '200':
          description: OK
components:
  schemas:
    entity:
      type: object
      properties:
        id: 
          type: string
    tag:
      type: object
      properties:
        id: 
          type: string
        displayName: 
          type: string
    todo:
      allOf:
        - $ref: "#/components/schemas/entity"
        - type: object
          properties:
            subject:
              type: string

AB#8849

Setup a samples repo as a submodule to make PR reviews easier

Darrel and I had further brainstorming on this and here is a recap.
We have multiple concerns here:

  • The launch.json configuration should be valid, the paths should resolve with minimal repos cloning/rework from people cloning this repo.
  • The parameters should be documented so people can configure other IDEs, use dotnet run, use a compiled version of kiota... #96
  • The unit tests coverage should be ramped up and test aspects, not just generate files #79
  • The PRs should provide a generation diff (more on that below)
  • We want to avoid overcrowding this repo with too many things

The solution we're suggesting is the following:

  • create a samples repo under the Microsoft Org, this repo will contain multiple projects (one for each language)
  • add this repo as a submodule to kiota's repository
  • update launch.json to point to this submodule's path

Someone PRing kiota to change the generation result should:

  1. run the generation on the sample repo
  2. create a PR in the generation repo
  3. update the submodule reference
  4. create the PR in Kiota, linking to that generation PR

This has the advantage of:

  • providing a generation diff for reviewers
  • not polluting the current repo
  • force us to start building proper unit tests
  • being simple for anyone who knows about submodules
  • not being blocking for anyone who doesn't know about submodule

Thoughts?

Originally posted by @baywet in #48 (comment)
AB#9017

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.