Newer
Older

Ben Mullins
committed
<?php
namespace Drupal\Tests\project_browser\Functional;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Url;
use Drupal\Tests\ApiRequestTrait;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\project_browser\Traits\PackageManagerFixtureUtilityTrait;

Ben Mullins
committed
use Drupal\package_manager\Event\PostApplyEvent;
use Drupal\package_manager\Event\PostCreateEvent;
use Drupal\package_manager\Event\PostRequireEvent;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreRequireEvent;
use Drupal\package_manager\ValidationResult;
use Drupal\package_manager_test_validation\EventSubscriber\TestSubscriber;
use Drupal\project_browser\ComposerInstaller\Installer;
use Drupal\project_browser\EnabledSourceHandler;

Narendra Singh Rathore
committed
use Drupal\project_browser\InstallState;

Ben Mullins
committed
use Drupal\project_browser_test\Datetime\TestTime;
use GuzzleHttp\RequestOptions;
use Psr\Http\Message\ResponseInterface;

Ben Mullins
committed
// cspell:ignore crashmore

Ben Mullins
committed
/**
* Tests the installer controller.
*
* @coversDefaultClass \Drupal\project_browser\Controller\InstallerController
*
* @group project_browser
*/
class InstallerControllerTest extends BrowserTestBase {
use PackageManagerFixtureUtilityTrait;
use ApiRequestTrait;

Ben Mullins
committed
/**
* A stage id.
*
* @var string
*/
protected $stageId;
/**
* The installer.
*
* @var \Drupal\project_browser\ComposerInstaller\Installer
*/
private $installer;
/**
* {@inheritdoc}
*/
protected static $modules = [
'package_manager_bypass',
'package_manager',
'package_manager_test_validation',

Ben Mullins
committed
'project_browser',
'project_browser_test',
'system',
'user',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Setup for install controller test.
*/

Ben Mullins
committed
protected function setUp(): void {
parent::setUp();
$connection = $this->container->get('database');
$query = $connection->insert('project_browser_projects')->fields([
'nid',
'title',
'author',
'created',
'project_usage_total',
'maintenance_status',
'development_status',
'status',
'field_security_advisory_coverage',
'field_project_type',
'project_data',
'field_project_machine_name',
]);
$query->values([
'nid' => 111,
'title' => 'An Awesome Module',
'author' => 'Detective Crashmore',
'created' => 1383917647,
'project_usage_total' => 455,
'maintenance_status' => 13028,
'development_status' => 9988,
'status' => 1,
'field_security_advisory_coverage' => 'covered',
'field_project_type' => 'full',
'project_data' => serialize([
'body' => [
'value' => $this->getRandomGenerator()->paragraphs(1),
],
]),

Ben Mullins
committed
'field_project_machine_name' => 'awesome_module',
]);
$query->values([
'nid' => 222,
'title' => 'Security Revoked Module',
'author' => 'Jamie Taco',
'created' => 1383917448,
'project_usage_total' => 455,
'maintenance_status' => 13028,
'development_status' => 9988,
'status' => 1,
'field_security_advisory_coverage' => 'covered',
'field_project_type' => 'full',
'project_data' => serialize([
'body' => [
'value' => $this->getRandomGenerator()->paragraphs(1),
],
]),
'field_project_machine_name' => 'security_revoked_module',

Ben Mullins
committed
]);
$query->values([
'nid' => 333,
'title' => 'Drupal core',
'author' => 'The usual gang of geniuses',
'created' => 1383917647,
'project_usage_total' => 987654321,
'maintenance_status' => 13028,
'development_status' => 9988,
'status' => 1,
'field_security_advisory_coverage' => 'covered',
'field_project_type' => 'full',
'project_data' => serialize([
'body' => [
'value' => $this->getRandomGenerator()->paragraphs(1),
],
]),
'field_project_machine_name' => 'core',
]);
$query->values([
'nid' => 444,
'title' => 'Metatag',
'author' => 'Dr. Doom',
'created' => 1383917448,
'project_usage_total' => 455,
'maintenance_status' => 13028,
'development_status' => 9988,
'status' => 1,
'field_security_advisory_coverage' => 'covered',
'field_project_type' => 'full',
'project_data' => serialize([
'body' => [
'value' => $this->getRandomGenerator()->paragraphs(1),
],
]),
'field_project_machine_name' => 'metatag',
]);

Ben Mullins
committed
$query->execute();
$this->initPackageManager();
$this->installer = $this->container->get(Installer::class);

Ben Mullins
committed
$this->drupalLogin($this->drupalCreateUser(['administer modules']));
$this->config('project_browser.admin_settings')

Fran Garcia-Linares
committed
->set('enabled_sources', ['project_browser_test_mock', 'drupal_core'])
->set('allow_ui_install', TRUE)
->save();
// Prime the non-volatile cache.
$this->container->get(EnabledSourceHandler::class)->getProjects();

Ben Mullins
committed
}
/**
* Confirms install endpoint not available if UI installs are not enabled.
*
* @covers ::access
*/
public function testUiInstallUnavailableIfDisabled() {
$this->config('project_browser.admin_settings')->set('allow_ui_install', FALSE)->save();
$this->drupalGet('admin/modules/project_browser/install-begin');

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(403);
$this->assertSession()->pageTextContains('Access denied');
}
/**
* Confirms prevention of requiring modules with revoked security status.
*
* @covers ::begin
*/
public function testInstallSecurityRevokedModule() {

Narendra Singh Rathore
committed
$this->assertSame([], $this->container->get(InstallState::class)->toArray());
$contents = $this->drupalGet('admin/modules/project_browser/install-begin');
$this->stageId = Json::decode($contents)['stage_id'];
$response = $this->getPostResponse('project_browser.stage.require', 'project_browser_test_mock/security_revoked_module', [
'stage_id' => $this->stageId,
]);
$this->assertSame(500, (int) $response->getStatusCode());
$this->assertSame('{"message":"security_revoked_module is not safe to add because its security coverage has been revoked"}', (string) $response->getBody());

Ben Mullins
committed
}
/**
* Confirms a require will stop if package already present.
*

Tim Plunkett
committed
* @covers ::require

Ben Mullins
committed
*/
public function testInstallAlreadyPresentPackage() {

Narendra Singh Rathore
committed
$this->assertSame([], $this->container->get(InstallState::class)->toArray());

Ben Mullins
committed
// Though core is not available as a choice in project browser, it works
// well for the purposes of this test as it's definitely already added
// via composer.
$contents = $this->drupalGet('admin/modules/project_browser/install-begin');
$this->stageId = Json::decode($contents)['stage_id'];
$response = $this->getPostResponse('project_browser.stage.require', 'project_browser_test_mock/core', [
'stage_id' => $this->stageId,
]);
$this->assertSame(500, (int) $response->getStatusCode());
$this->assertSame('{"message":"StageEventException: The following package is already installed: drupal\/core\n","phase":"require"}', (string) $response->getBody());

Ben Mullins
committed
}
/**
* Calls the endpoint that begins installation.
*

Tim Plunkett
committed
* @covers ::begin

Ben Mullins
committed
*/
private function doStart() {

Narendra Singh Rathore
committed
$this->assertSame([], $this->container->get(InstallState::class)->toArray());

Narendra Singh Rathore
committed
$contents = $this->drupalGet('admin/modules/project_browser/install-begin');
$this->stageId = Json::decode($contents)['stage_id'];

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(200);
$expected_output = sprintf('{"phase":"create","status":0,"stage_id":"%s"}', $this->stageId);
}
/**
* Calls the endpoint that continues to the require phase of installation.
*

Tim Plunkett
committed
* @covers ::require

Ben Mullins
committed
*/
private function doRequire() {
$response = $this->getPostResponse('project_browser.stage.require', 'project_browser_test_mock/awesome_module', [
'stage_id' => $this->stageId,
]);
$expected_output = sprintf('{"phase":"create","status":0,"stage_id":"%s"}', $this->stageId);

Ben Mullins
committed
$this->assertSame($expected_output, $this->getSession()->getPage()->getContent());

Narendra Singh Rathore
committed
$this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'project_browser_test_mock', 'requiring');

Ben Mullins
committed
}
/**
* Calls the endpoint that continues to the apply phase of installation.
*

Tim Plunkett
committed
* @covers ::apply

Ben Mullins
committed
*/
private function doApply() {

Narendra Singh Rathore
committed
$this->drupalGet("/admin/modules/project_browser/install-apply/$this->stageId");

Ben Mullins
committed
$expected_output = sprintf('{"phase":"apply","status":0,"stage_id":"%s"}', $this->stageId);
$this->assertSame($expected_output, $this->getSession()->getPage()->getContent());

Narendra Singh Rathore
committed
$this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'project_browser_test_mock', 'applying');

Ben Mullins
committed
}
/**
* Calls the endpoint that continues to the post apply phase of installation.
*

Tim Plunkett
committed
* @covers ::postApply

Ben Mullins
committed
*/
private function doPostApply() {

Narendra Singh Rathore
committed
$this->drupalGet("/admin/modules/project_browser/install-post_apply/$this->stageId");

Ben Mullins
committed
$expected_output = sprintf('{"phase":"post apply","status":0,"stage_id":"%s"}', $this->stageId);
$this->assertSame($expected_output, $this->getSession()->getPage()->getContent());

Narendra Singh Rathore
committed
$this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'project_browser_test_mock', 'applying');

Ben Mullins
committed
}
/**
* Calls the endpoint that continues to the destroy phase of installation.
*

Tim Plunkett
committed
* @covers ::destroy

Ben Mullins
committed
*/
private function doDestroy() {

Narendra Singh Rathore
committed
$this->drupalGet("/admin/modules/project_browser/install-destroy/$this->stageId");
$expected_output = sprintf('{"phase":"destroy","status":0,"stage_id":"%s"}', $this->stageId);

Ben Mullins
committed
$this->assertSame($expected_output, $this->getSession()->getPage()->getContent());

Narendra Singh Rathore
committed
$this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'project_browser_test_mock', 'applying');

Ben Mullins
committed
}
/**
* Calls every endpoint needed to do a UI install and confirms they work.
*/
public function testUiInstallerEndpoints() {
$this->doStart();
$this->doRequire();
$this->doApply();
$this->doPostApply();
$this->doDestroy();
}
/**
* Tests an error during a pre create event.
*

Tim Plunkett
committed
* @covers ::create

Ben Mullins
committed
*/
public function testPreCreateError() {
$message = t('This is a PreCreate error.');
$result = ValidationResult::createError([$message]);
TestSubscriber::setTestResult([$result], PreCreateEvent::class);
$contents = $this->drupalGet('admin/modules/project_browser/install-begin');

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(500);
$this->assertSame('{"message":"StageEventException: This is a PreCreate error.\n","phase":"create"}', $contents);

Ben Mullins
committed
}
/**
* Tests an exception during a pre create event.
*

Tim Plunkett
committed
* @covers ::create

Ben Mullins
committed
*/
public function testPreCreateException() {
$error = new \Exception('PreCreate did not go well.');
TestSubscriber::setException($error, PreCreateEvent::class);
$contents = $this->drupalGet('admin/modules/project_browser/install-begin');

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(500);
$this->assertSame('{"message":"StageEventException: PreCreate did not go well.","phase":"create"}', $contents);

Ben Mullins
committed
}
/**
* Tests an exception during a post create event.
*

Tim Plunkett
committed
* @covers ::create

Ben Mullins
committed
*/
public function testPostCreateException() {
$error = new \Exception('PostCreate did not go well.');
TestSubscriber::setException($error, PostCreateEvent::class);
$contents = $this->drupalGet('admin/modules/project_browser/install-begin');

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(500);
$this->assertSame('{"message":"StageEventException: PostCreate did not go well.","phase":"create"}', $contents);

Ben Mullins
committed
}
/**
* Tests an error during a pre require event.
*

Tim Plunkett
committed
* @covers ::require

Ben Mullins
committed
*/
public function testPreRequireError() {
$message = t('This is a PreRequire error.');
$result = ValidationResult::createError([$message]);
$this->doStart();
TestSubscriber::setTestResult([$result], PreRequireEvent::class);
$response = $this->getPostResponse('project_browser.stage.require', 'project_browser_test_mock/awesome_module', [
'stage_id' => $this->stageId,
]);
$this->assertSame(500, (int) $response->getStatusCode());
$this->assertSame('{"message":"StageEventException: This is a PreRequire error.\n","phase":"require"}', (string) $response->getBody());

Ben Mullins
committed
}
/**
* Tests an exception during a pre require event.
*

Tim Plunkett
committed
* @covers ::require

Ben Mullins
committed
*/
public function testPreRequireException() {
$error = new \Exception('PreRequire did not go well.');
TestSubscriber::setException($error, PreRequireEvent::class);
$this->doStart();
$response = $this->getPostResponse('project_browser.stage.require', 'project_browser_test_mock/awesome_module', [
'stage_id' => $this->stageId,
]);
$this->assertSame(500, (int) $response->getStatusCode());
$this->assertSame('{"message":"StageEventException: PreRequire did not go well.","phase":"require"}', (string) $response->getBody());

Ben Mullins
committed
}
/**
* Tests an exception during a post require event.
*

Tim Plunkett
committed
* @covers ::require

Ben Mullins
committed
*/
public function testPostRequireException() {
$error = new \Exception('PostRequire did not go well.');
TestSubscriber::setException($error, PostRequireEvent::class);
$this->doStart();
$response = $this->getPostResponse('project_browser.stage.require', 'project_browser_test_mock/awesome_module', [
'stage_id' => $this->stageId,
]);
$this->assertSame(500, (int) $response->getStatusCode());
$this->assertSame('{"message":"StageEventException: PostRequire did not go well.","phase":"require"}', (string) $response->getBody());

Ben Mullins
committed
}
/**
* Tests an error during a pre apply event.
*

Tim Plunkett
committed
* @covers ::apply

Ben Mullins
committed
*/
public function testPreApplyError() {
$message = t('This is a PreApply error.');
$result = ValidationResult::createError([$message]);
TestSubscriber::setTestResult([$result], PreApplyEvent::class);
$this->doStart();
$this->doRequire();

Narendra Singh Rathore
committed
$contents = $this->drupalGet("/admin/modules/project_browser/install-apply/$this->stageId");

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(500);
$this->assertSame('{"message":"StageEventException: This is a PreApply error.\n","phase":"apply"}', $contents);

Ben Mullins
committed
}
/**
* Tests an exception during a pre apply event.
*

Tim Plunkett
committed
* @covers ::apply

Ben Mullins
committed
*/
public function testPreApplyException() {
$error = new \Exception('PreApply did not go well.');
TestSubscriber::setException($error, PreApplyEvent::class);
$this->doStart();
$this->doRequire();

Narendra Singh Rathore
committed
$contents = $this->drupalGet("/admin/modules/project_browser/install-apply/$this->stageId");

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(500);
$this->assertSame('{"message":"StageEventException: PreApply did not go well.","phase":"apply"}', $contents);

Ben Mullins
committed
}
/**
* Tests an exception during a post apply event.
*

Tim Plunkett
committed
* @covers ::apply

Ben Mullins
committed
*/
public function testPostApplyException() {
$error = new \Exception('PostApply did not go well.');
TestSubscriber::setException($error, PostApplyEvent::class);
$this->doStart();
$this->doRequire();
$this->doApply();

Narendra Singh Rathore
committed
$contents = $this->drupalGet("/admin/modules/project_browser/install-post_apply/$this->stageId");

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(500);
$this->assertSame('{"message":"StageEventException: PostApply did not go well.","phase":"post apply"}', $contents);

Ben Mullins
committed
}
/**
* Confirms the various versions of the "install in progress" messages.
*

Tim Plunkett
committed
* @covers ::unlock

Ben Mullins
committed
*/
public function testInstallUnlockMessage() {
$this->doStart();
$this->doRequire();

Ben Mullins
committed
// Check for mid install unlock offer message.
$this->drupalGet('admin/modules/project_browser/install-begin');

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(418);

utkarsh_33
committed
$this->assertMatchesRegularExpression('/{"message":"The process for adding the project that was locked less than 1 minutes ago might still be in progress. Consider waiting a few more minutes before using \[\+unlock link\].","unlock_url":".*admin..modules..project_browser..install..unlock\?token=[a-zA-Z0-9_-]*"}/', $this->getSession()->getPage()->getContent());

Narendra Singh Rathore
committed
$expected = [
'project_browser_test_mock/awesome_module' => [
'source' => 'project_browser_test_mock',
'status' => 'requiring',
],
];
$this->assertSame($expected, $this->container->get(InstallState::class)->toArray());

Ben Mullins
committed
$this->assertFalse($this->installer->isAvailable());
$this->assertFalse($this->installer->isApplying());
TestTime::setFakeTimeByOffset("+800 seconds");
$this->drupalGet('admin/modules/project_browser/install-begin');

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(418);
$this->assertFalse($this->installer->isAvailable());
$this->assertFalse($this->installer->isApplying());

utkarsh_33
committed
$this->assertMatchesRegularExpression('/{"message":"The process for adding the project was locked 13 minutes ago. Use \[\+ unlock link\] to unlock the process.","unlock_url":".*admin..modules..project_browser..install..unlock\?token=[a-zA-Z0-9_-]*"}/', $this->getSession()->getPage()->getContent());

Ben Mullins
committed
$this->doApply();
TestTime::setFakeTimeByOffset('+800 seconds');
$this->drupalGet('admin/modules/project_browser/install-begin');

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(418);
$this->assertFalse($this->installer->isAvailable());
$this->assertTrue($this->installer->isApplying());

utkarsh_33
committed
$this->assertMatchesRegularExpression('/{"message":"The process for adding the project was locked 13 minutes ago. It should not be unlocked while changes are being applied to the site.","unlock_url":""}/', $this->getSession()->getPage()->getContent());

Ben Mullins
committed
TestTime::setFakeTimeByOffset("+55 minutes");
$this->drupalGet('admin/modules/project_browser/install-begin');

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(418);

utkarsh_33
committed
$this->assertMatchesRegularExpression('/{"message":"The process for adding the project was locked 55 minutes ago. It should not be unlocked while changes are being applied to the site.","unlock_url":""}/', $this->getSession()->getPage()->getContent());

Ben Mullins
committed
// Unlocking the stage becomes possible after 1 hour regardless of source.
TestTime::setFakeTimeByOffset("+75 minutes");
$this->drupalGet('admin/modules/project_browser/install-begin');

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(418);

utkarsh_33
committed
$this->assertMatchesRegularExpression('/{"message":"The process for adding the project was locked 1 hours, 15 minutes ago. Use \[\+ unlock link\] to unlock the process.","unlock_url":".*admin..modules..project_browser..install..unlock\?token=[a-zA-Z0-9_-]*"}/', $this->getSession()->getPage()->getContent());

Ben Mullins
committed
}
/**
* Confirms the break lock link is available and works.
*
* The break lock link is not available once the stage is applying.
*

Tim Plunkett
committed
* @covers ::unlock

Ben Mullins
committed
*/
public function testCanBreakLock() {
$this->doStart();
// Try beginning another install while one is in progress, but not yet in
// the applying stage.
$content = $this->drupalGet('admin/modules/project_browser/install-begin');

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(418);
$this->assertFalse($this->installer->isAvailable());
$this->assertFalse($this->installer->isApplying());
$json = Json::decode($content);

utkarsh_33
committed
$this->assertSame('The process for adding projects is locked, but that lock has expired. Use [+ unlock link] to unlock the process and try to add the project again.', $json['message']);

Ben Mullins
committed
$path = explode('?', $json['unlock_url'])[0];
$token = explode('=', $json['unlock_url'])[1];
$unlock_content = $this->drupalGet($path, ['query' => ['token' => $token]]);

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(200);
$this->assertTrue($this->installer->isAvailable());

utkarsh_33
committed
$this->assertStringContainsString('Operation complete, you can add a new project again.', $unlock_content);

Ben Mullins
committed
$this->assertTrue($this->installer->isAvailable());
$this->assertFalse($this->installer->isApplying());
}
/**
* Confirms stage can be unlocked despite a missing Project Browser lock.
*

Tim Plunkett
committed
* @covers ::unlock

Ben Mullins
committed
*/
public function testCanBreakStageWithMissingProjectBrowserLock() {
$this->doStart();

Narendra Singh Rathore
committed
$this->container->get(InstallState::class)->deleteAll();
$content = $this->drupalGet('admin/modules/project_browser/install-begin');

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(418);
$this->assertFalse($this->installer->isAvailable());
$this->assertFalse($this->installer->isApplying());
$json = Json::decode($content);

utkarsh_33
committed
$this->assertSame('The process for adding projects is locked, but that lock has expired. Use [+ unlock link] to unlock the process and try to add the project again.', $json['message']);

Ben Mullins
committed
$path = explode('?', $json['unlock_url'])[0];
$token = explode('=', $json['unlock_url'])[1];
$unlock_content = $this->drupalGet($path, ['query' => ['token' => $token]]);

Ben Mullins
committed
$this->assertSession()->statusCodeEquals(200);
$this->assertTrue($this->installer->isAvailable());

utkarsh_33
committed
$this->assertStringContainsString('Operation complete, you can add a new project again.', $unlock_content);

Ben Mullins
committed
$this->assertTrue($this->installer->isAvailable());
$this->assertFalse($this->installer->isApplying());
}
/**
* Confirm a module and its dependencies can be installed via the endpoint.
*

Tim Plunkett
committed
* @covers ::activateModule

Ben Mullins
committed
*/
public function testCoreModuleActivate(): void {
$assert_session = $this->assertSession();

Ben Mullins
committed
$this->drupalGet('admin/modules');
$assert_session->checkboxNotChecked('edit-modules-views-enable');
$assert_session->checkboxNotChecked('edit-modules-views-ui-enable');

Ben Mullins
committed
$response = $this->getPostResponse('project_browser.activate', 'drupal_core/views_ui');
$this->assertSame(200, (int) $response->getStatusCode());
$this->assertSame('{"status":0}', (string) $response->getBody());

Ben Mullins
committed
$this->rebuildContainer();
$this->drupalGet('admin/modules');
$assert_session->checkboxChecked('edit-modules-views-enable');
$assert_session->checkboxChecked('edit-modules-views-ui-enable');

Ben Mullins
committed
}
/**
* Confirms the project browser in progress input provides the expected value.
*
* @param string $project_id
* The ID of the project being enabled.

Narendra Singh Rathore
committed
* @param string $source
* The project source.
* @param string $status
* The install state.
*/
protected function assertInstallInProgress(string $project_id, string $source, ?string $status = NULL) {
$expect_install[$project_id] = [
'source' => $source,
'status' => $status,
];
$this->assertSame($expect_install, $this->container->get(InstallState::class)->toArray());
$this->drupalGet("/admin/modules/project_browser/install_in_progress/$project_id");

Narendra Singh Rathore
committed
$this->assertSame(sprintf('{"status":1,"phase":"%s"}', $status), $this->getSession()->getPage()->getContent());

Fran Garcia-Linares
committed
$this->drupalGet('/admin/modules/project_browser/install_in_progress/project_browser_test_mock/metatag');

Ben Mullins
committed
$this->assertSame('{"status":0}', $this->getSession()->getPage()->getContent());
}
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
/**
* Sends a POST request to the specified route with the provided project ID.
*
* @param string $route_name
* The route to which the POST request is sent.
* @param string|string[] $project_id
* The project ID(s) to include in the POST request body.
* @param array $route_parameters
* (optional) An associative array of route parameters, such as 'stage_id',
* that will be included in the URL.
*
* @return \Psr\Http\Message\ResponseInterface
* The response.
*/
private function getPostResponse(string $route_name, string|array $project_id, array $route_parameters = []): ResponseInterface {
$post_url = Url::fromRoute($route_name, $route_parameters);
$request_options = [
RequestOptions::HEADERS => [
'Content-Type' => 'application/json',
],
];
$request_options[RequestOptions::BODY] = Json::encode((array) $project_id);
return $this->makeApiRequest('POST', $post_url, $request_options);
}