Remove legacy acceptance criteria storage
linter / quality (pull_request) Successful in 2m4s
tests / ci (8.4) (pull_request) Successful in 1m52s

This commit is contained in:
2026-05-19 23:12:15 +00:00
parent 689db9e41b
commit 5aebd4f9ef
16 changed files with 109 additions and 145 deletions
@@ -51,7 +51,6 @@ class CreateProjectTicket
->findOrFail(Arr::get($attributes, 'ticket_status_id'));
$ticket = Ticket::query()->create([
'acceptance_criteria' => AcceptanceCriteriaChecklist::toText($acceptanceCriteria),
'assignee_user_id' => Arr::get($attributes, 'assignee_user_id'),
'author_user_id' => $actor?->id,
'description' => Arr::get($attributes, 'description'),
@@ -15,7 +15,7 @@ class AcceptanceCriteriaChecklist
* @param iterable<AcceptanceCriterion> $criteria
* @return array<int, array{checked: bool, indent: int, text: string}>
*/
public static function fromModels(iterable $criteria, ?string $legacyText = null): array
public static function fromModels(iterable $criteria): array
{
$models = ($criteria instanceof EloquentCollection ? $criteria : collect($criteria))
->sort(function (AcceptanceCriterion $left, AcceptanceCriterion $right): int {
@@ -32,7 +32,7 @@ class AcceptanceCriteriaChecklist
->values();
if ($models->isEmpty()) {
return self::normalize($legacyText);
return [];
}
/** @var array<int|string, iterable<AcceptanceCriterion>> $grouped */
@@ -53,10 +53,6 @@ class AcceptanceCriteriaChecklist
return self::normalizeFromArray($value);
}
if (is_string($value) || $value === null) {
return self::normalizeFromString($value);
}
return [];
}
@@ -97,29 +93,6 @@ class AcceptanceCriteriaChecklist
$ticket->unsetRelation('acceptanceCriteria');
}
/**
* @param array<int, array{checked: bool, indent: int, text: string}> $items
*/
public static function toText(array $items): ?string
{
$normalized = self::normalize($items);
if ($normalized === []) {
return null;
}
return implode("\n", array_map(
static fn (array $item): string => sprintf(
'%s- [%s] %s',
str_repeat(' ', $item['indent']),
$item['checked'] ? 'x' : ' ',
$item['text'],
),
$normalized,
));
}
/**
* @param array<int, array{checked: bool, indent: int, text: string}> $items
* @param array<int|string, iterable<AcceptanceCriterion>> $grouped
@@ -143,15 +116,9 @@ class AcceptanceCriteriaChecklist
*/
private static function normalizeFromArray(array $value): array
{
$rawItems = Arr::isAssoc($value) ? Arr::get($value, 'items', []) : $value;
if (! is_array($rawItems)) {
return [];
}
$items = [];
foreach ($rawItems as $rawItem) {
foreach ($value as $rawItem) {
if (! is_array($rawItem)) {
continue;
}
@@ -172,79 +139,6 @@ class AcceptanceCriteriaChecklist
return $items;
}
/**
* @return array<int, array{checked: bool, indent: int, text: string}>
*/
private static function normalizeFromString(?string $value): array
{
$text = trim((string) $value);
if ($text === '') {
return [];
}
$lines = preg_split('/\R/u', $text);
if (! is_array($lines)) {
return [];
}
return array_values(array_filter(array_map(
static fn (string $line): ?array => self::parseChecklistItem($line),
$lines,
)));
}
/**
* @return array{checked: bool, indent: int, text: string}|null
*/
private static function parseChecklistItem(string $line): ?array
{
$line = rtrim(str_replace("\t", ' ', $line));
if ($line === '') {
return null;
}
if (! preg_match('/^(?<indent>\s*)(?<content>.+)$/u', $line, $matches)) {
return null;
}
$content = trim((string) Arr::get($matches, 'content', ''));
if ($content === '') {
return null;
}
$checked = false;
if (preg_match('/^(?:[-*+]\s+)?\[(?<state>[xX ])\]\s*(?<text>.+)$/u', $content, $checkboxMatches)) {
$checked = strtolower((string) Arr::get($checkboxMatches, 'state', '')) === 'x';
$content = trim((string) Arr::get($checkboxMatches, 'text', ''));
} elseif (preg_match('/^[-*+]\s+(?<text>.+)$/u', $content, $bulletMatches)) {
$content = trim((string) Arr::get($bulletMatches, 'text', ''));
} else {
return null;
}
if ($content === '') {
return null;
}
return [
'checked' => $checked,
'indent' => self::indentLevel((string) Arr::get($matches, 'indent', '')),
'text' => $content,
];
}
private static function indentLevel(string $leadingWhitespace): int
{
$normalizedWhitespace = str_replace("\t", ' ', $leadingWhitespace);
return self::clampIndent(intdiv(strlen($normalizedWhitespace), 2));
}
private static function clampIndent(int $indent): int
{
return max(0, min(self::MAX_INDENT_LEVEL, $indent));
+1 -10
View File
@@ -86,10 +86,7 @@ class UpdateProjectTicket
/** @var Collection<int, TicketFieldValue> $existingFieldValues */
$existingFieldValues = $ticket->fieldValues
->keyBy('ticket_field_definition_id');
$currentAcceptanceCriteria = AcceptanceCriteriaChecklist::fromModels(
$ticket->acceptanceCriteria,
$ticket->acceptance_criteria,
);
$currentAcceptanceCriteria = AcceptanceCriteriaChecklist::fromModels($ticket->acceptanceCriteria);
$changes = [];
$summaryLabels = [];
@@ -249,7 +246,6 @@ class UpdateProjectTicket
* title: string
* } $validated
* @return array{
* acceptance_criteria: string|null,
* assignee_user_id: int|null,
* author_user_id: int|null,
* blocked_comment: string|null,
@@ -264,12 +260,7 @@ class UpdateProjectTicket
*/
private function buildTicketAttributes(Ticket $ticket, array $validated): array
{
$acceptanceCriteria = AcceptanceCriteriaChecklist::normalize(
Arr::get($validated, 'acceptance_criteria'),
);
return [
'acceptance_criteria' => AcceptanceCriteriaChecklist::toText($acceptanceCriteria),
'assignee_user_id' => Arr::exists($validated, 'assignee_user_id')
? Arr::get($validated, 'assignee_user_id')
: $ticket->assignee_user_id,
@@ -23,9 +23,15 @@ class StoreProjectTicketRequest extends FormRequest
*/
protected function prepareForValidation(): void
{
$acceptanceCriteria = $this->input('acceptance_criteria');
if ($acceptanceCriteria !== null && ! is_array($acceptanceCriteria)) {
return;
}
$this->merge([
'acceptance_criteria' => AcceptanceCriteriaChecklist::normalize(
$this->input('acceptance_criteria'),
$acceptanceCriteria,
),
]);
}
@@ -29,9 +29,15 @@ class UpdateProjectTicketRequest extends FormRequest
*/
protected function prepareForValidation(): void
{
$acceptanceCriteria = $this->input('acceptance_criteria');
if ($acceptanceCriteria !== null && ! is_array($acceptanceCriteria)) {
return;
}
$this->merge([
'acceptance_criteria' => AcceptanceCriteriaChecklist::normalize(
$this->input('acceptance_criteria'),
$acceptanceCriteria,
),
]);
}
-1
View File
@@ -21,7 +21,6 @@ class TicketResource extends JsonResource
$this->relationLoaded('acceptanceCriteria')
? $this->acceptanceCriteria
: $this->acceptanceCriteria()->get(),
$this->acceptance_criteria,
),
'assignee' => $this->whenLoaded(
'assignee',
-1
View File
@@ -228,7 +228,6 @@ class KanbanGitMcp
$ticket->relationLoaded('acceptanceCriteria')
? $ticket->acceptanceCriteria
: $ticket->acceptanceCriteria()->get(),
$ticket->acceptance_criteria,
),
'assignee_user_id' => $ticket->assignee_user_id,
'author_user_id' => $ticket->author_user_id,
-1
View File
@@ -10,7 +10,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
#[Fillable([
'acceptance_criteria',
'assignee_user_id',
'author_user_id',
'blocked_comment',