Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for updating groups #359

Merged
merged 7 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased](https://github.com/kbsali/php-redmine-api/compare/v2.4.0...v2.x)

### Added

- Added support for updating groups.

### Changed

- The last response is saved in `Redmine\Api\AbstractApi` to prevent race conditions with `Redmine\Client\Client` implementations.
Expand Down
19 changes: 19 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,25 @@ $client->getApi('group')->removeUser($groupId, $userId);
$client->getApi('group')->create([
'name' => 'asdf',
'user_ids' => [1, 2],
'custom_fields' => [
[
'id' => 123,
'name' => 'cf_name',
'value' => 'cf_value',
],
],
]);
$client->getApi('group')->update($groupId, [
'name' => 'asdf',
// Note: you can only add users this way; use removeUser to remove a user
'user_ids' => [1, 2],
'custom_fields' => [
[
'id' => 123,
'name' => 'cf_name',
'value' => 'cf_value',
],
],
]);

// ----------------------------
Expand Down
22 changes: 17 additions & 5 deletions src/Redmine/Api/Group.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Redmine\Exception\UnexpectedResponseException;
use Redmine\Serializer\PathSerializer;
use Redmine\Serializer\XmlSerializer;
use SimpleXMLElement;

/**
* Handling of groups.
Expand Down Expand Up @@ -101,7 +102,7 @@ public function listing($forceUpdate = false)
*
* @throws MissingParameterException Missing mandatory parameters
*
* @return string|false
* @return string|SimpleXMLElement|false
*/
public function create(array $params = [])
{
Expand All @@ -124,17 +125,28 @@ public function create(array $params = [])
}

/**
* Updates a group.
*
* NOT DOCUMENTED in Redmine's wiki.
*
* @see http://www.redmine.org/projects/redmine/wiki/Rest_Groups#PUT
*
* @param int $id
* @param int $id the group id
*
* @throws Exception Not implemented
* @return string empty string
*/
public function update($id, array $params = [])
public function update(int $id, array $params = [])
{
throw new \Exception('Not implemented');
$defaults = [
'name' => null,
'user_ids' => null,
];
$params = $this->sanitizeParams($defaults, $params);

return $this->put(
'/groups/' . $id . '.xml',
XmlSerializer::createFromArray(['group' => $params])->getEncoded()
);
}

/**
Expand Down
28 changes: 21 additions & 7 deletions tests/Integration/GroupXmlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace Redmine\Tests\Integration;

use Exception;
use PHPUnit\Framework\TestCase;
use Redmine\Exception\MissingParameterException;
use Redmine\Tests\Fixtures\MockClient;
Expand Down Expand Up @@ -48,15 +47,30 @@ public function testCreateComplex()
);
}

public function testUpdateNotImplemented()
public function testUpdateComplex()
{
/** @var \Redmine\Api\Group */
$api = MockClient::create()->getApi('group');
$this->assertInstanceOf('Redmine\Api\Group', $api);

$this->expectException(Exception::class);
$this->expectExceptionMessage('Not implemented');
$res = $api->update(5, [
'name' => 'Developers',
'user_ids' => [3, 5],
]);
$response = json_decode($res, true);

$api->update(1);
$this->assertEquals('PUT', $response['method']);
$this->assertEquals('/groups/5.xml', $response['path']);
$this->assertXmlStringEqualsXmlString(
<<< XML
<?xml version="1.0"?>
<group>
<name>Developers</name>
<user_ids type="array">
<user_id>3</user_id>
<user_id>5</user_id>
</user_ids>
</group>
XML,
$response['data']
);
}
}
137 changes: 137 additions & 0 deletions tests/Unit/Api/Group/CreateTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php

declare(strict_types=1);

namespace Redmine\Tests\Unit\Api\Group;

use PHPUnit\Framework\TestCase;
use Redmine\Api\Group;
use Redmine\Exception\MissingParameterException;
use Redmine\Http\HttpClient;
use Redmine\Http\Response;
use SimpleXMLElement;

/**
* @covers \Redmine\Api\Group::create
*/
class CreateTest extends TestCase
{
public function testCreateWithNameCreatesGroup()
{
$client = $this->createMock(HttpClient::class);
$client->expects($this->exactly(1))
->method('request')
->willReturnCallback(function (string $method, string $path, string $body = '') {
$this->assertSame('POST', $method);
$this->assertSame('/groups.xml', $path);
$this->assertXmlStringEqualsXmlString('<?xml version="1.0"?><group><name>Group Name</name></group>', $body);

return $this->createConfiguredMock(
Response::class,
[
'getContentType' => 'application/xml',
'getBody' => '<?xml version="1.0"?><group></group>',
]
);
});

// Create the object under test
$api = new Group($client);

// Perform the tests
$xmlElement = $api->create(['name' => 'Group Name']);

$this->assertInstanceOf(SimpleXMLElement::class, $xmlElement);
$this->assertXmlStringEqualsXmlString(
'<?xml version="1.0"?><group></group>',
$xmlElement->asXml(),
);
}

public function testCreateWithNameAndUserIdsCreatesGroup()
{
$client = $this->createMock(HttpClient::class);
$client->expects($this->exactly(1))
->method('request')
->willReturnCallback(function (string $method, string $path, string $body = '') {
$this->assertSame('POST', $method);
$this->assertSame('/groups.xml', $path);
$this->assertXmlStringEqualsXmlString('<?xml version="1.0"?><group><name>Group Name</name><user_ids type="array"><user_id>1</user_id><user_id>2</user_id><user_id>3</user_id></user_ids></group>', $body);

return $this->createConfiguredMock(
Response::class,
[
'getContentType' => 'application/xml',
'getBody' => '<?xml version="1.0"?><group></group>',
]
);
});

// Create the object under test
$api = new Group($client);

// Perform the tests
$xmlElement = $api->create(['name' => 'Group Name', 'user_ids' => [1, 2, 3]]);

$this->assertInstanceOf(SimpleXMLElement::class, $xmlElement);
$this->assertXmlStringEqualsXmlString(
'<?xml version="1.0"?><group></group>',
$xmlElement->asXml(),
);
}

public function testCreateWithNameAndCustomFieldsCreatesGroup()
{
$client = $this->createMock(HttpClient::class);
$client->expects($this->exactly(1))
->method('request')
->willReturnCallback(function (string $method, string $path, string $body = '') {
$this->assertSame('POST', $method);
$this->assertSame('/groups.xml', $path);
$this->assertXmlStringEqualsXmlString('<?xml version="1.0"?><group><name>Group Name</name><custom_fields type="array"><custom_field id="1"><value>5</value></custom_field></custom_fields></group>', $body);

return $this->createConfiguredMock(
Response::class,
[
'getContentType' => 'application/xml',
'getBody' => '<?xml version="1.0"?><group></group>',
]
);
});

// Create the object under test
$api = new Group($client);

// Perform the tests
$xmlElement = $api->create([
'name' => 'Group Name',
'custom_fields' => [
['id' => 1, 'value' => 5],
],
]);

$this->assertInstanceOf(SimpleXMLElement::class, $xmlElement);
$this->assertXmlStringEqualsXmlString(
'<?xml version="1.0"?><group></group>',
$xmlElement->asXml(),
);
}

public function testCreateThrowsExceptionIfNameIsMissing()
{
// Test values
$postParameter = [];

// Create the used mock objects
$client = $this->createMock(HttpClient::class);

// Create the object under test
$api = new Group($client);

$this->expectException(MissingParameterException::class);
$this->expectExceptionMessage('Theses parameters are mandatory: `name`');

// Perform the tests
$api->create($postParameter);
}
}
105 changes: 105 additions & 0 deletions tests/Unit/Api/Group/UpdateTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

declare(strict_types=1);

namespace Redmine\Tests\Unit\Api\Group;

use PHPUnit\Framework\TestCase;
use Redmine\Api\Group;
use Redmine\Http\HttpClient;
use Redmine\Http\Response;
use SimpleXMLElement;

/**
* @covers \Redmine\Api\Group::update
*/
class UpdateTest extends TestCase
{
public function testUpdateWithNameUpdatesGroup()
{
$client = $this->createMock(HttpClient::class);
$client->expects($this->exactly(1))
->method('request')
->willReturnCallback(function (string $method, string $path, string $body = '') {
$this->assertSame('PUT', $method);
$this->assertSame('/groups/1.xml', $path);
$this->assertXmlStringEqualsXmlString('<?xml version="1.0"?><group><name>Group Name</name></group>', $body);

return $this->createConfiguredMock(
Response::class,
[
'getContentType' => 'application/xml',
'getBody' => '',
]
);
});

// Create the object under test
$api = new Group($client);

// Perform the tests
$return = $api->update(1, ['name' => 'Group Name']);

$this->assertSame('', $return);
}

public function testUpdateWithUserIdsUpdatesGroup()
{
$client = $this->createMock(HttpClient::class);
$client->expects($this->exactly(1))
->method('request')
->willReturnCallback(function (string $method, string $path, string $body = '') {
$this->assertSame('PUT', $method);
$this->assertSame('/groups/1.xml', $path);
$this->assertXmlStringEqualsXmlString('<?xml version="1.0"?><group><user_ids type="array"><user_id>1</user_id><user_id>2</user_id><user_id>3</user_id></user_ids></group>', $body);

return $this->createConfiguredMock(
Response::class,
[
'getContentType' => 'application/xml',
'getBody' => '',
]
);
});

// Create the object under test
$api = new Group($client);

// Perform the tests
$return = $api->update(1, ['user_ids' => [1, 2, 3]]);

$this->assertSame('', $return);
}

public function testUpdateWithCustomFieldsUpdatesGroup()
{
$client = $this->createMock(HttpClient::class);
$client->expects($this->exactly(1))
->method('request')
->willReturnCallback(function (string $method, string $path, string $body = '') {
$this->assertSame('PUT', $method);
$this->assertSame('/groups/1.xml', $path);
$this->assertXmlStringEqualsXmlString('<?xml version="1.0"?><group><custom_fields type="array"><custom_field id="1"><value>5</value></custom_field></custom_fields></group>', $body);

return $this->createConfiguredMock(
Response::class,
[
'getContentType' => 'application/xml',
'getBody' => '',
]
);
});

// Create the object under test
$api = new Group($client);

// Perform the tests
$return = $api->update(1, [
'custom_fields' => [
['id' => 1, 'value' => 5],
],
]);

$this->assertSame('', $return);
}
}
Loading