Open GustavoBoaz opened 2 years ago
Validar por completo entregas no Git:
Resources: application.properties
springdoc.packagesToScan=
Ambiente Heroku: spring.profiles.active=prod ou Ambiente LocalHost: spring.profiles.active=dev
spring.profiles.active=prod
spring.profiles.active=dev
ex.
spring.profiles.active=prod springdoc.api-docs.path=/v3/api-docs springdoc.swagger-ui.path=/swagger-ui.html springdoc.swagger-ui.operationsSorter=method springdoc.swagger-ui.disable-swagger-default-url=true springdoc.swagger-ui.use-root-path=true springdoc.packagesToScan=
application-dev.properties
spring.jpa.hibernate.ddl-auto=update spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/db_blog?createDatabaseIfNotExist=true spring.datasource.username= spring.datasource.password= spring.datasource.driver-class-name =com.mysql.jdbc.Driver spring.jpa.show-sql=true
application-prod.properties
spring.jpa.generate-ddl=true spring.datasource.url=${JDBC_DATASOURCE_URL} spring.jpa.show-sql=true spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=Brazil/East
Models: Usuario
@OneToMany
@JsonIgnoreProperties
Voce pode validar o exemplo abaixo:
import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @Entity @Table(name = "tb_usuarios") public class Usuario { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotNull(message = "O atributo Nome é Obrigatório!") private String nome; @Size(max = 5000, message = "O link da foto não pode ser maior do que 5000 caractéres") private String foto; @NotNull(message = "O atributo Usuário é Obrigatório!") @Email(message = "O atributo Usuário deve ser um email válido!") private String usuario; @NotBlank(message = "O atributo Senha é Obrigatório!") @Size(min = 8, message = "A Senha deve ter no mínimo 8 caracteres") private String senha; @OneToMany(mappedBy = "usuario", cascade = CascadeType.REMOVE) @JsonIgnoreProperties("usuario") private List<Postagem> postagem; public Usuario(Long id, String nome, String foto, String usuario, String senha) { this.id = id; this.nome = nome; this.foto = foto; this.usuario = usuario; this.senha = senha; } public Usuario() {} /* Insira os Getters and Setters */ public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getFoto() { return foto; } public void setFoto(String foto) { this.foto = foto; } public String getUsuario() { return usuario; } public void setUsuario(String usuario) { this.usuario = usuario; } public String getSenha() { return senha; } public void setSenha(String senha) { this.senha = senha; } public List<Postagem> getPostagem() { return postagem; } public void setPostagem(List<Postagem> postagem) { this.postagem = postagem; } }
Postagem
@ManyToOne
import java.time.LocalDate; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import org.hibernate.annotations.UpdateTimestamp; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @Entity @Table(name = "tb_postagem") public class Postagem { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank(message = "O atributo título é Obrigatório!") @Size(min = 5, max = 100, message = "O atributo título deve conter no mínimo 05 e no máximo 100 caracteres") private String titulo; @NotBlank(message = "O atributo texto é Obrigatório!") @Size(min = 10, max = 1000, message = "O atributo texto deve conter no mínimo 10 e no máximo 1000 caracteres") private String texto; @UpdateTimestamp private LocalDate data; @ManyToOne @JsonIgnoreProperties("postagem") private Tema tema; @ManyToOne @JsonIgnoreProperties("postagem") private Usuario usuario; /*Insira os Getters and Setters*/ public void setId(Long id) { this.id = id; } public Long getId() { return id; } public String getTitulo() { return titulo; } public void setTitulo(String titulo) { this.titulo = titulo; } public String getTexto() { return texto; } public void setTexto(String texto) { this.texto = texto; } public LocalDate getData() { return data; } public void setData(LocalDate data) { this.data = data; } public Tema getTema() { return tema; } public void setTema(Tema tema) { this.tema = tema; } public Usuario getUsuario() { return usuario; } public void setUsuario(Usuario usuario) { this.usuario = usuario; } }
Tema
import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.validation.constraints.NotNull; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @Entity @Table(name = "tb_temas") public class Tema { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotNull(message = "O atributo Descrição é obrigatório") private String descricao; @OneToMany(mappedBy = "tema", cascade = CascadeType.REMOVE) @JsonIgnoreProperties("tema") private List<Postagem> postagem; /*Insira os Getters and Setters*/ public long getId() { return id; } public void setId(long id) { this.id = id; } public String getDescricao() { return descricao; } public void setDescricao(String descricao) { this.descricao = descricao; } public List<Postagem> getPostagem() { return postagem; } public void setPostagem(List<Postagem> postagem) { this.postagem = postagem; } }
DTOs: UserLogin
public class UsuarioLogin { private long id; private String nome; private String usuario; private String foto; private String senha; private String token; /* Insira os Getters and Setters */ public long getId() { return id; } public void setId(long id) { this.id = id; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getUsuario() { return usuario; } public String getFoto() { return foto; } public void setFoto(String foto) { this.foto = foto; } public String getToken() { return token; } public void setToken(String token) { this.token = token; } public void setUsuario(String usuario) { this.usuario = usuario; } public String getSenha() { return senha; } public void setSenha(String senha) { this.senha = senha; } }
Repository: UsuarioRepository
@Repository
import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface UsuarioRepository extends JpaRepository<Usuario, Long>{ public Optional<Usuario> findByUsuario(String usuario); public List<Usuario> findAllByNomeContainingIgnoreCase(String nome); }
PostagenRepository
import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface PostagemRepository extends JpaRepository<Postagem, Long> { public List<Postagem> findAllByTituloContainingIgnoreCase(String titulo); }
TemaRepository
import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface TemaRepository extends JpaRepository<Tema, Long> { public List<Tema> findAllByDescricaoContainingIgnoreCase(String descricao); }
Services: UsuarioService
import java.nio.charset.Charset; import java.util.Optional; import org.apache.commons.codec.binary.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.web.server.ResponseStatusException; @Service public class UsuarioService { @Autowired private UsuarioRepository usuarioRepository; public Optional<Usuario> cadastrarUsuario(Usuario usuario) { if (usuarioRepository.findByUsuario(usuario.getUsuario()).isPresent()) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Usuário já existe!", null); usuario.setSenha(criptografarSenha(usuario.getSenha())); return Optional.of(usuarioRepository.save(usuario)); } public Optional<Usuario> atualizarUsuario(Usuario usuario) { if (usuarioRepository.findById(usuario.getId()).isPresent()) { Optional<Usuario> buscaUsuario = usuarioRepository.findByUsuario(usuario.getUsuario()); if (buscaUsuario.isPresent()) { if (buscaUsuario.get().getId() != usuario.getId()) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Usuário já existe!", null); } usuario.setSenha(criptografarSenha(usuario.getSenha())); return Optional.of(usuarioRepository.save(usuario)); } throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Usuário não encontrado!", null); } public Optional<UsuarioLogin> logarUsuario(Optional<UsuarioLogin> usuarioLogin) { Optional<Usuario> usuario = usuarioRepository.findByUsuario(usuarioLogin.get().getUsuario()); if (usuario.isPresent()) { if (compararSenhas(usuarioLogin.get().getSenha(), usuario.get().getSenha())) { usuarioLogin.get().setId(usuario.get().getId()); usuarioLogin.get().setNome(usuario.get().getNome()); usuarioLogin.get().setFoto(usuario.get().getFoto()); usuarioLogin.get().setToken(generatorBasicToken(usuarioLogin.get().getUsuario(), usuarioLogin.get().getSenha())); usuarioLogin.get().setSenha(usuario.get().getSenha()); return usuarioLogin; } } throw new ResponseStatusException( HttpStatus.UNAUTHORIZED, "Usuário ou senha inválidos!", null); } private String criptografarSenha(String senha) { BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String senhaEncoder = encoder.encode(senha); return senhaEncoder; } private boolean compararSenhas(String senhaDigitada, String senhaBanco) { BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); return encoder.matches(senhaDigitada, senhaBanco); } private String generatorBasicToken(String email, String password) { String structure = email + ":" + password; byte[] structureBase64 = Base64.encodeBase64(structure.getBytes(Charset.forName("US-ASCII"))); return "Basic " + new String(structureBase64); } }
Controller: UsuarioController
@CrossOrigin
@CrossOrigin(origins = "*", allowedHeaders = "*")
@Valid
import java.util.List; import java.util.Optional; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/usuarios") @CrossOrigin(origins = "*", allowedHeaders = "*") public class UsuarioController { @Autowired private UsuarioService service; @Autowired private UsuarioRepository repository; @GetMapping("/all") public ResponseEntity <List<Usuario>> getAll() { return ResponseEntity.ok(repository.findAll()); } @GetMapping("/{id}") public ResponseEntity<Usuario> getById(@PathVariable long id) { return repository.findById(id) .map(resp -> ResponseEntity.ok(resp)) .orElse(ResponseEntity.notFound().build()); } @PostMapping("/logar") public ResponseEntity<UsuarioLogin> autenticationUsuario(@RequestBody Optional<UsuarioLogin> usuario) { return service.logarUsuario(usuario) .map(resp -> ResponseEntity.ok(resp)) .orElse(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()); } @PostMapping("/cadastrar") public ResponseEntity<Usuario> postUsuario(@Valid @RequestBody Usuario usuario) { return service.cadastrarUsuario(usuario) .map(resp -> ResponseEntity.status(HttpStatus.CREATED).body(resp)) .orElse(ResponseEntity.status(HttpStatus.BAD_REQUEST).build()); } @PutMapping("/atualizar") public ResponseEntity<Usuario> putUsuario(@Valid @RequestBody Usuario usuario){ return service.atualizarUsuario(usuario) .map(resp -> ResponseEntity.status(HttpStatus.OK).body(resp)) .orElse(ResponseEntity.status(HttpStatus.BAD_REQUEST).build()); } }
PostagemController
import java.util.List; import java.util.Optional; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ResponseStatusException; @RestController @RequestMapping("/postagens") @CrossOrigin(value = "*", allowedHeaders = "*") public class PostagemController { @Autowired private PostagemRepository repository; @GetMapping public ResponseEntity<List<Postagem>> getAll(){ return ResponseEntity.ok(repository.findAll()); } @GetMapping("/{id}") public ResponseEntity<Postagem> getById(@PathVariable long id){ return repository.findById(id).map(resp -> ResponseEntity.ok(resp)) .orElse(ResponseEntity.status(HttpStatus.BAD_REQUEST).build()); } @GetMapping("/titulo/{titulo}") public ResponseEntity<List<Postagem>> getByTitle(@PathVariable String titulo){ return ResponseEntity.ok(repository.findAllByTituloContainingIgnoreCase(titulo)); } @PostMapping public ResponseEntity<Postagem> post(@Valid @RequestBody Postagem postagem){ return ResponseEntity.status(HttpStatus.CREATED) .body(repository.save(postagem)); } @PutMapping public ResponseEntity<Postagem> put(@Valid @RequestBody Postagem postagem){ return repository.findById(postagem.getId()) .map(resp -> ResponseEntity.status(HttpStatus.OK).body(repository.save(postagem))) .orElse(ResponseEntity.status(HttpStatus.BAD_REQUEST).build()); } @ResponseStatus(HttpStatus.NO_CONTENT) @DeleteMapping("/{id}") public void delete(@PathVariable long id) { Optional<Postagem> post = repository.findById(id); if(post.isEmpty()) throw new ResponseStatusException(HttpStatus.BAD_REQUEST); repository.deleteById(id); } }
TemaController
import java.util.List; import java.util.Optional; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ResponseStatusException; @RestController @RequestMapping("/temas") @CrossOrigin(origins = "*", allowedHeaders = "*") public class TemaController { @Autowired private TemaRepository repository; @GetMapping public ResponseEntity<List<Tema>> getAll(){ return ResponseEntity.ok(repository.findAll()); } @GetMapping("/{id}") public ResponseEntity<Tema> getById(@PathVariable long id){ return repository.findById(id).map(resp -> ResponseEntity.ok(resp)) .orElse(ResponseEntity.status(HttpStatus.BAD_REQUEST).build()); } @GetMapping("/descricao/{descricao}") public ResponseEntity<List<Tema>> getByTitle(@PathVariable String descricao){ return ResponseEntity.ok(repository.findAllByDescricaoContainingIgnoreCase(descricao)); } @PostMapping public ResponseEntity<Tema> post(@Valid @RequestBody Tema tema){ return ResponseEntity.status(HttpStatus.CREATED) .body(repository.save(tema)); } @PutMapping public ResponseEntity<Tema> put(@Valid @RequestBody Tema tema){ return repository.findById(tema.getId()) .map(resp -> ResponseEntity.status(HttpStatus.CREATED).body(repository.save(tema))) .orElse(ResponseEntity.status(HttpStatus.BAD_REQUEST).build()); } @ResponseStatus(HttpStatus.NO_CONTENT) @DeleteMapping("/{id}") public void delete(@PathVariable long id) { Optional<Tema> tema = repository.findById(id); if(tema.isEmpty()) throw new ResponseStatusException(HttpStatus.BAD_REQUEST); repository.deleteById(id); } }
Security: UserDetailsImpl: implementar de acordo com o esquema abaixo
import java.util.Collection; import java.util.List; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; public class UserDetailImpl implements UserDetails { private static final long serialVersionUID = 1L; private String userName; private String password; private List<GrantedAuthority> authorities; public UserDetailImpl(Usuario user) { this.userName= user.getUsuario(); this.password = user.getSenha(); } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public String getPassword() { return password; } @Override public String getUsername() { return userName; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
UserDetailsServiceImpl: Implementar conforme esquema abaixo
import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @Service public class UserDetailsServiceImpl implements UserDetailsService { private @Autowired UsuarioRepository repository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Optional<UserModel> optional = repository.findByUsuario(username); if (optional.isPresent()) { return new UserDetailImpl(optional.get()); } else { throw new UsernameNotFoundException("Usuario não existe"); } } }
BasicSecurityConfig: Implementar conforme esquema abaixo
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @EnableWebSecurity public class BasicSecurityConfig extends WebSecurityConfigurerAdapter { private @Autowired UserDetailsServiceImpl service; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(service); auth.inMemoryAuthentication().withUser("boaz").password(passwordEncoder().encode("boaz")) .authorities("ROLE_ADMIN"); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(HttpMethod.POST, "/usuarios/cadastrar").permitAll() .antMatchers(HttpMethod.POST, "/usuarios/logar").permitAll() .anyRequest().authenticated() .and().httpBasic() .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and().cors() .and().csrf().disable(); } }
Configuration: Swagger: Implementar conforme abaixo, não esquecer de fornecer seus dados para Open API
import org.springdoc.core.customizers.OpenApiCustomiser; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import io.swagger.v3.oas.models.ExternalDocumentation; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; @Configuration public class SwaggerConfig { @Bean public OpenAPI springOpenAPI() { return new OpenAPI() .info(new Info() .title("Project Gees") .description("This is a Ecomerce project") .version("v0.0.1") .license(new License() .name("Gees Brazil") .url("<https://brazil.generation.org/>")) .contact(new Contact() .name("Github Boaz") .url("<https://github.com/GustavoBoaz/>") .email("gustavo.boaz@hotmail.com"))) .externalDocs(new ExternalDocumentation() .description("Github Project") .url("<https://github.com/GustavoBoaz/project_Gees>")); } private ApiResponse createApiResponse(String message) { return new ApiResponse().description(message); } @Bean public OpenApiCustomiser customerGlobalResponseStatus() { return openApi -> { openApi.getPaths().values().forEach(pathItem -> pathItem.readOperations().forEach(operation -> { ApiResponses api = operation.getResponses(); api.addApiResponse("200", createApiResponse("Sucess!")); api.addApiResponse("201", createApiResponse("Created!")); api.addApiResponse("400", createApiResponse("Request error!")); api.addApiResponse("401", createApiResponse("Not authorized!")); api.addApiResponse("500", createApiResponse("Internal server Error!")); })); }; } }
Ajustes realizados 😄
Validar por completo entregas no Git:
Resources: application.properties
springdoc.packagesToScan=
Ambiente Heroku:
spring.profiles.active=prod
ou Ambiente LocalHost:spring.profiles.active=dev
ex.
application-dev.properties
ex.
application-prod.properties
springdoc.packagesToScan=
ex.
Models: Usuario
@OneToMany
com postagen@JsonIgnoreProperties
no atributo postagemVoce pode validar o exemplo abaixo:
Postagem
@ManyToOne
com Usuario@ManyToOne
com Tema@JsonIgnoreProperties
no atributo usuario@JsonIgnoreProperties
no atributo temaVoce pode validar o exemplo abaixo:
Tema
@OneToMany
com Postagem@JsonIgnoreProperties
no atributo postagemVoce pode validar o exemplo abaixo:
DTOs: UserLogin
Voce pode validar o exemplo abaixo:
Repository: UsuarioRepository
@Repository
Voce pode validar o exemplo abaixo:
PostagenRepository
@Repository
Voce pode validar o exemplo abaixo:
TemaRepository
@Repository
Voce pode validar o exemplo abaixo:
Services: UsuarioService
Voce pode validar o exemplo abaixo:
Controller: UsuarioController
@CrossOrigin
esta correta@CrossOrigin(origins = "*", allowedHeaders = "*")
@Valid
nos metodos post e putVoce pode validar o exemplo abaixo:
PostagemController
@CrossOrigin
esta correta@CrossOrigin(origins = "*", allowedHeaders = "*")
@Valid
nos metodos post e putVoce pode validar o exemplo abaixo:
TemaController
@CrossOrigin
esta correta@CrossOrigin(origins = "*", allowedHeaders = "*")
@Valid
nos metodos post e putVoce pode validar o exemplo abaixo:
Security: UserDetailsImpl: implementar de acordo com o esquema abaixo
UserDetailsServiceImpl: Implementar conforme esquema abaixo
BasicSecurityConfig: Implementar conforme esquema abaixo
Configuration: Swagger: Implementar conforme abaixo, não esquecer de fornecer seus dados para Open API