Skip to content

Commit

Permalink
Merge pull request #359 from Art4/custom-fields-support-in-groups
Browse files Browse the repository at this point in the history
Add support for updating groups
  • Loading branch information
Art4 authored Jan 11, 2024
2 parents a7eff52 + 60cdb51 commit 19d3eb5
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 80 deletions.
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

0 comments on commit 19d3eb5

Please sign in to comment.