diff --git a/src/Api.php b/src/Api.php index 07405e5..0ab3eb9 100644 --- a/src/Api.php +++ b/src/Api.php @@ -58,13 +58,26 @@ class Api { return $this; } + public function memberModel($member): Model { + $eloquentClass = config('nami.models.member'); + return $eloquentClass::findByNamiId($member['id']) ?: $this->fillFromOverview(new $eloquentClass); + } + + public function membersOf($groupId): Collection { + return collect($this->http()->get(self::$url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$groupId.'/flist')->json()['data']); + } + + public function member($groupId, $memberId) { + return $this->http()->get(self::$url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$groupId.'/'.$memberId)->json()['data']; + } + public function hasGroup($groupId): bool { return $this->groups()->search(function($group) use ($groupId) { return $group->id == $groupId; }) !== false; } - public function groups($parentGroupId = null): Collection { + public function groups($parentGroupId = null): Collection { $parentGroupId = $parentGroupId ?: 'root'; return collect($this->http()->get(self::$url.'/ica/rest/nami/gruppierungen/filtered-for-navigation/gruppierung/node/'.$parentGroupId)->json()['data'])->map(function($group) { return Group::fromResponse($group); diff --git a/src/Group.php b/src/Group.php index 4fe5942..c8b75a5 100644 --- a/src/Group.php +++ b/src/Group.php @@ -25,4 +25,14 @@ class Group implements Arrayable { return Nami::subgroupsOf($this->id); } + public function members(): MemberCollection { + return new MemberCollection(Nami::membersOf($this->id)->map(function($member) { + return $this->member($member['id']); + })); + } + + public function member($id): Member { + return Member::fromNami(Nami::member($this->id, $id)); + } + } diff --git a/src/Member.php b/src/Member.php new file mode 100644 index 0000000..3c5df3f --- /dev/null +++ b/src/Member.php @@ -0,0 +1,52 @@ +<?php + +namespace Zoomyboy\LaravelNami; + +use \ArrayAccess; +use Illuminate\Support\Arr; +use Illuminate\Database\Eloquent\Concerns\HasAttributes; +use Illuminate\Database\Eloquent\Concerns\HidesAttributes; +use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Database\Eloquent\Model; + +class Member extends Model { + + use HasAttributes; + use HidesAttributes; + + protected static $overviewAttributes = [ + 'vorname' => 'firstname', + 'nachname' => 'lastname', + 'spitzname' => 'nickname', + 'staatsangehoerigkeitText' => 'other_country', + 'strasse' => 'address', + 'nameZusatz' => 'further_address', + 'plz' => 'zip', + 'ort' => 'location', + 'id' => 'id', + 'telefon1' => 'main_phone', + 'telefon2' => 'mobile_phone', + 'telefon3' => 'work_phone', + 'telefax' => 'fax', + 'email' => 'email', + 'emailVertretungsberechtigter' => 'email_parents' + ]; + + protected $guarded = []; + + public static function fromNami($item) { + $item = collect($item) + ->only(array_keys(static::$overviewAttributes)) + ->mapWithKeys(function($item, $key) { + return [ data_get(static::$overviewAttributes, $key, $key) => $item ]; + }) + ->toArray(); + + return (new self($item)); + } + + public function __construct($member) { + parent::__construct($member); + } + +} diff --git a/src/MemberCollection.php b/src/MemberCollection.php new file mode 100644 index 0000000..fa23d0d --- /dev/null +++ b/src/MemberCollection.php @@ -0,0 +1,17 @@ +<?php + +namespace Zoomyboy\LaravelNami; + +use Illuminate\Support\Collection; + +class MemberCollection extends Collection { + + public static function fromOverview($items) { + $self = new self($items); + + return $self->map(function($item) { + return Member::fromOverview($item); + }); + } + +} diff --git a/tests/Stub/Member.php b/tests/Stub/Member.php new file mode 100644 index 0000000..b26c7e8 --- /dev/null +++ b/tests/Stub/Member.php @@ -0,0 +1,14 @@ +<?php + +namespace Zoomyboy\LaravelNami\Tests\Stub; + +use Illuminate\Database\Eloquent\Model; + +class Member extends Model { + + public $fillable = [ 'firstname', 'nami_id' ]; + + public static function findByNamiId($id) { + return self::where('nami_id', $id)->first(); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 32ae056..72001a5 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,6 +4,8 @@ namespace Zoomyboy\LaravelNami\Tests; use Illuminate\Support\Facades\Config; use Zoomyboy\LaravelNami\NamiServiceProvider; +use Illuminate\Support\Facades\Http; +use Zoomyboy\LaravelNami\Tests\Stub\Member; class TestCase extends \Orchestra\Testbench\TestCase { @@ -26,4 +28,15 @@ class TestCase extends \Orchestra\Testbench\TestCase Config::set('nami.auth.groupid', '55555'); } + public function login() { + return [ + 'https://nami.dpsg.de/ica/pages/login.jsp' => Http::response('<html></html>', 200), + 'https://nami.dpsg.de/ica/rest/nami/auth/manual/sessionStartup' => Http::response($this->successJson, 200) + ]; + } + + public function fakeJson($file) { + return file_get_contents(__DIR__.'/json/'.$file); + } + } diff --git a/tests/Unit/MemberFakeTest.php b/tests/Unit/MemberFakeTest.php new file mode 100644 index 0000000..e69de29 diff --git a/tests/Unit/PullMemberTest.php b/tests/Unit/PullMemberTest.php new file mode 100644 index 0000000..a6470c1 --- /dev/null +++ b/tests/Unit/PullMemberTest.php @@ -0,0 +1,81 @@ +<?php + +namespace Zoomyboy\LaravelNami\Tests\Unit; + +use Zoomyboy\LaravelNami\Nami; +use Zoomyboy\LaravelNami\Tests\TestCase; +use Illuminate\Support\Facades\Http; +use Illuminate\Support\Facades\Config; +use Zoomyboy\LaravelNami\NamiServiceProvider; +use Zoomyboy\LaravelNami\LoginException; +use Zoomyboy\LaravelNami\Group; + +class PullMemberTest extends TestCase +{ + public $groupsResponse = '{"success":true,"data":[{"descriptor":"Group","name":"","representedClass":"de.iconcept.nami.entity.org.Gruppierung","id":103}],"responseType":"OK"}'; + + public function dataProvider() { + return [ + 'firstname' => ['firstname', ['Max', 'Jane']], + 'lastname' => ['lastname', ['Nach1', 'Nach2']], + 'nickname' => ['nickname', ['spitz1', null]], + 'other_country' => ['other_country', ['deutsch', null]], + 'address' => ['address', ['straße 1', 'straße 2']], + 'further_address' => ['further_address', ['addrz', null]], + 'zip' => ['zip', [12345, 5555]], + 'location' => ['location', ['SG', 'Köln']], + 'main_phone' => ['main_phone', ['+49888', '+49668']], + 'mobile_phone' => ['mobile_phone', ['+49176', '+49172']], + 'work_phone' => ['work_phone', ['+11111', '+22222']], + 'fax' => ['fax', ['+55111', '+55222']], + 'email' => ['email', ['test@example.com', 'test2@example.com']], + 'email_parents' => ['email_parents', ['testp@example.com', 'test2p@example.com']], + ]; + } + + /** + * @dataProvider dataProvider + */ + public function test_get_a_single_member($key, $values) { + Http::fake(array_merge($this->login(), [ + 'https://nami.dpsg.de/ica/rest/nami/gruppierungen/filtered-for-navigation/gruppierung/node/root' => Http::response($this->groupsResponse, 200), + 'https://nami.dpsg.de/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/103/16' => Http::response($this->fakeJson('member-16.json'), 200), + 'https://nami.dpsg.de/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/103/17' => Http::response($this->fakeJson('member-17.json'), 200) + ])); + + $this->setCredentials(); + + Nami::login(); + + $group = Nami::group(103); + + $this->assertEquals($values[0], $group->member(16)->{$key}); + $this->assertEquals($values[1], $group->member(17)->{$key}); + + Http::assertSentCount(5); + } + + /** + * @dataProvider dataProvider + */ + public function test_get_attribute_of_overview($key, $values) { + Http::fake(array_merge($this->login(), [ + 'https://nami.dpsg.de/ica/rest/nami/gruppierungen/filtered-for-navigation/gruppierung/node/root' => Http::response($this->groupsResponse, 200), + 'https://nami.dpsg.de/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/103/flist' => Http::response($this->fakeJson('member_overview.json'), 200), + 'https://nami.dpsg.de/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/103/16' => Http::response($this->fakeJson('member-16.json'), 200), + 'https://nami.dpsg.de/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/103/17' => Http::response($this->fakeJson('member-17.json'), 200) + ])); + + $this->setCredentials(); + + Nami::login(); + + $this->assertEquals([ + 16 => $values[0], + 17 => $values[1] + ], Nami::group(103)->members()->pluck($key, 'id')->toArray()); + + Http::assertSentCount(6); + } + +} diff --git a/tests/json/member-16.json b/tests/json/member-16.json new file mode 100644 index 0000000..fa08b5a --- /dev/null +++ b/tests/json/member-16.json @@ -0,0 +1,76 @@ +{ + "success": true, + "data": + { + "jungpfadfinder": null, + "mglType": "Mitglied", + "geschlecht": "männlich", + "staatsangehoerigkeit": "deutsch", + "ersteTaetigkeitId": null, + "ersteUntergliederung": "Wölfling", + "emailVertretungsberechtigter": "testp@example.com", + "lastUpdated": "2020-06-28 02:15:24", + "ersteTaetigkeit": null, + "nameZusatz": "addrz", + "id": 16, + "staatsangehoerigkeitId": 1054, + "version": 58, + "sonst01": false, + "sonst02": false, + "spitzname": "spitz1", + "landId": 1, + "staatsangehoerigkeitText": "deutsch", + "gruppierungId": 100105, + "mglTypeId": "MITGLIED", + "beitragsart": "Voller Beitrag", + "nachname": "Nach1", + "eintrittsdatum": "2005-05-01 00:00:00", + "rover": null, + "region": "Nordrhein-Westfalen (Deutschland)", + "status": "Aktiv", + "konfession": null, + "fixBeitrag": null, + "konfessionId": null, + "zeitschriftenversand": true, + "pfadfinder": null, + "telefon3": "+11111", + "kontoverbindung": + { + "id": null, + "institut": null, + "bankleitzahl": null, + "kontonummer": null, + "iban": null, + "bic": null, + "kontoinhaber": null, + "mitgliedsNummer": null, + "zahlungsKonditionId": null, + "zahlungsKondition": null + }, + "geschlechtId": 19, + "land": "Deutschland", + "email": "test@example.com", + "telefon1": "+49888", + "woelfling": null, + "telefon2": "+49176", + "strasse": "straße 1", + "vorname": "Max", + "mitgliedsNummer": 90166, + "gruppierung": "Solingen-Wald, Silva 100105", + "austrittsDatum": "", + "ort": "SG", + "ersteUntergliederungId": null, + "wiederverwendenFlag": false, + "regionId": 10, + "geburtsDatum": "1991-06-20 00:00:00", + "stufe": "Wölfling", + "genericField1": "", + "genericField2": "", + "telefax": "+55111", + "beitragsartId": 1, + "plz": "12345" + }, + "responseType": null, + "message": null, + "title": null +} diff --git a/tests/json/member-17.json b/tests/json/member-17.json new file mode 100644 index 0000000..11a2c8b --- /dev/null +++ b/tests/json/member-17.json @@ -0,0 +1,76 @@ +{ + "success": true, + "data": + { + "jungpfadfinder": null, + "mglType": "Mitglied", + "geschlecht": "männlich", + "staatsangehoerigkeit": "deutsch", + "ersteTaetigkeitId": null, + "ersteUntergliederung": null, + "emailVertretungsberechtigter": "test2p@example.com", + "lastUpdated": "2015-02-03 15:20:07", + "ersteTaetigkeit": null, + "nameZusatz": "", + "id": 17, + "staatsangehoerigkeitId": 1054, + "version": 17, + "sonst01": false, + "sonst02": false, + "spitzname": "", + "landId": 1, + "staatsangehoerigkeitText": "", + "gruppierungId": 100105, + "mglTypeId": "MITGLIED", + "beitragsart": "Voller Beitrag", + "nachname": "Nach2", + "eintrittsdatum": "2003-11-17 00:00:00", + "rover": null, + "region": "Nordrhein-Westfalen (Deutschland)", + "status": "Aktiv", + "konfession": null, + "fixBeitrag": null, + "konfessionId": null, + "zeitschriftenversand": true, + "pfadfinder": null, + "telefon3": "+22222", + "kontoverbindung": + { + "id": null, + "institut": null, + "bankleitzahl": null, + "kontonummer": null, + "iban": null, + "bic": null, + "kontoinhaber": null, + "mitgliedsNummer": null, + "zahlungsKonditionId": null, + "zahlungsKondition": null + }, + "geschlechtId": 19, + "land": "Deutschland", + "email": "test2@example.com", + "telefon1": "+49668", + "woelfling": null, + "telefon2": "+49172", + "strasse": "straße 2", + "vorname": "Jane", + "mitgliedsNummer": 90141, + "gruppierung": "Solingen-Wald, Silva 100105", + "austrittsDatum": "", + "ort": "Köln", + "ersteUntergliederungId": null, + "wiederverwendenFlag": false, + "regionId": 10, + "geburtsDatum": "1984-01-17 00:00:00", + "stufe": null, + "genericField1": null, + "genericField2": null, + "telefax": "+55222", + "beitragsartId": 1, + "plz": "5555" + }, + "responseType": null, + "message": null, + "title": null +} diff --git a/tests/json/member_overview.json b/tests/json/member_overview.json new file mode 100644 index 0000000..dc383ff --- /dev/null +++ b/tests/json/member_overview.json @@ -0,0 +1,58 @@ +{ + "success": true, + "data": [ + { + "id": 16 + }, + { + "id": 17 + } + ], + "responseType": "OK", + "totalEntries": 25, + "metaData": { + "totalProperty": "totalEntries", + "root": "data", + "id": "id", + "fields": [ + {"name":"id","header":null,"hidden":false,"width":80}, + {"name":"descriptor","header":null,"hidden":false,"flex":3}, + {"name":"entries_geschlecht","header":null,"hidden":false,"flex":3}, + {"name":"entries_staatangehoerigkeitText","header":null,"hidden":false,"flex":3}, + {"name":"entries_lastUpdated","header":null,"hidden":false,"flex":3}, + {"name":"entries_id","header":null,"hidden":true,"flex":3}, + {"name":"entries_version","header":null,"hidden":false,"flex":3}, + {"name":"entries_spitzname","header":null,"hidden":false,"flex":3}, + {"name":"entries_nachname","header":null,"hidden":false,"flex":3}, + {"name":"entries_eintrittsdatum","header":null,"hidden":false,"flex":3}, + {"name":"entries_rover","header":null,"hidden":false,"flex":3}, + {"name":"entries_status","header":null,"hidden":false,"flex":3}, + {"name":"entries_fixBeitrag","header":null,"hidden":false,"flex":3}, + {"name":"entries_pfadfinder","header":null,"hidden":false,"flex":3}, + {"name":"entries_telefon3","header":null,"hidden":false,"flex":3}, + {"name":"entries_kontoverbindung","header":null,"hidden":false,"flex":3}, + {"name":"entries_email","header":null,"hidden":false,"flex":3}, + {"name":"entries_telefon1","header":null,"hidden":false,"flex":3}, + {"name":"entries_woelfling","header":null,"hidden":false,"flex":3}, + {"name":"entries_telefon2","header":null,"hidden":false,"flex":3}, + {"name":"entries_beitragsarten","header":null,"hidden":false,"flex":3}, + {"name":"entries_austrittsDatum","header":null,"hidden":true,"flex":3}, + {"name":"entries_stufe","header":null,"hidden":false,"flex":3}, + {"name":"entries_genericField1","header":null,"hidden":true,"flex":3}, + {"name":"entries_genericField2","header":null,"hidden":true,"flex":3}, + {"name":"entries_jungpfadfinder","header":null,"hidden":false,"flex":3}, + {"name":"entries_mglType","header":null,"hidden":false,"flex":3}, + {"name":"entries_staatsangehoerigkeit","header":null,"hidden":false,"flex":3}, + {"name":"entries_ersteTaetigkeitId","header":null,"hidden":false,"flex":3}, + {"name":"entries_emailVertretungsberechtigter","header":null,"hidden":false,"flex":3}, + {"name":"entries_konfession","header":null,"hidden":false,"flex":3}, + {"name":"entries_rowCssClass","header":null,"hidden":false,"flex":3}, + {"name":"entries_vorname","header":null,"hidden":false,"flex":3}, + {"name":"entries_mitgliedsNummer","header":null,"hidden":false,"flex":3}, + {"name":"entries_ersteUntergliederungId","header":null,"hidden":false,"flex":3}, + {"name":"entries_wiederverwendenFlag","header":null,"hidden":false,"flex":3}, + {"name":"entries_geburtsDatum","header":null,"hidden":false,"flex":3}, + {"name":"entries_telefax","header":null,"hidden":false,"flex":3} + ] + } +}