Giter Site home page Giter Site logo

sigspec's Introduction

SigSpec for SignalR Core

Azure DevOps Azure DevOps Nuget

Experimental API endpoint specification and code generator for SignalR Core.

Run SigSpec.Console to see a demo spec and the generated TypeScript code.

Please let me know what you think here.

Based on NJsonSchema (see also: NSwag).

Original idea: RicoSuter/NSwag#691

Sample

Hub:

public class ChatHub : Hub<IChatClient>
{
    public Task Send(string message)
    {
        if (message == string.Empty)
        {
            return Clients.All.Welcome();
        }

        return Clients.All.Send(message);
    }

    public Task AddPerson(Person person)
    {
        return Task.CompletedTask;
    }

    public ChannelReader<Event> GetEvents()
    {
        var channel = Channel.CreateUnbounded<Event>();
        return channel.Reader;
    }
}

public class Event
{
    public string Type { get; set; }
}

public class Person
{
    [JsonProperty("firstName")]
    public string FirstName { get; set; }

    [JsonProperty("lastName")]
    public string LastName { get; set; }
}

public interface IChatClient
{
    Task Welcome();

    Task Send(string message);
}

Generated spec:

{
  "hubs": {
    "chat": {
      "name": "Chat",
      "description": "",
      "operations": {
        "Send": {
          "description": "",
          "parameters": {
            "message": {
              "type": [
                "null",
                "string"
              ],
              "description": ""
            }
          }
        },
        "AddPerson": {
          "description": "",
          "parameters": {
            "person": {
              "description": "",
              "oneOf": [
                {
                  "type": "null"
                },
                {
                  "$ref": "#/definitions/Person"
                }
              ]
            }
          }
        },
        "GetEvents": {
          "description": "",
          "parameters": {},
          "returntype": {
            "description": "",
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/definitions/Event"
              }
            ]
          },
          "type": "Observable"
        }
      },
      "callbacks": {
        "Welcome": {
          "description": "",
          "parameters": {}
        },
        "Send": {
          "description": "",
          "parameters": {
            "message": {
              "type": [
                "null",
                "string"
              ],
              "description": ""
            }
          }
        }
      }
    }
  },
  "definitions": {
    "Person": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "firstName": {
          "type": [
            "null",
            "string"
          ]
        },
        "lastName": {
          "type": [
            "null",
            "string"
          ]
        }
      }
    },
    "Event": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "Type": {
          "type": [
            "null",
            "string"
          ]
        }
      }
    }
  }
}

Generated TypeScript code:

import { HubConnection, IStreamResult } from "@aspnet/signalr"

export class ChatHub {
    constructor(private connection: HubConnection) {
    }

    send(message: string): Promise<void> {
        return this.connection.invoke('Send', message);
    }

    addPerson(person: Person): Promise<void> {
        return this.connection.invoke('AddPerson', person);
    }

    getEvents(): IStreamResult<Event> {
        return this.connection.stream('GetEvents');
    }

    registerCallbacks(implementation: IChatHubCallbacks) {
        this.connection.on('Welcome', () => implementation.welcome());
        this.connection.on('Send', (message) => implementation.send(message));
    }

    unregisterCallbacks(implementation: IChatHubCallbacks) {
        this.connection.off('Welcome', () => implementation.welcome());
        this.connection.off('Send', (message) => implementation.send(message));
    }
}

export interface IChatHubCallbacks {
    welcome(): void;
    send(message: string): void;
}

export interface Person {
    firstName: string;
    lastName: string;
}

export interface Event {
    Type: string;
}

Development

Release new version

  1. Update versions with dnt bump-versions patch
  2. Commit to "master" (via PR)
  3. Merge into "release" to start nuget.org publish (via PR)

sigspec's People

Contributors

alamaster99 avatar austinsc avatar beorosz avatar danzel avatar hoaol avatar jannesiera avatar jbriggs22 avatar mcjonesy-nz avatar ricosuter avatar terravenil avatar tomsmith27 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

sigspec's Issues

Create Nuget Package

As I want to use this package in my project now would it be possible to create a release in its current state so i can install a Nuget package into my project?

I noticed the Azure Devops build pipeline was already set up.

Bug in generated spec .json

Title

In SigSpec.AspNetCore the generated spec.json file always adds new type definitions if you keep refreshing the page

Steps to reproduce

  1. Start HelloSignalR sample project
  2. Go to /sigpsec/v1/sigspec.json
  3. Refresh the page (one or more times)

Current behaviour

New definitions are generated at each refresh (Person, Person2, Person3....)

Expected behaviour

The spec doesn't change at refresh

Screenshot

image

License

sadly there's no license in this repository, so it means all rights reserved

Publishing on NuGet

I have found this library to be quite useful, even though it's experimental. I published a build of it to NuGet so other people can play and experiment with it. I'm happy to either delist or add you to maintainers if you have any issues with it. Let me know.

Implementation frontend part similar to Swagger UI

Hi! Are you going to implement frontend part with SigSpec similar to Swagger UI? As I understand from source you already have dynamic proxy generation(TS) + Spec, so the last step would be integrate all that staff with a pretty UI :)

Compatibility with NSwagStudio

Is it possible to have this tool compatible/integrated with NSwagStudio?

We can reference the API endpoint and then it scans for all the available hubs in the API and generate the typescript contract.

Thanks!

Binary incompatible with Namotion.Reflection

Current package is binary incompatible with the transitively referenced current Namotion.Reflection package. SigSpec is trying to invoke XmlDocsExtensions.GetXmlDocsSummary(this CachedType type) but is has been changed in this commit RicoSuter/Namotion.Reflection@51eb057 where an optional parameter has been introduced.

This change is compatible in compile time but it's not binary compatible because SigSpec throws an exception: Unhandled exception. System.MissingMethodException: Method not found. SigSpec need to be recompiled and republished to mitigate this bug.

A possible woraround could be to downgrade the Namotion.Reflection package but I cannot do this on my project beacause latest NSwag requires this modified package, so NSwag and SigSpec is currently incompatible.

SignalR Core package update

Is there a way I can override the SignalR Core package and update it's version. I'm experiencing a bug that my onDisconnected is not being called on ungraceful disconnect.

XmlSummary for methods are taken from "Hub type" instead of method

Hi!
First I want to say that this is a very interesting project, at my company we are in the process of developing quite a lot of Signalr Services and have a need to get them documented in a structual way.
I have dowloaded the latest version and made some tests and like what I see :-) but I think I have found a little bug...

In SigSpecGenerator.cs, GenerateOperationAsync I think that
Description = await type.GetXmlSummaryAsync()
should be changed to
Description = await method.GetXmlSummaryAsync()

Would you be up for an rxjs option?

I can't believe this project exists. Thanks for creating. I love NSwag and was just lamenting that there was no equivalent for SignalR when I found this.

Would you be up if I tried implementing an option to generate a client that wraps callbacks with observables, exposing them as read only fields? This would be an alternative to generating an interface for callbacks.

Proposed Output

The one drawback with the output below is that unlike your current method, callbacks can only be registered and unregistered once. I prefer this and don't see the is as an issue, but if it is a problem then it would be easy to implement register and deregister methods.

export class ChatHub {
  
  welcome$ = this.registerCallback<void>('Welcome');
  send$ = this.registerCallback<string>('Send');

  constructor(private connection: HubConnection) {
  }
  send(message: string): Promise<void> { /* ... */ }
  addPerson(person: person): Promise<void> { /* ... */ }
  getEvents(message: IStreamResult<Event> ): Promise<Event> { /* ... */ }

  private disposingSubject = new Subject();
  dispose() {
    if (!this.disposingSubject.closed) {
      this.disposingSubject.next();
      this.disposingSubject.complete();
    }
  }

  /* Registers a new observable callback, completing and unregistering when disposingSubject is fired. */
  private registerCallback<T>(eventName: string) {
    return new Observable<T>(obs => {
      this.connection.on(eventName, val => obs.next(val));
      const unregisterSub = this.disposingSubject.subscribe(() => {
        this.connection.off(eventName);
        obs.complete();
        listenerSub.unsubscribe();
       })
    });
  }
}

Questions

If I did this I need some clarifications. I'm having a hard time grasping all of the design choices, and I don't want to go in the wrong direction:

  • I imagine I'd need to add a property to settings (I'm thinking an Enum called CallbacksType). There seem to be two TypeScript settings classes. Which would be the correct one to alter?
  • Since I'd need the value of that setting in both the File and Hub templates, I'd need to add settings to both models. Is this okay, or do you see a cleaner way?
    • Adding to the File model is simple enough, but the I'd have to either create a new subclass Hub model or add SigSpecToTypeScriptGeneratorSettingsBase as a property on Hub. It doesn't feel clean, either way.
    • Maybe there is a way to reference the File model from the Hub template?

Spec generator integration

Would it be a good next step to write an ASP.NET Core middleware that generates the JSON spec file at a URL like /signalr_spec.json? You could them point the console application at the spec URL and it would spit out the TS.

I don't particularly care about Swagger documentation right now. I'm just looking for the .TS generator stack.

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.