validate([ 'operations' => ['required', 'array', 'min:1', 'max:'.BatchRunner::MAX_OPERATIONS], 'operations.*.method' => ['required', Rule::in(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'get', 'post', 'put', 'patch', 'delete'])], 'operations.*.path' => [ 'required', 'string', 'max:500', function (string $attribute, mixed $value, Closure $fail): void { if (! is_string($value) || ! BatchRunner::allows($value)) { $fail(__('rules.batch.path')); } }, ], 'operations.*.body' => ['sometimes', 'nullable', 'array'], 'atomic' => ['sometimes', 'boolean'], ]); $outcome = $runner->run($request, $data['operations'], atomic: $request->boolean('atomic')); return $this->answer($outcome); } /** * Rows as JSON (`rows`) or a CSV file (`file`). `match` names the field * that finds an existing record to update; `dry_run` validates everything * and keeps nothing. */ public function import(Request $request, string $resource, BatchRunner $runner, Importer $importer): JsonResponse { abort_unless(array_key_exists($resource, AdminContent::WITH_HISTORY), 404); $request->validate([ 'rows' => ['required_without:file', 'array'], 'rows.*' => ['array'], 'file' => ['required_without:rows', 'file', 'mimes:csv,txt', 'max:2048'], 'match' => ['sometimes', 'nullable', 'string', Rule::in(['slug', 'name', 'full_name'])], 'atomic' => ['sometimes', 'boolean'], 'dry_run' => ['sometimes', 'boolean'], ]); $rows = $request->hasFile('file') ? $importer->rowsFromCsv($request->file('file')) : $request->input('rows'); $operations = $importer->operations($resource, $rows, $request->input('match')); // A dry run is all-or-nothing by nature: it keeps nothing either way. $outcome = $runner->run( $request, $operations, atomic: $request->boolean('atomic', true) || $request->boolean('dry_run'), rollback: $request->boolean('dry_run'), ); return $this->answer($outcome, ['dry_run' => $request->boolean('dry_run')]); } /** * @param array{results: array>, committed: bool} $outcome * @param array $meta */ protected function answer(array $outcome, array $meta = []): JsonResponse { $failed = collect($outcome['results'])->where('status', '>=', 400)->count(); return response()->json([ 'data' => $outcome['results'], 'meta' => [ ...$meta, 'committed' => $outcome['committed'], 'succeeded' => count($outcome['results']) - $failed, 'failed' => $failed, ], ]); } }