Compare commits

...

16 commits

22 changed files with 408 additions and 107 deletions

4
.gitignore vendored
View file

@ -67,3 +67,7 @@ coverage
test-results/
playwright-report/
.env
*Rascunho*
http_client/

55
pom.xml
View file

@ -9,13 +9,15 @@
<properties>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.24.5</quarkus.platform.version>
<compiler-plugin.version>3.11.0</compiler-plugin.version>
<quarkus.platform.version>3.25.0</quarkus.platform.version>
<compiler-plugin.version>3.14.0</compiler-plugin.version>
<surefire-plugin.version>3.1.2</surefire-plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.parameters>true</maven.compiler.parameters>
<lombok.version>1.18.38</lombok.version>
<hibernate.version></hibernate.version>
</properties>
<dependencyManagement>
@ -31,15 +33,41 @@
</dependencyManagement>
<dependencies>
<!-- Geração de código -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.38</version>
<version>${lombok.version}</version>
</dependency>
<!-- HTTP -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<!-- Validação -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
<!-- Persistência -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>jakarta.data</groupId>
<artifactId>jakarta.data-api</artifactId>
</dependency>
<!-- Testes -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
@ -50,6 +78,7 @@
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@ -57,6 +86,20 @@
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<parameters>true</parameters>
<annotationProcessorPaths>
<path>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-processor</artifactId>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
@ -109,7 +152,9 @@
</goals>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
<native.image.path>
${project.build.directory}/${project.build.finalName}-runner
</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
@ -121,4 +166,4 @@
</build>
</profile>
</profiles>
</project>
</project>

View file

@ -15,6 +15,7 @@
},
"dependencies": {
"@primeuix/themes": "^1.2.2",
"primeicons": "^7.0.0",
"primevue": "^4.3.6",
"vue": "^3.5.17"
},

View file

@ -11,6 +11,9 @@ importers:
'@primeuix/themes':
specifier: ^1.2.2
version: 1.2.2
primeicons:
specifier: ^7.0.0
version: 7.0.0
primevue:
specifier: ^4.3.6
version: 4.3.6(vue@3.5.18(typescript@5.8.3))
@ -1670,6 +1673,9 @@ packages:
resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==}
engines: {node: '>=18'}
primeicons@7.0.0:
resolution: {integrity: sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==}
primevue@4.3.6:
resolution: {integrity: sha512-Wwg2dH6pBmOdkj9L/OnrCQf9AKPHfY5CcfnDyWeh0tNlR+XjYKGl8qvMdJOvGO9jjg6UdsX5MSaU8vDDsSG+sg==}
engines: {node: '>=12.11.0'}
@ -3708,6 +3714,8 @@ snapshots:
dependencies:
parse-ms: 4.0.0
primeicons@7.0.0: {}
primevue@4.3.6(vue@3.5.18(typescript@5.8.3)):
dependencies:
'@primeuix/styled': 0.7.1

View file

@ -2,7 +2,6 @@
import { ref, type Ref } from 'vue';
import TabelaDeJogos from './components/TabelaDeJogos.vue';
import type Jogo from './modelo/Jogo';
import { Button } from 'primevue';
const jogos: Ref<Jogo[]> = ref([]);

View file

@ -12,6 +12,7 @@ defineProps<{
<Column field="id" sortable header="ID" />
<Column field="nome" sortable header="Nome" />
<Column field="estado" sortable header="Estado" />
<Column field="preco" sortable header="Preço" />
</DataTable>
</template>

View file

@ -1,11 +0,0 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import HelloWorld from '../HelloWorld.vue'
describe('HelloWorld', () => {
it('renders properly', () => {
const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } })
expect(wrapper.text()).toContain('Hello Vitest')
})
})

View file

@ -2,8 +2,13 @@ export default class Jogo {
constructor(
public id: string,
public nome: string,
public estado: Estado
public ano: number,
public estado: Estado,
public loja: string,
public preco: Preco
) { }
}
export type Estado = 'NOVO' | 'ZERADO';
export type Preco = 'VALOR' | 'DE_GRACA' | 'DESCONHECIDO' | 'PRESENTE' | number;
export type Estado = 'NOVO' | 'JOGUEI' | 'SATISFEITO' | 'PALHA' | 'ETERNO';

View file

@ -0,0 +1,20 @@
package casa.sotu.organizajogos.api.excecao;
public class OrganizaJogosExcecao extends RuntimeException {
public OrganizaJogosExcecao(String mensagem, Throwable causa) {
super(mensagem, causa);
}
public OrganizaJogosExcecao(Throwable causa) {
super(causa);
}
public OrganizaJogosExcecao(String mensagem) {
super(mensagem);
}
public OrganizaJogosExcecao() {
}
}

View file

@ -0,0 +1,18 @@
package casa.sotu.organizajogos.api.excecao;
import java.math.BigDecimal;
import casa.sotu.organizajogos.api.modelo.Preco.TipoDePreco;
import lombok.Getter;
@Getter
public class PrecoInvalidoExcecao extends OrganizaJogosExcecao {
private final BigDecimal valor;
private final TipoDePreco tipo;
public PrecoInvalidoExcecao(BigDecimal valor, TipoDePreco tipo) {
this.tipo = tipo;
this.valor = valor;
}
}

View file

@ -0,0 +1,36 @@
package casa.sotu.organizajogos.api.json;
import java.io.IOException;
import java.math.BigDecimal;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import casa.sotu.organizajogos.api.modelo.Preco;
import casa.sotu.organizajogos.api.modelo.Preco.TipoDePreco;
public class DesserializadorDePreco extends StdDeserializer<Preco> {
protected DesserializadorDePreco() {
this(Preco.class);
}
protected DesserializadorDePreco(Class<?> vc) {
super(vc);
}
@Override
public Preco deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
String value = p.getValueAsString();
try {
return new Preco(new BigDecimal(value));
} catch (NumberFormatException e) {
return new Preco(TipoDePreco.valueOf(value.toUpperCase()));
}
}
}

View file

@ -0,0 +1,31 @@
package casa.sotu.organizajogos.api.json;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import casa.sotu.organizajogos.api.modelo.Preco;
import casa.sotu.organizajogos.api.modelo.Preco.TipoDePreco;
public class SerializadorDePreco extends StdSerializer<Preco> {
protected SerializadorDePreco() {
this(Preco.class);
}
protected SerializadorDePreco(Class<Preco> t) {
super(t);
}
@Override
public void serialize(Preco preco, JsonGenerator generator, SerializerProvider provider) throws IOException {
if (preco.tipo() == TipoDePreco.VALOR) {
generator.writeString(preco.valor().toString());
} else {
generator.writeString(preco.tipo().name());
}
}
}

View file

@ -1,5 +1,5 @@
package casa.sotu.organizajogos.api.modelo;
public enum Estado {
NOVO, ZERADO;
NOVO, JOGUEI, SATISFEITO, ETERNO;
}

View file

@ -1,12 +1,41 @@
package casa.sotu.organizajogos.api.modelo;
import java.time.Year;
import java.util.UUID;
import lombok.Data;
import casa.sotu.organizajogos.api.modelo.Preco.TipoDePreco;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
@Data
@Getter
@Setter
@Entity
public class Jogo {
@Id
private UUID id;
@NotBlank
private String nome;
private Year ano;
@NotNull
@Enumerated(EnumType.STRING)
private Estado estado;
@NotBlank
private String loja;
@NotNull
@AttributeOverride(name = "tipo", column = @Column(name = "tipo_de_preco", nullable = false))
private Preco preco = new Preco(TipoDePreco.DESCONHECIDO);
}

View file

@ -0,0 +1,48 @@
package casa.sotu.organizajogos.api.modelo;
import java.math.BigDecimal;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import casa.sotu.organizajogos.api.excecao.PrecoInvalidoExcecao;
import casa.sotu.organizajogos.api.json.DesserializadorDePreco;
import casa.sotu.organizajogos.api.json.SerializadorDePreco;
import jakarta.persistence.Embeddable;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
@Embeddable
@JsonSerialize(using = SerializadorDePreco.class)
@JsonDeserialize(using = DesserializadorDePreco.class)
public record Preco(
@Min(0) BigDecimal valor,
@NotNull @Enumerated(EnumType.STRING) TipoDePreco tipo) {
public Preco {
if (tipo == TipoDePreco.VALOR) {
if (valor == null) {
throw new PrecoInvalidoExcecao(valor, tipo);
}
} else {
valor = null;
}
}
public Preco(BigDecimal valor) {
this(valor, TipoDePreco.VALOR);
}
public Preco(TipoDePreco tipo) {
this(null, tipo);
}
public enum TipoDePreco {
VALOR,
DE_GRACA,
DESCONHECIDO,
PRESENTE;
}
}

View file

@ -1,31 +1,11 @@
package casa.sotu.organizajogos.api.persistencia;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import casa.sotu.organizajogos.api.modelo.Jogo;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.data.repository.CrudRepository;
import jakarta.data.repository.Repository;
@ApplicationScoped
public class ArmarioJogo {
private Map<UUID, Jogo> jogos = new HashMap<>();
public void criar(Jogo jogo) {
jogo.setId(UUID.randomUUID());
jogos.put(jogo.getId(), jogo);
}
public Jogo pegar(UUID id) {
return jogos.get(id);
}
public void remover(UUID id) {
jogos.remove(id);
}
public List<Jogo> listar() {
return List.copyOf(jogos.values());
}
@Repository
public interface ArmarioJogo extends CrudRepository<Jogo, UUID> {
}

View file

@ -3,10 +3,16 @@ package casa.sotu.organizajogos.api.recursos;
import java.util.List;
import java.util.UUID;
import org.jboss.resteasy.reactive.ResponseStatus;
import org.jboss.resteasy.reactive.RestResponse.StatusCode;
import casa.sotu.organizajogos.api.modelo.Jogo;
import casa.sotu.organizajogos.api.persistencia.ArmarioJogo;
import jakarta.data.exceptions.OptimisticLockingFailureException;
import jakarta.validation.Valid;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
@ -14,41 +20,49 @@ import jakarta.ws.rs.PathParam;
@Path("jogos")
public class RecursoJogo {
private ArmarioJogo armarioJogo;
public RecursoJogo(ArmarioJogo armarioJogo) {
this.armarioJogo = armarioJogo;
}
@GET
public List<Jogo> listaJogos() {
return armarioJogo.listar();
return armarioJogo.findAll().toList();
}
@GET
@Path("{id}")
public Jogo pegaJogo(@PathParam("id") UUID id) {
return this.armarioJogo.pegar(id);
return this.armarioJogo.findById(id).orElseThrow(NotFoundException::new);
}
@POST
public void criaJogo(Jogo jogo) {
this.armarioJogo.criar(jogo);
@ResponseStatus(StatusCode.CREATED)
public UUID criaJogo(@Valid Jogo jogo) {
jogo.setId(UUID.randomUUID());
this.armarioJogo.insert(jogo);
return jogo.getId();
}
@PUT
@Path("{id}")
public void atualizaJogo(@PathParam("id") UUID id, Jogo jogo) {
Jogo atual = this.armarioJogo.pegar(id);
atual.setNome(jogo.getNome());
atual.setEstado(jogo.getEstado());
@ResponseStatus(StatusCode.OK)
public void atualizaJogo(@PathParam("id") UUID id, @Valid Jogo jogo) {
jogo.setId(id);
try {
armarioJogo.update(jogo);
} catch (OptimisticLockingFailureException e) {
throw new NotFoundException(e);
}
}
@DELETE
@Path("{id}")
@ResponseStatus(StatusCode.OK)
public void remove(@PathParam("id") UUID id) {
this.armarioJogo.remover(id);
this.armarioJogo.deleteById(id);
}
}

View file

@ -1,2 +1,11 @@
# Quarkus Configuration file
# key = value
quarkus.datasource.db-kind=pg
quarkus.datasource.username=postgres
quarkus.datasource.password=${POSTGRES_PASSWORD}
quarkus.datasource.jdbc.url=jdbc:postgresql://${POSTGRES_URL}/
quarkus.hibernate-orm.schema-management.strategy=update
%dev.quarkus.hibernate-orm.schema-management.strategy=drop-and-create
%dev.quarkus.hibernate-orm.dev-ui.allow-hql=true
%dev.quarkus.datasource.dev-ui.allowed-db-host=*
%dev.quarkus.hibernate-orm.sql-load-script=populacao.sql

View file

@ -0,0 +1,108 @@
INSERT INTO public.jogo (
id,
ano,
valor,
estado,
loja,
nome,
tipo_de_preco
)
VALUES (
'7942e64a-878c-44fb-b6b7-646edddf5d02'::uuid,
2000,
NULL,
'JOGUEI',
'STEAM',
'Cavaleiros de Marta',
'DE_GRACA'
),
(
'fdb23f2a-faeb-482d-a05c-9d2ee9c28295'::uuid,
2001,
85.95,
'NOVO',
'STEAM',
'Cavaleiros de Marta 2',
'VALOR'
),
(
'aef5aa80-0557-4f31-a627-e53f9c741d59'::uuid,
1995,
10.00,
'SATISFEITO',
'GOG',
'Em Busca do Rodo Perdido',
'VALOR'
),
(
'10fde0ae-530f-46b1-a9a4-74efaf50d137'::uuid,
2000,
NULL,
'SATISFEITO',
'XBOX',
'Sábado de Sol',
'DESCONHECIDO'
),
(
'69282ca9-f722-4758-af46-d6be4f70a651'::uuid,
2007,
NULL,
'ETERNO',
'GOG',
'Pergaminho 4',
'DESCONHECIDO'
),
(
'aaef0acc-c6bc-4209-b12e-c1dbc6b8ac39'::uuid,
2012,
NULL,
'ETERNO',
'PS3',
'Pergaminho 5',
'PRESENTE'
),
(
'2c9028a3-65e6-43e7-a3ac-a198c9970206'::uuid,
2020,
30.00,
'SATISFEITO',
'GOG',
'GSP 3',
'VALOR'
),
(
'c4c8b05c-5b9c-43fa-97ae-f46fdd182cd8'::uuid,
2020,
23.48,
'JOGUEI',
'XBOX',
'Laehop',
'VALOR'
),
(
'61819dbc-0eca-431d-b4e4-c651be8fe056'::uuid,
2002,
500.00,
'JOGUEI',
'WII',
'Caríssimo, O Jogo',
'VALOR'
),
(
'2976135b-6270-45ea-9beb-7431df88eac4'::uuid,
2010,
NULL,
'NOVO',
'STEAM',
'Valeu, Mamãe',
'PRESENTE'
),
(
'82f59d12-f89c-4015-abcc-d0998376a6e8'::uuid,
2023,
NULL,
'SATISFEITO',
'EPIC',
'Injeção Na Testa',
'DE_GRACA'
);

View file

@ -1,9 +0,0 @@
package org.acme.getting.started;
import io.quarkus.test.junit.QuarkusIntegrationTest;
@QuarkusIntegrationTest
public class GreetingResourceIT extends GreetingResourceTest {
// Execute the same tests but in native mode.
}

View file

@ -1,35 +0,0 @@
package org.acme.getting.started;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;
import java.util.UUID;
import org.junit.jupiter.api.Test;
import io.quarkus.test.junit.QuarkusTest;
@QuarkusTest
public class GreetingResourceTest {
@Test
public void testHelloEndpoint() {
given()
.when().get("/hello")
.then()
.statusCode(200)
.body(is("hello"));
}
@Test
public void testGreetingEndpoint() {
String uuid = UUID.randomUUID().toString();
given()
.pathParam("name", uuid)
.when().get("/hello/greeting/{name}")
.then()
.statusCode(200)
.body(is("hello " + uuid));
}
}