resources.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. package tst
  2. import (
  3. "fmt"
  4. "strings"
  5. . "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/common" // . "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/gen"
  6. TS "git.eugeniocarvalho.dev/eugeniucarvalho/gg/generators/typescript"
  7. )
  8. var (
  9. FormatMap map[string]string
  10. // TemplatesMethods = map[string]func(p *Project, method *Method) (*G.Statement, error){
  11. // "post": GenCreateStmts,
  12. // "put": GenUpdateStmts,
  13. // "delete": GenDeleteStmts,
  14. // "get_one": GenGetStmtsOne,
  15. // "get_list": GenGetStmtsList,
  16. // }
  17. angularServiceOption = []TS.CodeInterface{}
  18. angularServiceOptionMap = map[string]string{}
  19. )
  20. func GenResources(p *Project) {
  21. var (
  22. // angularServiceStmtName string
  23. angularResourceName string
  24. ServiceStmt = "ServiceStmt"
  25. // angularServiceMethods []TS.CodeInterface
  26. angularServiceStmtMethods []TS.CodeInterface
  27. )
  28. // imports(module)
  29. // module.Line().Export().Class().Id(ServiceStmt).Block(
  30. // TS.Public().Id("Url").Call(TS.Raw("u: string, options:{}")).Op(":").String().Block(
  31. // TS.Raw(""),
  32. // ),
  33. // ).Line()
  34. module.Line().Raw(`
  35. export interface HttpOptions {
  36. headers?: HttpHeaders;
  37. params?: any;
  38. withCredentials?: boolean;
  39. id?: string;
  40. }
  41. export class ServiceStmt {
  42. // constructor(protected api: `).Id(ApiClassName(p)).Raw(`) {}
  43. constructor(protected api: ApiInterface) {}
  44. public Url(u: string, opts: {}): string {
  45. (u.match(/{(\w+)}/gm)||[]).map(argExpression => {
  46. const
  47. prop = argExpression.replace(/({|})/gm,''),
  48. value = opts[prop];
  49. if (!value) { throw Error(`).Raw("`Url params '${prop}' is required`").Raw(`);}
  50. u = u.replace(argExpression, value);
  51. });
  52. return u;
  53. }
  54. _event(url, opt: HttpOptions): Observable<EventSourcePolyfill> {
  55. opt = this.api.options(opt);
  56. const _url = new URL(this.Url(url, opt)), { params = null } = opt;
  57. if (params) {
  58. params.map.forEach((values, prop) => values.map(value => _url.searchParams.append(prop, value)));
  59. }
  60. return new Observable(o => o.next(new EventSourcePolyfill(
  61. _url.toString(),
  62. { heartbeatTimeout: 86400000, headers: { Authorization: this.api.Auth.Token() } },
  63. ))).pipe(take(1));
  64. }
  65. _get(url, opt: HttpOptions): Observable<ServiceResponse> {
  66. opt = this.api.options(opt);
  67. return this.api.http.get<ServiceResponse>(this.Url(url, opt), opt).pipe(take(1));
  68. }
  69. _put(url, entity: any, opt: HttpOptions): Observable<ServiceResponse> {
  70. opt = this.api.options(opt);
  71. return this.api.http.put<ServiceResponse>(this.Url(url, opt), entity, opt).pipe(take(1));
  72. }
  73. _patch(url, entity: any, opt: HttpOptions): Observable<ServiceResponse> {
  74. opt = this.api.options(opt);
  75. return this.api.http.patch<ServiceResponse>(this.Url(url, opt), entity, opt).pipe(take(1));
  76. }
  77. _post(url, entity: any, opt: HttpOptions): Observable<ServiceResponse> {
  78. opt = this.api.options(opt);
  79. return this.api.http.post<ServiceResponse>(this.Url(url, opt), entity, opt).pipe(take(1));
  80. }
  81. _delete(url, opt: HttpOptions): Observable<ServiceResponse> {
  82. opt = this.api.options(opt);
  83. return this.api.http.delete<ServiceResponse>(this.Url(url, opt), opt).pipe(take(1));
  84. }
  85. }`).Line()
  86. for _, resource := range p.Resources {
  87. FormatMap = map[string]string{}
  88. // angularServiceMethods = []TS.CodeInterface{}
  89. angularServiceStmtMethods = []TS.CodeInterface{}
  90. angularResourceName = strings.Title(resource.ID) + "Service"
  91. module.Comment(resource.Description).Line()
  92. // angularServiceMethods = append(angularServiceMethods, TS.Constructor(
  93. // TS.Protected().Id("api").Op(":").Id(ApiClassName(p)),
  94. // ).Block(
  95. // // TS.This().Dot("Url").Op("="),
  96. // ))
  97. // angularServiceStmtMethods = append(angularServiceStmtMethods, TS.Public().Id("Url").Op(":").String())
  98. // angularServiceStmtMethods = append(angularServiceStmtMethods, TS.Constructor(
  99. // TS.Protected().Id("api").Op(":").Id(ApiClassName(p)),
  100. // TS.Protected().Id("options").Op(":").Id("ServiceOptions"),
  101. // ).Block(
  102. // // TS.Raw("this.url").Op("=").Lit(p.UrlFromMethod()).Endl(),
  103. // TS.Raw("super();"),
  104. // TS.Raw("if (this.options == null)").Block(
  105. // // TS.Raw("this.options = new ServiceOptions();"),
  106. // TS.Raw("this.options = {};"),
  107. // ),
  108. // // TS.Super().Call(TS.Id("options")),
  109. // ))
  110. // angularServiceStmtName = angularResourceName + "Stmt"
  111. // angularServiceMethods = append(angularServiceMethods, TS.Id("Options").Params(
  112. // TS.Id("options?").Op(":").Id("ServiceOptions"),
  113. // ).Op(":").Id(angularServiceStmtName).Block(
  114. // TS.Return().New().Id(angularServiceStmtName).Call(
  115. // TS.Id("this.api"),
  116. // TS.Id("options"),
  117. // ),
  118. // ).Line())
  119. // angularServiceStmtMethods = append(angularServiceStmtMethods, TS.Line().Raw(`
  120. // options(opts?: HttpOptions): this {
  121. // opts && Object.assign(this.opts, opts)
  122. // return this;
  123. // }`).Line())
  124. for _, method := range resource.Methods {
  125. // if method.Template == "implement" {
  126. // continue
  127. // }
  128. GenAngularMethodStmt(
  129. p,
  130. resource,
  131. method,
  132. &angularServiceStmtMethods,
  133. angularResourceName,
  134. )
  135. // Vai ser usado no metodo de construcao da classe chamado em createAngularService
  136. for paramName, param := range method.Parameters {
  137. // fmt.Println("param >>>>>>>>>", resource.ID, method.ID, paramName)
  138. if _, find := angularServiceOptionMap[paramName]; find {
  139. continue
  140. }
  141. angularServiceOptionMap[paramName] = param.Location
  142. angularServiceOption = append(angularServiceOption,
  143. TS.Comment(param.Description).Id(paramName+"?").Op(":").Id(TS.ConvType(param.Type)).Endl(),
  144. )
  145. }
  146. }
  147. module.Line().Export().Class().Id(
  148. angularResourceName,
  149. ).Extends().Id(ServiceStmt).Block(
  150. angularServiceStmtMethods...,
  151. ).Line()
  152. // module.Line().Export().Class().Id(
  153. // angularResourceName,
  154. // ).Block(
  155. // angularServiceMethods...,
  156. // ).Line()
  157. }
  158. // Cria o arquivo de servido da api
  159. createAngularService(p, module)
  160. // Fim do laco de recurso
  161. // Salva o arquivo do modulo
  162. }
  163. func ApiClassName(p *Project) string {
  164. return p.Name + "Api"
  165. }
  166. func createAngularService(p *Project, module *TS.File) error {
  167. // var (
  168. // service = Ts.NewFile("api.service.ts")
  169. // )
  170. // Define a class que representa uma resposta da api
  171. // "Class gerencia todos os serviços da api.",
  172. module.Export().Interface().Id("ServiceResponse").Block(
  173. TS.Raw("resultSizeEstimate: number;"),
  174. TS.Raw("nextPageToken: string;"),
  175. TS.Raw("entity?:object;"),
  176. TS.Raw("itens?:object[];"),
  177. )
  178. // Define todos os parametros atribuiveis as opcoes de uma requisicao da api
  179. module.Line().Export().Interface().Id("ServiceOptions").BlockFun(func(g *TS.Group) {
  180. g.Stmts = angularServiceOption
  181. })
  182. //
  183. createClientClass(p, module)
  184. //
  185. createAuthClass(p, module)
  186. module.Line().Comment("Objeto acessivel pelo usuario para realizar requisicoes à api.").Raw(`
  187. @Injectable({ providedIn: 'root' })
  188. // @Injectable()
  189. export class `).Raw(ApiClassName(p)).Raw(` {
  190. public Client: Client;
  191. // public BASE_URL = environment.BASE_URL;
  192. public Auth: Auth;
  193. // public queryParams: any = {};
  194. public poolReady = [];
  195. public httpOptions = {
  196. headers: new HttpHeaders({
  197. 'Content-Type': 'application/json',
  198. 'Accept': 'application/json'
  199. }),
  200. withCredentials: true,
  201. };
  202. constructor(
  203. public http: HttpClient,
  204. // public route: ActivatedRoute,
  205. // public router: Router,
  206. // @Inject(ApiOptionsParams) public apiOptions: `).Raw(ApiClassName(p) + "Options").Raw(`,
  207. public apiOptions: `).Raw(ApiClassName(p) + "Options").Raw(`,
  208. ) {
  209. console.log('apiOptions `).Raw(ApiClassName(p)).Raw(`',apiOptions);
  210. // this.route.queryParams.subscribe(params => { this.queryParams = params; });
  211. this.Client = new Client(this);
  212. if (Boolean(window[oauthWindowProp])) {
  213. this.Auth = window[oauthWindowProp];
  214. } else {
  215. this.Auth = window[oauthWindowProp] = new Auth(this);
  216. }
  217. }
  218. onready(fn: () => void) {
  219. this.Auth.onAuthorize$.pipe(filter(grant => Boolean(grant))).subscribe(fn);
  220. // if (this.Auth.grant) {
  221. // fn();
  222. // } else {
  223. // this.poolReady.push(fn);
  224. // }
  225. }
  226. options(opts?: HttpOptions): HttpOptions {
  227. opts = Object.assign({
  228. headers: new HttpHeaders(),
  229. params: {},
  230. }, opts);
  231. const headers = {
  232. 'Authorization': this.Auth.Token()
  233. };
  234. this.copyHeader(this.httpOptions.headers, headers);
  235. this.copyHeader(opts.headers, headers);
  236. const params = opts.params;
  237. // Converte a query para base64
  238. if (params.q) {
  239. params.q = window.btoa(unescape(encodeURIComponent(params.q)));
  240. }
  241. delete opts.headers;
  242. delete opts.params;
  243. const options = Object.assign({
  244. headers: new HttpHeaders(headers),
  245. params: new HttpParams({ fromObject: params }),
  246. }, opts);
  247. // const options = Object.assign({
  248. // headers: new HttpHeaders(hopts),
  249. // params: {}
  250. // }, opts);
  251. // // Converte a query para base64
  252. // if (options.params.q) {
  253. // options.params.q = window.btoa(unescape(encodeURIComponent(options.params.q)));
  254. // }
  255. return options;
  256. }
  257. copyHeader(headers, hopts) {
  258. if (!headers) { return; }
  259. headers.keys().map(key => { hopts[key] = headers.get(key); });
  260. }
  261. }`)
  262. // .Injetable("{providedIn: 'root'}").Export().Class().Id(ApiClassName(p)).Block(
  263. // TS.Public().Id("Client").Op(":").Id("Client").Endl(),
  264. // TS.Public().Id("baseURL").Op("=").Lit(p.BaseURL).Endl(),
  265. // TS.Public().Id("Auth").Op(":").Id("Auth").Endl(),
  266. // TS.Public().Id("httpOptions").Id("=").Block(
  267. // TS.Id("headers").Op(":").Raw("new HttpHeaders").Call(TS.Block(
  268. // TS.Raw("'Content-Type': 'application/json',"),
  269. // TS.Raw("'Accept': 'application/json'"),
  270. // )),
  271. // ).Endl(),
  272. // // TS.Public().Id("http").Op(":").Id("HttpClient").Endl(),
  273. // TS.Constructor(
  274. // TS.Public().Id("http").Op(":").Id("HttpClient"),
  275. // ).Block(
  276. // TS.This().Dot("Client").Op("=").New().Id("Client").Call(TS.This()).Endl(),
  277. // // TS.Id("this.http").Op("=").New().Id("Client").Call(TS.This()).Endl(),
  278. // ),
  279. // TS.Id("Options").Params(
  280. // TS.Id("opts").Op(":").Id("ServiceOptions"),
  281. // ).Op(":").Id("{headers: HttpHeaders,params: any}").Block(
  282. // TS.Return().Block(
  283. // TS.Raw("headers: this.httpOptions.headers,"),
  284. // // TS.Raw("params : opts.Params()"),
  285. // TS.Raw("params : opts"),
  286. // ),
  287. // ),
  288. // ).Line()
  289. module.Line().Raw(`
  290. export function ApiOptionsProvider(options) {
  291. console.log('ApiOptionsProvider', options);
  292. return options;
  293. }
  294. @NgModule({
  295. imports: [
  296. CommonModule,
  297. HttpClientModule,
  298. ]
  299. })
  300. `).Export().
  301. Class().
  302. Id(p.Name + "ApiModule").
  303. Block(
  304. TS.Raw(`
  305. static forRoot( options?: `).Raw(ApiClassName(p) + "Options").Raw(` ) : ModuleWithProviders {
  306. // static forRoot( options?:any ) : ModuleWithProviders {
  307. return({
  308. ngModule: `).Raw(p.Name + "ApiModule").Raw(`,
  309. providers: [
  310. {
  311. provide: ApiOptionsParams,
  312. useValue: options,
  313. },
  314. {
  315. provide: `).Raw(ApiClassName(p) + "Options").Raw(`,
  316. useFactory: ApiOptionsProvider,
  317. deps:[ApiOptionsParams]
  318. }
  319. ]
  320. });
  321. }
  322. `),
  323. ).
  324. Line()
  325. return nil
  326. }
  327. // createClientClass cria a classe de cliente que guarda a referencia para todos os servicos da api
  328. func createClientClass(p *Project, file *TS.File) {
  329. var (
  330. angularResourceName string
  331. angularClientStmts = []TS.CodeInterface{}
  332. angularClientConstructor = []TS.CodeInterface{}
  333. )
  334. for _, resource := range p.Resources {
  335. angularResourceName = strings.Title(resource.ID) + "Service"
  336. angularClientStmts = append(
  337. angularClientStmts,
  338. TS.Public().Id(strings.Title(resource.ID)).Op(":").Id(angularResourceName).Endl(),
  339. )
  340. angularClientConstructor = append(
  341. angularClientConstructor,
  342. TS.This().Dot(strings.Title(resource.ID)).Op("=").New().Id(angularResourceName).Call(TS.Id("api")).Endl(),
  343. )
  344. }
  345. angularClientConstructor = append(
  346. angularClientConstructor,
  347. TS.Raw("this.Generic = new ServiceStmt").Call(TS.Id("api")).Endl(),
  348. )
  349. // angularClientStmts = append(
  350. // angularClientStmts,
  351. // TS.Raw(`public onready = new EventEmitter<any>();`),
  352. // )
  353. angularClientStmts = append(
  354. angularClientStmts,
  355. TS.Public().Id("Generic: ServiceStmt").Endl(),
  356. )
  357. angularClientStmts = append(
  358. angularClientStmts,
  359. TS.Constructor(
  360. TS.Protected().Id("api").Op(":").Id("ApiInterface"),
  361. ).Block(angularClientConstructor...),
  362. )
  363. file.Line().Comment(
  364. "Class gerencia todos os serviços da api.",
  365. ).Export().Class().Id("Client").Block(angularClientStmts...)
  366. }
  367. // func GenAngularMethod(p *Project, r *Resource, method *Method, methods *[]TS.CodeInterface, angularResourceName string) {
  368. // params := []TS.CodeInterface{}
  369. // param := "entity"
  370. // methodId := method.ID
  371. // callOptionsParam := ""
  372. // switch strings.ToLower(method.HttpMethod) {
  373. // case "post":
  374. // params = append(params, TS.Id(param).Op(":").Id(method.Entity))
  375. // case "filter":
  376. // // methodId = "get"
  377. // param = ""
  378. // case "get":
  379. // param = ""
  380. // // params = append(params, TS.Id(param).Op(":").String())
  381. // case "get_list":
  382. // param = ""
  383. // // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
  384. // case "patch":
  385. // param = ""
  386. // params = append(params, TS.Id(param).Op(":").Id(method.Entity))
  387. // // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
  388. // case "put":
  389. // params = append(params, TS.Id(param).Op(":").Id(method.Entity))
  390. // callOptionsParam = "{id: entity.id}"
  391. // case "delete":
  392. // // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
  393. // params = append(params, TS.Id(param).Op(":").Id(method.Entity))
  394. // callOptionsParam = "{id: entity.id}"
  395. // param = ""
  396. // default:
  397. // panic(fmt.Sprintf("O método http '%s' para o recurso %s não foi definido!", method.HttpMethod, method.ID))
  398. // }
  399. // // *methods = append(*methods, TS.Id(method.ID).Params(params...).Op(":").Id("Observable<ServiceResponse>").Block(
  400. // *methods = append(*methods, TS.Id(method.ID).Params(params...).Op(":").Id("Observable<ServiceResponse>").Block(
  401. // TS.Return().This().Dot("options").Call(TS.Raw(callOptionsParam)).Dot(methodId).Call(TS.Id(param)).Endl(),
  402. // ).Line())
  403. // }
  404. func GenAngularMethodStmt(p *Project, r *Resource, method *Method, methods *[]TS.CodeInterface, angularResourceName string) {
  405. var (
  406. typ string
  407. params = []TS.CodeInterface{}
  408. paramsHttp = []TS.CodeInterface{}
  409. // id = method.ID
  410. // ret = TS.Id("Observable")
  411. param = "entity"
  412. template string
  413. templateValue interface{}
  414. defined bool
  415. )
  416. if templateValue, defined = method.Custom["ts.template.method"]; defined {
  417. template = templateValue.(string)
  418. } else {
  419. template = method.HttpMethod
  420. }
  421. template = strings.ToLower(template)
  422. switch template {
  423. case "post":
  424. typ = "post"
  425. params = append(params, TS.Id(param).Op(":").Id(method.Entity))
  426. // ret.TypeAliase(method.Entity)
  427. case "filter":
  428. typ = "get"
  429. // ret.TypeAliase(method.Entity)
  430. param = ""
  431. // params = append(params, TS.Id(param).Op(":").String())
  432. case "get":
  433. typ = "get"
  434. // ret.TypeAliase(method.Entity)
  435. param = ""
  436. case "put":
  437. typ = "put"
  438. // ret.TypeAliase(method.Entity)
  439. params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
  440. case "delete":
  441. typ = "delete"
  442. // ret.TypeAliase(method.Entity)
  443. // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
  444. case "patch":
  445. typ = "patch"
  446. params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
  447. // ret.TypeAliase(method.Entity)
  448. // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
  449. case "sse":
  450. typ = "event"
  451. default:
  452. panic(fmt.Sprintf("Method '%s' template not defined!", template))
  453. }
  454. // paramsHttp = append(paramsHttp, TS.Raw("this.Url(url, opt.params)"))
  455. paramsHttp = append(paramsHttp, TS.Raw(fmt.Sprintf("`${this.api.apiOptions.BASE_URL}%s`", method.Path)))
  456. switch typ {
  457. case "post", "put", "patch":
  458. paramsHttp = append(paramsHttp, TS.Id("entity"))
  459. }
  460. params = append(params, TS.Id("opt?: HttpOptions"))
  461. paramsHttp = append(paramsHttp, TS.Id("opt"))
  462. // *methods = append(*methods, TS.Id(method.ID).Params(params...).Op(":").Id("Observable<ServiceResponse>").Block(
  463. *methods = append(*methods, TS.Id(method.ID).Params(params...).Block(
  464. // TS.Raw("let opt = this.api.Options(this.options)").Endl(),
  465. // TS.Raw("let url = ").Lit(p.GetUrlFromMethod(method)).Endl(),
  466. // TS.Raw("let url = ").Raw(fmt.Sprintf("`${this.api.BASE_URL}%s`", method.Path)).Endl(),
  467. TS.Line().Raw(fmt.Sprintf("return this._%s", typ)).Call(paramsHttp...).Endl(),
  468. ).Line())
  469. }