diff --git a/.github/workflows/client-docs.yml b/.github/workflows/client-docs.yml index b961c1e..33eee07 100644 --- a/.github/workflows/client-docs.yml +++ b/.github/workflows/client-docs.yml @@ -3,8 +3,11 @@ name: Update phplist-api-client OpenAPI on: push: branches: - - '**' + - dev + - main pull_request: + branches: + - main jobs: generate-openapi: diff --git a/.github/workflows/front-docs.yml b/.github/workflows/front-docs.yml new file mode 100644 index 0000000..46d544d --- /dev/null +++ b/.github/workflows/front-docs.yml @@ -0,0 +1,120 @@ +name: Update phplist-web-frontend OpenAPI + +on: + push: + branches: + - dev + - main + pull_request: + branches: + - main +jobs: + generate-openapi: + runs-on: ubuntu-22.04 + outputs: + source_branch: ${{ steps.branch.outputs.source_branch }} + steps: + - name: Determine source branch + id: branch + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + echo "source_branch=${{ github.head_ref }}" >> "$GITHUB_OUTPUT" + else + echo "source_branch=${{ github.ref_name }}" >> "$GITHUB_OUTPUT" + fi + + - name: Checkout Source Repository + uses: actions/checkout@v3 + + - name: Setup PHP with Composer and Extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + extensions: mbstring, dom, fileinfo, mysql + + - name: Cache Composer Dependencies + uses: actions/cache@v3 + with: + path: ~/.composer/cache + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Install Composer Dependencies + run: composer install --no-interaction --prefer-dist + + - name: Generate OpenAPI Specification JSON + run: vendor/bin/openapi -o docs/latest-restapi.json --format json src + + - name: Upload OpenAPI Artifact + uses: actions/upload-artifact@v4 + with: + name: openapi-json + path: docs/latest-restapi.json + + update-web-frontend: + runs-on: ubuntu-22.04 + needs: generate-openapi + env: + TARGET_BRANCH: ${{ needs.generate-openapi.outputs.source_branch }} + steps: + - name: Checkout phpList-web-frontend Repository + uses: actions/checkout@v3 + with: + repository: phpList/web-frontend + token: ${{ secrets.PUSH_WEB_FRONTEND }} + fetch-depth: 0 + + - name: Prepare target branch + run: | + git fetch origin + + if git ls-remote --exit-code --heads origin "$TARGET_BRANCH" >/dev/null 2>&1; then + git checkout "$TARGET_BRANCH" + git pull --rebase origin "$TARGET_BRANCH" + else + git checkout -b "$TARGET_BRANCH" + fi + + - name: Download Generated OpenAPI JSON + uses: actions/download-artifact@v4 + with: + name: openapi-json + path: ./new-openapi + + - name: Compare and Check for Differences + id: diff + run: | + # Compare the openapi files if old exists, else always deploy + if [ -f openapi.json ]; then + diff openapi.json new-openapi/latest-restapi.json > openapi-diff.txt || true + if [ -s openapi-diff.txt ]; then + echo "diff=true" >> "$GITHUB_OUTPUT" + else + echo "diff=false" >> "$GITHUB_OUTPUT" + fi + else + echo "No previous openapi.json, will add." + echo "diff=true" >> "$GITHUB_OUTPUT" + fi + + - name: Update and Commit OpenAPI File + if: steps.diff.outputs.diff == 'true' + run: | + set -euo pipefail + cp new-openapi/latest-restapi.json openapi.json + git config user.name "github-actions" + git config user.email "github-actions@web-frontend.workflow" + git add openapi.json + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "Update openapi.json from web frontend workflow $(date -u +"%Y-%m-%dT%H:%M:%SZ")" + git fetch origin "$TARGET_BRANCH" + git rebase "origin/$TARGET_BRANCH" + git push origin HEAD:"$TARGET_BRANCH" + + - name: Skip Commit if No Changes + if: steps.diff.outputs.diff == 'false' + run: echo "No changes to openapi.json, skipping commit." diff --git a/composer.json b/composer.json index 92598e8..477d302 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,7 @@ }, "require": { "php": "^8.1", - "phplist/core": "dev-main", + "phplist/core": "dev-dev", "friendsofsymfony/rest-bundle": "*", "symfony/test-pack": "^1.0", "symfony/process": "^6.4", diff --git a/src/Messaging/Controller/BounceController.php b/src/Messaging/Controller/BounceController.php index 3ab0794..30587aa 100644 --- a/src/Messaging/Controller/BounceController.php +++ b/src/Messaging/Controller/BounceController.php @@ -81,8 +81,15 @@ public function __construct( response: 200, description: 'Success', content: new OA\JsonContent( - type: 'array', - items: new OA\Items(ref: '#/components/schemas/BounceView') + properties: [ + new OA\Property( + property: 'items', + type: 'array', + items: new OA\Items(ref: '#/components/schemas/BounceView') + ), + new OA\Property(property: 'pagination', ref: '#/components/schemas/CursorPagination') + ], + type: 'object' ) ), new OA\Response( diff --git a/src/Subscription/Controller/SubscribePageController.php b/src/Subscription/Controller/SubscribePageController.php index ef7a59c..1959dd8 100644 --- a/src/Subscription/Controller/SubscribePageController.php +++ b/src/Subscription/Controller/SubscribePageController.php @@ -6,11 +6,13 @@ use Doctrine\ORM\EntityManagerInterface; use OpenApi\Attributes as OA; +use PhpList\Core\Domain\Common\Model\Filter\PaginatedFilter; use PhpList\Core\Domain\Identity\Model\PrivilegeFlag; use PhpList\Core\Domain\Subscription\Model\SubscribePage; use PhpList\Core\Domain\Subscription\Service\Manager\SubscribePageManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Common\Controller\BaseController; +use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider; use PhpList\RestBundle\Common\Validator\RequestValidator; use PhpList\RestBundle\Subscription\Request\SubscribePageDataRequest; use PhpList\RestBundle\Subscription\Request\SubscribePageRequest; @@ -30,10 +32,86 @@ public function __construct( private readonly SubscribePageManager $subscribePageManager, private readonly SubscribePageNormalizer $normalizer, private readonly EntityManagerInterface $entityManager, + private readonly PaginatedDataProvider $paginatedProvider, ) { parent::__construct($authentication, $validator); } + #[Route('/', name: 'get_all', methods: ['GET'])] + #[OA\Get( + path: '/api/v2/subscribe-pages', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production.', + summary: 'Get subscribe pages list', + tags: ['subscriptions'], + parameters: [ + new OA\Parameter( + name: 'php-auth-pw', + description: 'Session key obtained from login', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'after_id', + description: 'Last id (starting from 0)', + in: 'query', + required: false, + schema: new OA\Schema(type: 'integer', default: 1, minimum: 1) + ), + new OA\Parameter( + name: 'limit', + description: 'Number of results per page', + in: 'query', + required: false, + schema: new OA\Schema(type: 'integer', default: 25, maximum: 100, minimum: 1) + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success', + content: new OA\JsonContent( + properties: [ + new OA\Property( + property: 'items', + type: 'array', + items: new OA\Items(ref: '#/components/schemas/SubscribePage') + ), + new OA\Property(property: 'pagination', ref: '#/components/schemas/CursorPagination') + ], + type: 'object' + ) + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 404, + description: 'Not Found', + content: new OA\JsonContent(ref: '#/components/schemas/NotFoundErrorResponse') + ), + ] + )] + public function getPages(Request $request): JsonResponse + { + $admin = $this->requireAuthentication($request); + if (!$admin->getPrivileges()->has(PrivilegeFlag::Subscribers)) { + throw $this->createAccessDeniedException('You are not allowed to view subscribe pages.'); + } + + return $this->json( + $this->paginatedProvider->getPaginatedList( + request: $request, + normalizer: $this->normalizer, + className: SubscribePage::class, + filter: new PaginatedFilter(), + ), + Response::HTTP_OK + ); + } + #[Route('/{id}', name: 'get', requirements: ['id' => '\\d+'], methods: ['GET'])] #[OA\Get( path: '/api/v2/subscribe-pages/{id}',