Skip to content

ansel86castro/cybtans-cli

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

73 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Logo Cybtans CLI

Build Status

Cybtans CLI

The Cybtans Command Line Interface aka Cybtans CLI is a cross-platform Protocol Buffers compiler for creating RESTfull APIs using definitions in the Protobuff Language. The main advantage this tool provides is that it frees the developer from common boilerplate code. This allows the developer to focus on the business logic, resulting in a clean architecture with autogenerated clients components to be used in React ,Angular, Blazor, Xamarin or WPF apps. Thus improving the integration between backend and frontend development teams, due to the client libraries are auto-documented from the protobuf definitions.

On the other hand you can use the cybtans cli to support the development of services with GRPC. Some use cases includes generating protobuff definitions from .NET assemblies along with mapping code to convert between protobuff models and POCO. Also you can autogenerate REST APIs that proxy incoming request to your GRPC services.

In addition the cybtans-cli allows you to create a microservice project structure ready for development. For example run the following command.

> cybtans-cli service -n MyMicroservice -o ./MyMicroservice -sln .\MySolution.sln

This will generate the following projects structure using a convention described below:

  • MyMicroservice.Clients: This NET Standard project contains typed REST client using Refit. You can use these clases for consuming your endpoints in integration tests, mvc, blazor or xamaring apps for example. REST client are autogenerated from protbuff services.
  • MyMicroservice.Models This NET Standard project contains the services models, aka the data exposed throught the Web API. You can use these models with the .NET client libraries. Model classes are autogenerated from protobuff messages.
  • MyMicroservice.Data This project contains your database entities or data layer. You can define Repository interfaces here. You can use the entities defined in this project to autogenerate proto definitions like message and services.
  • MyMicroservice.Data.EntityFramework This project contains Entity Framework dependant code like DbContexts, Database Mappings and Repository implementations
  • MyMicroservice.RestApi This project contains the Rest API code. You only need to take care of registering the additional dependencies , setup middlewares, etc. Api Controllers are autogenerated from protobuff services.
  • MyMicroservice.Services This project is where your business logic goes. Service interfaces are autogenerated. You need to implement the interfaces.
  • MyMicroservice.Services.Tests This project is to create unit or integration tests. If using Integration Tests, you can use the MyMicroservice.Clients classes for calling your endpoints using an in-memory test server.
  • Proto This folder contains definitions in the protobuff language for messages and services. The cli generates models, clients, controllers, services from these proto definitions.

Note that this is a useful command and it is not necessary to use this architectural convention. The cybtans cli tool uses configuration files to override the default conventions so that you can organize your project the way you want and generate the code that best suits your needs or style.

Getting Started

In order to generate REST APIs using Protobuff files, download the cybtans cli for your platform. Extract it and optional add the locating directory to your PATH.

Next create a file named cybtans.json in your solution. In case you use the cybtans-cli for generating the projects the file is already generated for you. If you want to use .NET 5 just change netcoreapp3.1 for net5.0

{
    "Service": "MyMicroservice",
    "Steps":[
        {
          "Type": "messages",
          "Output": ".",
          "ProtoFile": "./Proto/Data.proto",
          "AssemblyFile": "./MyMicroservice.Data/bin/Debug/net5.0/MyMicroservice.Data.dll"
        },
        {
            "Type": "proto",
            "Output": ".",
            "ProtoFile": "./Proto/MyMicroservice.proto"
        }
    ]
}

This cybtans.json defines configuration settings like the microservice name and steps, each step represents a code generation process. In the example above there are two steps, one for generating a proto file with messages from a .dll containing the entities, and the second for generating C# code from a proto file that includes the proto generated in the first step.

For example lets define the following entity in the MyMicroservice.Data project.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Cybtans.Entities;

[Description("Customer Entity")]
[GenerateMessage("CustomerDto")]
public class Customer
{
    public Guid Id {get; set;}

    [Description("Customer's Name")]
    [Required]
    public string Name { get; set; }

    [Description("Customer's FirstLastName")]    
    public string FirstLastName { get; set; }

    [Description("Customer's SecondLastName")]    
    [Obsolete]
    public string SecondLastName { get; set; }

    [Description("Customer's Profile Id, can be null")]    
    public Guid? CustomerProfileId { get; set; }
    
    public virtual CustomerProfile CustomerProfile { get; set; }

    [MessageExcluded]
    public virtual ICollection<Order> Orders { get; set; } = new HashSet<Order>();
}

Run the cybtans cli from the solution directory using the folowing command.

> cybtans-cli .

This command generates the Proto/data.proto file with the following definition.

message CustomerDto {
    option description = "Customer Entity";

    string name = 1 [required = true, description = "Customer's Name"];
    string firstLastName = 2 [description = "Customer's FirstLastName"];
    string secondLastName = 3 [description = "Customer's SecondLastName", deprecated = true];
    guid customerProfileId = 4 [optional = true, description = "Customer's Profile Id, can be null"];
    CustomerProfileDto customerProfile = 5;
    guid id = 6;
    datetime createDate = 7 [optional = true];
    datetime updateDate = 8 [optional = true];
}

In addition you can generate a service definition by using the attribute [GenerateMessage("CustomerDto", Service = ServiceType.Interface)]. This generates request messages a a service with CRUD operations as shown below.

message GetAllRequest {
	string filter = 1 [optional = true];
	string sort = 2 [optional = true];
	int32 skip = 3 [optional = true];
	int32 take = 4 [optional = true];
}

message GetCustomerRequest {
	guid id = 1;
}

message UpdateCustomerRequest {
	guid id = 1;
	CustomerDto value = 2 [(ts).partial = true];
}

message DeleteCustomerRequest{
	guid id = 1;
}

message GetAllCustomerResponse {
	repeated CustomerDto items = 1;
	int64 page = 2;
	int64 totalPages = 3;
	int64 totalCount = 4;
}

message CreateCustomerRequest {
	CustomerDto value = 1 [(ts).partial = true];
}

service CustomerService {
	option (prefix) ="api/Customer";

	rpc GetAll(GetAllRequest) returns (GetAllCustomerResponse){		
		option method = "GET";		
	};

	rpc Get(GetCustomerRequest) returns (CustomerDto){	
		option template = "{id}"; 
		option method = "GET";
	};

	rpc Create(CreateCustomerRequest) returns (CustomerDto){			
		option method = "POST";		
	};

	rpc Update(UpdateCustomerRequest) returns (CustomerDto){			
		option template = "{id}"; 
		option method = "PUT";		
	};

	rpc Delete(DeleteCustomerRequest) returns (void){
		option template = "{id}"; 
		option method = "DELETE";		
	};
}

You can control what properties are included in the protobuff message. Use the [MessageExcluded] attribute to exclude properties from the message definition. These attributes are located in the Cybtans.Entities.Proto package. Install this package with

> dotnet add package Cybtans.Entities.Proto --version 1.2.1

Finally in the main proto MyMicroservice.proto, the one that was specified in the cybtans.json import the Data.proto as shown below:

syntax = "proto3";

import "Data.proto";

package MyMicroservice;

// Add addtional services and messages here
.......
.......

All the supported options by the cybtans-cli are located in the cybtans.proto file as a reference. It's not required to include this file if using the cybtans-cli for generating REST APIs. But it's mandatory to include this file when using the GRPC compiler.

As mentioned before you can generate typescript clients for React and Angular as shown below.

{
    "Service": "MyMicroservice",
    "Steps":[
        {
          "Type": "messages",
          "Output": ".",
          "ProtoFile": "./Proto/Data.proto",
          "AssemblyFile": "./MyMicroservice.RestApi/bin/Debug/netcoreapp3.1/MyMicroservice.Data.dll"
        },
        {
            "Type": "proto",
            "Output": ".",
            "ProtoFile": "./Proto/MyMicroservice.proto",
            "Gateway": "./App.Gateway/Controllers/MyMicroservice",
            "Clients": [
            {
                "Output": "./react-app/src/services/my-microservice",
                "Framework": "react"
            },
            {
                "Output": "./angular-app/src/app/services/my-microservice",
                "Framework": "angular"
            }
            ]
        }
    ]
}

Override Conventions

You can control how to generate code from the proto files. For example specifiying output directories and namespaces that fits your code organization:

{
  "Service": "MyWebAPI",
  "Steps": [
    {
      "Type": "proto",
      "ProtoFile": "./Proto/api.proto",
      "Models": {
        "Output": "./MyWebAPI/Generated/Models",
        "Namespace": "Api.Models"
      },
      "Services": {
        "Output": "./MyWebAPI/Generated/Services",
        "Namespace": "Api.Services.Definitions"
      },
      "Controllers": {
        "Output": "./MyWebAPI/Generated/Controllers",
        "Namespace": "Api.Controllers"
      },
      "CSharpClients":{
        "Output": "../MyWebAPIGateway/Generated/Clients",
        "Namespace": "Api.Clients"
      },
      "GatewayOptions":{
        "Output": "../MyWebAPIGateway/Generated/Controllers",
        "Namespace": "Api.Controllers"
      },
      "Clients": [
        {
          "Output": "../WebApp/src/app/services",
          "Framework": "angular"
        }
      ]
    }
  ]
}

GRPC Development Support

Generate grpc compatible proto files

You can use the cybtans cli with the GRPC compiler in order to generate messages from .NET assemblies and mapping code. For example add the following settings to the cybtans.json

{
 
  "Steps": [
    {
      "Type": "messages",
      "Output": ".",
      "ProtoFile": "./protos/data.proto",
      "AssemblyFile": "../MyGrpcService.Data/bin/Debug/net5.0/MyGrpcService.Data.dll",      
      "Grpc": {
        "Enable": true,
        "MappingOutput": "../MyGrpcService.Service/Mappings",
        "MappingNamespace": "MyGrpcService.Service",
        "GrpcNamespace": "MyGrpcService.Service"
      }
    }
  ]
}

Using this configuration the cybtans cli generates a protoc (google protobuff compiler) compatible file that looks like this:

syntax = "proto3";

option csharp_namespace = "MyGrpcSerivice.Service";

import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";

message CustomerDto {
    option description = "Customer Entity";

    string name = 1;
    string firstLastName = 2;
    string secondLastName = 3;
    string customerProfileId = 4;
    CustomerProfileDto customerProfile = 5;
    string id = 6;
    google.protobuf.Timestamp createDate = 7 [optional = true];
    google.protobuf.Timestamp updateDate = 8 [optional = true];
}

In addition the cybtans cli allows you to generate REST API Controllers that calls your grpc services. For example define the following cybtans.json.

{
  "Service": "Cybtans.Tests.Grpc",
  "Steps": [
    {
      "Type": "proto",
      "ProtoFile": "../Proto/greet.proto",
      "Models": {
        "Output": "../Cybtans.Tests.Gateway/Generated/Models",
        "Namespace": "Cybtans.Tests.Gateway.Models",
        "UseCytansSerialization": false
      },
      "Services": {
        "Output": "../Cybtans.Tests.Gateway/Generated/Repository/Definitions",
        "Namespace": "Cybtans.Test.Gateway.Repository.Definition",
        "Grpc": {
          "Output": "../Cybtans.Tests.Gateway/Generated/Repository/Implementation",
          "Namespace": "Cybtans.Test.Gateway.Repository.Implementation",
          "AutoRegister" :  true
        }
      },
      "Controllers": {
        "Output": "../Cybtans.Tests.Gateway/Controllers/Grpc"       
      },
      "Clients": [
        {
          "Output": "../react-app/src/services/grpc",
          "Framework": "react"
        },
        {
          "Output": "../angular-app/src/app/services/grpc",
          "Framework": "angular"
        }
      ]
    }
  ]
}

Then create a Grpc project with a proto file as shown below.

syntax = "proto3";

option csharp_namespace = "Cybtans.Tests.Grpc";

import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/wrappers.proto";
import "google/protobuf/empty.proto";

//important to add this file for grpc
import "cybtans.proto";

package greet;

// The greeting service definition.
service Greeter {
  option (prefix) = "greeter";

  //important add this seeting to generate REST Api Controllers
  option (grpc_proxy) = true;

  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply){
    option (template) = "hello"; 
    option (method) = "GET";		
  }
}

message HelloRequest {
  string name = 1;  
}

// The response message containing the greetings.
message HelloReply {
  string message = 1;  
  HellowInfo info = 2; 
}

message HellowInfo {
	int32 id = 1;
	Type type = 3;	
	InnerA innerA = 4;  
	enum Type{
		A = 0;
		B = 1;
	}
	message InnerA {
		InnerB b = 1;
		message InnerB {			
			Type type = 1;
			enum Type{
				A = 0;
				B = 1;
			}
		}
	}
}

Then run the cybtans cli with > cybtans-cli . and as a result you will find the following files generated for you.

Grpc-Gateway

In the API Gateway project remember to register the Grpc clients. For example as shown below:

 services.AddGrpcClient<Greeter.GreeterClient>(o =>
  {                
      o.Address = new Uri(Configuration["GreteerServiceUrl"]);
  });

Note that there is an autogenerated ProtobuffMppingExtensions.cs file. This file contains extension methods to convert from POCO objects to Protobuff messages and the other way around.

More examples can be found in the Cybtans SDK Repository. The Cybtans SDK project contains several packages which supports the development with the cybtans cli.

Sponsorship

If you like this project you are welcome to give your support at https://paypal.me/anselcastrocabrera.

Powered By Cybtans www.cybtans.com

Copyright (c) 2021 Ansel Castro

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published