Skip to content

Commit e38d4fa

Browse files
committed
Improve adding packages from external registries with a Stimulus controller and AJAX requests
1 parent e764646 commit e38d4fa

File tree

5 files changed

+126
-29
lines changed

5 files changed

+126
-29
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { Controller } from '@hotwired/stimulus';
2+
3+
/* stimulusFetch: 'lazy' */
4+
export default class extends Controller {
5+
static targets = ['form', 'packageList'];
6+
static values = {
7+
packageUrl: String,
8+
};
9+
10+
formTarget: HTMLFormElement;
11+
packageListTarget: HTMLElement;
12+
packageUrlValue: string;
13+
14+
submitForm(event: Event) {
15+
event.preventDefault();
16+
17+
const formData = new FormData(this.formTarget);
18+
19+
const data = [];
20+
for (const [key, value] of formData) {
21+
data.push(`${encodeURIComponent(key)}=${encodeURIComponent(value.toString())}`);
22+
}
23+
const body = data.join("&");
24+
25+
fetch(this.formTarget.getAttribute('action'), {
26+
method: 'POST',
27+
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
28+
body,
29+
})
30+
.then(response => response.json())
31+
.then((data: PackageResultResponse) => {
32+
for (const result of data.results) {
33+
const packageUrl = this.packageUrlValue.replace('place/holder', result.packageName);
34+
35+
const packageName = !result.error ? `<a href="${packageUrl}" target="_blank">${result.packageName}</a>` : result.packageName;
36+
const registryName = result.registryName ? result.registryName : '';
37+
let message = result.message;
38+
if (result.error) {
39+
message = `<div class="text-warning">${message}</div>`;
40+
} else if (result.created) {
41+
message = `<div class="text-success">${message}</div>`;
42+
}
43+
44+
const listItem = document.createElement('div');
45+
listItem.innerHTML = `
46+
<div class="row gap-md-2 my-2">
47+
<div class="col-md-3 col-lg-2">${packageName}</div>
48+
<div class="col-md-3 col-lg-2">${registryName}</div>
49+
<div class="col-md-6 col-lg-8">${message}</div>
50+
</div>
51+
`;
52+
53+
this.packageListTarget.appendChild(listItem);
54+
}
55+
});
56+
57+
const packagesInput: HTMLFormElement = this.formTarget.querySelector('[name="package_add_mirroring_form[packages]"]');
58+
packagesInput.value = '';
59+
}
60+
}
61+
62+
interface PackageResultResponse {
63+
results: PackageResult[];
64+
}
65+
66+
interface PackageResult {
67+
packageName: string;
68+
registryName: string;
69+
message: string;
70+
created: boolean;
71+
error: boolean;
72+
}

src/Controller/Dashboard/DashboardPackagesController.php

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -149,25 +149,43 @@ public function addMirror(Request $request): Response
149149
if ($form->isSubmitted() && $form->isValid()) {
150150
$registry = $form->get('registry')->getData();
151151

152-
$packageNames = explode(PHP_EOL, $form->get('packages')->getData());
153-
$packageNames = array_map('trim', $packageNames);
152+
$packageNamesInput = $form->get('packages')->getData();
153+
$packageNames = preg_split('#(\s|,)+#', $packageNamesInput);
154154

155155
$results = [];
156156

157157
foreach ($packageNames as $packageName) {
158-
if (null !== $this->packageRepository->findOneBy(['name' => $packageName])) {
158+
if (!preg_match('#[a-z0-9_.-]+/[a-z0-9_.-]+#', $packageName)) {
159159
$results[] = [
160+
'packageName' => $packageName,
161+
'registryName' => null,
162+
'created' => false,
160163
'error' => true,
161-
'message' => "The package $packageName already exists and was skipped",
164+
'message' => "The package name $packageName is invalid.",
165+
];
166+
167+
continue;
168+
}
169+
170+
if (null !== $this->packageRepository->findOneBy(['name' => $packageName])) {
171+
$results[] = [
172+
'packageName' => $packageName,
173+
'registryName' => null,
174+
'created' => false,
175+
'error' => false,
176+
'message' => "The package $packageName already exists and was skipped.",
162177
];
163178

164179
continue;
165180
}
166181

167182
if (!$this->metadataResolver->provides($packageName, $registry)) {
168183
$results[] = [
184+
'packageName' => $packageName,
185+
'registryName' => $registry->getName(),
186+
'created' => false,
169187
'error' => true,
170-
'message' => "The package $packageName could not be found and was skipped",
188+
'message' => "The package $packageName could not be found and was skipped.",
171189
];
172190

173191
continue;
@@ -183,14 +201,17 @@ public function addMirror(Request $request): Response
183201
$this->messenger->dispatch(new UpdatePackage($package->getId()));
184202

185203
$results[] = [
204+
'packageName' => $packageName,
205+
'registryName' => $registry->getName(),
206+
'created' => true,
186207
'error' => false,
187-
'message' => "The package $packageName was created successfully",
208+
'message' => "The package $packageName was created successfully.",
188209
];
189210

190211
$this->entityManager->flush();
191212
}
192213

193-
return $this->render('dashboard/packages/add_mirroring_results.html.twig', [
214+
return $this->json([
194215
'results' => $results,
195216
]);
196217
}

templates/dashboard/packages/add_mirroring.html.twig

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,30 @@
33
{% block page_title %}Mirror packages from registry{% endblock %}
44

55
{% block page_content %}
6-
{{ form_start(form) }}
7-
{{ form_row(form.packages, {attr: {rows: 10}}) }}
6+
<div {{ stimulus_controller('packages-mirror', {packageUrl: dashboard_path('dashboard_packages_info', {packageName: 'place/holder'})}) }}>
7+
<div
8+
class="border-top border-bottom my-3"
9+
{{ stimulus_target('packages-mirror', 'packageList') }}
10+
>
11+
</div>
812

9-
{{ form_row(form.registry) }}
13+
{{ form_start(form, {
14+
attr: {
15+
'action': dashboard_path('dashboard_packages_add_mirroring'),
16+
'data-action': 'packages-mirror#submitForm',
17+
'data-packages-mirror-target': 'form',
18+
},
19+
}) }}
20+
<div class="row">
21+
<div class="col-lg-8">
22+
{{ form_row(form.packages, {attr: {rows: 5}}) }}
23+
</div>
24+
<div class="col-lg-4">
25+
{{ form_row(form.registry) }}
26+
</div>
27+
</div>
1028

11-
<button class="btn btn-primary">Add packages</button>
12-
{{ form_end(form) }}
29+
<button class="btn btn-primary">Add packages</button>
30+
{{ form_end(form) }}
31+
</div>
1332
{% endblock %}

templates/dashboard/packages/add_mirroring_results.html.twig

Lines changed: 0 additions & 15 deletions
This file was deleted.

tsconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
"include": ["assets/**/*"],
33
"compilerOptions": {
44
"noImplicitAny": true,
5-
"module": "es6",
6-
"target": "es5",
5+
"module": "esnext",
6+
"target": "es6",
77
"jsx": "react",
88
"allowJs": true,
99
"moduleResolution": "node"

0 commit comments

Comments
 (0)