resources.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  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. console.log({ ...this.httpOptions.headers });
  228. opts = Object.assign(
  229. {
  230. headers: new HttpHeaders({
  231. ...this.httpOptions.headers,
  232. }),
  233. params: {},
  234. },
  235. opts
  236. );
  237. // const headers = {
  238. // Authorization: this.Auth.Token(),
  239. // };
  240. opts.headers.set("Authorization", this.Auth.Token());
  241. //this.copyHeader(this.httpOptions.headers, headers);
  242. //this.copyHeader(opts.headers, headers);
  243. const params = opts.params;
  244. // Converte a query para base64
  245. if (params.q) {
  246. params.q = window.btoa(unescape(encodeURIComponent(params.q)));
  247. }
  248. // delete opts.headers;
  249. // delete opts.params;
  250. // const options = Object.assign(
  251. // {
  252. // headers: new HttpHeaders(headers),
  253. // params: new HttpParams({ fromObject: params }),
  254. // },
  255. // opts
  256. // );
  257. // const options = Object.assign({
  258. // headers: new HttpHeaders(hopts),
  259. // params: {}
  260. // }, opts);
  261. // // Converte a query para base64
  262. // if (options.params.q) {
  263. // options.params.q = window.btoa(unescape(encodeURIComponent(options.params.q)));
  264. // }
  265. return opts;
  266. }
  267. options(opts?: HttpOptions): HttpOptions {
  268. opts = Object.assign({
  269. headers: new HttpHeaders(),
  270. params: {},
  271. }, opts);
  272. const headers = {
  273. 'Authorization': this.Auth.Token()
  274. };
  275. this.copyHeader(this.httpOptions.headers, headers);
  276. this.copyHeader(opts.headers, headers);
  277. const params = opts.params;
  278. // Converte a query para base64
  279. if (params.q) {
  280. params.q = window.btoa(unescape(encodeURIComponent(params.q)));
  281. }
  282. delete opts.headers;
  283. delete opts.params;
  284. const options = Object.assign({
  285. headers: new HttpHeaders(headers),
  286. params: new HttpParams({ fromObject: params }),
  287. }, opts);
  288. // const options = Object.assign({
  289. // headers: new HttpHeaders(hopts),
  290. // params: {}
  291. // }, opts);
  292. // // Converte a query para base64
  293. // if (options.params.q) {
  294. // options.params.q = window.btoa(unescape(encodeURIComponent(options.params.q)));
  295. // }
  296. return options;
  297. }
  298. copyHeader(headers, hopts) {
  299. if (!headers) { return; }
  300. headers.keys().map(key => { hopts[key] = headers.get(key); });
  301. }
  302. }`)
  303. // .Injetable("{providedIn: 'root'}").Export().Class().Id(ApiClassName(p)).Block(
  304. // TS.Public().Id("Client").Op(":").Id("Client").Endl(),
  305. // TS.Public().Id("baseURL").Op("=").Lit(p.BaseURL).Endl(),
  306. // TS.Public().Id("Auth").Op(":").Id("Auth").Endl(),
  307. // TS.Public().Id("httpOptions").Id("=").Block(
  308. // TS.Id("headers").Op(":").Raw("new HttpHeaders").Call(TS.Block(
  309. // TS.Raw("'Content-Type': 'application/json',"),
  310. // TS.Raw("'Accept': 'application/json'"),
  311. // )),
  312. // ).Endl(),
  313. // // TS.Public().Id("http").Op(":").Id("HttpClient").Endl(),
  314. // TS.Constructor(
  315. // TS.Public().Id("http").Op(":").Id("HttpClient"),
  316. // ).Block(
  317. // TS.This().Dot("Client").Op("=").New().Id("Client").Call(TS.This()).Endl(),
  318. // // TS.Id("this.http").Op("=").New().Id("Client").Call(TS.This()).Endl(),
  319. // ),
  320. // TS.Id("Options").Params(
  321. // TS.Id("opts").Op(":").Id("ServiceOptions"),
  322. // ).Op(":").Id("{headers: HttpHeaders,params: any}").Block(
  323. // TS.Return().Block(
  324. // TS.Raw("headers: this.httpOptions.headers,"),
  325. // // TS.Raw("params : opts.Params()"),
  326. // TS.Raw("params : opts"),
  327. // ),
  328. // ),
  329. // ).Line()
  330. module.Line().Raw(`
  331. export function ApiOptionsProvider(options) {
  332. console.log('ApiOptionsProvider', options);
  333. return options;
  334. }
  335. @NgModule({
  336. imports: [
  337. CommonModule,
  338. HttpClientModule,
  339. ]
  340. })
  341. `).Export().
  342. Class().
  343. Id(p.Name + "ApiModule").
  344. Block(
  345. TS.Raw(`
  346. static forRoot( options?: `).Raw(ApiClassName(p) + "Options").Raw(` ) : ModuleWithProviders {
  347. // static forRoot( options?:any ) : ModuleWithProviders {
  348. return({
  349. ngModule: `).Raw(p.Name + "ApiModule").Raw(`,
  350. providers: [
  351. {
  352. provide: ApiOptionsParams,
  353. useValue: options,
  354. },
  355. {
  356. provide: `).Raw(ApiClassName(p) + "Options").Raw(`,
  357. useFactory: ApiOptionsProvider,
  358. deps:[ApiOptionsParams]
  359. }
  360. ]
  361. });
  362. }
  363. `),
  364. ).
  365. Line()
  366. return nil
  367. }
  368. // createClientClass cria a classe de cliente que guarda a referencia para todos os servicos da api
  369. func createClientClass(p *Project, file *TS.File) {
  370. var (
  371. angularResourceName string
  372. angularClientStmts = []TS.CodeInterface{}
  373. angularClientConstructor = []TS.CodeInterface{}
  374. )
  375. for _, resource := range p.Resources {
  376. angularResourceName = strings.Title(resource.ID) + "Service"
  377. angularClientStmts = append(
  378. angularClientStmts,
  379. TS.Public().Id(strings.Title(resource.ID)).Op(":").Id(angularResourceName).Endl(),
  380. )
  381. angularClientConstructor = append(
  382. angularClientConstructor,
  383. TS.This().Dot(strings.Title(resource.ID)).Op("=").New().Id(angularResourceName).Call(TS.Id("api")).Endl(),
  384. )
  385. }
  386. angularClientConstructor = append(
  387. angularClientConstructor,
  388. TS.Raw("this.Generic = new ServiceStmt").Call(TS.Id("api")).Endl(),
  389. )
  390. // angularClientStmts = append(
  391. // angularClientStmts,
  392. // TS.Raw(`public onready = new EventEmitter<any>();`),
  393. // )
  394. angularClientStmts = append(
  395. angularClientStmts,
  396. TS.Public().Id("Generic: ServiceStmt").Endl(),
  397. )
  398. angularClientStmts = append(
  399. angularClientStmts,
  400. TS.Constructor(
  401. TS.Protected().Id("api").Op(":").Id("ApiInterface"),
  402. ).Block(angularClientConstructor...),
  403. )
  404. file.Line().Comment(
  405. "Class gerencia todos os serviços da api.",
  406. ).Export().Class().Id("Client").Block(angularClientStmts...)
  407. }
  408. // func GenAngularMethod(p *Project, r *Resource, method *Method, methods *[]TS.CodeInterface, angularResourceName string) {
  409. // params := []TS.CodeInterface{}
  410. // param := "entity"
  411. // methodId := method.ID
  412. // callOptionsParam := ""
  413. // switch strings.ToLower(method.HttpMethod) {
  414. // case "post":
  415. // params = append(params, TS.Id(param).Op(":").Id(method.Entity))
  416. // case "filter":
  417. // // methodId = "get"
  418. // param = ""
  419. // case "get":
  420. // param = ""
  421. // // params = append(params, TS.Id(param).Op(":").String())
  422. // case "get_list":
  423. // param = ""
  424. // // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
  425. // case "patch":
  426. // param = ""
  427. // params = append(params, TS.Id(param).Op(":").Id(method.Entity))
  428. // // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
  429. // case "put":
  430. // params = append(params, TS.Id(param).Op(":").Id(method.Entity))
  431. // callOptionsParam = "{id: entity.id}"
  432. // case "delete":
  433. // // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
  434. // params = append(params, TS.Id(param).Op(":").Id(method.Entity))
  435. // callOptionsParam = "{id: entity.id}"
  436. // param = ""
  437. // default:
  438. // panic(fmt.Sprintf("O método http '%s' para o recurso %s não foi definido!", method.HttpMethod, method.ID))
  439. // }
  440. // // *methods = append(*methods, TS.Id(method.ID).Params(params...).Op(":").Id("Observable<ServiceResponse>").Block(
  441. // *methods = append(*methods, TS.Id(method.ID).Params(params...).Op(":").Id("Observable<ServiceResponse>").Block(
  442. // TS.Return().This().Dot("options").Call(TS.Raw(callOptionsParam)).Dot(methodId).Call(TS.Id(param)).Endl(),
  443. // ).Line())
  444. // }
  445. func GenAngularMethodStmt(p *Project, r *Resource, method *Method, methods *[]TS.CodeInterface, angularResourceName string) {
  446. var (
  447. typ string
  448. params = []TS.CodeInterface{}
  449. paramsHttp = []TS.CodeInterface{}
  450. // id = method.ID
  451. // ret = TS.Id("Observable")
  452. param = "entity"
  453. template string
  454. templateValue interface{}
  455. defined bool
  456. )
  457. if templateValue, defined = method.Custom["ts.template.method"]; defined {
  458. template = templateValue.(string)
  459. } else {
  460. template = method.HttpMethod
  461. }
  462. template = strings.ToLower(template)
  463. switch template {
  464. case "post":
  465. typ = "post"
  466. params = append(params, TS.Id(param).Op(":").Id(method.Entity))
  467. // ret.TypeAliase(method.Entity)
  468. case "filter":
  469. typ = "get"
  470. // ret.TypeAliase(method.Entity)
  471. param = ""
  472. // params = append(params, TS.Id(param).Op(":").String())
  473. case "get":
  474. typ = "get"
  475. // ret.TypeAliase(method.Entity)
  476. param = ""
  477. case "put":
  478. typ = "put"
  479. // ret.TypeAliase(method.Entity)
  480. params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
  481. case "delete":
  482. typ = "delete"
  483. // ret.TypeAliase(method.Entity)
  484. // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
  485. case "patch":
  486. typ = "patch"
  487. params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
  488. // ret.TypeAliase(method.Entity)
  489. // params = append(params, TS.Id(param).Op(":").Id(method.Entity).Op("|").String())
  490. case "sse":
  491. typ = "event"
  492. default:
  493. panic(fmt.Sprintf("Method '%s' template not defined!", template))
  494. }
  495. path := method.Path
  496. for _, parameter := range method.Parameters {
  497. alias := getCustom(parameter.Custom, "ts.api.alias")
  498. if aliasString, ok := alias.(string);ok {
  499. path = strings.ReplaceAll(
  500. path,
  501. fmt.Sprintf("{%s}", parameter.ID),
  502. fmt.Sprintf("{%s}", aliasString),
  503. )
  504. }
  505. }
  506. // paramsHttp = append(paramsHttp, TS.Raw("this.Url(url, opt.params)"))
  507. paramsHttp = append(paramsHttp, TS.Raw(fmt.Sprintf("`${this.api.apiOptions.BASE_URL}%s`", path)))
  508. switch typ {
  509. case "post", "put", "patch":
  510. paramsHttp = append(paramsHttp, TS.Id("entity"))
  511. }
  512. params = append(params, TS.Id("opt?: HttpOptions"))
  513. paramsHttp = append(paramsHttp, TS.Id("opt"))
  514. // *methods = append(*methods, TS.Id(method.ID).Params(params...).Op(":").Id("Observable<ServiceResponse>").Block(
  515. *methods = append(*methods, TS.Id(method.ID).Params(params...).Block(
  516. // TS.Raw("let opt = this.api.Options(this.options)").Endl(),
  517. // TS.Raw("let url = ").Lit(p.GetUrlFromMethod(method)).Endl(),
  518. // TS.Raw("let url = ").Raw(fmt.Sprintf("`${this.api.BASE_URL}%s`", method.Path)).Endl(),
  519. TS.Line().Raw(fmt.Sprintf("return this._%s", typ)).Call(paramsHttp...).Endl(),
  520. ).Line())
  521. }