<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[ncleguizamon]]></title><description><![CDATA[Blog ]]></description><link>https://ncleguizamon.co/</link><image><url>https://ncleguizamon.co/favicon.png</url><title>ncleguizamon</title><link>https://ncleguizamon.co/</link></image><generator>Ghost 5.81</generator><lastBuildDate>Wed, 06 May 2026 11:02:56 GMT</lastBuildDate><atom:link href="https://ncleguizamon.co/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Cómo usar ObjectMapper para mapear entidades en un servicio reactivo con Spring WebFlux]]></title><description><![CDATA[<h2 id></h2><p>Cuando trabajamos con aplicaciones reactivas en Spring WebFlux, muchas veces necesitamos transformar datos entre distintas capas de la aplicaci&#xF3;n. En este post, veremos c&#xF3;mo usar <code>ObjectMapper</code> para mapear resultados de una base de datos a objetos de dominio en un entorno no bloqueante.</p><h3 id="contexto-del-c%C3%B3digo"> Contexto del c&</h3>]]></description><link>https://ncleguizamon.co/reactivecommons-utils-reactive/</link><guid isPermaLink="false">68e681162de59800011e87e1</guid><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Wed, 22 Oct 2025 15:46:08 GMT</pubDate><media:content url="https://cdn.ncleguizamon.co/apps/blog-ncle/2025/10/map-reactive-.png" medium="image"/><content:encoded><![CDATA[<h2 id></h2><img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2025/10/map-reactive-.png" alt="C&#xF3;mo usar ObjectMapper para mapear entidades en un servicio reactivo con Spring WebFlux"><p>Cuando trabajamos con aplicaciones reactivas en Spring WebFlux, muchas veces necesitamos transformar datos entre distintas capas de la aplicaci&#xF3;n. En este post, veremos c&#xF3;mo usar <code>ObjectMapper</code> para mapear resultados de una base de datos a objetos de dominio en un entorno no bloqueante.</p><h3 id="contexto-del-c%C3%B3digo"> Contexto del c&#xF3;digo</h3><p>Supongamos que tienes un servicio que obtiene configuraciones desde una base de datos reactiva, filtradas por un estado. Este es el m&#xE9;todo que implementamos:</p><pre><code class="language-xml">  testImplementation &apos;org.reactivecommons.utils:object-mapper:0.1.0&apos;
    implementation &apos;org.reactivecommons.utils:object-mapper:0.1.0&apos;</code></pre><p>Uso</p><pre><code class="language-java">private final ObjectMapper objectMapper;


@Override
public Flux&lt;&gt; getConfig(String state) {
    return repository.findByState(state)
            .map(res -&gt; objectMapper.convertValue(res, ProcessConfig.class));
}</code></pre><p><br><br></p><h3 id="%F0%9F%94%8D-desglosemos-cada-parte">&#x1F50D; Desglosemos cada parte</h3><h4 id="1-private-final-objectmapper-objectmapper">1. <code>private final ObjectMapper objectMapper;</code></h4><p><code>ObjectMapper</code> es una clase de la biblioteca <strong>Jackson</strong>, muy usada para convertir objetos Java en JSON y viceversa. Sin embargo, tambi&#xE9;n es &#xFA;til para mapear entre diferentes clases Java (por ejemplo, de entidades a DTOs o clases de dominio).</p><p>Se declara como <code>private final</code> para asegurar que:</p><ul><li><strong>Privado</strong>: Solo la clase actual puede acceder al mapper.</li><li><strong>Final</strong>: No puede ser reasignado, lo cual es ideal para componentes inyectados.</li></ul><p>&#x1F4A1; <em>En una aplicaci&#xF3;n Spring Boot, normalmente se inyecta v&#xED;a constructor para que est&#xE9; gestionado por el contenedor de Spring.</em></p><h4 id="2-repositoryfindbystatestate">2. <code>repository.findByState(state)</code></h4><p>Este m&#xE9;todo devuelve un <code>Flux&lt;Entidad&gt;</code>, es decir, un flujo reactivo de elementos que cumplen con el filtro <code>state</code>.</p><blockquote>&#x26A0;&#xFE0F; Asumimos que <code>res</code> es una entidad que viene directamente del repositorio, probablemente una entidad JPA o una clase espec&#xED;fica de persistencia.</blockquote><h4 id="3-mapresobjectmapperconvertvalueres-processconfigclass">3. <code>.map(res -&gt; objectMapper.convertValue(res, ProcessConfig.class))</code></h4><p>Aqu&#xED; ocurre la magia. Cada resultado del <code>Flux</code> es transformado (<code>map</code>) a una nueva instancia de <code>ReentryProcessConfig</code>.</p><p>Esto es &#xFA;til si tu capa de persistencia devuelve una clase diferente a la que usas en la l&#xF3;gica de negocio. Por ejemplo:</p><ul><li><code>ProcessConfigEntity</code> (persistencia) &#x2192; <code>ProcessConfig</code> (dominio)</li></ul><h4 id="%F0%9F%A7%A0-%C2%BFpor-qu%C3%A9-no-usar-directamente-el-constructor-o-un-mapper-manual">&#x1F9E0; &#xBF;Por qu&#xE9; no usar directamente el constructor o un mapper manual?</h4><ul><li><strong>Simplicidad</strong>: <code>ObjectMapper</code> puede copiar todos los campos que coincidan en nombre y tipo, sin escribir c&#xF3;digo repetitivo.</li><li><strong>Flexibilidad</strong>: Puedes aprovechar anotaciones como <code>@JsonIgnore</code> o <code>@JsonProperty</code>.</li><li><strong>Compatibilidad</strong>: &#xDA;til si ya usas Jackson en otros puntos del sistema.</li></ul><h3 id="%F0%9F%A7%BC-buenas-pr%C3%A1cticas">&#x1F9FC; Buenas pr&#xE1;cticas</h3><ul><li>&#x2705; Usa <code>ObjectMapper</code> solo cuando las clases sean similares y no haya l&#xF3;gica de transformaci&#xF3;n compleja.</li><li>&#x2705; Considera usar <strong>MapStruct</strong> o <strong>ModelMapper</strong> si necesitas mayor control o rendimiento.</li><li>&#x2705; Aseg&#xFA;rate de no introducir errores silenciosos: <code>ObjectMapper</code> no lanza excepciones si faltan campos, lo cual puede ocultar bugs.</li></ul><hr><h3 id="%F0%9F%8E%AF-conclusi%C3%B3n">&#x1F3AF; Conclusi&#xF3;n</h3><p>El uso de <code>ObjectMapper</code> en servicios reactivas como <code>Flux</code> permite transformar f&#xE1;cilmente entidades a objetos de dominio sin bloquear el flujo de datos. Es una herramienta poderosa que, usada con cuidado, puede hacer tu c&#xF3;digo m&#xE1;s limpio y mantenible.</p>]]></content:encoded></item><item><title><![CDATA[Subir archivos por partes en s3 java reactiva]]></title><description><![CDATA[Subir archivos por partes a s3, archivos muy grandes.]]></description><link>https://ncleguizamon.co/subir-archivos-por-partes-en-s3-java-reactiva/</link><guid isPermaLink="false">675cac288e47f500011f3f09</guid><category><![CDATA[spring webflux]]></category><category><![CDATA[s3]]></category><category><![CDATA[java]]></category><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Fri, 13 Dec 2024 22:49:50 GMT</pubDate><media:content url="https://cdn.ncleguizamon.co/apps/blog-ncle/2024/12/multipart_s3.png" medium="image"/><content:encoded><![CDATA[<img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2024/12/multipart_s3.png" alt="Subir archivos por partes en s3 java reactiva"><p></p><p>Uno de los principales problemas al manejar grandes cantidades de datos es la capacidad limitada de la m&#xE1;quina encargada de procesarlos. Almacenar la informaci&#xF3;n en memoria no garantiza un sistema resiliente, ya que, en caso de falla, se requerir&#xED;a reiniciar el proceso desde cero. A continuaci&#xF3;n, se presenta un caso de uso para una estaci&#xF3;n de datos, cuyo almacenamiento posterior se realiza en un archivo en S3. Este enfoque permitir&#xE1; paralelizar las consultas y transformaciones en diferentes nodos, de manera que, si alguno de ellos falla, solo se necesite reprocesar la parte faltante y no toda la informaci&#xF3;n.</p><p></p><figure class="kg-card kg-image-card"><img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2024/12/master-slave.png" class="kg-image" alt="Subir archivos por partes en s3 java reactiva" loading="lazy" width="542" height="212"></figure><p></p><ol><li><strong>Initiate Multipart Upload</strong>:  (master) , Cuando env&#xED;a una solicitud para iniciar una carga multipart.</li><li><strong>Carga de las partes</strong>: (Slaves) En cada solicitud de carga, debe incluir el ID de la carga multiparte que obtuvo en el paso 1.</li><li><strong>Finalizaci&#xF3;n (o detenci&#xF3;n) de una carga multiparte</strong>:(master),  Despu&#xE9;s de cargar todas las partes del archivo, puede utilizar la operaci&#xF3;n de finalizaci&#xF3;n. Una vez m&#xE1;s, debe especificar el ID de carga en la solicitud.&#xA0;</li></ol><p>Ejemplo de uso extrayendo datos de una base de datos, el cual usa la funcionalidad anterior mente mencionada, pero el tema de paralelismo y entre los nodos depende mucho de tu arquitectura o dise&#xF1;o, :</p><figure class="kg-card kg-code-card"><pre><code class="language-java">   public Mono&lt;Void&gt; uploadFile(LocalDate processDate, String bucketName, String kmsKeyId) {
        String objectKey = &quot;folder/file.txt&quot;;
        Mono&lt;String&gt; headerMono = Mono.fromCallable(this::getLineHeaderFile);
        return s3Operation.initiateMultipartUpload(bucketName, objectKey , kmsKeyId)
                .zipWith(headerMono)
                .flatMap(res -&gt; uploadFilePart(Tuples.of(processDate, res.getT2()),
                        bucketName, objectKey, res.getT1())
                        .zipWith(Mono.just(res))
                )
                .flatMap(res1 -&gt; {
                    var uploadId = res1.getT2().getT1();
                    return s3Operation.completeMultipartUpload(bucketName, objectKey, uploadId, res1.getT1());
                })
                .flatMap(res -&gt; {
                    log.info(res);
                    return Mono.empty();
                });
    }</code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">master</span></p></figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-java">
   
    private String getLineDetailFile(Data  data){
    //justify data a string
    return &quot;test data&quot;;

    }
   
   public Flux&lt;String&gt; getDataToFile(LocalDate initRange, LocalDate endRange) {
        return infoReaderRepository
                .getAll(initRange, endRange) // get info
                .map(this::getLineDetailFile);
    }
 
 public Flux&lt;Tuple2&lt;Long, List&lt;String&gt;&gt;&gt; getRangeDate(LocalDate processDate) {
    return Flux.just(&quot;1&quot;, &quot;2&quot;)
                .flatMap(res -&gt; getDataToFile(res.getT1(), res.getT2()))
                .buffer(100000)
                .index()
                .parallel()
                .runOn(Schedulers.boundedElastic()).sequential();
    }
  
  
  
  public Mono&lt;List&lt;Tuple2&lt;Integer, String&gt;&gt;&gt; uploadFilePart(Tuple2&lt;LocalDate, String&gt; processDate,
                                                                            String bucketName, String objectKey,
                                                                            String uploadId) {

        return getRangeDate(processDate.getT1())
                .flatMap(res -&gt; getByte(res.getT2(), res.getT1(), processDate.getT2())
                        .zipWith(Mono.just(res.getT1())
                        ))
                .flatMap(res -&gt; {
                    log.info(&quot;length  &quot; + res.getT1().length);
                    var partNumber = Math.toIntExact(res.getT2() + 1);
                    return s3Operation.multipartUpload(bucketName, objectKey, uploadId, res.getT1(), partNumber)
                            .map(tag -&gt; Tuples.of(partNumber, tag));
                })
                .collectList();
    }
    </code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Slaves</span></p></figcaption></figure><p></p><p>validamos las partes o la data pese mas de 5 mg , y concatenamos el header del archivo en este caso </p><figure class="kg-card kg-code-card"><pre><code class="language-java"> public Mono&lt;byte[]&gt; getByte(List&lt;String&gt; res, Long index, String header) {
        return Mono.just(res.stream()
                        .reduce(&quot;&quot;, (acc, line) -&gt; acc + line).
                        getBytes(StandardCharsets.UTF_8))
                .map(str -&gt; {
                    byte[] byteArray = str;
                    final int MIN_SIZE = 5 * 1024 * 1024;
                    if (index == 0) {
                        byte[] bytes1 = header.getBytes(StandardCharsets.UTF_8);
                        byte[] combinedBytes = new byte[bytes1.length + str.length];
                        System.arraycopy(bytes1, 0, combinedBytes, 0, bytes1.length);
                        System.arraycopy(str, 0, combinedBytes, bytes1.length, str.length);
                        byteArray = combinedBytes;
                    }
                    if ((byteArray.length &lt; MIN_SIZE)) {
                        var paddedArray = new byte[MIN_SIZE];
                        System.arraycopy(byteArray, 0, paddedArray, 0, byteArray.length);
                        Arrays.fill(paddedArray, byteArray.length, MIN_SIZE, (byte) 0);
                        byteArray = paddedArray;
                    }
                    return byteArray;
                });
    }
    </code></pre><figcaption><p dir="ltr"><span style="white-space: pre-wrap;">Slaves</span></p></figcaption></figure><p>En el adaptador de s3 vamos a tener la implementaci&#xF3;n de  s3Operation</p><p>AdapterS3.java</p><pre><code class="language-java">private final S3BucketOperations s3Operations;


    @Override
    public Mono&lt;List&lt;String&gt;&gt; getItems(String bucketName) {
        return s3Operations.listBucketObjects(bucketName)
                .flatMap(s3Objects -&gt;
                        Mono.just(
                                s3Objects.stream().map(S3Object::key)
                                        .collect(Collectors.toList())
                        )
                );

    }


    @Override
    public Mono&lt;String&gt; initiateMultipartUpload(String bucketName, String objectKey , String kmsKeyId) {
        CreateMultipartUploadRequest createRequest = CreateMultipartUploadRequest.builder()
                .bucket(bucketName)
                .key(objectKey)
                .serverSideEncryption(ServerSideEncryption.AWS_KMS)
                .ssekmsKeyId(kmsKeyId)
                .build();
        return s3Operations.initiateMultipartUpload(createRequest);
    }


    @Override
    public Mono&lt;String&gt; multipartUpload(String bucketName, String objectKey, String uploadId, byte[] part, int partNumber) {

        return s3Operations.uploadPart(bucketName, objectKey, uploadId, partNumber, part)
                .map(UploadPartResponse::eTag)
                .log(&quot; Part uploaded: &quot;)
                .onErrorResume(e -&gt; {
                    log.info(&quot;Error multipartUpload part: &quot; + e.getMessage());
                    return Mono.error(e);
                });

    }

    @Override
    public Mono&lt;String&gt; completeMultipartUpload(String bucketName, String objectKey, String uploadId, List&lt;Tuple2&lt;Integer, String&gt;&gt; eTags) {
        List&lt;CompletedPart&gt; completedParts = eTags.stream()
                .map(tuple -&gt;
                        CompletedPart.builder()
                                .partNumber(tuple.getT1())
                                .eTag(tuple.getT2())
                                .build()
                )
                .sorted(Comparator.comparingInt(CompletedPart::partNumber))
                .collect(Collectors.toList());

        log.info(&quot;completedParts: &quot; + completedParts);
        CompleteMultipartUploadRequest completeRequest = CompleteMultipartUploadRequest.builder()
                .bucket(bucketName)
                .key(objectKey)
                .uploadId(uploadId)
                .multipartUpload(CompletedMultipartUpload.builder().parts(completedParts).build())
                .build();
        return s3Operations.completeMultipartUpload(completeRequest)
                .then(Mono.just(uploadId))
                .onErrorResume(e -&gt; {
                    log.info(&quot;Error completeMultipartUpload part: &quot; + e.getMessage());
                    return Mono.error(e);
                });
    }

    </code></pre><p>S3BucketOperations.java </p><pre><code class="language-java">
    private final S3AsyncClient s3AsyncClient;


    public Mono&lt;Void&gt; completeMultipartUpload( CompleteMultipartUploadRequest completeRequest ) {

        return Mono.fromFuture(() -&gt; s3AsyncClient.completeMultipartUpload(completeRequest))
                .then()
                .onErrorResume(e -&gt;{
                    log.info(&quot;Error completeMultipartUpload part: &quot;+e.getMessage());
                    return Mono.error(e);
                });
    }

    public Mono&lt;UploadPartResponse&gt; uploadPart(String bucketName, String objectKey, String uploadId,
                                                Integer partNumber, byte[] partData) {
        UploadPartRequest uploadPartRequest = UploadPartRequest.builder()
                .bucket(bucketName)
                .key(objectKey)
                .uploadId(uploadId)
                .partNumber(partNumber)
                .build();

        return Mono.fromFuture(() -&gt; s3AsyncClient.uploadPart(uploadPartRequest , AsyncRequestBody.fromBytes(partData)))
                .onErrorResume(e -&gt;{
                  log.info(&quot;Error uploading part: &quot;+e.getMessage());
                    return Mono.error(e);
                });
    }
    public Mono&lt;String&gt; initiateMultipartUpload(CreateMultipartUploadRequest createRequest) {

        return Mono.fromFuture(() -&gt; s3AsyncClient.createMultipartUpload(createRequest))
                .map(CreateMultipartUploadResponse::uploadId)
                .onErrorResume(e -&gt;{
                    log.info(&quot;Error initiateMultipartUpload part: &quot;+e.getMessage());
                    return Mono.error(e);
                });
    }


    public Mono&lt;Boolean&gt; uploadObject(String bucketName,String objectKey, byte[] fileContent) {

        return Mono.fromFuture(
                s3AsyncClient.putObject(configurePutObject(bucketName,objectKey),
                        AsyncRequestBody.fromBytes(fileContent)))
                .map(response -&gt; response.sdkHttpResponse().isSuccessful());
    }

    public Mono&lt;List&lt;S3Object&gt;&gt; listBucketObjects(String bucketName){
        return Mono.fromFuture(s3AsyncClient.listObjects(ListObjectsRequest
                .builder()
                .bucket(bucketName)
                .build())).map(ListObjectsResponse::contents);
    }

    public Mono&lt;InputStream&gt; getObject(String bucketName,String objectKey) {
        Mono.fromFuture(s3AsyncClient.deleteObject(DeleteObjectRequest.builder()
                .bucket(bucketName)
                .key(objectKey).build()))
                .delayElement(Duration.ofMinutes(1))
                .subscribe();
        return Mono.fromFuture(s3AsyncClient.getObject(GetObjectRequest.builder()
                .key(objectKey)
                .bucket(bucketName)
                .build(), AsyncResponseTransformer.toBytes()))
                .map(BytesWrapper::asInputStream);
    }

    private PutObjectRequest configurePutObject(String bucketName, String objectKey) {
        return PutObjectRequest.builder()
                .bucket(bucketName)
                .key(objectKey)
                .build();
    }
    </code></pre><p></p><p>happy code :).</p><p></p><p>Referencias: </p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.aws.amazon.com/es_es/amazonglacier/latest/dev/uploading-an-archive-mpu-using-java.html"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Carga de archivos grandes por partes con Amazon&#xA0;SDK for Java - Amazon&#xA0;S3 Glacier</div><div class="kg-bookmark-description">Ejemplos de c&#xF3;digo Java acerca de c&#xF3;mo cargar archivos grandes por partes en S3 Glacier con Amazon SDK para Java.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://docs.aws.amazon.com/assets/images/favicon.ico" alt="Subir archivos por partes en s3 java reactiva"><span class="kg-bookmark-author">Amazon&#xA0;S3 Glacier</span></div></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.freecodecamp.org/news/upload-large-files-with-aws/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">&#x200B;&#x200B;How to Upload Large Files Efficiently with AWS S3 Multipart Upload</div><div class="kg-bookmark-description">Imagine running a media streaming platform where users upload large high-definition videos. Uploading such large files can be slow and may fail if the network is unreliable. Using traditional single-part uploads can be cumbersome and inefficient for&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://cdn.freecodecamp.org/universal/favicons/favicon.ico" alt="Subir archivos por partes en s3 java reactiva"><span class="kg-bookmark-author">freeCodeCamp.org</span><span class="kg-bookmark-publisher">Destiny Erhabor</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.freecodecamp.org/news/content/images/2024/07/mr-cup-fabien-barral-o6GEPQXnqMY-unsplash.jpg" alt="Subir archivos por partes en s3 java reactiva"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Failover DB Cluster rds, read-only transaction, sprint boot]]></title><description><![CDATA[SQL [UPDATE tbl_]; cannot execute UPDATE in a read-only transaction]]></description><link>https://ncleguizamon.co/failover-db-cluster-rds-read-only-transaction-sprint-boot/</link><guid isPermaLink="false">6725585e8e47f500011f3e59</guid><category><![CDATA[spring webflux]]></category><category><![CDATA[rds]]></category><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Fri, 01 Nov 2024 23:57:48 GMT</pubDate><media:content url="https://cdn.ncleguizamon.co/apps/blog-ncle/2024/11/rds-mz.png" medium="image"/><content:encoded><![CDATA[<img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2024/11/rds-mz.png" alt="Failover DB Cluster rds, read-only transaction, sprint boot"><p></p><p>Una de las ventajas que tiene el cluster rds , la posibilidad de tener instancias en Multi-AZ, las cuales te permite tener alta disponibilidad  de tu cluster de base de datos, cuando hay <strong>Failover </strong>en caso de fallos, RDS cambia autom&#xE1;ticamente a la instancia diferente, pude suceder que el pool de conexi&#xF3;n se quede en la instacia que no es de escritura </p><blockquote><strong> cannot execute UPDATE in a read-only transaction </strong></blockquote><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1200/1*4XUmzGsElgwj9_46Go9jWg.png" class="kg-image" alt="Failover DB Cluster rds, read-only transaction, sprint boot" loading="lazy" width="1200" height="631"></figure><p>En  Spring Boot hay diferentes componentes que se utiliza para verificar la salud de una conexi&#xF3;n con la base de datos,<code>ConnectionFactory, </code>Este indicador de salud forma parte de las capacidades de gesti&#xF3;n de salud permitiendo monitorear el estado de las conexiones de manera efectiva, normal mente ConnectionFactoryHealthIndicator  te habitar&#xED;a que fallo la conexion, pero en algunos caso algunos pods o instancias de nuestra aplicacion no se conecta de forma correcta . </p><figure class="kg-card kg-image-card"><img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2024/11/image.png" class="kg-image" alt="Failover DB Cluster rds, read-only transaction, sprint boot" loading="lazy" width="1333" height="176"></figure><p>para poder identificar mejor como esta el pool y si esta usando el pool correcto vamos a customizar HealthCheck, que revise que este conectado de forma correcta.</p><ol><li>implementamos  la dependencia </li></ol><blockquote>org.springframework.boot:spring-boot-starter-actuator</blockquote><ol><li>Creamos la clase ConnectionPoolValidator, la cual se va encargar de consultar , que no la instancia no sea de lectura con el comando</li></ol><blockquote> SHOW transaction_read_only;</blockquote><pre><code class="language-java">@Log
@Component
public class ConnectionPoolValidator {
    private final ConnectionPool connectionPool;

    public ConnectionPoolValidator( @Qualifier(&quot;postgresConnection&quot;) ConnectionPool connectionPool) {
        this.connectionPool = connectionPool;
    }

    public Flux&lt;Boolean&gt; validateConnection() {
        return connectionPool.create()
                .flatMapMany(connection -&gt; connection
                        .createStatement(&quot;SHOW transaction_read_only;&quot;)
                        .execute())
                .flatMap(result -&gt; result.map((row, rowMetadata) -&gt; row.get(0, String.class)))
                .flatMap(value -&gt; {
                    log.info(&quot;Connection not reader is: &quot; + &quot;off&quot;.equals(value) );
                    return Mono.just(&quot;off&quot;.equals(value));
                })
                .onErrorResume(e -&gt; {
                            log.info(&quot;Connection validation error: &quot; + e.getMessage());
                            return Mono.just(false);
                        }
                )
                .doFinally(signalType -&gt; connectionPool.close());
    }
}</code></pre><ol start="3"><li>Creamos nuestra clase HealthCheck</li></ol><pre><code class="language-java">@Component(&quot;HealthCheckWriderRds&quot;)
@RequiredArgsConstructor
public class HealthCheck implements HealthIndicator {

    private final ConnectionPoolValidator connectionPoolValidator;

    @Override
    public Health health() {
        if (!Boolean.TRUE.equals(connectionPoolValidator.validateConnection().blockFirst())) {
            return Health.down()
                    .withDetail(&quot;Error Code&quot;, &quot;down&quot;).build();
        }
        return Health.up().build();
    }
} </code></pre><ol start="4"><li>validamos que la siguiente url te responda , seg&#xFA;n sea tu caso</li></ol><p>http://localhost:8080/actuator/health</p><ol start="5"><li> configuramos el livenessProbe, la cual va reiniciar cuando el health , no sea valido .</li></ol><pre><code class="language-yaml">apiVersion: v1
kind: Pod
metadata:
  labels:
    test: myimage
  name: liveness-http
spec:
  containers:
  - name: myimage 
    image: myimage:0.0.1
    args:
    - liveness
    livenessProbe:
      httpGet:
        path: actuator/health
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3</code></pre><p>El tiempo puede variar seg&#xFA;n tu configuraci&#xF3;n y lo que demore el Failover, tambi&#xE9;n se recomienda agregar  <code>-Dsun.net.inetaddr.ttl=0</code> se utiliza en aplicaciones Java para configurar el tiempo de vida (TTL, por sus siglas en ingl&#xE9;s) de las entradas de cach&#xE9; del sistema de nombres de dominio (DNS).</p><p>happy code :).</p><p><strong>Reference</strong>:</p><p><a href="https://www.baeldung.com/spring-boot-health-indicators">https://www.baeldung.com/spring-boot-health-indicators</a></p>]]></content:encoded></item><item><title><![CDATA[Solicitar nueva en ip linux, dhcp]]></title><description><![CDATA[<p>Cuando esta trabajando con Linux un tiene que cambiar la Ip local, por alg&#xFA;n ajuste de interfaz de red y normal mente uno reinicia, para poder evitar el reinicio podr&#xED;as usar el siguiente comando para poder cambiar la Ip usando el cliente dhcp.</p><pre><code class="language-bash">  sudo dhclient
</code></pre><p><code>dhclient</code></p>]]></description><link>https://ncleguizamon.co/solicitar-nueva-ip-linux-dhcp/</link><guid isPermaLink="false">6690046d8e47f500011f3d01</guid><category><![CDATA[linux]]></category><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Sat, 06 Jul 2024 12:30:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1613677135043-a2512fbf49fa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fGNtZHxlbnwwfHx8fDE3MjA3MTU0MzB8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1613677135043-a2512fbf49fa?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fGNtZHxlbnwwfHx8fDE3MjA3MTU0MzB8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Solicitar nueva en ip linux, dhcp"><p>Cuando esta trabajando con Linux un tiene que cambiar la Ip local, por alg&#xFA;n ajuste de interfaz de red y normal mente uno reinicia, para poder evitar el reinicio podr&#xED;as usar el siguiente comando para poder cambiar la Ip usando el cliente dhcp.</p><pre><code class="language-bash">  sudo dhclient
</code></pre><p><code>dhclient</code> es el software cliente DHCP. Cuando un equipo (cliente) se conecta a una red, puede usar <code>dhclient</code> para obtener autom&#xE1;ticamente informaci&#xF3;n de configuraci&#xF3;n, como direcci&#xF3;n IP, m&#xE1;scara de subred, puerta de enlace predeterminada y servidores DNS, desde un servidor DHCP.</p><p> Cuando se ejecuta <code>dhclient</code>, env&#xED;a un mensaje DHCPDISCOVER por difusi&#xF3;n en la red, solicitando informaci&#xF3;n de configuraci&#xF3;n. Un servidor DHCP responde con un mensaje DHCPOFFER que contiene una direcci&#xF3;n IP y otras configuraciones de red. El cliente (usando <code>dhclient</code>) luego env&#xED;a un DHCPREQUEST para aceptar la configuraci&#xF3;n ofrecida. Finalmente, el servidor DHCP reconoce (DHCPACK) la solicitud del cliente, y el cliente configura su interfaz de red con los ajustes proporcionados.</p><p> DHCP se utiliza com&#xFA;nmente en redes donde las direcciones IP se asignan din&#xE1;micamente (en lugar de ser est&#xE1;ticas), permitiendo una gesti&#xF3;n y asignaci&#xF3;n eficientes de direcciones IP.</p><p> <code>dhclient</code> lee su configuraci&#xF3;n desde <code>/etc/dhcp/dhclient.conf</code>, donde se pueden especificar opciones adicionales y par&#xE1;metros sobre c&#xF3;mo debe comportarse el cliente y qu&#xE9; informaci&#xF3;n debe solicitar al servidor DHCP.</p><p></p>]]></content:encoded></item><item><title><![CDATA[spring webflux conectar reader and writer rds]]></title><description><![CDATA[<p>En el mundo de la programaci&#xF3;n reactiva, Spring WebFlux ha surgido como una poderosa herramienta para construir aplicaciones robustas y eficientes. En esta gu&#xED;a, exploraremos c&#xF3;mo conectar Reader y Writer a una base de datos relacional (RDS) utilizando Spring WebFlux.</p><p>La interacci&#xF3;n</p>]]></description><link>https://ncleguizamon.co/spring-webflux-conectar-reader-and-writer-rds/</link><guid isPermaLink="false">6628472c8e47f500011f3aac</guid><category><![CDATA[spring webflux]]></category><category><![CDATA[java]]></category><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Mon, 29 Apr 2024 17:22:30 GMT</pubDate><media:content url="https://cdn.ncleguizamon.co/apps/blog-ncle/2024/04/Sprint-y-rds.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2024/04/Sprint-y-rds.jpg" alt="spring webflux conectar reader and writer rds"><p>En el mundo de la programaci&#xF3;n reactiva, Spring WebFlux ha surgido como una poderosa herramienta para construir aplicaciones robustas y eficientes. En esta gu&#xED;a, exploraremos c&#xF3;mo conectar Reader y Writer a una base de datos relacional (RDS) utilizando Spring WebFlux.</p><p>La interacci&#xF3;n fluida entre la capa de lectura y escritura es fundamental para el desarrollo de aplicaciones de alta calidad. En este contexto, aprovechar la capacidad de WebFlux para gestionar operaciones de entrada y salida de manera reactiva puede marcar la diferencia en el rendimiento y la escalabilidad de tu aplicaci&#xF3;n.</p><p>Desde la configuraci&#xF3;n inicial hasta la implementaci&#xF3;n pr&#xE1;ctica, te guiaremos a trav&#xE9;s de los pasos necesarios para integrar eficazmente Reader y Writer con tu base de datos relacional utilizando Spring WebFlux. Prep&#xE1;rate para sumergirte en un viaje de descubrimiento mientras desbloqueamos los secretos de esta potente combinaci&#xF3;n tecnol&#xF3;gica.</p><p>Para este ejemplo vamos a usar <a href="https://github.com/bancolombia/scaffold-clean-architecture" rel="noreferrer"><strong>scaffold-clean-architecture</strong> </a> para seguir patr&#xF3;n de desarrollo de buenas practicas, la cual no va ser una limitante si se implementa en otros patrones.</p><ol><li>Creamos 2 paquetes uno para <strong>Writer </strong>y para <strong>Reader</strong></li></ol><blockquote><strong>Writer </strong>-&gt; co.ncleguizamon.posgres.r2dbc-postgresql</blockquote><blockquote><strong>Reader</strong> -&gt;  co.ncleguizamon.posgres.r2dbc-postgresql-reader</blockquote><p>para el caso de <a href="https://github.com/bancolombia/scaffold-clean-architecture" rel="noreferrer"><strong>clean architecture</strong></a> seria generar dos Driven Adapter por comandos</p><p><strong>Writer </strong></p><pre><code class="language-bash">   gradle generateDrivenAdapter --type=r2dbc
   </code></pre><p><strong>Reader</strong> </p><pre><code class="language-bash">   gradle generateDrivenAdapter --type=generic --name=r2dbc-postgresql-reader 
   </code></pre><ol start="2"><li>Crear bean </li></ol><pre><code class="language-bash">@Bean
    @Profile(&quot;local&quot;)
    public SecretPostgres secretsModelLocal(PostgresqlConnectionProperties postgresqlProperties) {
        return SecretPostgres.builder()
                .dbname(postgresqlProperties.getDatabase())
                .host(postgresqlProperties.getHost())
                .password(postgresqlProperties.getPassword())
                .username(postgresqlProperties.getUsername())
                .port(postgresqlProperties.getPort())
                .schema(postgresqlProperties.getSchema())
                .urlReaderRds(postgresqlProperties.geturlReaderRds())
                .build();
    }   </code></pre><ol><li>En cada paquete creamos nuestra configuraci&#xF3;n de nuestra carpeta de config</li></ol><p>y una clase de configuraci&#xF3;n, en este caso llamada Config.java</p><p> <strong>Writer </strong></p><pre><code class="language-java">@Configuration
@AllArgsConstructor
@EnableConfigurationProperties({PostgresqlConnectionProperties.class})
@EnableR2dbcRepositories(basePackages = &quot;co.ncleguizamon.posgres.r2dbc-postgresql&quot;, entityOperationsRef = &quot;postgresEntityTemplate&quot;)
public class Config {
    public static final int INITIAL_SIZE = 1;
    public static final int MAX_SIZE = 2;
    public static final int MAX_IDLE_TIME = 30;

    SecretPostgres secretPostgres;

    @Bean
    public R2dbcEntityOperations postgresEntityTemplate(
            @Qualifier(&quot;postgresConnection&quot;) ConnectionFactory connectionFactory) {
        DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
        return new R2dbcEntityTemplate(databaseClient, PostgresDialect.INSTANCE);
    }

    @Bean(name = &quot;postgresConnection&quot;)
    public ConnectionPool getConnectionConfig() {
        PostgresqlConnectionConfiguration dbConfiguration = PostgresqlConnectionConfiguration.builder()
                .host(secretPostgres.getHost())
                .port(secretPostgres.getPort())
                .database(secretPostgres.getDbname())
                .schema(secretPostgres.getSchema())
                .username(secretPostgres.getUsername())
                .password(secretPostgres.getPassword())
                .build();

        ConnectionPoolConfiguration poolConfiguration = ConnectionPoolConfiguration.builder()
                .connectionFactory(new PostgresqlConnectionFactory(dbConfiguration))
                .name(&quot;api-postgres-connection-pool&quot;)
                .initialSize(INITIAL_SIZE)
                .maxSize(MAX_SIZE)
                .maxIdleTime(Duration.ofMinutes(MAX_IDLE_TIME))
                .validationQuery(&quot;SELECT 1&quot;)
                .build();

        return new ConnectionPool(poolConfiguration);
    
    // opcinal si usas 

    @Bean
    public TransactionalOperator transactionalOperator(
            @Qualifier(&quot;postgresConnection&quot;) ConnectionFactory connectionFactory) {
        return TransactionalOperator.create(new R2dbcTransactionManager(connectionFactory));
    }
}   </code></pre><p>PostgresqlConnectionProperties.java</p><pre><code class="language-java">@Data
@ConfigurationProperties(prefix = &quot;spring.r2bdc-postgres&quot;)
public class PostgresqlConnectionProperties {

    private String database;
    private String schema;
    private String username;
    private String password;
    private String host;
    private String urlReaderRds;
    private Integer port;
    

}
   </code></pre><p><strong>Reader</strong> </p><pre><code class="language-java">@Configuration
@AllArgsConstructor
@EnableR2dbcRepositories(basePackages = &quot;co.ncleguizamon.posgres.r2dbc-postgresql-reader&quot;,
        entityOperationsRef = &quot;postgresReaderEntityTemplate&quot;)
public class Config{
    public static final int INITIAL_SIZE = 3;
    public static final int MAX_SIZE = 6;
    public static final int MAX_IDLE_TIME = 30;

    SecretPostgres secretPostgres;

    @Bean
    public R2dbcEntityOperations postgresReaderEntityTemplate(
            @Qualifier(&quot;postgresReaderConnection&quot;) ConnectionFactory connectionFactory) {
        DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
        return new R2dbcEntityTemplate(databaseClient, PostgresDialect.INSTANCE);
    }

    @Bean(name = &quot;postgresReaderConnection&quot;)
    public ConnectionPool getConnectionConfig() String urlReaderRds) {
        PostgresqlConnectionConfiguration dbConfiguration = PostgresqlConnectionConfiguration.builder()
                .host(secretPostgres.urlReaderRds == null? secretPostgres.getHost(): secretPostgres.urlReaderRds)
                .port(secretPostgres.getPort())
                .database(secretPostgres.getDbname())
                .schema(secretPostgres.getSchema())
                .username(secretPostgres.getUsername())
                .password(secretPostgres.getPassword())
                .build();

        ConnectionPoolConfiguration poolConfiguration = ConnectionPoolConfiguration.builder()
                .connectionFactory(new PostgresqlConnectionFactory(dbConfiguration))
                .name(&quot;api-postgres-connection-pool-reader&quot;)
                .initialSize(INITIAL_SIZE)
                .maxSize(MAX_SIZE)
                .maxIdleTime(Duration.ofMinutes(MAX_IDLE_TIME))
                .validationQuery(&quot;SELECT 1&quot;)
                .build();

        return new ConnectionPool(poolConfiguration);
    }
   </code></pre><p><strong>@EnableR2dbcRepositories</strong></p><p><code>@EnableR2dbcRepositories</code> se utiliza para habilitar los repositorios de Spring Data R2DBC para el paquete base especificado (<code>com.example.repository</code>). Spring generar&#xE1; autom&#xE1;ticamente implementaciones para las interfaces de repositorio que se encuentren en este paquete, lo que te permitir&#xE1; usarlas en tu aplicaci&#xF3;n.</p><p>Aseg&#xFA;rate de haber incluido las dependencias necesarias en tu proyecto para usar Spring Data R2DBC, como <code>spring-boot-starter-data-r2dbc</code> si est&#xE1;s utilizando Spring Boot.</p><p></p><p><strong>entityOperationsRef</strong></p><p>Referencia del <strong>bean </strong>que hace referencia a R2dbcEntityOperations, seg&#xFA;n sea el repository, configurado .</p><p>Para usar cada uno de los dos paquetes , debes implementar unas interface por cada uno de los paquetes , para poder usarlos en tus casos de uso , ejemplo.</p><p><strong>Reader</strong> , solo lecturas</p><pre><code class="language-java">public interface UserReaderService {


    Flux&lt;User&gt; findByAllId(Long id);

    Flux&lt;User&gt;  getList(Long id);
    ....

}

   </code></pre><p></p><p></p><p> <strong>Writer </strong>, updates , create , delete</p><pre><code class="language-java">public interface UserWriterService {


    Mono&lt;User&gt; deleteById(Long id);
    Mono&lt;User&gt; create(Long id);

    .....
    
}

   </code></pre><p>Implementa en tu Adapter, seg&#xFA;n sea el caso.</p><figure class="kg-card kg-code-card"><pre><code class="language-java">@Repository
@RequiredArgsConstructor
public class UserRepositoryAdapter implements UserReaderService {
// repository en el paquete postgresReaderEntityTemplate
    private final UserDataRepository userDataRepository;


    ....


    
   </code></pre><figcaption><p><span style="white-space: pre-wrap;">ejemplo: reader</span></p></figcaption></figure><p>recuerda si usas <strong>jpa</strong> debes usar  <strong>@EnableJpaRepositories</strong></p><p><strong>Reference</strong>: </p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.spring.io/spring-data/r2dbc/docs/current/api/org/springframework/data/r2dbc/repository/config/EnableR2dbcRepositories.html"><div class="kg-bookmark-content"><div class="kg-bookmark-title">EnableR2dbcRepositories (Spring Data R2DBC 3.2.5 API)</div><div class="kg-bookmark-description">declaration: package: org.springframework.data.r2dbc.repository.config, annotation type: EnableR2dbcRepositories</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://docs.spring.io/favicon.ico" alt="spring webflux conectar reader and writer rds"></div></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/spring-projects/spring-data-r2dbc/issues/406"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Repositories cannot be used with two different database systems &#xB7; Issue #406 &#xB7; spring-projects/spring-data-r2dbc</div><div class="kg-bookmark-description">There is a conflict with methods query generator in repository when you have 2 different database servers mssql and mysql at the same time. And it renders query with dialect of only certain databas&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg" alt="spring webflux conectar reader and writer rds"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">spring-projects</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/2820630b13ca824df16536f46ae7d3ff803bf2f7ea662315dedbe15f9020396d/spring-projects/spring-data-r2dbc/issues/406" alt="spring webflux conectar reader and writer rds"></div></a></figure><p></p>]]></content:encoded></item><item><title><![CDATA[Prueba Unitaria de R2dbcEntityTemplate: Validando la Persistencia y Recuperación de Datos]]></title><description><![CDATA[Test unitarios r2dbcentitytemplate spring boot, reactivo
]]></description><link>https://ncleguizamon.co/prueba-unitaria-de-r2dbcentitytemplate-validando-la-persistencia-y-recuperacion-de-datos/</link><guid isPermaLink="false">66248da62dd7ff0001e116f4</guid><category><![CDATA[spring webflux]]></category><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Mon, 23 Oct 2023 21:47:26 GMT</pubDate><media:content url="https://cdn.ncleguizamon.co/apps/blog-ncle/2023/10/1_Xhct1rcbMzmbedNWfwLxIw.png" medium="image"/><content:encoded><![CDATA[<img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2023/10/1_Xhct1rcbMzmbedNWfwLxIw.png" alt="Prueba Unitaria de R2dbcEntityTemplate: Validando la Persistencia y Recuperaci&#xF3;n de Datos"><p><code>R2dbcEntityTemplate</code> es un componente central de Spring Data R2DBC que simplifica el acceso a la base de datos y la manipulaci&#xF3;n de datos al proporcionar una API reactiva de alto nivel para interactuar con una base de datos relacional. Sigue el patr&#xF3;n de plantilla (template pattern), donde maneja las operaciones comunes de la base de datos, como la inserci&#xF3;n, actualizaci&#xF3;n, eliminaci&#xF3;n y consulta de datos, lo que te permite centrarte en la l&#xF3;gica de negocio de tu aplicaci&#xF3;n.</p><p>Para testear una clase que implemente <code>R2dbcEntityTemplate</code>  lo vamos hacer con junit .</p><p><strong>Ejemplo</strong>:</p><p>Clase java TestAdapter</p><pre><code class="language-java">@Repository
@RequiredArgsConstructor
public class TestAdapter implements TestService {

    private final R2dbcEntityTemplate template;
    @Override
    public Mono&lt;Void&gt; delete(Test test) {
        return template.delete(TestData.class)
                .matching(
                        Query.query(Criteria.where(&quot;id&quot;).is(test.getId())
                                .and(STATE).is(test.getState())
                                .and(CONSECUTIVE).is(test.getConsecutive())
                        )

                )
                .all().then();
    }
    @Override
    public Mono&lt;Void&gt; update(Test test) {
       return template.update(TestData.class)
         .matching(
                    Query.query(Criteria.where(ID).is(test.getT1())))
               .apply(Update.update(CONSECUTIVE, test.getT2())
               )
               .then();
    }
    

}</code></pre><p>Clase java TestAdapterTest</p><p>moqueamos R2dbcEntityTemplate</p><pre><code class="language-java">   private final R2dbcEntityTemplate template
            = Mockito.mock(R2dbcEntityTemplate.class, RETURNS_DEEP_STUBS);
            
            
            
            // example test
          @Test
    void delete(){     
    Mockito.when(template.delete(TestData.class)
    .matching(any(Query.class)).all())
                .thenReturn(Mono.empty());

        TestAdapter.delete(test)
                .as(StepVerifier::create)
                .verifyComplete();
    }      
            
</code></pre><p>update</p><pre><code class="language-java">   private final R2dbcEntityTemplate template
            = Mockito.mock(R2dbcEntityTemplate.class, RETURNS_DEEP_STUBS);
            
            
            
            // example test update
          @Test
    void update(){     
   
        Mockito.when(template.update(TestData.class)
        .matching(any(Query.class)).apply(any()))
                .thenReturn(Mono.empty());

        TestAdapter.update(tets)
                .as(StepVerifier::create)
                .verifyComplete();
    }      
            </code></pre><p></p><p></p><p>happy code :).</p>]]></content:encoded></item><item><title><![CDATA[Test unitario subida de archivos - Spring webflux]]></title><description><![CDATA[Ejemplo de subida de archivos en Spring webflux y test unitarios.]]></description><link>https://ncleguizamon.co/test-unitario-subida-de-archivos-spring-webflux/</link><guid isPermaLink="false">66248da62dd7ff0001e116f3</guid><category><![CDATA[spring webflux]]></category><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Thu, 08 Jun 2023 21:57:56 GMT</pubDate><media:content url="https://cdn.ncleguizamon.co/apps/blog-ncle/2023/06/image.png" medium="image"/><content:encoded><![CDATA[<img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2023/06/image.png" alt="Test unitario subida de archivos - Spring webflux"><p>Cuando esta trabajando con <strong>Sprint webflux , </strong>necesitas subir archivos a tu aplicaci&#xF3;n puedes usar los BodyExtractors.toMultipartData() , lo cuales te permitir&#xE1;n obtener los archivos enviados por la api y con una funci&#xF3;n puedes extraer &#xA0;los byte[] de un archivo de tipo FilePart.</p><p>Ejemplo :</p><pre><code class="language-java "> private  Mono&lt;data&gt; getFile(ServerRequest serverRequest){
        return serverRequest.body(BodyExtractors.toMultipartData())
                .flatMap(parts -&gt; {
                    Map&lt;String, Part&gt; part = parts.toSingleValueMap();
                    return Mono.just((FilePart) part.get(&quot;file&quot;));
                })
                .flatMap(filePart -&gt; {
                    Mono&lt;byte[]&gt; dataStreamFile =      validateContent(filePart.content());
                    
                    return  dataStreamFile;
                })
                .flatMap(data );
    }
    
       private Mono&lt;byte[]&gt; validateContent(Flux&lt;DataBuffer&gt; file) {
        return file.single().map(dataBuffer -&gt; {
            if (dataBuffer.readableByteCount() &lt;= ZERO) {
                throw new IllegalArgumentException(&quot;error no records were found in the file&quot;);
            }
            if (dataBuffer.readableByteCount() &gt; MAX_FILE_SIZE) {
                throw new IllegalArgumentException(&quot;error while loading file, exceeds the allowed upload capacity&quot;);
            }
            byte[] bytes = new byte[dataBuffer.readableByteCount()];
            dataBuffer.read(bytes);
            return bytes;
        }).onErrorMap(throwable -&gt; new RuntimeException(&quot;Error process file content&quot;));
    }</code></pre><p>Para los test unitarios, puedes importar el archivo en byte[] y lo asignas al MultipartBodyBuilder , con el nombre del &#xA0;Part que vas a extraer en tu funci&#xF3;n.</p><p>Ejemplo:</p><pre><code class="language-java">MultipartBodyBuilder builder  = new MultipartBodyBuilder();
        builder.part(&quot;file&quot;, bytes,MediaType.MULTIPART_FORM_DATA)
                .header(&quot;Content-Disposition&quot;, &quot;form-data; name=file; filename=prueba.xlsx&quot;);     </code></pre><p>y despues se lo asignas al body de tu test unitario.</p><p>Ejemplo.</p><pre><code class="language-java">
    WebTestClient webTestClient;
   @Test
    void validateMultiple() throws URISyntaxException, IOException {

        byte[] bytes = Files.readAllBytes(Paths.get(getClass().getClassLoader()
                .getResource(&quot;prueba.xlsx&quot;).toURI()));

        MultipartBodyBuilder builder  = new MultipartBodyBuilder();
        builder.part(&quot;file&quot;, bytes,MediaType.MULTIPART_FORM_DATA)
                .header(&quot;Content-Disposition&quot;, &quot;form-data; name=file; filename=prueba.xlsx&quot;);

      
        webTestClient.post().uri(uriBuilder -&gt; uriBuilder
                        .path(BASE_ROUTE+ &quot;/upload&quot;)
                        .build())
                .contentType(MediaType.MULTIPART_FORM_DATA)
                .body(BodyInserters.fromMultipartData(builder.build()))
                .exchange()
                .expectStatus()
                .isOk();
    }</code></pre><p>happy code :).</p><p><strong>Referencias</strong>:</p><p><a href="https://www.javadoc.io/doc/org.springframework/spring-web/5.0.3.RELEASE/org/springframework/http/client/MultipartBodyBuilder.html">https://www.javadoc.io/doc/org.springframework/spring-web/5.0.3.RELEASE/org/springframework/http/client/MultipartBodyBuilder.html</a></p>]]></content:encoded></item><item><title><![CDATA[Runs run pipeline azure devops]]></title><description><![CDATA[Rest Api run Azure pipeline.]]></description><link>https://ncleguizamon.co/runs-run-pipeline-azure-devops/</link><guid isPermaLink="false">66248da62dd7ff0001e116f2</guid><category><![CDATA[azure_devops]]></category><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Mon, 05 Dec 2022 16:18:11 GMT</pubDate><media:content url="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/12/Azure.png" medium="image"/><content:encoded><![CDATA[<img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/12/Azure.png" alt="Runs run pipeline azure devops"><p>Azure devops nos permite llamar un api Rest para poder ejecutar cualquier funcionalidad de Azure devops y principalmente poder automatizar un despliegue en cierta hora y de forma autom&#xE1;tica por Azure pipelines.</p><p>1. Para poder ejecutar el api Rest tienes que generar un token de autenticaci&#xF3;n en la plataforma de Azure devops:</p><p></p><figure class="kg-card kg-image-card"><img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/12/image-2.png" class="kg-image" alt="Runs run pipeline azure devops" loading="lazy" width="454" height="446"></figure><p></p><p>Te creas un nuevo token, puedes seleccionar los permisos que quieres ejecutar</p><figure class="kg-card kg-image-card"><img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/12/image-3.png" class="kg-image" alt="Runs run pipeline azure devops" loading="lazy" width="944" height="792"></figure><p>Remplaza los siguientes valores y ejecuta el curl : </p><p><strong>organization:  </strong>El nombre de la organizaci&#xF3;n de Azure DevOps.</p><p><strong>project :  </strong>ID del proyecto o nombre del proyecto</p><figure class="kg-card kg-image-card"><img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/12/image-4.png" class="kg-image" alt="Runs run pipeline azure devops" loading="lazy" width="1471" height="68"></figure><p><strong>pipelineId :</strong> id del pipeline, lo puedes identificar en Azure devops, buscando el pipeline y el en la url, te sale <strong>definitionId. </strong></p><figure class="kg-card kg-image-card"><img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/12/image-5.png" class="kg-image" alt="Runs run pipeline azure devops" loading="lazy" width="581" height="40"></figure><p><strong>TOKEN :</strong> token personal generado, en el paso 1.</p><pre><code class="language-bash">
curl --request POST \
  --url &apos;https://dev.azure.com/{organization}/{project}/_apis/pipelines/{pipelineId}/runs?api-version=7.0&apos; \
  --header &apos;Authorization: Basic {TOKEN}&apos; \
  --header &apos;Content-Type: application/json&apos; \
  --data &apos;{
	&quot;definitionId&quot;: &quot;{pipelineId}&quot;,
	&quot;templateParameters&quot;: {
		&quot;key&quot;: &quot;Value&quot;, 
		
	}
}&apos;</code></pre><p>templateParameters, son los par&#xE1;metros que tenga el pipeline seg&#xFA;n sea el caso.</p><p>Revisa que el pipeline se este ejecutando. </p><p></p><p><strong>Referencias</strong>:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://learn.microsoft.com/en-us/rest/api/azure/devops/pipelines/runs/run-pipeline?view=azure-devops-rest-7.0"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Runs - Run Pipeline - REST API (Azure DevOps Pipelines)</div><div class="kg-bookmark-description">Learn more about Pipelines service - Runs a pipeline.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://learn.microsoft.com/favicon.ico" alt="Runs run pipeline azure devops"><span class="kg-bookmark-author">Microsoft Learn</span><span class="kg-bookmark-publisher">wnjenkin</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://learn.microsoft.com/en-us/media/logos/logo-ms-social.png" alt="Runs run pipeline azure devops"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Apache camel con aws sqs v2]]></title><description><![CDATA[Apache camel integrado a aws sqs ]]></description><link>https://ncleguizamon.co/apache-camel-con-aws-sqs-v2/</link><guid isPermaLink="false">66248da62dd7ff0001e116ec</guid><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Mon, 18 Jul 2022 14:33:58 GMT</pubDate><media:content url="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/07/camel-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/07/camel-1.png" alt="Apache camel con aws sqs v2"><p>Unos de los principales problemas que se tiene a la hora de querer implementar la ultima versi&#xF3;n de apache camel es no tener una buena documentaci&#xF3;n o si la hay no hay ejemplos funcionales, primero hay que tener en cuenta que vamos a trabajar con la version apache camel-core &#xA0;<strong>3.18.0</strong> y de aws v2 <strong>2.17.232.</strong></p><p><strong> </strong>Es muy importante la compatibilidad entre versiones, porque te puede dar muchos errores. </p><p>Dependencias java </p><pre><code class="language-xml">&lt;properties&gt;
		&lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
		&lt;project.reporting.outputEncoding&gt;UTF-8&lt;/project.reporting.outputEncoding&gt;
		&lt;aws.sdk2.version&gt;2.17.232&lt;/aws.sdk2.version&gt;
	&lt;/properties&gt;
&lt;dependencyManagement&gt;
	&lt;dependencies&gt;

	&lt;dependency&gt;
		&lt;groupId&gt;software.amazon.awssdk&lt;/groupId&gt;
		&lt;artifactId&gt;bom&lt;/artifactId&gt;
		&lt;version&gt;${aws.sdk2.version}&lt;/version&gt;
		&lt;type&gt;pom&lt;/type&gt;
		&lt;scope&gt;import&lt;/scope&gt;
	&lt;/dependency&gt;
	&lt;/dependencies&gt;
	&lt;/dependencyManagement&gt;


&lt;dependencies&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
			&lt;artifactId&gt;camel-core&lt;/artifactId&gt;
			&lt;version&gt;3.18.0&lt;/version&gt;
		&lt;/dependency&gt;
		&lt;!-- https://mvnrepository.com/artifact/org.apache.camel/camel-aws2-sqs --&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
			&lt;artifactId&gt;camel-aws2-sqs&lt;/artifactId&gt;
			&lt;version&gt;3.18.0&lt;/version&gt;
		&lt;/dependency&gt;


		&lt;!-- use the same version as your Camel core version --&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;software.amazon.awssdk&lt;/groupId&gt;
			&lt;artifactId&gt;sqs&lt;/artifactId&gt;
		&lt;/dependency&gt;


		&lt;!-- logging --&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;org.slf4j&lt;/groupId&gt;
			&lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
			&lt;version&gt;1.7.7&lt;/version&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;org.slf4j&lt;/groupId&gt;
			&lt;artifactId&gt;slf4j-log4j12&lt;/artifactId&gt;
			&lt;version&gt;1.7.7&lt;/version&gt;
		&lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;log4j&lt;/groupId&gt;
			&lt;artifactId&gt;log4j&lt;/artifactId&gt;
			&lt;version&gt;1.2.17&lt;/version&gt;
		&lt;/dependency&gt;

	&lt;/dependencies&gt;</code></pre><p>En el siguiente fragmento de c&#xF3;digo, creamos el client de aws sqs , y se lo asignamos al contexto de de camel.</p><p>MainApp.java </p><pre><code class="language-java">
public class MainApp {
    public static void main(String... args) throws Exception {
    // create aws sqs client
        SqsClient sqsClient = SqsClient.builder()
                .region(Region.US_EAST_1).build();
            DefaultCamelContext ctx = new DefaultCamelContext();

            try {
                ctx.getRegistry().bind(&quot;sqsClient&quot;, sqsClient);
                ctx.addRoutes(new SQSRouteBuilder());

                ctx.start();

                Thread.sleep(60 * 60 * 1000);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
}</code></pre><p>A continuaci&#xF3;n vamos a configurar el comportamiento, para obtener los mensajes del sqs.</p><p>SQSRouteBuilder.java</p><pre><code class="language-java ">
public class SQSRouteBuilder extends RouteBuilder {
	public void configure() {


		from(&quot;aws2-sqs://demo-camel-test?amazonSQSClient=#sqsClient&quot;)
				.log(&quot;Processing ..... ${body}&quot;)
				.to(&quot;mock:result&quot;)
	}
}
</code></pre><p></p><p><strong>Notas</strong>: &#xA0;</p><ul><li>Revisa &#xA0;las versiones de compatibilidad entre versiones del core de camel y de aws o de los .jar que uses.</li><li>Coloca en tu maquina unas credenciales validas para que te puedas conectar a las colas de sqs </li><li>Si necesitas registrar componentes en la versi&#xF3;n de camel core &#xA0; <strong>3.18.0 , </strong>debes hacerlo con el &#xA0;Register ejemplo </li></ul><pre><code class="language-java ">
MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
// configuration ibm mq 
  ctx.getRegistry().bind(&quot;mq&quot;, JmsComponent.jmsComponentAutoAcknowledge(mqQueueConnectionFactory));
</code></pre><p><strong>Referencias</strong>: </p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://camel.apache.org/components/3.18.x/aws2-sqs-component.html"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Apache Camel helps you integrate over 300 different systems!</div><div class="kg-bookmark-description">Apache Camel &#x2122; is a versatile open-source integration framework based on known Enterprise Integration Patterns. Camel empowers you to define routing and mediation rules in a variety of domain-specific languages, including a Java-based Fluent API, Spring or Blueprint XML Configuration files, and a Sc&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://camel.apache.org/favicon-196x196.png" alt="Apache camel con aws sqs v2"><span class="kg-bookmark-author">Apache Camel</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://camel.apache.org/_/img/logo-d-f21b25ba38.svg" alt="Apache camel con aws sqs v2"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://tomd.xyz/camel-standalone-example/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Running Apache Camel standalone</div><div class="kg-bookmark-description">How to run Apache Camel in a standalone Java app</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://tomd.xyz/favicon.ico" alt="Apache camel con aws sqs v2"><span class="kg-bookmark-author">Tom Donohue</span><span class="kg-bookmark-publisher">Tom Donohue</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://tomd.xyz/assets/img/camel-standalone-header.png" alt="Apache camel con aws sqs v2"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Variables de entorno con junit 5]]></title><description><![CDATA[<p>Cuando estas testeando c&#xF3;digo que por alguna raz&#xF3;n dependa de alguna variable de entorno o Properties en java puedes usar junit-jupiter.</p><p>Variable que se quiere mockear en &#xA0;java </p><pre><code class="language-java"> String testVarible = System.getenv(&quot;testVarible&quot;);</code></pre><h3 id="dependencies"><strong> Dependencies</strong></h3><p></p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.junit.jupiter&</code></pre>]]></description><link>https://ncleguizamon.co/variables-de-entorno-con-junit-5/</link><guid isPermaLink="false">66248da62dd7ff0001e116eb</guid><category><![CDATA[java]]></category><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Thu, 23 Jun 2022 21:59:27 GMT</pubDate><media:content url="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/06/junit-5.png" medium="image"/><content:encoded><![CDATA[<img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/06/junit-5.png" alt="Variables de entorno con junit 5"><p>Cuando estas testeando c&#xF3;digo que por alguna raz&#xF3;n dependa de alguna variable de entorno o Properties en java puedes usar junit-jupiter.</p><p>Variable que se quiere mockear en &#xA0;java </p><pre><code class="language-java"> String testVarible = System.getenv(&quot;testVarible&quot;);</code></pre><h3 id="dependencies"><strong> Dependencies</strong></h3><p></p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.junit.jupiter&lt;/groupId&gt;
    &lt;artifactId&gt;junit-jupiter&lt;/artifactId&gt;
    &lt;version&gt;5.8.1&lt;/version&gt;
    &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;</code></pre><p>Ejemplo:</p><pre><code class="language-java">import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;


import org.junit.jupiter.api.Test;
import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
import uk.org.webcompere.systemstubs.jupiter.SystemStub;

@ExtendWith(SystemStubsExtension.class)
public class test {
    @SystemStub
    private static EnvironmentVariables environmentVariables=new EnvironmentVariables();


    @BeforeAll
    public static void beforeAll() {
        environmentVariables.set(&quot;testVarible&quot;, &quot;value1&quot;);
    }
    
      @Test
    public void TestEven1() throws FileNotFoundException {
    
            environmentVariables.set(&quot;testVarible&quot;,  &quot;EstaEsMiVariable&quot;);

    
    }</code></pre><p>si usas JUnit 4 podr&#xED;as usar</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://ncleguizamon.co/crea-variables-de-entorno-para-test-java/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Crea variables de entorno para test junit java</div><div class="kg-bookmark-description">Agrega variables de entorno para test junit java.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://ncleguizamon.co/favicon.png" alt="Variables de entorno con junit 5"><span class="kg-bookmark-author">ncleguizamon</span><span class="kg-bookmark-publisher">Nestor Camilo Leguizam&#xF3;n</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://ncleguizamon.cohttps://cdn.ncleguizamon.co/apps/blog-ncle/2021/03/java-img-.jpg" alt="Variables de entorno con junit 5"></div></a></figure><p>happy code :).</p><p><strong><strong>Referencia</strong></strong></p><p><a href="https://junit.org/junit5/docs/current/user-guide/">https://junit.org/junit5/docs/current/user-guide/</a></p><p><a href="https://www.baeldung.com/java-system-stubs">https://www.baeldung.com/java-system-stubs</a></p>]]></content:encoded></item><item><title><![CDATA[ETL aws glue desarrollo local]]></title><description><![CDATA[aws glue test local]]></description><link>https://ncleguizamon.co/etl-aws-glue-desarrollo-local/</link><guid isPermaLink="false">66248da62dd7ff0001e116ea</guid><category><![CDATA[glue]]></category><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Wed, 18 May 2022 02:49:48 GMT</pubDate><media:content url="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/05/jupiter-logo.png" medium="image"/><content:encoded><![CDATA[<img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/05/jupiter-logo.png" alt="ETL aws glue desarrollo local"><p>Unos de los principales problemas que se tiene cuando comienzas con glue aws es como puedes probar sin tener que desplegar en la consola de aws la cual puede ser compleja y los logs no se pueden visualizar de manera f&#xE1;cil, para poder comprender como funciona tu script. </p><p>Aws nos provee una opcion para poder desarrollar localmente con docker usando una imagen que esta en docker hub &#xA0;<code>amazon/aws-glue-libs:glue_libs_3.0.0_image_01</code> y la ejecuci&#xF3;n del contenedor en un equipo local (Mac/Windows/Linux). Esta imagen de contenedor ha sido probada para detectar trabajos de versi&#xF3;n 3.0 de Spark de AWS Glue.</p><p> Esta imagen contiene lo siguiente:</p><ul><li>Amazon Linux</li><li>Biblioteca de ETL de AWS Glue (<a href="https://github.com/awslabs/aws-glue-libs" rel="noopener noreferrer">aws-glue-libs</a>)</li><li>Apache Spark 3.1.1</li><li>Servidor de historial de Spark</li><li>Laboratorio de Jupyter</li><li>Livy</li><li>Otras dependencias de biblioteca ( AWS Glue)</li></ul><p><strong>Configuracion</strong> </p><pre><code class="language-bash">docker pull amazon/aws-glue-libs:glue_libs_1.0.0_image_01</code></pre><p>Levantar contenedor Jupyter notebooks</p><pre><code class="language-bash">docker run -itd -p 8888:8888 -p 4040:4040 -v ~/.aws:/root/.aws:ro --name glue_jupyter \amazon/aws-glue-libs:glue_libs_1.0.0_image_01 \
/home/jupyter/jupyter_start.sh</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/05/jupiter-1-1.png" class="kg-image" alt="ETL aws glue desarrollo local" loading="lazy"></figure><p></p><figure class="kg-card kg-image-card"><img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/05/jupiter-2.png" class="kg-image" alt="ETL aws glue desarrollo local" loading="lazy"></figure><h5 id="spark-submit"><strong>spark-submit</strong></h5><p>Puede ejecutar un script de trabajo de AWS Glue ejecutando el comando <code>spark-submit</code> en el contenedor, puedes ejecutar tus scripts </p><pre><code class="language-yaml">$ docker run -it -v ~/.aws:/home/glue_user/.aws -v $WORKSPACE_LOCATION:/home/glue_user/workspace/ -e AWS_PROFILE=$PROFILE_NAME -e DISABLE_SSL=true --rm -p 4040:4040 -p 18080:18080 --name glue_spark_submit amazon/aws-glue-libs:glue_libs_3.0.0_image_01 spark-submit /home/glue_user/workspace/src/$SCRIPT_FILE_NAME
...22/01/26 09:08:55 INFO DAGScheduler: Job 0 finished: fromRDD at DynamicFrame.scala:305, took 3.639886 s
root
|-- family_name: string
|-- name: string
|-- links: array
| |-- element: struct
| | |-- note: string
| | |-- url: string
|-- gender: string
|-- image: string
|-- identifiers: array
| |-- element: struct
| | |-- scheme: string
| | |-- identifier: string
|-- other_names: array
| |-- element: struct
| | |-- lang: string
| | |-- note: string
| | |-- name: string
|-- sort_name: string
|-- images: array
| |-- element: struct
| | |-- url: string
|-- given_name: string
|-- birth_date: string
|-- id: string
|-- contact_details: array
| |-- element: struct
| | |-- type: string
| | |-- value: string
|-- death_date: string

...</code></pre><p></p><p><strong>Referencia</strong></p><p><a href="https://noise.getoto.net/2020/09/21/building-an-aws-glue-etl-pipeline-locally-without-an-aws-account/">https://noise.getoto.net/2020/09/21/building-an-aws-glue-etl-pipeline-locally-without-an-aws-account/</a></p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.aws.amazon.com/es_es/glue/latest/dg/aws-glue-programming-etl-libraries.html"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Desarrollo y pruebas de scripts de trabajo de AWS Glue - AWS Glue</div><div class="kg-bookmark-description">Utilice la biblioteca Scala de AWS Glue disponible p&#xFA;blicamente para desarrollar y probar localmente sus scripts de ETL de Python o Scala.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://docs.aws.amazon.com/assets/images/favicon.ico" alt="ETL aws glue desarrollo local"><span class="kg-bookmark-author">AWS Glue</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://docs.aws.amazon.com/es_es/glue/latest/dg/images/docker-jupyter-lab-1.png" alt="ETL aws glue desarrollo local"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Instalar Sonarqube docker-compose]]></title><description><![CDATA[app[][o.s.a.ProcessLauncherImpl] Launch process[[key='es', ipcIndex=1, logFilenamePrefix=es]] from [/opt/sonarqube/elasticsearch]: /opt/sonarqube/elasticsearch/bin/elasticsearch]]></description><link>https://ncleguizamon.co/instalar-sonarqube-docker-compose/</link><guid isPermaLink="false">66248da62dd7ff0001e116e7</guid><category><![CDATA[docker]]></category><category><![CDATA[docker-compose]]></category><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Fri, 01 Apr 2022 17:42:05 GMT</pubDate><media:content url="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/04/sonar.png" medium="image"/><content:encoded><![CDATA[<img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2022/04/sonar.png" alt="Instalar Sonarqube docker-compose"><p>Unos de los principales problemas a la hora de instalar sonarqube en tu maquina local con docker nos sale el siguiente error, o por problema de memoria </p><blockquote>app[][o.s.a.ProcessLauncherImpl] Launch process[[key=&apos;es&apos;, ipcIndex=1, logFilenamePrefix=es]] from [/opt/sonarqube/elasticsearch]: /opt/sonarqube/elasticsearch/bin/elasticsearch</blockquote><p>se puede arreglar con docker compose &#xA0;</p><p><strong>Creamos los directorios</strong></p><figure class="kg-card kg-code-card"><pre><code class="language-bash"> 
mkdir -p ./sonarqube/data
mkdir -p ./sonarqube/extensions
mkdir -p ./sonarqube/logs
mkdir -p ./postgresql/conf
mkdir -p ./postgresql/data</code></pre><figcaption>Directorios a crear</figcaption></figure><p><strong>Creamos dos archivos </strong></p><pre><code class="language-yaml"> 
&#x251C;&#x2500;&#x2500; docker-compose.yml
&#x251C;&#x2500;&#x2500; init.sh</code></pre><p>modificamos el archivo docker-compose.yml</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml">
version: &quot;3.7&quot;
services:
  sonarqube:
    image: sonarqube:8.9.7-community
    depends_on:
      - db
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: sonar
    
    volumes:
      
      - type: bind
        source: ./sonarqube/data
        target: /opt/sonarqube/data
      - type: bind
        source: ./sonarqube/extensions
        target: /opt/sonarqube/extensions
      - type: bind
        source: ./sonarqube/logs
        target: /opt/sonarqube/logs
    ports:
      - &quot;9000:9000&quot;
  init:
      image: bash
      privileged: true
      user: root
      volumes:
        - ./init.sh:/mnt/init.sh 
      command: [&quot;sh&quot;, &quot;-e&quot;, &quot;/mnt/init.sh&quot;]
  db:
    image: postgres:12
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar
    volumes:
      - type: bind
        source: ./postgresql/conf
        target: /var/lib/postgresql
      - type: bind
        source: ./postgresql/data
        target: /var/lib/postgresql/data

</code></pre><figcaption>docker-compose.yml</figcaption></figure><p>modificamos el archivo init.sh</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">
sysctl -w vm.max_map_count=524288
sysctl -w fs.file-max=131072
</code></pre><figcaption>init.sh</figcaption></figure><p>Ejecutamos en la terminal</p><pre><code class="language-bash">
docker-compose up -d</code></pre><p>Verificamos que est&#xE9;n corriendo los dos contenedores </p><pre><code class="language-bash">
docker container ls
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS                    NAMES
5deca7c22ea4        sonarqube:8.9.7-community   &quot;bin/run.sh bin/sona&#x2026;&quot;   20 hours ago        Up 39 seconds       0.0.0.0:9000-&gt;9000/tcp   sonar_sonarqube_1
ca515bffb4bb        postgres:12                 &quot;docker-entrypoint.s&#x2026;&quot;   20 hours ago        Up 40 seconds       5432/tcp                 sonar_db_1
</code></pre><p></p><p>Referencia:</p><p><a href="https://hub.docker.com/_/sonarqube">https://hub.docker.com/_/sonarqube</a></p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://stackoverflow.com/questions/71339914/how-can-docker-compose-run-comands-as-no-root-user"><div class="kg-bookmark-content"><div class="kg-bookmark-title">how can docker-compose run comands as no-root user</div><div class="kg-bookmark-description">how can docker-compose run comands as no-root user? My docker-compose.yml just show below:version: &amp;quot;3&amp;quot;services: sonarqube-h: image: sonarqube:community user: &amp;quot;${UID}:${G...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon.png?v=c78bd457575a" alt="Instalar Sonarqube docker-compose"><span class="kg-bookmark-author">Stack Overflow</span><span class="kg-bookmark-publisher">cao ting</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon@2.png?v=73d79a89bded" alt="Instalar Sonarqube docker-compose"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Error crear dominio personalizado cognito]]></title><description><![CDATA[Custom domain is not a valid subdomain: Was not able to resolve the root domain, please ensure an A record exists for the root domain. (Service: AWSCognitoIdentityProviderService; Status Code: 400; Error Code: InvalidParameterException; Request ID:  ; Proxy: null)]]></description><link>https://ncleguizamon.co/error-crear-dominio-personalizado-cognito/</link><guid isPermaLink="false">66248da62dd7ff0001e116e2</guid><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Thu, 16 Dec 2021 16:25:40 GMT</pubDate><media:content url="https://cdn.ncleguizamon.co/apps/blog-ncle/2021/12/cognito.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2021/12/cognito.jpg" alt="Error crear dominio personalizado cognito"><p></p><p></p><p>Para resolver el problema, intent&#xE9; agregar &quot;registros A&quot; para el dominio de &#xE1;pice &quot;test.com&quot; junto con el subdominio de primer nivel &quot;prod.test.com&quot; y el subdominio de segundo nivel &quot;auth.prod.test.com&quot;. </p><p>Esto cre&#xF3; el dominio personalizado requerido &quot;dev.auth.prod.test.com&quot; en el grupo de usuarios de manera apropiada. Adem&#xE1;s de esto, prob&#xE9; otro escenario en el que elimin&#xE9; los &quot;registros A&quot; para el dominio apex &quot;test.com&quot; y el primer nivel subdominio &quot;prod.test.com&quot; y se registra un recorpset &#xA0;&quot;registros A&quot; para el subdominio de segundo nivel &quot;auth.prod.test.com&quot; solamente , se pudo crear el dominio.</p><p>para validar </p><pre><code class="language-bash">But if we do
$ dig A auth.prod.test.com +short

</code></pre><p>para concluir si nesecitas un dominio <strong>dev.auth.prod.test.com </strong>necesitas un recorpset de tipo A , al sub dominio<strong> auth.prod.test.com,</strong> puede ser cualquier ip, lo importante es que exista el registro de tipo A.</p><p>happy code .</p>]]></content:encoded></item><item><title><![CDATA[Auto aprovisionamiento y desa aprovisionamiento de lambdas aws]]></title><description><![CDATA[Auto aprovisionamiento y desa aprovisionamiento de lambdas automáticamente. ]]></description><link>https://ncleguizamon.co/auto-aprovisionamiento-y-desa-aprovisionamiento-de-lambdas-aws-2/</link><guid isPermaLink="false">66248da62dd7ff0001e116dd</guid><category><![CDATA[lambdas]]></category><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Fri, 12 Nov 2021 21:25:00 GMT</pubDate><media:content url="https://cdn.ncleguizamon.co/apps/blog-ncle/2021/11/cloudNative.png" medium="image"/><content:encoded><![CDATA[<img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2021/11/cloudNative.png" alt="Auto aprovisionamiento y desa aprovisionamiento de lambdas aws"><p>Unos de los principales problemas que se tiene con lambdas aprovisionadas es su alto costo el cual podr&#xED;a llegar a ser demasiado alto.</p><figure class="kg-card kg-image-card"><img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2021/11/cloudNative-1.png" class="kg-image" alt="Auto aprovisionamiento y desa aprovisionamiento de lambdas aws" loading="lazy"></figure><p>El el post anterior les mostr&#xE9; como se proporcionaba con <strong>ApplicationAutoScaling </strong>de amazon </p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://ncleguizamon.co/provisioned-concurrency-for-lambda-functions/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Provisioned Concurrency for Lambda Functions</div><div class="kg-bookmark-description">Aprovisiona funciones lambdas con pol&#xED;ticas de escalado seg&#xFA;n la concurrencia en aws.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://ncleguizamon.co/favicon.png" alt="Auto aprovisionamiento y desa aprovisionamiento de lambdas aws"><span class="kg-bookmark-author">ncleguizamon</span><span class="kg-bookmark-publisher">Nestor Camilo Leguizam&#xF3;n</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://ncleguizamon.cohttps://cdn.ncleguizamon.co/apps/blog-ncle/2021/06/concurrency.png" alt="Auto aprovisionamiento y desa aprovisionamiento de lambdas aws"></div></a></figure><p>hay una sesi&#xF3;n de <strong>AWS::ApplicationAutoScaling::ScalableTarget </strong>el cual se llama<strong> </strong> &#xA0;<strong>ScheduledActions, </strong>el cual nos permite<strong> </strong>ejecutar acciones autom&#xE1;ticas cada cierto tiempo y modificar nuestro aprovisionamiento, por ejemplo un aprovisionamiento como una desaprovechamiento despu&#xE9;s de cierta hora.</p><pre><code class="language-yaml">  ..............
  
   ScheduledActions:                                  
        - ScalableTargetAction:
            MinCapacity: 5
            MaxCapacity: 5
          Schedule: &apos;cron(0 30 17 * * ?)&apos;
          #Schedule: rate(10 minutes)
          ScheduledActionName: scale-out
          Timezone: &quot;America/Bogota&quot;
        - ScalableTargetAction:
            MinCapacity: 0
            MaxCapacity: 0
          Schedule: &apos;cron(0 50 7 * * ?)&apos;
          #Schedule: rate(5 minutes)
          ScheduledActionName: scale-in
          Timezone: &quot;America/Bogota&quot;
          
  ..................</code></pre><p>pueden consultar la informaci&#xF3;n oficial en <a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationautoscaling-scalabletarget-scheduledaction.html">https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationautoscaling-scalabletarget-scheduledaction.html</a></p><p>Ejemplo de template <strong>Cloudformation</strong>:</p><pre><code class="language-yaml">AWSTemplateFormatVersion: &apos;2010-09-09&apos;
Transform: &apos;AWS::Serverless-2016-10-31&apos;
Description: Escalin api 


Parameters: 
  LambdaFunction:
    Type: String
    Default: &quot;CreateOrder&quot;
  


Resources:
  lambdaByScalableTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Condition: CognitoLambdaTriggerActivateAutoscaling
    Properties:
      MaxCapacity: 0
      MinCapacity: 0
      ResourceId: !Sub function:${LambdaFunction}:production # You need to specify an alis or version here
      RoleARN: !Sub arn:aws:iam::${AWS::AccountId}:role/aws-service-role/lambda.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_LambdaConcurrency
      ScalableDimension: lambda:function:ProvisionedConcurrency
      ServiceNamespace: lambda
      ScheduledActions:                                  
        - ScalableTargetAction:
            MinCapacity: 5
            MaxCapacity: 5
          Schedule: &apos;cron(0 30 17 * * ?)&apos;
          #Schedule: rate(10 minutes)
          ScheduledActionName: scale-out
          Timezone: &quot;America/Bogota&quot;
        - ScalableTargetAction:
            MinCapacity: 0
            MaxCapacity: 0
          Schedule: &apos;cron(0 50 7 * * ?)&apos;
          #Schedule: rate(5 minutes)
          ScheduledActionName: scale-in
          Timezone: &quot;America/Bogota&quot;
      SuspendedState:
        DynamicScalingInSuspended: false
        DynamicScalingOutSuspended: false
        ScheduledScalingSuspended: false

  lambdaTargetTrackingScalingPolicy:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    Properties:
      PolicyName: utilization
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref TriggerByScalableTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 0.7
        PredefinedMetricSpecification:
          PredefinedMetricType: LambdaProvisionedConcurrencyUtilization</code></pre><p></p><p>para ver las actividades que se han ejecutado en las lambda </p><pre><code class="language-bash">aws application-autoscaling describe-scaling-activities \
--service-namespace lambda \
--scalable-dimension lambda:function:ProvisionedConcurrency \
--resource-id function:CreateOrder:prod</code></pre><figure class="kg-card kg-code-card"><pre><code class="language-bash">{
    &quot;ScalingActivities&quot;: [
        {
            &quot;ActivityId&quot;: &quot;6d2bf4ed-6eb5-4218-b4bb-f81fe6d7446e&quot;,
            &quot;ServiceNamespace&quot;: &quot;lambda&quot;,
            &quot;ResourceId&quot;: &quot;function:CreateOrder:prod&quot;,
            &quot;ScalableDimension&quot;: &quot;lambda:function:ProvisionedConcurrency&quot;,
            &quot;Description&quot;: &quot;Setting desired concurrency to 0.&quot;,
            &quot;Cause&quot;: &quot;maximum capacity was set to 0&quot;,
            &quot;StartTime&quot;: 1596374119.716,
            &quot;EndTime&quot;: 1596374155.789,
            &quot;StatusCode&quot;: &quot;Successful&quot;,
            &quot;StatusMessage&quot;: &quot;Successfully set desired concurrency to 0. Change successfully fulfilled by lambda.&quot;
        },
        {
            &quot;ActivityId&quot;: &quot;2ea46a62-2dbe-4576-aa42-0675b6448f0a&quot;,
            &quot;ServiceNamespace&quot;: &quot;lambda&quot;,
            &quot;ResourceId&quot;: &quot;function:CreateOrder:prod&quot;,
            &quot;ScalableDimension&quot;: &quot;lambda:function:ProvisionedConcurrency&quot;,
            &quot;Description&quot;: &quot;Setting min capacity to 0 and max capacity to 0&quot;,
            &quot;Cause&quot;: &quot;scheduled action name scale-in was triggered&quot;,
            &quot;StartTime&quot;: 1596374119.415,
            &quot;EndTime&quot;: 1596374119.431,
            &quot;StatusCode&quot;: &quot;Successful&quot;,
            &quot;StatusMessage&quot;: &quot;Successfully set min capacity to 0 and max capacity to 0&quot;
        },
----------------------------</code></pre><figcaption>output</figcaption></figure><p>happy code .</p><p><strong>Referencia:</strong></p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://aws.amazon.com/es/blogs/compute/scheduling-aws-lambda-provisioned-concurrency-for-recurring-peak-usage/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Scheduling AWS Lambda Provisioned Concurrency for recurring peak usage | Amazon Web Services</div><div class="kg-bookmark-description">This post is contributed by Jerome Van Der Linden, AWS Solutions Architect Concurrency of an AWS Lambda function is the number of requests it can handle at any given time. This metric is the average number of requests per second multiplied by the average duration in seconds. For example, if a Lambda&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://a0.awsstatic.com/main/images/site/touch-icon-ipad-144-smile.png" alt="Auto aprovisionamiento y desa aprovisionamiento de lambdas aws"><span class="kg-bookmark-author">Amazon Web Services</span><span class="kg-bookmark-publisher">Chris Munns</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://d2908q01vomqb2.cloudfront.net/1b6453892473a467d07372d45eb05abc2031647a/2020/08/13/800ms-perf.png" alt="Auto aprovisionamiento y desa aprovisionamiento de lambdas aws"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-as-scheduledaction.html"><div class="kg-bookmark-content"><div class="kg-bookmark-title">AWS::AutoScaling::ScheduledAction - AWS CloudFormation</div><div class="kg-bookmark-description">Use the AWS CloudFormation AWS::AutoScaling::ScheduledAction resource for AutoScaling.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://docs.aws.amazon.com/assets/images/favicon.ico" alt="Auto aprovisionamiento y desa aprovisionamiento de lambdas aws"><span class="kg-bookmark-author">AWS CloudFormation</span></div></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Recibir parámetros de jenkins con un archivo groovy]]></title><description><![CDATA[Parámetros de jenkins en Pipeline: Groovy
]]></description><link>https://ncleguizamon.co/recibir-parametros-de-jenkins-con-un-archivo-groovy/</link><guid isPermaLink="false">66248da62dd7ff0001e116db</guid><category><![CDATA[jenkins]]></category><dc:creator><![CDATA[Nestor Camilo Leguizamon]]></dc:creator><pubDate>Wed, 15 Sep 2021 23:26:49 GMT</pubDate><media:content url="https://cdn.ncleguizamon.co/apps/blog-ncle/2021/09/How-to-run-groovy-script-in-Jenkins.png" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card"><img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2021/09/groovy-app-1.png" class="kg-image" alt="Recibir par&#xE1;metros de jenkins con un archivo groovy" loading="lazy"></figure><img src="https://cdn.ncleguizamon.co/apps/blog-ncle/2021/09/How-to-run-groovy-script-in-Jenkins.png" alt="Recibir par&#xE1;metros de jenkins con un archivo groovy"><p>Jenkins nos permite automatizar y parametrizar nuestros despliegues de una forma muy sencilla y f&#xE1;cil , pero &#xFA;ltimamente he estado utilizando plantillas de terceros los cuales no puedo acceder al c&#xF3;digo fuente y agregarle par&#xE1;metros por lo cual &#xA0;no me permite ser din&#xE1;mico y me a limitado y para hacer diferentes despliegues tengo que modificar directamente el el repositorio.</p><p>Por lo cual e estado buscando una manera que me permita ingresar dichos par&#xE1;metros de forma din&#xE1;mica, lo primero que se identifico es que no se debe usar la estructura b&#xE1;sica del pipeline e jenkins sino usar &#xA0;un archivo groovy .</p><pre><code class="language-yaml">.......

node() {
    properties([
        parameters([
        booleanParam(
            defaultValue: false,
            description: &apos;isFoo should be false&apos;,
            name: &apos;isFoo&apos;
        ),
        stringParam(
            defaultValue: &quot;Nestor&quot;,
            description: &apos;Name user&apos;,
            name: &apos;Name&apos;
        ),
           ])

    ])
    print &quot;DEBUG: parameter isFoo = ${params.isFoo}&quot;
   sh &quot;echo sh isFoo is ${params.isFoo}&quot;
    
//Comandos de template 
.....
}</code></pre><p>Para obtener o usar los par&#xE1;metros los podemos usar como hacemos en un pipeline normal ejemplo ${params.isFoo} .</p><p><strong>Opciones de par&#xE1;metros</strong></p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th style="text-align:left">Tipo de Parametro</th>
<th style="text-align:center">Descripci&#xF2;n</th>
<th style="text-align:right">Parametro</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">String</td>
<td style="text-align:center">Parametros de tipo texto</td>
<td style="text-align:right">stringParam</td>
</tr>
<tr>
<td style="text-align:left">boolean</td>
<td style="text-align:center">Parametros de tipo texto</td>
<td style="text-align:right">booleanParam</td>
</tr>
<tr>
<td style="text-align:left">Select option</td>
<td style="text-align:center">lista de Parametros</td>
<td style="text-align:right">choiceParam</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p><strong>Nota</strong>: la lista anterior solo son unos de los que encontrado y probado.</p><p><strong>Referencia</strong></p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://stackoverflow.com/questions/28572080/how-to-access-parameters-in-a-parameterized-build"><div class="kg-bookmark-content"><div class="kg-bookmark-title">How to access parameters in a Parameterized Build?</div><div class="kg-bookmark-description">How do you access parameters set in the &#x201C;This build is parameterized&#x201D; section of a &#x201C;Workflow&#x201D; Jenkins job? TEST CASE Create a WORKFLOW job.Enable &#x201C;This build is parameterized&#x201D;.Add a STRING PAR...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon.png?v=c78bd457575a" alt="Recibir par&#xE1;metros de jenkins con un archivo groovy"><span class="kg-bookmark-author">Stack Overflow</span><span class="kg-bookmark-publisher">Vizionz</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon@2.png?v=73d79a89bded" alt="Recibir par&#xE1;metros de jenkins con un archivo groovy"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.jenkins.io/doc/pipeline/steps/workflow-cps/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Pipeline: Groovy</div><div class="kg-bookmark-description">Jenkins &#x2013; an open source automation server which enables developers around the world to reliably build, test, and deploy their software</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.jenkins.io/apple-touch-icon.png" alt="Recibir par&#xE1;metros de jenkins con un archivo groovy"><span class="kg-bookmark-author">Pipeline: Groovy</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.jenkins.io/images/logo-title-opengraph.png" alt="Recibir par&#xE1;metros de jenkins con un archivo groovy"></div></a></figure>]]></content:encoded></item></channel></rss>