Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
93 / 93
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
GenerateVrsyncFeedAction
100.00% covered (success)
100.00%
93 / 93
100.00% covered (success)
100.00%
4 / 4
11
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
73 / 73
100.00% covered (success)
100.00%
1 / 1
6
 normalizePublishedProperties
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 logGeneration
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Publishing\Connectors\GroupOlx\Application\Actions;
6
7use Illuminate\Support\Facades\Log;
8use ImovelZapAi\Publishing\Connectors\GroupOlx\Application\Ports\FeedStorageInterface;
9use ImovelZapAi\Publishing\Connectors\GroupOlx\Application\Ports\GroupOlxFeedBuilderInterface;
10use ImovelZapAi\Publishing\Connectors\GroupOlx\Application\Ports\GroupOlxFeedValidatorInterface;
11use ImovelZapAi\Publishing\Connectors\GroupOlx\Application\Ports\GroupOlxListingNormalizerInterface;
12use ImovelZapAi\Publishing\Connectors\GroupOlx\Application\Ports\GroupOlxPublicFeedInterface;
13use ImovelZapAi\Publishing\Connectors\GroupOlx\Application\Ports\GroupOlxPublicationLogWriterInterface;
14use ImovelZapAi\Publishing\Connectors\GroupOlx\Application\Ports\PublishedGroupOlxPropertiesQueryInterface;
15use ImovelZapAi\Publishing\Connectors\GroupOlx\Domain\Exceptions\GroupOlxMappingException;
16use ImovelZapAi\Publishing\Connectors\GroupOlx\Domain\Exceptions\GroupOlxXmlBuildException;
17use ImovelZapAi\Publishing\Domain\Canonical\NormalizedListing;
18
19/**
20 * Gera o feed VRSync do Grupo OLX — executado apenas pelo worker.
21 *
22 * Fluxo: Property → NormalizedListing → Mapper → Builder → Validator → Storage → PublicationLog
23 */
24final class GenerateVrsyncFeedAction
25{
26    public function __construct(
27        private readonly PublishedGroupOlxPropertiesQueryInterface $publishedProperties,
28        private readonly GroupOlxListingNormalizerInterface $listingNormalizer,
29        private readonly GroupOlxFeedBuilderInterface $feedBuilder,
30        private readonly GroupOlxFeedValidatorInterface $validator,
31        private readonly GroupOlxPublicFeedInterface $publicFeed,
32        private readonly FeedStorageInterface $storage,
33        private readonly GroupOlxPublicationLogWriterInterface $publicationLogger,
34    ) {}
35
36    public function execute(string $tenantId, ?int $triggeredBy = null): bool
37    {
38        $startedAt = microtime(true);
39        $warnings = [];
40
41        try {
42            $normalized = $this->normalizePublishedProperties($tenantId, $warnings);
43        } catch (\Throwable $exception) {
44            Log::warning('Group OLX feed generation failed while loading properties.', [
45                'tenant_id' => $tenantId,
46                'error' => $exception->getMessage(),
47            ]);
48
49            $this->logGeneration(
50                tenantId: $tenantId,
51                listingCount: 0,
52                startedAt: $startedAt,
53                success: false,
54                contentHash: null,
55                errors: [$exception->getMessage()],
56                warnings: [],
57                triggeredBy: $triggeredBy,
58            );
59
60            return false;
61        }
62
63        if ($normalized === []) {
64            $this->storage->delete($tenantId);
65            $this->logGeneration(
66                tenantId: $tenantId,
67                listingCount: 0,
68                startedAt: $startedAt,
69                success: true,
70                contentHash: null,
71                errors: [],
72                warnings: $warnings,
73                triggeredBy: $triggeredBy,
74            );
75
76            return true;
77        }
78
79        try {
80            $build = $this->feedBuilder->build($normalized);
81        } catch (GroupOlxMappingException|GroupOlxXmlBuildException $exception) {
82            $message = $exception instanceof GroupOlxMappingException
83                ? $exception->errors
84                : [$exception->getMessage()];
85
86            $this->logGeneration(
87                tenantId: $tenantId,
88                listingCount: count($normalized),
89                startedAt: $startedAt,
90                success: false,
91                contentHash: null,
92                errors: $message,
93                warnings: $warnings,
94                triggeredBy: $triggeredBy,
95            );
96
97            return false;
98        }
99
100        $validation = $this->validator->validateBuild($build);
101
102        if (! $validation->isValid()) {
103            $this->logGeneration(
104                tenantId: $tenantId,
105                listingCount: $build->listingCount(),
106                startedAt: $startedAt,
107                success: false,
108                contentHash: null,
109                errors: $validation->errors,
110                warnings: $warnings,
111                triggeredBy: $triggeredBy,
112            );
113
114            return false;
115        }
116
117        $this->publicFeed->publish($tenantId, $build->xml, $build->listingCount());
118        $this->logGeneration(
119            tenantId: $tenantId,
120            listingCount: $build->listingCount(),
121            startedAt: $startedAt,
122            success: true,
123            contentHash: hash('sha256', $build->xml),
124            errors: [],
125            warnings: $warnings,
126            triggeredBy: $triggeredBy,
127        );
128
129        return true;
130    }
131
132    /**
133     * @param  list<string>  $warnings
134     * @return list<NormalizedListing>
135     */
136    private function normalizePublishedProperties(string $tenantId, array &$warnings): array
137    {
138        $normalized = [];
139
140        foreach ($this->publishedProperties->execute($tenantId) as $property) {
141            $listing = $this->listingNormalizer->toNormalized($property);
142
143            if (! $listing->isValidForExport()) {
144                $warnings = array_merge($warnings, $listing->exportValidationErrors());
145
146                continue;
147            }
148
149            $normalized[] = $listing;
150        }
151
152        return $normalized;
153    }
154
155    /**
156     * @param  list<string>  $errors
157     * @param  list<string>  $warnings
158     */
159    private function logGeneration(
160        string $tenantId,
161        int $listingCount,
162        float $startedAt,
163        bool $success,
164        ?string $contentHash,
165        array $errors = [],
166        array $warnings = [],
167        ?int $triggeredBy = null,
168    ): void {
169        $durationMs = (int) round((microtime(true) - $startedAt) * 1000);
170
171        $this->publicationLogger->record(
172            tenantId: $tenantId,
173            listingCount: $listingCount,
174            durationMs: $durationMs,
175            success: $success,
176            errors: $errors,
177            warnings: $warnings,
178            triggeredBy: $triggeredBy,
179            contentHash: $contentHash,
180        );
181    }
182}