package tst import ( "fmt" "strings" . "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/common" // . "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/gen" TS "git.eugeniocarvalho.dev/eugeniucarvalho/gg/generators/typescript" ) var ( FormatMap map[string]string // TemplatesMethods = map[string]func(p *Project, method *Method) (*G.Statement, error){ // "post": GenCreateStmts, // "put": GenUpdateStmts, // "delete": GenDeleteStmts, // "get_one": GenGetStmtsOne, // "get_list": GenGetStmtsList, // } angularServiceOption = []TS.CodeInterface{} angularServiceOptionMap = map[string]string{} ) func GenResources(p *Project) { var ( // angularServiceStmtName string angularResourceName string ServiceStmt = "ServiceStmt" // angularServiceMethods []TS.CodeInterface angularServiceStmtMethods []TS.CodeInterface ) // imports(module) // module.Line().Export().Class().Id(ServiceStmt).Block( // TS.Public().Id("Url").Call(TS.Raw("u: string, options:{}")).Op(":").String().Block( // TS.Raw(""), // ), // ).Line() module.Line().Raw(` export interface HttpOptions { headers?: HttpHeaders; params?: any; withCredentials?: boolean; id?: string; } export class ServiceStmt { // constructor(protected api: `).Id(ApiClassName(p)).Raw(`) {} constructor(protected api: ApiInterface) {} public Url(u: string, opts: {}): string { (u.match(/{(\w+)}/gm)||[]).map(argExpression => { const prop = argExpression.replace(/({|})/gm,''), value = opts[prop]; if (!value) { throw Error(`).Raw("`Url params '${prop}' is required`").Raw(`);} u = u.replace(argExpression, value); }); return u; } _event(url, opt: HttpOptions): Observable { opt = this.api.options(opt); const _url = new URL(this.Url(url, opt)), { params = null } = opt; if (params) { params.map.forEach((values, prop) => values.map(value => _url.searchParams.append(prop, value))); } return new Observable(o => o.next(new EventSourcePolyfill( _url.toString(), { heartbeatTimeout: 86400000, headers: { Authorization: this.api.Auth.Token() } }, ))).pipe(take(1)); } _get(url, opt: HttpOptions): Observable { opt = this.api.options(opt); return this.api.http.get(this.Url(url, opt), opt).pipe(take(1)); } _put(url, entity: any, opt: HttpOptions): Observable { opt = this.api.options(opt); return this.api.http.put(this.Url(url, opt), entity, opt).pipe(take(1)); } _patch(url, entity: any, opt: HttpOptions): Observable { opt = this.api.options(opt); return this.api.http.patch(this.Url(url, opt), entity, opt).pipe(take(1)); } _post(url, entity: any, opt: HttpOptions): Observable { opt = this.api.options(opt); return this.api.http.post(this.Url(url, opt), entity, opt).pipe(take(1)); } _delete(url, opt: HttpOptions): Observable { opt = this.api.options(opt); return this.api.http.delete(this.Url(url, opt), opt).pipe(take(1)); } }`).Line() for _, resource := range p.Resources { FormatMap = map[string]string{} // angularServiceMethods = []TS.CodeInterface{} angularServiceStmtMethods = []TS.CodeInterface{} angularResourceName = strings.Title(resource.ID) + "Service" module.Comment(resource.Description).Line() // angularServiceMethods = append(angularServiceMethods, TS.Constructor( // TS.Protected().Id("api").Op(":").Id(ApiClassName(p)), // ).Block( // // TS.This().Dot("Url").Op("="), // )) // angularServiceStmtMethods = append(angularServiceStmtMethods, TS.Public().Id("Url").Op(":").String()) // angularServiceStmtMethods = append(angularServiceStmtMethods, TS.Constructor( // TS.Protected().Id("api").Op(":").Id(ApiClassName(p)), // TS.Protected().Id("options").Op(":").Id("ServiceOptions"), // ).Block( // // TS.Raw("this.url").Op("=").Lit(p.UrlFromMethod()).Endl(), // TS.Raw("super();"), // TS.Raw("if (this.options == null)").Block( // // TS.Raw("this.options = new ServiceOptions();"), // TS.Raw("this.options = {};"), // ), // // TS.Super().Call(TS.Id("options")), // )) // angularServiceStmtName = angularResourceName + "Stmt" // angularServiceMethods = append(angularServiceMethods, TS.Id("Options").Params( // TS.Id("options?").Op(":").Id("ServiceOptions"), // ).Op(":").Id(angularServiceStmtName).Block( // TS.Return().New().Id(angularServiceStmtName).Call( // TS.Id("this.api"), // TS.Id("options"), // ), // ).Line()) // angularServiceStmtMethods = append(angularServiceStmtMethods, TS.Line().Raw(` // options(opts?: HttpOptions): this { // opts && Object.assign(this.opts, opts) // return this; // }`).Line()) for _, method := range resource.Methods { // if method.Template == "implement" { // continue // } GenAngularMethodStmt( p, resource, method, &angularServiceStmtMethods, angularResourceName, ) // Vai ser usado no metodo de construcao da classe chamado em createAngularService for paramName, param := range method.Parameters { // fmt.Println("param >>>>>>>>>", resource.ID, method.ID, paramName) if _, find := angularServiceOptionMap[paramName]; find { continue } angularServiceOptionMap[paramName] = param.Location angularServiceOption = append(angularServiceOption, TS.Comment(param.Description).Id(paramName+"?").Op(":").Id(TS.ConvType(param.Type)).Endl(), ) } } module.Line().Export().Class().Id( angularResourceName, ).Extends().Id(ServiceStmt).Block( angularServiceStmtMethods..., ).Line() // module.Line().Export().Class().Id( // angularResourceName, // ).Block( // angularServiceMethods..., // ).Line() } // Cria o arquivo de servido da api createAngularService(p, module) // Fim do laco de recurso // Salva o arquivo do modulo } func ApiClassName(p *Project) string { return p.Name + "Api" } func createAngularService(p *Project, module *TS.File) error { // var ( // service = Ts.NewFile("api.service.ts") // ) // Define a class que representa uma resposta da api // "Class gerencia todos os serviços da api.", module.Export().Interface().Id("ServiceResponse").Block( TS.Raw("resultSizeEstimate: number;"), TS.Raw("nextPageToken: string;"), TS.Raw("entity?:object;"), TS.Raw("itens?:object[];"), ) // Define todos os parametros atribuiveis as opcoes de uma requisicao da api module.Line().Export().Interface().Id("ServiceOptions").BlockFun(func(g *TS.Group) { g.Stmts = angularServiceOption }) // createClientClass(p, module) // createAuthClass(p, module) module.Line().Comment("Objeto acessivel pelo usuario para realizar requisicoes à api.").Raw(` @Injectable({ providedIn: 'root' }) // @Injectable() export class `).Raw(ApiClassName(p)).Raw(` { public Client: Client; // public BASE_URL = environment.BASE_URL; public Auth: Auth; // public queryParams: any = {}; public poolReady = []; public httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json', 'Accept': 'application/json' }), withCredentials: true, }; constructor( public http: HttpClient, // public route: ActivatedRoute, // public router: Router, // @Inject(ApiOptionsParams) public apiOptions: `).Raw(ApiClassName(p) + "Options").Raw(`, public apiOptions: `).Raw(ApiClassName(p) + "Options").Raw(`, ) { console.log('apiOptions `).Raw(ApiClassName(p)).Raw(`',apiOptions); // this.route.queryParams.subscribe(params => { this.queryParams = params; }); this.Client = new Client(this); if (Boolean(window[oauthWindowProp])) { this.Auth = window[oauthWindowProp]; } else { this.Auth = window[oauthWindowProp] = new Auth(this); } } onready(fn: () => void) { this.Auth.onAuthorize$.pipe(filter(grant => Boolean(grant))).subscribe(fn); // if (this.Auth.grant) { // fn(); // } else { // this.poolReady.push(fn); // } } options(opts?: HttpOptions): HttpOptions { opts = Object.assign({ headers: new HttpHeaders(), params: {}, }, opts); const headers = { 'Authorization': this.Auth.Token() }; this.copyHeader(this.httpOptions.headers, headers); this.copyHeader(opts.headers, headers); const params = opts.params; // Converte a query para base64 if (params.q) { params.q = window.btoa(unescape(encodeURIComponent(params.q))); } delete opts.headers; delete opts.params; const options = Object.assign({ headers: new HttpHeaders(headers), params: new HttpParams({ fromObject: params }), }, opts); // const options = Object.assign({ // headers: new HttpHeaders(hopts), // params: {} // }, opts); // // Converte a query para base64 // if (options.params.q) { // options.params.q = window.btoa(unescape(encodeURIComponent(options.params.q))); // } return options; } copyHeader(headers, hopts) { if (!headers) { return; } headers.keys().map(key => { hopts[key] = headers.get(key); }); } }`) // .Injetable("{providedIn: 'root'}").Export().Class().Id(ApiClassName(p)).Block( // TS.Public().Id("Client").Op(":").Id("Client").Endl(), // TS.Public().Id("baseURL").Op("=").Lit(p.BaseURL).Endl(), // TS.Public().Id("Auth").Op(":").Id("Auth").Endl(), // TS.Public().Id("httpOptions").Id("=").Block( // TS.Id("headers").Op(":").Raw("new HttpHeaders").Call(TS.Block( // TS.Raw("'Content-Type': 'application/json',"), // TS.Raw("'Accept': 'application/json'"), // )), // ).Endl(), // // TS.Public().Id("http").Op(":").Id("HttpClient").Endl(), // TS.Constructor( // TS.Public().Id("http").Op(":").Id("HttpClient"), // ).Block( // TS.This().Dot("Client").Op("=").New().Id("Client").Call(TS.This()).Endl(), // // TS.Id("this.http").Op("=").New().Id("Client").Call(TS.This()).Endl(), // ), // TS.Id("Options").Params( // TS.Id("opts").Op(":").Id("ServiceOptions"), // ).Op(":").Id("{headers: HttpHeaders,params: any}").Block( // TS.Return().Block( // TS.Raw("headers: this.httpOptions.headers,"), // // TS.Raw("params : opts.Params()"), // TS.Raw("params : opts"), // ), // ), // ).Line() module.Line().Raw(` export function ApiOptionsProvider(options) { console.log('ApiOptionsProvider', options); return options; } @NgModule({ imports: [ CommonModule, HttpClientModule, ] }) `).Export(). Class(). Id(p.Name + "ApiModule"). Block( TS.Raw(` static forRoot( options?: `).Raw(ApiClassName(p) + "Options").Raw(` ) : ModuleWithProviders { // static forRoot( options?:any ) : ModuleWithProviders { return({ ngModule: `).Raw(p.Name + "ApiModule").Raw(`, providers: [ { provide: ApiOptionsParams, useValue: options, }, { provide: `).Raw(ApiClassName(p) + "Options").Raw(`, useFactory: ApiOptionsProvider, deps:[ApiOptionsParams] } ] }); } `), ). Line() return nil } // createClientClass cria a classe de cliente que guarda a referencia para todos os servicos da api func createClientClass(p *Project, file *TS.File) { var ( angularResourceName string angularClientStmts = []TS.CodeInterface{} angularClientConstructor = []TS.CodeInterface{} ) for _, resource := range p.Resources { angularResourceName = strings.Title(resource.ID) + "Service" angularClientStmts = append( angularClientStmts, TS.Public().Id(strings.Title(resource.ID)).Op(":").Id(angularResourceName).Endl(), ) angularClientConstructor = append( angularClientConstructor, TS.This().Dot(strings.Title(resource.ID)).Op("=").New().Id(angularResourceName).Call(TS.Id("api")).Endl(), ) } angularClientConstructor = append( angularClientConstructor, TS.Raw("this.Generic = new ServiceStmt").Call(TS.Id("api")).Endl(), ) // angularClientStmts = append( // angularClientStmts, // TS.Raw(`public onready = new EventEmitter();`), // ) angularClientStmts = append( angularClientStmts, TS.Public().Id("Generic: ServiceStmt").Endl(), ) angularClientStmts = append( angularClientStmts, TS.Constructor( TS.Protected().Id("api").Op(":").Id("ApiInterface"), ).Block(angularClientConstructor...), ) file.Line().Comment( "Class gerencia todos os serviços da api.", ).Export().Class().Id("Client").Block(angularClientStmts...) } // func GenAngularMethod(p *Project, r *Resource, method *Method, methods *[]TS.CodeInterface, angularResourceName string) { // params := []TS.CodeInterface{} // param := "entity" // methodId := method.ID // callOptionsParam := "" // switch strings.ToLower(method.HttpMethod) { // case "post": // params = append(params, TS.Id(param).Op(":").Id(method.Entity)) // case "filter": // // methodId = "get" // param = "" // case "get": // param = "" // // params = append(params, TS.Id(param).Op(":").String()) // case "get_list": // param = "" // // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String()) // case "patch": // param = "" // params = append(params, TS.Id(param).Op(":").Id(method.Entity)) // // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String()) // case "put": // params = append(params, TS.Id(param).Op(":").Id(method.Entity)) // callOptionsParam = "{id: entity.id}" // case "delete": // // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String()) // params = append(params, TS.Id(param).Op(":").Id(method.Entity)) // callOptionsParam = "{id: entity.id}" // param = "" // default: // panic(fmt.Sprintf("O método http '%s' para o recurso %s não foi definido!", method.HttpMethod, method.ID)) // } // // *methods = append(*methods, TS.Id(method.ID).Params(params...).Op(":").Id("Observable").Block( // *methods = append(*methods, TS.Id(method.ID).Params(params...).Op(":").Id("Observable").Block( // TS.Return().This().Dot("options").Call(TS.Raw(callOptionsParam)).Dot(methodId).Call(TS.Id(param)).Endl(), // ).Line()) // } func GenAngularMethodStmt(p *Project, r *Resource, method *Method, methods *[]TS.CodeInterface, angularResourceName string) { var ( typ string params = []TS.CodeInterface{} paramsHttp = []TS.CodeInterface{} // id = method.ID // ret = TS.Id("Observable") param = "entity" template string templateValue interface{} defined bool ) if templateValue, defined = method.Custom["ts.template.method"]; defined { template = templateValue.(string) } else { template = method.HttpMethod } template = strings.ToLower(template) switch template { case "post": typ = "post" params = append(params, TS.Id(param).Op(":").Id(method.Entity)) // ret.TypeAliase(method.Entity) case "filter": typ = "get" // ret.TypeAliase(method.Entity) param = "" // params = append(params, TS.Id(param).Op(":").String()) case "get": typ = "get" // ret.TypeAliase(method.Entity) param = "" case "put": typ = "put" // ret.TypeAliase(method.Entity) params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String()) case "delete": typ = "delete" // ret.TypeAliase(method.Entity) // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String()) case "patch": typ = "patch" params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String()) // ret.TypeAliase(method.Entity) // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String()) case "sse": typ = "event" default: panic(fmt.Sprintf("Method '%s' template not defined!", template)) } path := method.Path for _, parameter := range method.Parameters { alias := getCustom(parameter.Custom, "ts.api.alias") if aliasString, ok := alias.(string);ok { path = strings.ReplaceAll( path, fmt.Sprintf("{%s}", parameter.ID), fmt.Sprintf("{%s}", aliasString), ) } } // paramsHttp = append(paramsHttp, TS.Raw("this.Url(url, opt.params)")) paramsHttp = append(paramsHttp, TS.Raw(fmt.Sprintf("`${this.api.apiOptions.BASE_URL}%s`", path))) switch typ { case "post", "put", "patch": paramsHttp = append(paramsHttp, TS.Id("entity")) } params = append(params, TS.Id("opt?: HttpOptions")) paramsHttp = append(paramsHttp, TS.Id("opt")) // *methods = append(*methods, TS.Id(method.ID).Params(params...).Op(":").Id("Observable").Block( *methods = append(*methods, TS.Id(method.ID).Params(params...).Block( // TS.Raw("let opt = this.api.Options(this.options)").Endl(), // TS.Raw("let url = ").Lit(p.GetUrlFromMethod(method)).Endl(), // TS.Raw("let url = ").Raw(fmt.Sprintf("`${this.api.BASE_URL}%s`", method.Path)).Endl(), TS.Line().Raw(fmt.Sprintf("return this._%s", typ)).Call(paramsHttp...).Endl(), ).Line()) }