Example #1
0
        public async Task CreateClient(TextWriter writer)
        {
            var interfacesToWrite = new InterfaceManager();

            writer.WriteLine($"<?php");
            writer.WriteLine();

            if (phpOptions.Namespace != null)
            {
                writer.WriteLine($"namespace {phpOptions.Namespace};");
            }

            writer.WriteLine(
                @"use threax\halcyonclient\HalEndpointClient;
use threax\halcyonclient\CurlHelper;"
                );

            await WriteClient(interfacesToWrite, writer);

            //PHP does not include any interfaces / classes for the data

            //Write out common interfaces we reuse in all clients
            writer.WriteLine(@"
class HalEndpointDocQuery {
    public $includeRequest;
    public $includeResponse;
}");

            //End Write Interfaces
        }
Example #2
0
        private async Task WriteClient(InterfaceManager interfacesToWrite, TextWriter writer)
        {
            foreach (var client in await clientGenerator.GetEndpointDefinitions())
            {
                //Write injector
                if (client.IsEntryPoint)
                {
                    writer.WriteLine($@"
class {client.Name}Injector {{
    private $url;
    private $fetcher;
    private $instance = NULL;

    public function __construct(string $url, CurlHelper $fetcher) {{
        $this->url = $url;
        $this->fetcher = $fetcher;
    }}

    public function load(): {client.Name}{ResultClassSuffix} {{
        if ($this->instance === NULL) {{
            $this->instance = {client.Name}{ResultClassSuffix}::Load($this->url, $this->fetcher);
        }}

        return $this->instance;
    }}
}}");
                }

                writer.WriteLine($@"
class {client.Name}{ResultClassSuffix} {{
    private $client;");

                if (client.IsEntryPoint)
                {
                    writer.WriteLine($@"
    public static function Load(string $url, CurlHelper $fetcher): {client.Name}{ResultClassSuffix} {{
        $result = HalEndpointClient::Load($url, $fetcher);
        return new {client.Name}{ResultClassSuffix}($result);
    }}");
                }

                //Write accessor for data

                writer.WriteLine($@"
    public function __construct(HalEndpointClient $client) {{
        $this->client = $client;
    }}

    public function getData() {{
        return $this->client->getData();
    }}");

                //Write data interface if we haven't found it yet
                interfacesToWrite.Add(client.Schema);

                if (client.IsCollectionView)
                {
                    WriteEmbedAccessor(writer, "items", "values", client.CollectionType);
                }

                //Write out any embedded properties
                foreach (var embedded in client.Schema.Properties.Where(i => i.Value.IsHalEmbedded()))
                {
                    var embeddedItem = embedded.Value.Item;
                    if (embeddedItem.HasReference)
                    {
                        var reference = embeddedItem.Reference;                                     //Get the reference
                        var def       = client.Schema.Definitions.First(i => i.Value == reference); //Find reference in definitions, njsonschema will have the objects the same, so this is a valid way to look this up
                        var typeHint  = def.Key.Replace('\\', '/').Split('/').Last();
                        WriteEmbedAccessor(writer, embedded.Key, embedded.Key, typeHint);
                    }
                }

                foreach (var link in client.Links)
                {
                    String returnOpen     = null;
                    String returnClose    = null;
                    String linkReturnType = null;
                    var    linkRequestArg = "";
                    var    loadFuncType   = "load";

                    //Only take a request or upload, prefer requests
                    if (link.EndpointDoc.RequestSchema != null)
                    {
                        interfacesToWrite.Add(link.EndpointDoc.RequestSchema);
                        linkRequestArg = $"$data";
                    }

                    if (link.EndpointDoc.ResponseSchema != null)
                    {
                        if (link.EndpointDoc.ResponseSchema.IsRawResponse())
                        {
                            linkReturnType = $"";
                            loadFuncType   = "loadRaw";
                        }
                        else
                        {
                            interfacesToWrite.Add(link.EndpointDoc.ResponseSchema);
                            linkReturnType = $": {link.EndpointDoc.ResponseSchema.Title}{ResultClassSuffix}";
                            returnOpen     = $"new {link.EndpointDoc.ResponseSchema.Title}{ResultClassSuffix}(";
                            returnClose    = ")";
                        }
                    }

                    if (linkReturnType == null)
                    {
                        linkReturnType = VoidReturnType;
                    }

                    var loadFunc = "Link";
                    var inArgs   = "";
                    var outArgs  = "";

                    if (linkRequestArg != "")
                    {
                        inArgs  += linkRequestArg;
                        outArgs += ", $data";
                        loadFunc = "LinkWithData";
                    }

                    var funcName = link.Rel;
                    if (link.Rel == "self")
                    {
                        //Self links make refresh functions, also clear in and out args
                        funcName = "refresh";
                        inArgs   = "";
                        outArgs  = "";
                        loadFunc = "Link";
                    }

                    var lowerFuncName = funcName.Substring(0, 1).ToLowerInvariant() + funcName.Substring(1);
                    var upperFuncName = funcName.Substring(0, 1).ToUpperInvariant() + funcName.Substring(1);

                    var fullLoadFunc = loadFuncType + loadFunc;

                    if (!link.DocsOnly)
                    {
                        //Write link
                        writer.Write($@"
    public function {lowerFuncName}({inArgs}){linkReturnType} {{
        $r = $this->client->{fullLoadFunc}(""{link.Rel}""{outArgs});");

                        //If the returns are set return r, otherwise void out the promise so we don't leak on void functions
                        if (returnOpen != null && returnClose != null)
                        {
                            writer.Write($@"
        return {returnOpen}$r{returnClose};");
                        }

                        writer.WriteLine($@"
    }}

    public function can{upperFuncName}(): bool {{
        return $this->client->hasLink(""{link.Rel}"");
    }}

    public function linkFor{upperFuncName}() {{
        return $this->client->getLink(""{link.Rel}"");
    }}");
                    }

                    if (link.EndpointDoc.HasDocumentation)
                    {
                        //Write link docs
                        writer.WriteLine($@"
    public function get{upperFuncName}Docs(HalEndpointDocQuery $query = NULL) {{
        return $this->client->loadLinkDoc(""{link.Rel}"", $query)->getData();
    }}

    public function has{upperFuncName}Docs(): bool {{
        return $this->client->hasLinkDoc(""{link.Rel}"");
    }}");
                    }
                }

                //Close class
                writer.WriteLine("}");
            }
        }
        public async Task CreateClient(TextWriter writer)
        {
            var interfacesToWrite = new InterfaceManager();

            writer.WriteLine(
                $@"using Threax.AspNetCore.Halcyon.Client;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Net.Http;
using System.Linq;

namespace {options.Namespace} {{"
                );

            await WriteClient(interfacesToWrite, writer);

            writer.WriteLine("}");

            //Write interfaces, kind of weird, no good docs for this
            var settings = new CSharpGeneratorSettings()
            {
                Namespace = options.Namespace,
                GenerateDataAnnotations = false,
                ClassStyle = CSharpClassStyle.Poco,
                RequiredPropertiesMustBeDefined = false,
                EnumNameGenerator = new EnumValueEnumNameGenerator(),
                ArrayType         = "List" //This is imported in the using statements above (System.Collections.Generic.List)
            };

            //Gather up everything to write, skip duplicate instances of the same thing
            Dictionary <String, CodeArtifact> codeArtifacts = new Dictionary <String, CodeArtifact>();

            foreach (var item in interfacesToWrite.Interfaces)
            {
                //Remove any properties from item that are hal embeds
                var propertiesToRemove = item.Value.Properties.Where(i => i.Value.IsHalEmbedded()).ToList();
                foreach (var remove in propertiesToRemove)
                {
                    item.Value.Properties.Remove(remove.Key);
                }

                var resolver = new CSharpTypeResolver(settings);
                resolver.RegisterSchemaDefinitions(new Dictionary <String, JsonSchema4>()
                {
                    { item.Key, item.Value }
                });                                                                                                     //Add all discovered generators

                var generator = new CSharpGenerator(item.Value, settings, resolver);
                var artifacts = generator.GenerateTypes();
                foreach (var artifact in artifacts.Artifacts)
                {
                    if (!codeArtifacts.ContainsKey(artifact.TypeName))
                    {
                        codeArtifacts.Add(artifact.TypeName, artifact);
                    }
                }
            }

            //Write the classes officially
            //From TypeScriptGenerator.cs GenerateFile, (NJsonSchema 9.10.49)
            var model = new FileTemplateModel()
            {
                Namespace = settings.Namespace ?? string.Empty,
                TypesCode = ConversionUtilities.TrimWhiteSpaces(string.Join("\n\n", CodeArtifactCollection.OrderByBaseDependency(codeArtifacts.Values).Select(p => p.Code))),
            };

            var template = settings.TemplateFactory.CreateTemplate("CSharp", "File", model);
            var classes  = ConversionUtilities.TrimWhiteSpaces(template.Render());

            writer.WriteLine(classes);
            //End Write Interfaces
        }
        private async Task WriteClient(InterfaceManager interfacesToWrite, TextWriter writer)
        {
            foreach (var client in await clientGenerator.GetEndpointDefinitions())
            {
                //Write injector
                if (client.IsEntryPoint)
                {
                    writer.WriteLine($@"
public class {client.Name}Injector 
{{
    private string url;
    private IHttpClientFactory fetcher;
    private Task<{client.Name}{ResultClassSuffix}> instanceTask = default(Task<{client.Name}{ResultClassSuffix}>);

    public {client.Name}Injector(string url, IHttpClientFactory fetcher)
    {{
        this.url = url;
        this.fetcher = fetcher;
    }}

    public Task<{client.Name}{ResultClassSuffix}> Load()
    {{
        if (this.instanceTask == default(Task<{client.Name}{ResultClassSuffix}>))
        {{
            this.instanceTask = {client.Name}{ResultClassSuffix}.Load(this.url, this.fetcher);
        }}
        return this.instanceTask;
    }}
}}");
                }

                writer.WriteLine($@"
public class {client.Name}{ResultClassSuffix} 
{{
    private HalEndpointClient client;");

                if (client.IsEntryPoint)
                {
                    writer.WriteLine($@"
    public static async Task<{client.Name}{ResultClassSuffix}> Load(string url, IHttpClientFactory fetcher)
    {{
        var result = await HalEndpointClient.Load(new HalLink(url, ""GET""), fetcher);
        return new {client.Name}{ResultClassSuffix}(result);
    }}");
                }

                //Write accessor for data

                writer.WriteLine($@"
    public {client.Name}{ResultClassSuffix}(HalEndpointClient client) 
    {{
        this.client = client;
    }}

    private {client.Name} strongData = default({client.Name});
    public {client.Name} Data 
    {{
        get
        {{
            if(this.strongData == default({client.Name}))
            {{
                this.strongData = this.client.GetData<{client.Name}>();  
            }}
            return this.strongData;
        }}
    }}");

                //Write data interface if we haven't found it yet
                interfacesToWrite.Add(client.Schema);

                if (client.IsCollectionView)
                {
                    WriteEmbedAccessor(writer, "items", "values", client.CollectionType);
                }

                //Write out any embedded properties
                foreach (var embedded in client.Schema.Properties.Where(i => i.Value.IsHalEmbedded()))
                {
                    var embeddedItem = embedded.Value.Item;
                    if (embeddedItem.HasReference)
                    {
                        var reference = embeddedItem.Reference;                                     //Get the reference
                        var def       = client.Schema.Definitions.First(i => i.Value == reference); //Find reference in definitions, njsonschema will have the objects the same, so this is a valid way to look this up
                        var typeHint  = def.Key.Replace('\\', '/').Split('/').Last();
                        WriteEmbedAccessor(writer, embedded.Key, embedded.Key, typeHint);
                    }
                }

                foreach (var link in client.Links)
                {
                    String returnOpen     = null;
                    String returnClose    = null;
                    String linkReturnType = null;
                    var    linkRequestArg = "";
                    var    loadFuncType   = "Load";

                    //Only take a request or upload, prefer requests
                    if (link.EndpointDoc.RequestSchema != null)
                    {
                        interfacesToWrite.Add(link.EndpointDoc.RequestSchema);
                        if (link.EndpointDoc.RequestSchema.IsArray())
                        {
                            linkRequestArg = $"IEnumerable<{link.EndpointDoc.RequestSchema.Title}> data";
                        }
                        else
                        {
                            linkRequestArg = $"{link.EndpointDoc.RequestSchema.Title} data";
                        }
                    }

                    if (link.EndpointDoc.ResponseSchema != null)
                    {
                        if (link.EndpointDoc.ResponseSchema.IsRawResponse())
                        {
                            linkReturnType = $"Task<RawEndpointResult>";
                            loadFuncType   = "LoadRaw";
                        }
                        else
                        {
                            interfacesToWrite.Add(link.EndpointDoc.ResponseSchema);
                            linkReturnType = $"Task<{link.EndpointDoc.ResponseSchema.Title}{ResultClassSuffix}>";
                            returnOpen     = $"new {link.EndpointDoc.ResponseSchema.Title}{ResultClassSuffix}(";
                            returnClose    = ")";
                        }
                    }

                    if (linkReturnType == null)
                    {
                        linkReturnType = VoidReturnType;
                    }

                    var loadFunc = "Link";
                    var inArgs   = "";
                    var outArgs  = "";

                    if (linkRequestArg != "")
                    {
                        inArgs  += linkRequestArg;
                        outArgs += ", data";
                        loadFunc = "LinkWithData";
                    }

                    var funcName = link.Rel;
                    if (link.Rel == "self")
                    {
                        //Self links make refresh functions, also clear in and out args
                        funcName = "refresh";
                        inArgs   = "";
                        outArgs  = "";
                        loadFunc = "Link";
                    }

                    var lowerFuncName = funcName.Substring(0, 1).ToLowerInvariant() + funcName.Substring(1);
                    var upperFuncName = funcName.Substring(0, 1).ToUpperInvariant() + funcName.Substring(1);

                    var fullLoadFunc = loadFuncType + loadFunc;

                    if (!link.DocsOnly)
                    {
                        //Write link
                        writer.Write($@"
    public async {linkReturnType} {upperFuncName}({inArgs}) 
    {{
        var result = await this.client.{fullLoadFunc}(""{link.Rel}""{outArgs});");

                        //If the returns are set return r, otherwise void out the promise so we don't leak on void functions
                        if (returnOpen != null && returnClose != null)
                        {
                            writer.WriteLine($@"
        return {returnOpen}result{returnClose};");
                        }
                        else if (linkReturnType == VoidReturnType) //Write a then that will hide the promise result and become void.
                        {
                            //Don't write anything for this
                        }
                        else //Returning the respose directly
                        {
                            writer.Write(@"
        return result;");
                        }

                        writer.WriteLine($@"
    }}

    public bool Can{upperFuncName} 
    {{
        get 
        {{
            return this.client.HasLink(""{link.Rel}"");
        }}
    }}

    public HalLink LinkFor{upperFuncName} 
    {{
        get 
        {{
            return this.client.GetLink(""{link.Rel}"");
        }}
    }}");
                    }

                    if (link.EndpointDoc.HasDocumentation)
                    {
                        //Write link docs
                        writer.WriteLine($@"
    public async Task<HalEndpointDoc> Get{upperFuncName}Docs(HalEndpointDocQuery query = null) 
    {{
        var result = await this.client.LoadLinkDoc(""{link.Rel}"", query);
        return result.GetData<HalEndpointDoc>();
    }}

    public bool Has{upperFuncName}Docs() {{
        return this.client.HasLinkDoc(""{link.Rel}"");
    }}");
                    }
                }

                //Close class
                //Close class
                writer.WriteLine("}");
            }
        }
        private async Task WriteClient(InterfaceManager interfacesToWrite, TextWriter writer)
        {
            foreach (var client in await clientGenerator.GetEndpointDefinitions())
            {
                //Write injector
                if (client.IsEntryPoint)
                {
                    writer.WriteLine($@"
export class {client.Name}Injector {{
    private instancePromise: Promise<{client.Name}{ResultClassSuffix}>;

    constructor(private url: string, private fetcher: Fetcher, private data?: any) {{}}

    public load(): Promise<{client.Name}{ResultClassSuffix}> {{
        if (!this.instancePromise) {{
            if (this.data) {{
                this.instancePromise = Promise.resolve(new {client.Name}{ResultClassSuffix}(new hal.HalEndpointClient(this.data, this.fetcher)));
            }}
            else {{
                this.instancePromise = {client.Name}{ResultClassSuffix}.Load(this.url, this.fetcher);
            }}
        }}
        return this.instancePromise;
    }}
}}");
                }

                writer.WriteLine($@"
export class {client.Name}{ResultClassSuffix} {{
    private client: hal.HalEndpointClient;");

                if (client.IsEntryPoint)
                {
                    writer.WriteLine($@"
    public static Load(url: string, fetcher: Fetcher): Promise<{client.Name}{ResultClassSuffix}> {{
        return hal.HalEndpointClient.Load({{
            href: url,
            method: ""GET""
        }}, fetcher)
            .then(c => {{
                 return new {client.Name}{ResultClassSuffix}(c);
             }});
            }}");
                }

                //Write accessor for data

                writer.WriteLine($@"
    constructor(client: hal.HalEndpointClient) {{
        this.client = client;
    }}

    private strongData: {client.Name} = undefined;
    public get data(): {client.Name} {{
        this.strongData = this.strongData || this.client.GetData<{client.Name}>();
        return this.strongData;
    }}");

                //Write data interface if we haven't found it yet
                interfacesToWrite.Add(client.Schema);

                if (client.IsCollectionView)
                {
                    WriteEmbedAccessor(writer, "items", "values", client.CollectionType);
                }

                //Write out any embedded properties
                foreach (var embedded in client.Schema.Properties.Where(i => i.Value.IsHalEmbedded()))
                {
                    var embeddedItem = embedded.Value.Item;
                    if (embeddedItem.HasReference)
                    {
                        var reference = embeddedItem.Reference;                                     //Get the reference
                        var def       = client.Schema.Definitions.First(i => i.Value == reference); //Find reference in definitions, njsonschema will have the objects the same, so this is a valid way to look this up
                        var typeHint  = def.Key.Replace('\\', '/').Split('/').Last();
                        WriteEmbedAccessor(writer, embedded.Key, embedded.Key, typeHint);
                    }
                }

                foreach (var link in client.Links)
                {
                    String returnOpen     = null;
                    String returnClose    = null;
                    String linkReturnType = null;
                    var    linkRequestArg = "";
                    var    loadFuncType   = "Load";

                    //Only take a request or upload, prefer requests
                    if (link.EndpointDoc.RequestSchema != null)
                    {
                        interfacesToWrite.Add(link.EndpointDoc.RequestSchema);
                        linkRequestArg = $"data: {link.EndpointDoc.RequestSchema.Title}";
                        if (link.EndpointDoc.RequestSchema.IsArray())
                        {
                            linkRequestArg += "[]";
                        }
                    }

                    if (link.EndpointDoc.ResponseSchema != null)
                    {
                        if (link.EndpointDoc.ResponseSchema.IsRawResponse())
                        {
                            linkReturnType = $": Promise<Response>";
                            loadFuncType   = "LoadRaw";
                        }
                        else
                        {
                            interfacesToWrite.Add(link.EndpointDoc.ResponseSchema);
                            linkReturnType = $": Promise<{link.EndpointDoc.ResponseSchema.Title}{ResultClassSuffix}>";
                            returnOpen     = $"new {link.EndpointDoc.ResponseSchema.Title}{ResultClassSuffix}(";
                            returnClose    = ")";
                        }
                    }

                    if (linkReturnType == null)
                    {
                        linkReturnType = VoidReturnType;
                    }

                    var loadFunc = "Link";
                    var inArgs   = "";
                    var outArgs  = "";

                    if (linkRequestArg != "")
                    {
                        inArgs  += linkRequestArg;
                        outArgs += ", data";
                        loadFunc = "LinkWithData";
                    }

                    var funcName = link.Rel;
                    if (link.Rel == "self")
                    {
                        //Self links make refresh functions, also clear in and out args
                        funcName = "refresh";
                        inArgs   = "";
                        outArgs  = "";
                        loadFunc = "Link";
                    }

                    var lowerFuncName = funcName.Substring(0, 1).ToLowerInvariant() + funcName.Substring(1);
                    var upperFuncName = funcName.Substring(0, 1).ToUpperInvariant() + funcName.Substring(1);

                    var fullLoadFunc = loadFuncType + loadFunc;

                    if (!link.DocsOnly)
                    {
                        //Write link
                        writer.Write($@"
    public {lowerFuncName}({inArgs}){linkReturnType} {{
        return this.client.{fullLoadFunc}(""{link.Rel}""{outArgs})");

                        //If the returns are set return r, otherwise void out the promise so we don't leak on void functions
                        if (returnOpen != null && returnClose != null)
                        {
                            writer.WriteLine($@"
               .then(r => {{
                    return {returnOpen}r{returnClose};
                }});");
                        }
                        else if (linkReturnType == VoidReturnType) //Write a then that will hide the promise result and become void.
                        {
                            writer.Write(".then(hal.makeVoid);");
                        }
                        else //Returning the respose directly
                        {
                            writer.Write(";");
                        }

                        writer.WriteLine($@"
    }}

    public can{upperFuncName}(): boolean {{
        return this.client.HasLink(""{link.Rel}"");
    }}

    public linkFor{upperFuncName}(): hal.HalLink {{
        return this.client.GetLink(""{link.Rel}"");
    }}");
                    }

                    if (link.EndpointDoc.HasDocumentation)
                    {
                        //Write link docs
                        writer.WriteLine($@"
    public get{upperFuncName}Docs(query?: HalEndpointDocQuery): Promise<hal.HalEndpointDoc> {{
        return this.client.LoadLinkDoc(""{link.Rel}"", query)
            .then(r => {{
                return r.GetData<hal.HalEndpointDoc>();
            }});
    }}

    public has{upperFuncName}Docs(): boolean {{
        return this.client.HasLinkDoc(""{link.Rel}"");
    }}");
                    }
                }

                //Close class
                writer.WriteLine("}");
            }
        }
        public async Task CreateClient(TextWriter writer)
        {
            var interfacesToWrite = new InterfaceManager();

            writer.WriteLine(
                @"import * as hal from 'htmlrapier.halcyon/src/EndpointClient';
import { Fetcher } from 'htmlrapier/src/fetcher';"
                );

            await WriteClient(interfacesToWrite, writer);

            //Write interfaces, kind of weird, no good docs for this
            var settings = new TypeScriptGeneratorSettings()
            {
                TypeStyle = TypeScriptTypeStyle.Interface,
                ForceAllPropertiesOptional = true,
                DateTimeType      = TypeScriptDateTimeType.String,
                EnumNameGenerator = new EnumValueEnumNameGenerator(),
            };

            //Gather up everything to write, skip duplicate instances of the same thing
            Dictionary <String, CodeArtifact> codeArtifacts = new Dictionary <String, CodeArtifact>();
            ExtensionCode lastExtensionCode = null;

            foreach (var item in interfacesToWrite.Interfaces)
            {
                //Remove any properties from item that are hal embeds
                var propertiesToRemove = item.Value.Properties.Where(i => i.Value.IsHalEmbedded()).ToList();
                foreach (var remove in propertiesToRemove)
                {
                    item.Value.Properties.Remove(remove.Key);
                }

                var resolver = new TypeScriptTypeResolver(settings);
                resolver.RegisterSchemaDefinitions(new Dictionary <String, JsonSchema4>()
                {
                    { item.Key, item.Value }
                });                                                                                                     //Add all discovered generators

                var generator = new TypeScriptGenerator(item.Value, settings, resolver);
                var artifacts = generator.GenerateTypes();
                foreach (var artifact in artifacts.Artifacts)
                {
                    if (!codeArtifacts.ContainsKey(artifact.TypeName))
                    {
                        codeArtifacts.Add(artifact.TypeName, artifact);
                    }
                }
                lastExtensionCode = artifacts.ExtensionCode;
            }

            //Write the classes officially
            //From TypeScriptGenerator.cs GenerateFile, (NJsonSchema 9.10.49)
            var model = new FileTemplateModel(settings)
            {
                Types         = ConversionUtilities.TrimWhiteSpaces(string.Join("\n\n", CodeArtifactCollection.OrderByBaseDependency(codeArtifacts.Values).Select(p => p.Code))),
                ExtensionCode = (TypeScriptExtensionCode)lastExtensionCode
            };

            var template = settings.TemplateFactory.CreateTemplate("TypeScript", "File", model);
            var classes  = ConversionUtilities.TrimWhiteSpaces(template.Render());

            writer.WriteLine(classes);

            //Write out common interfaces we reuse in all clients
            writer.WriteLine(@"
export interface HalEndpointDocQuery {
    includeRequest?: boolean;
    includeResponse?: boolean;
}");

            //End Write Interfaces
        }