Skip to content

Commit 574891f

Browse files
authored
Merge pull request #3495 from codeeu/dev
Dev
2 parents d60de0e + eb84d22 commit 574891f

5 files changed

Lines changed: 89 additions & 9 deletions

File tree

app/Services/Support/Gmail/GmailConnector.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
interface GmailConnector
66
{
77
/**
8-
* @return array{messages: GmailMessage[], next_history_id: ?string}
8+
* @return array{messages: GmailMessage[], next_history_id: ?string, warnings: string[]}
99
*/
1010
public function fetchNewMessages(
1111
string $mailbox,

app/Services/Support/Gmail/GmailIngestService.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ public function __construct(
1919
}
2020

2121
/**
22-
* @return array{ingested: int, duplicates: int, cursor_updated: bool}
22+
* @return array{ingested: int, duplicates: int, cursor_updated: bool, warnings: string[]}
2323
*/
2424
public function pollAndIngest(int $max = 25): array
2525
{
2626
if (!config('support_gmail.enabled')) {
27-
return ['ingested' => 0, 'duplicates' => 0, 'cursor_updated' => false];
27+
return ['ingested' => 0, 'duplicates' => 0, 'cursor_updated' => false, 'warnings' => []];
2828
}
2929

3030
$lockName = (string) config('support_gmail.lock.name', 'support:gmail:poll');
@@ -34,7 +34,7 @@ public function pollAndIngest(int $max = 25): array
3434
// to avoid double-ingesting on multiple nodes silently.
3535
$lock = Cache::lock($lockName, $ttlSeconds);
3636
if (!$lock->get()) {
37-
return ['ingested' => 0, 'duplicates' => 0, 'cursor_updated' => false];
37+
return ['ingested' => 0, 'duplicates' => 0, 'cursor_updated' => false, 'warnings' => []];
3838
}
3939

4040
$mailbox = (string) config('support_gmail.user', 'me');
@@ -99,6 +99,7 @@ public function pollAndIngest(int $max = 25): array
9999
'ingested' => $ingested,
100100
'duplicates' => $duplicates,
101101
'cursor_updated' => true,
102+
'warnings' => $result['warnings'] ?? [],
102103
];
103104
} finally {
104105
optional($lock)->release();

app/Services/Support/Gmail/GoogleGmailConnector.php

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Google\Client as GoogleClient;
66
use Google\Service\Gmail as GmailService;
7+
use Google\Service\Gmail\Label as GoogleLabel;
78
use Google\Service\Gmail\Message as GoogleMessage;
89
use Illuminate\Support\Str;
910

@@ -46,18 +47,27 @@ public function fetchNewMessages(
4647
$this->ensureValidToken();
4748

4849
$q = trim($query);
50+
$labelId = null;
51+
$warnings = [];
4952
if ($label) {
50-
// Label scoping is done via labelIds, but keep query readable too.
51-
$q = trim($q.' label:'.Str::of($label)->replace(' ', '-'));
53+
// Label filtering is optional. If the label doesn't exist, we ingest without label scoping
54+
// rather than failing the whole poll.
55+
$labelId = $this->resolveLabelIdOrNull($mailbox, $label);
56+
if ($labelId === null && trim((string) $label) !== '') {
57+
$warnings[] = sprintf(
58+
'Configured Gmail label "%s" was not found; polling without label filter.',
59+
trim((string) $label),
60+
);
61+
}
5262
}
5363

5464
$params = [
5565
'q' => $q,
5666
'maxResults' => $max,
5767
];
5868

59-
if ($label) {
60-
$params['labelIds'] = [$label];
69+
if ($labelId) {
70+
$params['labelIds'] = [$labelId];
6171
}
6272

6373
// V1: we use search-based ingestion; historyId is only used as a stored cursor.
@@ -81,9 +91,34 @@ public function fetchNewMessages(
8191
return [
8292
'messages' => $messages,
8393
'next_history_id' => $nextHistoryId,
94+
'warnings' => $warnings,
8495
];
8596
}
8697

98+
private function resolveLabelIdOrNull(string $mailbox, string $label): ?string
99+
{
100+
$label = trim($label);
101+
if ($label === '') {
102+
return null;
103+
}
104+
105+
// If user already provided a label ID (usually "Label_..."), use it directly.
106+
if (Str::startsWith($label, 'Label_')) {
107+
return $label;
108+
}
109+
110+
$labels = $this->gmail->users_labels->listUsersLabels($mailbox)->getLabels() ?? [];
111+
112+
/** @var GoogleLabel $l */
113+
foreach ($labels as $l) {
114+
if ($l->getId() === $label || $l->getName() === $label) {
115+
return (string) $l->getId();
116+
}
117+
}
118+
119+
return null;
120+
}
121+
87122
private function ensureValidToken(): void
88123
{
89124
$token = $this->client->getAccessToken();

app/Services/Support/Gmail/NullGmailConnector.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public function fetchNewMessages(
1414
return [
1515
'messages' => [],
1616
'next_history_id' => $sinceHistoryId,
17+
'warnings' => [],
1718
];
1819
}
1920
}

tests/Unit/Support/GmailIngestServiceTest.php

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public function fetchNewMessages(
4040
new GmailMessage('m2', 't2', 'Subj 2', 'sender2@example.com', "Hello 2"),
4141
],
4242
'next_history_id' => '123',
43+
'warnings' => [],
4344
];
4445
}
4546
};
@@ -54,6 +55,7 @@ public function fetchNewMessages(
5455

5556
$this->assertSame(2, $res['ingested']);
5657
$this->assertSame(1, $res['duplicates']);
58+
$this->assertSame([], $res['warnings']);
5759

5860
$cursor = SupportGmailCursor::query()->where('mailbox', 'me')->where('label', 'Support-AI')->first();
5961
$this->assertNotNull($cursor);
@@ -89,9 +91,50 @@ public function fetchNewMessages(
8991
);
9092

9193
$res = $svc->pollAndIngest(25);
92-
$this->assertSame(['ingested' => 0, 'duplicates' => 0, 'cursor_updated' => false], $res);
94+
$this->assertSame(['ingested' => 0, 'duplicates' => 0, 'cursor_updated' => false, 'warnings' => []], $res);
9395

9496
$lock->release();
9597
}
98+
99+
public function test_poll_passes_through_connector_warnings(): void
100+
{
101+
config()->set('support_gmail.enabled', true);
102+
config()->set('support_gmail.lock.name', 'test:support:gmail:poll:warnings');
103+
config()->set('support_gmail.lock.ttl_seconds', 30);
104+
config()->set('support_gmail.user', 'me');
105+
config()->set('support_gmail.label', 'Missing-Label');
106+
config()->set('support_gmail.query', 'newer_than:7d');
107+
108+
$fakeConnector = new class implements GmailConnector {
109+
public function fetchNewMessages(
110+
string $mailbox,
111+
?string $label,
112+
string $query,
113+
?string $sinceHistoryId,
114+
int $max = 25,
115+
): array {
116+
return [
117+
'messages' => [],
118+
'next_history_id' => null,
119+
'warnings' => [
120+
'Configured Gmail label "Missing-Label" was not found; polling without label filter.',
121+
],
122+
];
123+
}
124+
};
125+
126+
$svc = new GmailIngestService(
127+
connector: $fakeConnector,
128+
intake: app(CaseIntakeService::class),
129+
logger: app(SupportActionLogger::class),
130+
);
131+
132+
$res = $svc->pollAndIngest(25);
133+
134+
$this->assertSame(
135+
['Configured Gmail label "Missing-Label" was not found; polling without label filter.'],
136+
$res['warnings'],
137+
);
138+
}
96139
}
97140

0 commit comments

Comments
 (0)