Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Ada] Add Ada support for server code generator #7256

Merged
merged 36 commits into from
Jan 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
53fd17e
Add Ada client petstore samples
stcarrez Oct 7, 2017
35f04b4
Add some description for the samples
stcarrez Oct 7, 2017
07bb281
Update the documentation to explain how to build, how to use the gene…
stcarrez Oct 7, 2017
9f6104a
Add server support for path parameters
stcarrez Oct 14, 2017
e68c43c
Add and fix Ada server code package declaration
stcarrez Oct 14, 2017
de89527
Implement the Ada server side operations
stcarrez Oct 14, 2017
71e95a5
Update the code generation to generate server Ada implementation code
stcarrez Oct 28, 2017
e2524ee
Merge remote-tracking branch 'upstream/master' into ada-server-6680
stcarrez Dec 3, 2017
1f824f0
Improvement of Ada server support: generate the swagger.json template…
stcarrez Dec 8, 2017
3436dd9
Define toModelName operation to the creation of a model identifier
stcarrez Dec 10, 2017
18af3f8
Add support for server permission generation
stcarrez Dec 10, 2017
7423393
Use the #lambdaAdaComment filter to indent correctly a multi-line des…
stcarrez Dec 10, 2017
d35eb5a
Fix model generation to support arrays
stcarrez Dec 10, 2017
e651ca7
Update the generated GNAT project file
stcarrez Dec 10, 2017
462682e
Refactoring and improvement of server code generation
stcarrez Dec 10, 2017
0731f04
Server code generation improvement
stcarrez Dec 21, 2017
c15a266
Ada server main template
stcarrez Dec 21, 2017
ce44ca4
Ada server code improvement
stcarrez Dec 21, 2017
53d4f7e
Skeleton for the server side implementation
stcarrez Dec 21, 2017
d4660e2
Generate an empty Ada server implementation
stcarrez Dec 21, 2017
9264f62
Templates for the Ada server implementation
stcarrez Dec 21, 2017
0758433
Add a README.md file and a GNAT config.gpr file
stcarrez Dec 21, 2017
9ea5142
New templates to document the generated Ada server
stcarrez Dec 21, 2017
0085080
Add server configuration file for the Ada server
stcarrez Dec 21, 2017
c3aac08
Fix the log message in the Ada server to report the correct URI to co…
stcarrez Dec 21, 2017
11f5b96
Generate the Ada server configuration file
stcarrez Dec 21, 2017
85ec66b
Improvement of Ada code model to support nullable types
stcarrez Dec 23, 2017
61a3adc
Update the Ada server templates
stcarrez Dec 23, 2017
cd1215b
Refactor the Ada code generator
stcarrez Dec 24, 2017
10498d5
Improvement and cleanup of Ada client and server code
stcarrez Dec 24, 2017
99424c2
Fix the GNAT project file name to use a lower case name
stcarrez Dec 24, 2017
605b821
Regenerate the model and client Ada files
stcarrez Dec 26, 2017
b45db84
Update the Ada client sample to take into account the Nullable types
stcarrez Dec 26, 2017
a52170f
Regenerate some files with Ada Swagger Codegen
stcarrez Dec 26, 2017
da84d6a
Merge remote-tracking branch 'upstream/master' into ada-server-6680
stcarrez Dec 26, 2017
471bcef
Ignore generation of petstore.gpr
stcarrez Dec 26, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package io.swagger.codegen.languages;

import java.io.File;
import java.io.IOException;
import java.io.Writer;

import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import io.swagger.codegen.*;

public class AdaServerCodegen extends AbstractAdaCodegen implements CodegenConfig {

public AdaServerCodegen() {
super();
}

@Override
public CodegenType getTag() {
return CodegenType.SERVER;
}

@Override
public String getName() {
return "ada-server";
}

@Override
public String getHelp() {
return "Generates an Ada server implementation (beta).";
}

@Override
public void processOpts() {
super.processOpts();
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) {
packageName = (String) additionalProperties.get(CodegenConstants.PACKAGE_NAME);
}
String srcPrefix = "src" + File.separator;
String serverPrefix = srcPrefix + "server" + File.separator + toFilename(modelPackage);
String modelPrefix = srcPrefix + "model" + File.separator + toFilename(modelPackage);
String implPrefix = srcPrefix + toFilename(modelPackage);
supportingFiles.add(new SupportingFile("model-spec.mustache", null, modelPrefix + "-models.ads"));
supportingFiles.add(new SupportingFile("model-body.mustache", null, modelPrefix + "-models.adb"));
supportingFiles.add(new SupportingFile("server-skeleton-spec.mustache", null, serverPrefix + "-skeletons.ads"));
supportingFiles.add(new SupportingFile("server-skeleton-body.mustache", null, serverPrefix + "-skeletons.adb"));
supportingFiles.add(new SupportingFile("server-spec.mustache", null, implPrefix + "-servers.ads"));
supportingFiles.add(new SupportingFile("server-body.mustache", null, implPrefix + "-servers.adb"));

supportingFiles.add(new SupportingFile("swagger.mustache", "web" + File.separator + "swagger", "swagger.json"));

if (additionalProperties.containsKey(CodegenConstants.PROJECT_NAME)) {
projectName = (String) additionalProperties.get(CodegenConstants.PROJECT_NAME);
} else {
// default: set project based on package name
// e.g. petstore.api (package name) => petstore_api (project name)
projectName = packageName.replaceAll("\\.", "_");
}
String configBaseName = modelPackage.toLowerCase();
supportingFiles.add(new SupportingFile("gnat-project.mustache", "", toFilename(projectName) + ".gpr"));
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("config.gpr", "", "config.gpr"));
supportingFiles.add(new SupportingFile("server-properties.mustache", "", configBaseName + ".properties"));

/*
* Additional Properties. These values can be passed to the templates and
* are available in models, apis, and supporting files
*/
additionalProperties.put("package", this.modelPackage);
additionalProperties.put("packageConfig", configBaseName);
additionalProperties.put("packageDir", "server");
additionalProperties.put("mainName", "server");
additionalProperties.put(CodegenConstants.PROJECT_NAME, projectName);

String names[] = this.modelPackage.split("\\.");
String pkgName = names[0];
additionalProperties.put("packageLevel1", pkgName);
supportingFiles.add(new SupportingFile("package-spec-level1.mustache", null,
"src" + File.separator + toFilename(names[0]) + ".ads"));
if (names.length > 1) {
String fileName = toFilename(names[0]) + "-" + toFilename(names[1]) + ".ads";
pkgName = names[0] + "." + names[1];
additionalProperties.put("packageLevel2", pkgName);
supportingFiles.add(new SupportingFile("package-spec-level2.mustache", null,
"src" + File.separator + fileName));
}
pkgName = this.modelPackage;
supportingFiles.add(new SupportingFile("server.mustache", null,
"src" + File.separator + toFilename(pkgName) + "-server.adb"));
additionalProperties.put("packageName", toFilename(pkgName));

// add lambda for mustache templates
additionalProperties.put("lambdaAdaComment", new Mustache.Lambda() {
@Override
public void execute(Template.Fragment fragment, Writer writer) throws IOException {
String content = fragment.execute();
content = content.trim().replaceAll("\n$", "");
writer.write(content.replaceAll("\n", "\n -- "));
}
});
}

@Override
public String apiFileFolder() {
return outputFolder + "/" + apiPackage().replace('.', File.separatorChar);
}

@Override
public String modelFileFolder() {
return outputFolder + "/model/" + modelPackage().replace('.', File.separatorChar);
}
}
102 changes: 102 additions & 0 deletions modules/swagger-codegen/src/main/resources/Ada/README.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# {{appDescription}} - Swagger Ada Server

## Overview

This Ada server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project.
By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server,
you can easily generate a server stub.

## Building

To build the server you will need the GNAT Ada compiler as well as
the [Swagger Ada library](https://github.com/stcarrez/swagger-ada).

When the GNAT Ada compiler and Swagger Ada libraries are installed,
run the following command:

```
gprbuild -p -P{{projectName}}
```

After the build is successfull, you will get the server binary
in bin/{{packageName}}-server and you can start it as follows:
```
./bin/{{packageName}}-server
```

## Structure of the server

The server consists of several Ada packages that are generated from
the OpenAPI specification.

Source file | Package | Description
------------ | ------------- | -------------
src/{{packageName}}.ads|{{package}}|The server root package declaration
src/{{packageName}}-servers.ads|{{package}}.Servers|The server declaration and instantiation
src/{{packageName}}-servers.adb|{{package}}.Servers|The server implementation (empty stubs)
src/server/{{packageName}}-skeletons.ads|{{package}}.Skeletons|The server skeleton declaration
src/server/{{packageName}}-skeletons.adb|{{package}}.Skeletons|The server skeleton implementation
src/server/{{packageName}}-models.ads|{{package}}.Skeletons|The server model types declaration
src/server/{{packageName}}-models.adb|{{package}}.Skeletons|The server model types implementation
src/{{packageName}}-server.adb|{{package}}.Server|The server main procedure

Files generated in **src/server** should not be modified. The server implementation
files (**src/{{packageName}}-server.ads** and **src/{{packageName}}-server.adb**) should
be modified to implement the server operations. You can also customize the server
main procedure according to your needs.

## Server model

The server instance is represented by the **{{package}}.Servers.Server_Type** Ada type.
The REST API will need an instance of it to make the operation call. Two server model
exists:

* The instance per request model creates an instance of the server type for each request.
* The shared instance model shares the same instance across all concurrent REST requests. This instance is protected using an Ada protected object which holds the server instance.

The choice of the server model is made at the compilation time by instantiating either
the **{{package}}.Skeletons.Skeleton** package or the **{{package}}.Skeletons.Shared_Instance**
package. Such instantiation is done in **src/{{packageName}}-server.ads** and the default
is to use the **Shared_Instance**.

## Implementing a server operation

All you have to do is implement the server operation in the **src/{{packageName}}-servers.adb** file.
The package already contains the operation with its parameters and you only have to replace
the **null** instruction by real code.

# Documentation

## API Documentation

All URIs are relative to *{{basePath}}*

Method | HTTP request | Description
------------- | ------------- | -------------
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}[**{{nickname}}**]({{apiDocPath}}{{classname}}.md#{{nickname}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}}
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}

## Models
{{#models}}{{#model}} - [{{package}}.Models.{{classname}}]({{modelDocPath}}{{classname}}.md)
{{/model}}{{/models}}

## Authorization
{{^authMethods}} All endpoints do not require authorization.
{{/authMethods}}{{#authMethods}}{{#last}} Authentication schemes defined for the API:{{/last}}{{/authMethods}}
{{#authMethods}}## {{{name}}}

{{#isApiKey}}- **Type**: API key
- **API key parameter name**: {{{keyParamName}}}
- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}}
{{/isApiKey}}
{{#isBasic}}- **Type**: HTTP basic authentication
{{/isBasic}}
{{#isOAuth}}- **Type**: OAuth
- **Flow**: {{{flow}}}
- **Authorization URL**: {{{authorizationUrl}}}
- **Scopes**: {{^scopes}}N/A{{/scopes}}
{{#scopes}} - **{{{scope}}}**: {{{description}}}
{{/scopes}}
{{/isOAuth}}

{{/authMethods}}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ package body {{package}}.Clients is
{{#operation}}

-- {{summary}}{{#vendorExtensions.x-has-notes}}
-- {{unescapedNotes}}{{/vendorExtensions.x-has-notes}}
-- {{#lambdaAdaComment}}{{unescapedNotes}}{{/lambdaAdaComment}}{{/vendorExtensions.x-has-notes}}
procedure {{operationId}}
(Client : in out Client_Type{{#hasParams}};{{/hasParams}}{{#allParams}}
{{paramName}} : in {{^isFile}}{{^isString}}{{^isPrimitiveType}}{{^isContainer}}{{package}}.Models.{{/isContainer}}{{/isPrimitiveType}}{{/isString}}{{/isFile}}{{dataType}}{{#hasMore}};{{/hasMore}}{{/allParams}}{{#returnType}};
Expand All @@ -19,8 +19,9 @@ package body {{package}}.Clients is
Reply : Swagger.Value_Type;
{{/returnType}}
begin
Client.Set_Accept (({{#hasProduces}}{{#produces}}{{#vendorExtensions.x-has-uniq-produces}}1 => {{/vendorExtensions.x-has-uniq-produces}}Swagger.Clients.{{adaMediaType}}{{#hasMore}},
{{/hasMore}}{{/produces}}{{/hasProduces}}));{{#hasBodyParam}}
{{#hasProduces}}
Client.Set_Accept (({{#produces}}{{#vendorExtensions.x-has-uniq-produces}}1 => {{/vendorExtensions.x-has-uniq-produces}}Swagger.Clients.{{adaMediaType}}{{#hasMore}},
{{/hasMore}}{{/produces}}));{{/hasProduces}}{{#hasBodyParam}}
Client.Initialize (Req, ({{#hasConsumes}}{{#consumes}}{{#vendorExtensions.x-has-uniq-consumes}}1 -> {{/vendorExtensions.x-has-uniq-consumes}}Swagger.Clients.{{adaMediaType}}{{#hasMore}},
{{/hasMore}}{{/consumes}}{{/hasConsumes}}{{^hasConsumes}}1 => Swagger.Clients.APPLICATION_JSON{{/hasConsumes}}));{{#bodyParams}}{{#vendorExtensions.x-is-model-type}}
{{package}}.Models.Serialize (Req.Stream, "{{baseName}}", {{paramName}});{{/vendorExtensions.x-is-model-type}}{{^vendorExtensions.x-is-model-type}}{{#isFile}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ package {{package}}.Clients is
{{#operations}}
{{#operation}}
-- {{summary}}{{#vendorExtensions.x-has-notes}}
-- {{unescapedNotes}}{{/vendorExtensions.x-has-notes}}
-- {{#lambdaAdaComment}}{{unescapedNotes}}{{/lambdaAdaComment}}{{/vendorExtensions.x-has-notes}}
procedure {{operationId}}
(Client : in out Client_Type{{#hasParams}};{{/hasParams}}{{#allParams}}
{{paramName}} : in {{^isFile}}{{^isString}}{{^isPrimitiveType}}{{^isContainer}}{{package}}.Models.{{/isContainer}}{{/isPrimitiveType}}{{/isString}}{{/isFile}}{{dataType}}{{#hasMore}};{{/hasMore}}{{/allParams}}{{#returnType}};
Expand Down
43 changes: 43 additions & 0 deletions modules/swagger-codegen/src/main/resources/Ada/client.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
with {{package}}.Clients;
with {{package}}.Models;
with Swagger;
with Util.Http.Clients.Curl;
with Ada.Text_IO;
with Ada.Command_Line;
with Ada.Calendar.Formatting;
with Ada.Exceptions;
procedure {{package}}.Client is

use Ada.Text_IO;

procedure Usage;

Server : constant Swagger.UString := Swagger.To_UString ("http://localhost:8080/v2");
Arg_Count : constant Natural := Ada.Command_Line.Argument_Count;
Arg : Positive := 1;

procedure Usage is
begin
Put_Line ("Usage: {{projectName}} {params}...");
end Usage;

begin
if Arg_Count <= 1 then
Usage;
return;
end if;
Util.Http.Clients.Curl.Register;
declare
Command : constant String := Ada.Command_Line.Argument (Arg);
Item : constant String := Ada.Command_Line.Argument (Arg + 1);
C : {{package}}.Clients.Client_Type;
begin
C.Set_Server (Server);
Arg := Arg + 2;

exception
when E : Constraint_Error =>
Put_Line ("Constraint error raised: " & Ada.Exceptions.Exception_Message (E));

end;
end {{package}}.Client;
88 changes: 88 additions & 0 deletions modules/swagger-codegen/src/main/resources/Ada/config.gpr
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
abstract project Config is
for Source_Dirs use ();

type Yes_No is ("yes", "no");

type Library_Type_Type is ("relocatable", "static");

type Mode_Type is ("distrib", "debug", "optimize", "profile");
Mode : Mode_Type := external ("MODE", "debug");

Coverage : Yes_No := External ("COVERAGE", "no");
Processors := External ("PROCESSORS", "1");

package Builder is
case Mode is
when "debug" =>
for Default_Switches ("Ada") use ("-g", "-j" & Processors);
when others =>
for Default_Switches ("Ada") use ("-g", "-O2", "-j" & Processors);
end case;
end Builder;

package compiler is
warnings := ("-gnatwua");
defaults := ("-gnat2012");
case Mode is
when "distrib" =>
for Default_Switches ("Ada") use defaults & ("-gnatafno", "-gnatVa", "-gnatwa");

when "debug" =>
for Default_Switches ("Ada") use defaults & warnings
& ("-gnata", "-gnatVaMI", "-gnaty3abcefhiklmnprstxM99");

when "optimize" =>
for Default_Switches ("Ada") use defaults & warnings
& ("-gnatn", "-gnatp", "-fdata-sections", "-ffunction-sections");

when "profile" =>
for Default_Switches ("Ada") use defaults & warnings & ("-pg");
end case;

case Coverage is
when "yes" =>
for Default_Switches ("ada") use Compiler'Default_Switches ("Ada") &
("-fprofile-arcs", "-ftest-coverage");
when others =>
end case;
end compiler;

package binder is
case Mode is
when "debug" =>
for Default_Switches ("Ada") use ("-E");

when others =>
for Default_Switches ("Ada") use ("-E");

end case;
end binder;

package linker is
case Mode is
when "profile" =>
for Default_Switches ("Ada") use ("-pg");

when "distrib" =>
for Default_Switches ("Ada") use ("-s");

when "optimize" =>
for Default_Switches ("Ada") use ("-Wl,--gc-sections");

when others =>
null;
end case;

case Coverage is
when "yes" =>
for Default_Switches ("ada") use Linker'Default_Switches ("ada") &
("-fprofile-arcs");
when others =>
end case;
end linker;

package Ide is
for VCS_Kind use "git";
end Ide;

end Config;
Loading