Documentation ¶
Index ¶
Constants ¶
View Source
const BackendCommandsDefinitions = `package {{.Module.Id}}
// WARNING: generated file
import (
"errors"
"fmt"
"regexp"
"strings"
"github.com/function61/eventkit/command"
{{if .CommandsImports.Date}} "github.com/function61/eventkit/guts"{{end}}
)
// handlers
type CommandHandlers interface { {{range .Module.Commands}}
{{.AsGoStructName}}(*{{.AsGoStructName}}, *command.Ctx) error{{end}}
}
// invoker
func CommandInvoker(handlers CommandHandlers) command.Invoker {
return &invoker{handlers}
}
type invoker struct {
handlers CommandHandlers
}
func (i *invoker) Invoke(cmdGeneric command.Command, ctx *command.Ctx) error {
switch cmd := cmdGeneric.(type) { {{range .Module.Commands}}
case *{{.AsGoStructName}}:
return i.handlers.{{.AsGoStructName}}(cmd, ctx){{end}}
default:
// should not ever happen, because this is asserted in httpcommand
return fmt.Errorf("unknown command: " + cmdGeneric.Key())
}
}
// structs
{{range .Module.Commands}}
type {{.AsGoStructName}} struct { {{range .Fields}}
{{.Key}} {{.AsGoType $.Module}} ` + "`json:\"{{.Key}}\"`" + `{{end}}
}
func (x *{{.AsGoStructName}}) Validate() error {
{{.MakeValidation $.Module}}
return nil
}
func (x *{{.AsGoStructName}}) MiddlewareChain() string { return "{{.MiddlewareChain}}" }
func (x *{{.AsGoStructName}}) Key() string { return "{{.Command}}" }
{{end}}
// allocators
var Allocators = command.Allocators{
{{range .Module.Commands}}
"{{.Command}}": func() command.Command { return &{{.AsGoStructName}}{} },{{end}}
}
// util functions
func regexpValidation(fieldName string, pattern string, content string) error {
if !regexp.MustCompile(pattern).MatchString(content) {
return fmt.Errorf("field %s does not match pattern %s", fieldName, pattern)
}
return nil
}
func noNewlinesValidation(fieldName string, content string) error {
if strings.ContainsAny(content, "\r\n") {
return errors.New("single-line field " + fieldName + " contains newlines")
}
return nil
}
func fieldEmptyValidationError(fieldName string) error {
return errors.New("field " + fieldName + " cannot be empty")
}
func fieldLengthValidationError(fieldName string, maxLength int, got int) error {
return fmt.Errorf("field %s exceeded maximum length %d (got %d)", fieldName, maxLength, got)
}
`
View Source
const BackendEventDefinitions = `` /* 1240-byte string literal not displayed */
View Source
const BackendRestEndpoints = `` /* 2608-byte string literal not displayed */
View Source
const BackendTypes = `` /* 1740-byte string literal not displayed */
View Source
const BackendUiRoutes = `` /* 225-byte string literal not displayed */
View Source
const DocsCommands = `` /* 406-byte string literal not displayed */
View Source
const DocsEvents = `` /* 408-byte string literal not displayed */
View Source
const DocsRestEndpoints = `` /* 897-byte string literal not displayed */
View Source
const DocsTypes = `{{if .Module.Types.StringConsts}}
Constants
---------
| const | value |
|-------|-------|
{{range .Module.Types.StringConsts}}| {{.Key}} | {{.Value}} |
{{end}}
{{end}}
{{range .Module.Types.Enums}}
enum {{.Name}}
---------
{{range .StringMembers}}
- {{.}}{{end}}
{{end}}
{{range .Module.Types.Types}}
{{.Name}}
---------
` + "```" + `
{{.AsTypeScriptCode}}
` + "```" + `
{{end}}
`
View Source
const FrontendCommandDefinitions = `` /* 1371-byte string literal not displayed */
View Source
const FrontendDatatypes = `` /* 666-byte string literal not displayed */
View Source
const FrontendRestEndpoints = `// tslint:disable
// WARNING: generated file
// WHY: wouldn't make sense complicating code generation to check
// if we need template string or not in path string
{{if .Module.Types.EndpointsProducesAndConsumesTypescriptTypes}}import { {{range .Module.Types.EndpointsProducesAndConsumesTypescriptTypes}}
{{.}},{{end}}
} from '{{$.Opts.FrontendModulePrefix}}{{.Module.Path}}_types';{{end}}
import {
getJson,
{{if .AnyEndpointHasConsumes}} postJson,{{end}}
} from 'f61ui/httputil';
{{range .Module.Types.Endpoints}}
// {{.Path}}
export function {{.Name}}({{.TypescriptArgs}}) {
return {{if .Consumes}}postJson<{{if .Consumes}}{{.Consumes.AsTypeScriptType}}{{else}}void{{end}}, {{if .Produces}}{{.Produces.AsTypeScriptType}}{{else}}void{{end}}>{{else}}getJson<{{if .Produces}}{{.Produces.AsTypeScriptType}}{{else}}void{{end}}>{{end}}(` + "`{{.TypescriptPath}}`" + `{{if .Consumes}}, body{{end}});
}
{{if not .Consumes}}
export function {{.Name}}Url({{.TypescriptArgs}}): string {
return ` + "`{{.TypescriptPath}}`" + `;
}{{end}}
{{end}}
`
View Source
const FrontendUiRoutes = `// tslint:disable
// WARNING: generated file
import { parseQueryParams, queryParams, makeQueryParams } from 'f61ui/httputil';
export interface RouteHandlers { {{range .Module.UiRoutes}}
{{.Id}}: ({{if .HasOpts}}opts: {{.TsOptsName}}{{end}}) => JSX.Element;{{end}}
notFound: (url: string) => JSX.Element;
}
{{range .Module.UiRoutes}}
{{if .HasOpts}}export interface {{.TsOptsName}} { {{range .PathPlaceholders}}
{{.}}: string;{{end}}{{range .QueryParams}}
{{.Key}}{{if .Type.Nullable}}?{{end}}: {{if eq .Type.NameRaw "integer"}}number{{else}}string{{end}};{{end}}
}{{end}}
{{if .Title}}
export const {{.Id}}Title = '{{.Title}}';
{{end}}
// {{.Path}}
export function {{.Id}}URL({{if .HasOpts}}opts: {{.TsOptsName}}{{end}}): string {
const query: queryParams = {};
{{range .QueryParams}}
{{if .Type.Nullable}} if (opts.{{.Key}} !== undefined) {
{{end}} query.{{.Key}} = opts.{{.Key}}{{if eq .Type.NameRaw "integer"}}.toString(){{end}};{{if .Type.Nullable}}
}{{end}}
{{end}}
return makeQueryParams(` + "`{{.TsPath}}`" + `, query);
}
export function {{.Id}}Match(path: string, query: queryParams): {{if .HasOpts}}{{.TsOptsName}}{{else}}{}{{end}} | null {
const matches = {{.PathReJavaScript}}.exec(path);
if (matches == null) {
return null;
}
{{range .QueryParams}}{{if eq .Type.NameRaw "string"}}
const {{.Key}}Par = query.{{.Key}};{{else}}
let {{.Key}}Par: number | undefined;
if (query.{{.Key}} !== undefined) {
// parseInt() accepts garbage after the number, and "+" accepts empty string
if (query.{{.Key}} === '') {
throw new Error("Invalid URL param: '{{.Key}}'; expecting integer, got empty string")
}
const parsed = +query.{{.Key}};
if (isNaN(parsed)) {
throw new Error("Invalid URL param: '{{.Key}}'; expecting integer")
}
{{.Key}}Par = parsed;
}{{end}}{{if not .Type.Nullable}}
if ({{.Key}}Par === undefined) {
throw new Error("Required URL param '{{.Key}}' missing");
} {{end}}
{{end}}
assertNoUnrecognizedKeys(Object.keys(query), [{{range .QueryParams}}'{{.Key}}', {{end}}]);
return { {{range $idx, $key := .PathPlaceholders}}
{{$key}}: matches[{{add $idx 1}}],{{end}}{{range .QueryParams}}
{{.Key}}: {{.Key}}Par,{{end}}
};
}
// ---------------
{{end}}
export function handle(url: string, handlers: RouteHandlers): JSX.Element {
const qpos = url.indexOf('?');
// "/search?query=foo" => "/search"
const path = qpos === -1 ? url : url.substr(0, qpos);
const queryPars = parseQueryParams(qpos === -1 ? '' : url.substr(qpos + 1));
{{range .Module.UiRoutes}}
const {{.Id}}Opts = {{.Id}}Match(path, queryPars);
if ({{.Id}}Opts) {
return handlers.{{.Id}}({{if .HasOpts}}{{.Id}}Opts{{end}});
}
{{end}}
return handlers.notFound(path);
}
// for when you need to check if url can be routed to this route collection
export function hasRouteFor(url: string): boolean {
const qpos = url.indexOf('?');
// "/search?query=foo" => "/search"
const path = qpos === -1 ? url : url.substr(0, qpos);
const queryPars = parseQueryParams(qpos === -1 ? '' : url.substr(qpos + 1));
{{range .Module.UiRoutes}}
if ({{.Id}}Match(path, queryPars)) {
return true
} {{end}}
return false;
}
function assertNoUnrecognizedKeys(gotKeys: string[], allowedKeys: string[]) {
const unrecognizedKeys = gotKeys.filter(key => allowedKeys.indexOf(key) === -1);
if (unrecognizedKeys.length > 0) {
throw new Error("Unrecognized keys in URL params: "+unrecognizedKeys.join(', '));
}
}
`
View Source
const FrontendVersion = `` /* 164-byte string literal not displayed */
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.