mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 15:35:09 +00:00
se creo el endpoint para la vacuna y el smart contract
This commit is contained in:
parent
07d5d9e5f2
commit
44e3f4210d
5 changed files with 373 additions and 0 deletions
|
|
@ -31,6 +31,22 @@ type Receta struct {
|
|||
ExpectedSupplyDuration string `json:"expectedSupplyDuration"`
|
||||
}
|
||||
|
||||
type Vacuna struct {
|
||||
ID string `json:"id"` // identificador único para el ledger
|
||||
Identificador string `json:"identificador"`
|
||||
Status string `json:"status"` // podés validarlo con enums si querés
|
||||
StatusChange string `json:"statusChange"` // como string (ISO8601)
|
||||
StatusReason string `json:"statusReason"`
|
||||
VaccinateCode string `json:"vaccinateCode"`
|
||||
AdministradedProduct string `json:"administradedProduct"`
|
||||
Manufacturer string `json:"manufacturer"`
|
||||
LotNumber string `json:"lotNumber"`
|
||||
ExpirationDate string `json:"expirationDate"` // como string ISO8601
|
||||
DniPaciente string `json:"dniPaciente"`
|
||||
Reactions string `json:"reactions"` // puede ser un string o una estructura si querés después
|
||||
}
|
||||
|
||||
|
||||
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
|
||||
recetas := []Receta{
|
||||
{
|
||||
|
|
@ -282,3 +298,86 @@ func (s *SmartContract) GetRecetasPorDniYEstado(ctx contractapi.TransactionConte
|
|||
|
||||
return recetasFiltradas, nil
|
||||
}
|
||||
|
||||
func (s *SmartContract) CreateVacuna(ctx contractapi.TransactionContextInterface, vacuna Vacuna) error {
|
||||
exists, err := s.VacunaExists(ctx, vacuna.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return fmt.Errorf("la vacuna %s ya existe", vacuna.ID)
|
||||
}
|
||||
|
||||
vacunaJSON, err := json.Marshal(vacuna)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.GetStub().PutState(vacuna.ID, vacunaJSON)
|
||||
}
|
||||
|
||||
func (s *SmartContract) VacunaExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
|
||||
vacunaJSON, err := ctx.GetStub().GetState(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return vacunaJSON != nil, nil
|
||||
}
|
||||
|
||||
unc (s *SmartContract) ReadVacuna(ctx contractapi.TransactionContextInterface, id string) (*Vacuna, error) {
|
||||
vacunaJSON, err := ctx.GetStub().GetState(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error al leer del ledger: %v", err)
|
||||
}
|
||||
if vacunaJSON == nil {
|
||||
return nil, fmt.Errorf("la vacuna %s no existe", id)
|
||||
}
|
||||
|
||||
var vacuna Vacuna
|
||||
err = json.Unmarshal(vacunaJSON, &vacuna)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &vacuna, nil
|
||||
}
|
||||
|
||||
func (s *SmartContract) GetVacunasPorDniYEstado(ctx contractapi.TransactionContextInterface, dni string, estado string) ([]*Vacuna, error) {
|
||||
if dni == "" {
|
||||
return nil, fmt.Errorf("el dni es obligatorio")
|
||||
}
|
||||
|
||||
resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error al obtener datos del ledger: %v", err)
|
||||
}
|
||||
defer resultsIterator.Close()
|
||||
|
||||
var vacunasFiltradas []*Vacuna
|
||||
for resultsIterator.HasNext() {
|
||||
queryResponse, err := resultsIterator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var vacuna Vacuna
|
||||
// Solo deserializamos si es posible (podría fallar si no es una vacuna)
|
||||
if err := json.Unmarshal(queryResponse.Value, &vacuna); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Validamos que tenga un DNI y coincida
|
||||
if vacuna.DniPaciente != dni {
|
||||
continue
|
||||
}
|
||||
|
||||
// Si se pasó un estado, lo filtramos
|
||||
if estado != "" && vacuna.Status != estado {
|
||||
continue
|
||||
}
|
||||
|
||||
vacunasFiltradas = append(vacunasFiltradas, &vacuna)
|
||||
}
|
||||
|
||||
return vacunasFiltradas, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
package com.code.hyperledger.controllers;
|
||||
|
||||
import com.code.hyperledger.Utils.Hashing;
|
||||
import com.code.hyperledger.models.AssetIdDto;
|
||||
import com.code.hyperledger.models.Vacuna;
|
||||
import com.code.hyperledger.services.VacunaService;
|
||||
import org.hyperledger.fabric.client.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/vacunas")
|
||||
public class VacunaController {
|
||||
|
||||
@Autowired
|
||||
private VacunaService vacunaService;
|
||||
|
||||
@PostMapping("/crear")
|
||||
public ResponseEntity<AssetIdDto> crearVacuna(@RequestBody Vacuna vacuna) {
|
||||
System.out.println("\n--> Submit Transaction: CrearVacuna");
|
||||
|
||||
String now = LocalDateTime.now().toString();
|
||||
String dni = vacuna.getDniPaciente();
|
||||
String id = dni + now;
|
||||
String assetId = Hashing.sha256(id);
|
||||
vacuna.setId(assetId);
|
||||
|
||||
AssetIdDto assetIdDto = new AssetIdDto();
|
||||
assetIdDto.setDni(dni);
|
||||
assetIdDto.setTimeStamp(now);
|
||||
|
||||
try {
|
||||
vacunaService.registrarVacuna(vacuna);
|
||||
return new ResponseEntity<>(assetIdDto, HttpStatus.OK);
|
||||
} catch (CommitStatusException | EndorseException | CommitException | SubmitException e) {
|
||||
e.printStackTrace();
|
||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/obtener")
|
||||
public ResponseEntity<Vacuna> obtenerVacuna(@RequestBody Map<String, String> requestBody) {
|
||||
try {
|
||||
String id = requestBody.get("id");
|
||||
if (id == null || id.isEmpty()) {
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
Vacuna vacuna = vacunaService.obtenerVacuna(id);
|
||||
return new ResponseEntity<>(vacuna, HttpStatus.OK);
|
||||
} catch (IOException | GatewayException e) {
|
||||
e.printStackTrace();
|
||||
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/todas")
|
||||
public ResponseEntity<List<Vacuna>> obtenerTodasLasVacunas() {
|
||||
try {
|
||||
List<Vacuna> vacunas = vacunaService.obtenerTodasLasVacunas();
|
||||
return new ResponseEntity<>(vacunas, HttpStatus.OK);
|
||||
} catch (IOException | GatewayException e) {
|
||||
e.printStackTrace();
|
||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/filtrar")
|
||||
public ResponseEntity<List<Vacuna>> obtenerVacunasPorDniYEstado(@RequestBody Map<String, String> filtros) {
|
||||
try {
|
||||
String dni = filtros.get("dni");
|
||||
String estado = filtros.get("estado"); // puede venir null o vacío
|
||||
|
||||
if (dni == null || dni.isEmpty()) {
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
List<Vacuna> vacunas = vacunaService.obtenerVacunasPorDniYEstado(dni, estado);
|
||||
return new ResponseEntity<>(vacunas, HttpStatus.OK);
|
||||
} catch (IOException | GatewayException e) {
|
||||
e.printStackTrace();
|
||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.code.hyperledger.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Vacuna {
|
||||
private String id;
|
||||
private String identificador;
|
||||
private String status;
|
||||
|
||||
private String statusChange;
|
||||
private String statusReason;
|
||||
private String vaccinateCode;
|
||||
private String administradedProduct;
|
||||
private String manufacturer;
|
||||
private String lotNumber;
|
||||
private String expirationDate;
|
||||
private String dniPaciente;
|
||||
private String reactions;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.code.hyperledger.models;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class VacunaDto {
|
||||
private String identificador;
|
||||
private String status;
|
||||
//@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
|
||||
private String statusChange;
|
||||
private String statusReason;
|
||||
private String vaccinateCode;
|
||||
private String administradedProduct;
|
||||
private String manufacturer;
|
||||
private String lotNumber;
|
||||
private String expirationDate;
|
||||
private String dniPaciente;
|
||||
private String reactions;
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
package com.code.hyperledger.services;
|
||||
|
||||
import com.code.hyperledger.models.Vacuna;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import io.grpc.Grpc;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.TlsChannelCredentials;
|
||||
import lombok.SneakyThrows;
|
||||
import org.hyperledger.fabric.client.*;
|
||||
import org.hyperledger.fabric.client.identity.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Service
|
||||
public class VacunaService {
|
||||
|
||||
private static final String MSP_ID = System.getenv().getOrDefault("MSP_ID", "Org1MSP");
|
||||
private static final String CHANNEL_NAME = System.getenv().getOrDefault("CHANNEL_NAME", "mychannel");
|
||||
private static final String CHAINCODE_NAME = System.getenv().getOrDefault("CHAINCODE_NAME", "basic");
|
||||
|
||||
private static final Path CRYPTO_PATH = Paths
|
||||
.get("../../test-network/organizations/peerOrganizations/org1.example.com");
|
||||
private static final Path CERT_DIR_PATH = CRYPTO_PATH.resolve("users/User1@org1.example.com/msp/signcerts");
|
||||
private static final Path KEY_DIR_PATH = CRYPTO_PATH.resolve("users/User1@org1.example.com/msp/keystore");
|
||||
private static final Path TLS_CERT_PATH = CRYPTO_PATH.resolve("peers/peer0.org1.example.com/tls/ca.crt");
|
||||
|
||||
private static final String PEER_ENDPOINT = "localhost:7051";
|
||||
private static final String OVERRIDE_AUTH = "peer0.org1.example.com";
|
||||
|
||||
private Contract contract;
|
||||
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
private static Path getFirstFilePath(Path dirPath) throws IOException {
|
||||
try (var keyFiles = Files.list(dirPath)) {
|
||||
return keyFiles.findFirst().orElseThrow();
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
var channel = newGrpcConnection();
|
||||
|
||||
var builder = Gateway.newInstance()
|
||||
.identity(newIdentity())
|
||||
.signer(newSigner())
|
||||
.connection(channel)
|
||||
.evaluateOptions(options -> options.withDeadlineAfter(5, TimeUnit.SECONDS))
|
||||
.endorseOptions(options -> options.withDeadlineAfter(15, TimeUnit.SECONDS))
|
||||
.submitOptions(options -> options.withDeadlineAfter(5, TimeUnit.SECONDS))
|
||||
.commitStatusOptions(options -> options.withDeadlineAfter(1, TimeUnit.MINUTES));
|
||||
|
||||
try (var gateway = builder.connect()) {
|
||||
this.setContract(gateway);
|
||||
}
|
||||
}
|
||||
|
||||
private static ManagedChannel newGrpcConnection() throws IOException {
|
||||
var credentials = TlsChannelCredentials.newBuilder()
|
||||
.trustManager(TLS_CERT_PATH.toFile())
|
||||
.build();
|
||||
return Grpc.newChannelBuilder(PEER_ENDPOINT, credentials)
|
||||
.overrideAuthority(OVERRIDE_AUTH)
|
||||
.build();
|
||||
}
|
||||
|
||||
private Identity newIdentity() throws IOException, CertificateException {
|
||||
try (var certReader = Files.newBufferedReader(getFirstFilePath(CERT_DIR_PATH))) {
|
||||
var certificate = Identities.readX509Certificate(certReader);
|
||||
return new X509Identity(MSP_ID, certificate);
|
||||
}
|
||||
}
|
||||
|
||||
private Signer newSigner() throws IOException, InvalidKeyException {
|
||||
try (var keyReader = Files.newBufferedReader(getFirstFilePath(KEY_DIR_PATH))) {
|
||||
var privateKey = Identities.readPrivateKey(keyReader);
|
||||
return Signers.newPrivateKeySigner(privateKey);
|
||||
}
|
||||
}
|
||||
|
||||
private void setContract(final Gateway gateway) {
|
||||
var network = gateway.getNetwork(CHANNEL_NAME);
|
||||
contract = network.getContract(CHAINCODE_NAME);
|
||||
}
|
||||
|
||||
public void registrarVacuna(Vacuna vacuna)
|
||||
throws CommitStatusException, EndorseException, CommitException, SubmitException {
|
||||
contract.submitTransaction(
|
||||
"CreateVacuna",
|
||||
vacuna.getId(),
|
||||
vacuna.getNombre(),
|
||||
vacuna.getLaboratorio(),
|
||||
vacuna.getFechaAplicacion(),
|
||||
vacuna.getDniPaciente(),
|
||||
vacuna.getEstado()
|
||||
);
|
||||
}
|
||||
|
||||
public Vacuna obtenerVacuna(String vacunaId) throws GatewayException, IOException {
|
||||
var evaluateResult = contract.evaluateTransaction("ReadVacuna", vacunaId);
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
return objectMapper.readValue(evaluateResult, Vacuna.class);
|
||||
}
|
||||
|
||||
public List<Vacuna> obtenerTodasLasVacunas() throws GatewayException, IOException {
|
||||
var evaluateResult = contract.evaluateTransaction("GetAllVacunas");
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
return objectMapper.readValue(evaluateResult,
|
||||
objectMapper.getTypeFactory().constructCollectionType(List.class, Vacuna.class));
|
||||
}
|
||||
|
||||
public List<Vacuna> obtenerVacunasPorDniYEstado(String dni, String estado) throws GatewayException, IOException {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
byte[] evaluateResult;
|
||||
if (estado == null || estado.isBlank()) {
|
||||
evaluateResult = contract.evaluateTransaction("GetVacunasPorDni", dni);
|
||||
} else {
|
||||
evaluateResult = contract.evaluateTransaction("GetVacunasPorDniYEstado", dni, estado);
|
||||
}
|
||||
return objectMapper.readValue(evaluateResult,
|
||||
objectMapper.getTypeFactory().constructCollectionType(List.class, Vacuna.class));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue