Skip to content

Commit

Permalink
Merge pull request #4 from phpcfdi/revision-3.0.1-1
Browse files Browse the repository at this point in the history
Mejoras en la integración contínua y pruebas (versión 3.0.1)
  • Loading branch information
eclipxe13 authored Oct 17, 2024
2 parents 6ee35eb + 7ef90a0 commit c36792d
Show file tree
Hide file tree
Showing 22 changed files with 302 additions and 72 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,18 @@ jobs:
run: phpstan analyse --no-progress --verbose

tests:
name: Tests on PHP ${{ matrix.php-versions }}
name: Tests on PHP ${{ matrix.php-version }}
runs-on: "ubuntu-latest"
strategy:
matrix:
php-versions: ['8.2', '8.3']
php-version: ['8.2', '8.3']
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
php-version: ${{ matrix.php-version }}
coverage: xdebug
tools: composer:v2
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
jobs:

tests-coverage:
name: Tests on PHP 8.3 (code coverage)
name: Create code coverage
runs-on: "ubuntu-latest"
steps:
- name: Checkout
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/system.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ on:
jobs:

system-tests:
name: System test on PHP ${{ matrix.php-versions }}
name: System test on PHP ${{ matrix.php-version }}
runs-on: "ubuntu-latest"
strategy:
matrix:
php-versions: ['8.2', '8.3']
php-version: ['8.2', '8.3']
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
php-version: ${{ matrix.php-version }}
coverage: xdebug
tools: composer:v2
env:
Expand All @@ -42,5 +42,5 @@ jobs:
restore-keys: ${{ runner.os }}-composer-
- name: Install project dependencies
run: composer upgrade --no-interaction --no-progress --prefer-dist --no-dev
- name: System test with PHP ${{ matrix.php-versions }}
- name: System test with PHP ${{ matrix.php-version }}
run: php bin/sat-pys-scraper --json build/result.json --xml build/result.xml --sort key
10 changes: 5 additions & 5 deletions .phive/phars.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="php-cs-fixer" version="^3.51.0" installed="3.51.0" location="./tools/php-cs-fixer" copy="false"/>
<phar name="phpcs" version="^3.9.0" installed="3.9.0" location="./tools/phpcs" copy="false"/>
<phar name="phpcbf" version="^3.9.0" installed="3.9.0" location="./tools/phpcbf" copy="false"/>
<phar name="phpstan" version="^1.10.60" installed="1.10.60" location="./tools/phpstan" copy="false"/>
<phar name="composer-normalize" version="^2.42.0" installed="2.42.0" location="./tools/composer-normalize" copy="false"/>
<phar name="php-cs-fixer" version="^3.64.0" installed="3.64.0" location="./tools/php-cs-fixer" copy="false"/>
<phar name="phpcs" version="^3.10.2" installed="3.10.2" location="./tools/phpcs" copy="false"/>
<phar name="phpcbf" version="^3.10.2" installed="3.10.2" location="./tools/phpcbf" copy="false"/>
<phar name="phpstan" version="^1.12.3" installed="1.12.3" location="./tools/phpstan" copy="false"/>
<phar name="composer-normalize" version="^2.43.0" installed="2.43.0" location="./tools/composer-normalize" copy="false"/>
</phive>
3 changes: 1 addition & 2 deletions Docker.README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ The project installed on `/opt/sat-pys-scraper/` and the entry point is the comm
docker run -it --rm --user="$(id -u):$(id -g)" \
sat-pys-scraper --help

# generar en un volumen
# create output using volume
docker run -it --rm --user="$(id -u):$(id -g)" --volume="${PWD}:/local" \
sat-pys-scraper --xml /local/output.xml


# pipe output to file (xml, sorted by key)
docker run -it --rm --user="$(id -u):$(id -g)" \
sat-pys-scraper --xml - > output.xml
Expand Down
40 changes: 12 additions & 28 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,41 +1,25 @@
FROM debian:bookworm
FROM php:8.3-cli-alpine

COPY . /opt/sat-pys-scraper/
COPY . /opt/sat-pys-scraper
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer

# install dependencies for php modules
RUN set -e \
&& export DEBIAN_FRONTEND=noninteractive \
# Update debian base system
&& apt-get update -y \
&& apt-get dist-upgrade -y \
# Install repository PHP from Ondřej Surý
&& apt-get install -y lsb-release ca-certificates curl \
&& curl --no-progress-meter https://packages.sury.org/php/apt.gpg --output /etc/apt/trusted.gpg.d/php.gpg \
&& echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | tee /etc/apt/sources.list.d/php.list \
&& apt-get update -y \
&& apt-get dist-upgrade -y \
# Install required packages
&& apt-get install -y \
unzip git \
php-cli php-curl php-zip php-xml \
# Clean APT
&& rm -rf /var/lib/apt/lists/*
&& apk add git libzip-dev \
&& docker-php-ext-install zip

# set up php
RUN set -e \
# Set up PHP
&& find /etc/php/ -type f -name "*.ini" -exec sed -i 's/^variables_order.*/variables_order=EGPCS/' "{}" \; \
&& mv /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini \
&& sed -i 's/^variables_order.*/variables_order=EGPCS/' /usr/local/etc/php/php.ini \
&& php -i

# build project
RUN set -e \
# Install composer
&& curl --progress-bar https://getcomposer.org/download/latest-stable/composer.phar --output /usr/local/bin/composer \
&& chmod +x /usr/local/bin/composer \
&& export COMPOSER_ALLOW_SUPERUSER=1 \
&& (composer diagnose --no-interaction || true)

RUN set -e \
&& rm -r -f /opt/sat-pys-scraper/composer.lock /opt/sat-pys-scraper/vendor \
&& composer update --working-dir=/opt/sat-pys-scraper --no-dev --prefer-dist --optimize-autoloader --no-interaction \
&& rm -rf "$(composer config cache-dir --global)" "$(composer config data-dir --global)" "$(composer config home --global)"

ENV TZ="America/Mexico_City"

ENTRYPOINT ["/usr/bin/php", "/opt/sat-pys-scraper/bin/sat-pys-scraper"]
ENTRYPOINT ["/usr/local/bin/php", "/opt/sat-pys-scraper/bin/sat-pys-scraper"]
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2023 PhpCfdi https://www.phpcfdi.com/
Copyright (c) 2023 - 2024 PhpCfdi https://www.phpcfdi.com/

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,20 @@ Un objeto `Classification` solamente contiene las propiedades `key` y `name`.

Todos los objetos de datos implementan `JsonSerializable`, por lo que puedes usar esta característica para exportar a formato JSON.

### Excepciones

La clase `Scraper` y -por consecuencia- también la clase `Generator` generan excepciones.
En el caso de una excepción de tipo HTTP se tira una excepción `HttpException`.
En el caso de una excepción HTTP y tenga un código de error del servicio remoto se tira una excepción `HttpServerException`.

La jerarquía de excepciones es:

```text
- PysException (interface)
- HttpException (class)
- HttpServerException (class)
```

## Soporte

Puedes obtener soporte abriendo un ticket en Github.
Expand Down
23 changes: 21 additions & 2 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,32 @@ versión, aunque sí su incorporación en la rama principal de trabajo. Generalm

## Listado de cambios

### Versión 3.0.0 2023-03-07
### Versión 3.0.1 2024-10-15

La aplicación del SAT devuelve un error 500 frecuentemente (1 de cada 3 veces) desde 2024-07-15.
Este error parece estar relacionado con la distribución de cargas por parte del SAT, así que
reintentar la llamada HTTP sobre la misma conexión no soluciona el problema y hay que crear
un nuevo cliente HTTP. Para intentar solventarlo, se modifica la librería para tirar
excepciones con errores HTTP e intentar solventar el error.

Se cambia la construcción de imagen de docker, ahora depende de `php:8.3-cli-alpine`.

Se actualiza el archivo de licencia a 2024.

Se hacen otros cambios en el entorno de desarrollo:

- Se modifica la prueba funcional para poder hacer hasta 5 reintentos reconstruyendo el cliente http.
- Se prueba el correcto orden para llamar a los métodos para obtener datos.
- Se utiliza la variable `php-version` en singular para las matrices de pruebas.
- Se actualizan las herramientas de desarrollo.

### Versión 3.0.0 2024-03-07

- Se cambia el método `SatPysScraper::run()` para una mejor inyección de dependencias y capacidad de pruebas.
- Se introduce una excepción dedicada para los errores de procesamiento de argumentos.
- Se cambia la forma de procesar los argumentos para usar `array_shift`.

### Versión 2.0.0 2023-03-07
### Versión 2.0.0 2024-03-07

- Se corrige el nodo principal, el nombre correcto es `<pys>`.
- Se cambia el comando de ejecución `bin/sat-pys-scraper` para exportar a JSON y XML al mismo tiempo.
Expand Down
6 changes: 0 additions & 6 deletions docs/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@

## Lista de tareas pendientes

### `phcps`

En el flujo de trabajo `build.yml` usando `actions/setup-php-action` la herramienta `phcps` se está instalando usando `squizlabs` en lugar de `PHPCSStandards`.
Por lo tanto, en lugar de usar la herramienta de la acción, se está usando `phive` para instalarla.
Cuando se actualice la herramienta `actions/setup-php-action` se debe cambiar a la instalación normal.

### PHP 8.3

Migrar a PHP 8.3 en cuanto las herramientas (como *PHP_CodeSniffer*) lo permitan.
Expand Down
11 changes: 11 additions & 0 deletions src/Exceptions/HttpException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace PhpCfdi\SatPysScraper\Exceptions;

use RuntimeException;

class HttpException extends RuntimeException implements PysException
{
}
9 changes: 9 additions & 0 deletions src/Exceptions/HttpServerException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace PhpCfdi\SatPysScraper\Exceptions;

class HttpServerException extends HttpException
{
}
9 changes: 9 additions & 0 deletions src/Exceptions/PysException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace PhpCfdi\SatPysScraper\Exceptions;

interface PysException
{
}
6 changes: 6 additions & 0 deletions src/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace PhpCfdi\SatPysScraper;

use PhpCfdi\SatPysScraper\Exceptions\HttpException;
use PhpCfdi\SatPysScraper\Exceptions\HttpServerException;

readonly class Generator
{
public function __construct(
Expand All @@ -12,6 +15,9 @@ public function __construct(
) {
}

/**
* @throws HttpServerException|HttpException
*/
public function generate(): Data\Types
{
$types = new Data\Types();
Expand Down
59 changes: 43 additions & 16 deletions src/Scraper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace PhpCfdi\SatPysScraper;

use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\RequestOptions;
use LogicException;
use Symfony\Component\DomCrawler\Crawler;
Expand All @@ -14,20 +16,18 @@ final class Scraper implements ScraperInterface
/** @noinspection HttpUrlsUsage */
public const PYS_URL = 'http://pys.sat.gob.mx/PyS/catPyS.aspx';

private Crawler|null $crawler;
private Crawler|null $crawler = null;

public function __construct(private readonly ClientInterface $client)
{
}

/** @return array<int|string, string> */
public function obtainTypes(): array
{
$crawler = $this->sendGet();
return $this->extractSelectValues($crawler, 'cmbTipo');
}

/** @return array<int|string, string> */
public function obtainSegments(int|string $type): array
{
$inputs = [
Expand All @@ -39,7 +39,6 @@ public function obtainSegments(int|string $type): array
return $this->extractSelectValues($crawler, 'cmbSegmento');
}

/** @return array<int|string, string> */
public function obtainFamilies(int|string $type, int|string $segment): array
{
$inputs = [
Expand All @@ -52,7 +51,6 @@ public function obtainFamilies(int|string $type, int|string $segment): array
return $this->extractSelectValues($crawler, 'cmbFamilia');
}

/** @return array<int|string, string> */
public function obtainClasses(int|string $type, int|string $segment, int|string $family): array
{
$inputs = [
Expand All @@ -74,27 +72,41 @@ private function getLastCrawler(): Crawler
return $this->crawler;
}

/**
* @throws Exceptions\HttpException
*/
private function sendGet(): Crawler
{
$response = $this->client->request('GET', self::PYS_URL);
try {
$response = $this->client->request('GET', self::PYS_URL);
} catch (GuzzleException $exception) {
throw $this->wrapGuzzleException($exception);
}
$crawler = new Crawler((string) $response->getBody(), self::PYS_URL);
$this->crawler = $crawler;
return $crawler;
}

/** @param array<string, scalar> $data */
/**
* @param array<string, scalar> $data
* @throws Exceptions\HttpException
*/
private function sendPost(array $data): Crawler
{
$currentState = $this->extractState($this->getLastCrawler());
$response = $this->client->request('POST', self::PYS_URL, [
RequestOptions::HEADERS => [
'Accept-Encoding' => 'gzip, deflate',
'Referer' => self::PYS_URL,
'X-Requested-With' => 'XMLHttpRequest',
'X-Microsoft-Ajax' => 'delta=false',
],
RequestOptions::FORM_PARAMS => array_merge(['__ASYNCPOST' => 'false'], $currentState, $data),
]);
try {
$response = $this->client->request('POST', self::PYS_URL, [
RequestOptions::HEADERS => [
'Accept-Encoding' => 'gzip, deflate',
'Referer' => self::PYS_URL,
'X-Requested-With' => 'XMLHttpRequest',
'X-Microsoft-Ajax' => 'delta=false',
],
RequestOptions::FORM_PARAMS => array_merge(['__ASYNCPOST' => 'false'], $currentState, $data),
]);
} catch (GuzzleException $exception) {
throw $this->wrapGuzzleException($exception);
}
$crawler = new Crawler((string) $response->getBody(), self::PYS_URL);
$this->crawler = $crawler;
return $crawler;
Expand All @@ -117,4 +129,19 @@ private function extractState(Crawler $crawler): array
$form = $crawler->filter('#form1')->form();
return $form->getPhpValues();
}

private function wrapGuzzleException(GuzzleException $exception): Exceptions\HttpException
{
if ($exception instanceof ServerException) {
return new Exceptions\HttpServerException(
message: $exception->getMessage(),
previous: $exception
);
}

return new Exceptions\HttpException(
message: $exception->getMessage(),
previous: $exception
);
}
}
Loading

0 comments on commit c36792d

Please sign in to comment.