diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1027b0d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + ci: + name: CI + uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1 + with: + phplinting: false + simple_matrix: true + phpcoverage_force_off: true + endtoend: false + extra_jobs: | + - php: '8.0' + db: mysql80 + phpunit: true + phpunit_suite: 'all' \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5b3d1d4..0000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: php -php: -- 7.1 -env: - matrix: - - DB=MYSQL CORE_RELEASE=4 - global: - secure: Le917O5p+3nccje9JNHyvFuQk44wkoXmfDYTV5tyfqH1yvTOS9aD2zUkSORbGBcxwFKbXxJxlhSH/TBub/ZjXoAlURw10oS8uzG5T4LVPkyKUNcph54Mbgs4E05K6IzOg78VlRZ6IOjBsXh/8NI51uEstgJZ/dajjPdERgjrd+k= -before_script: -- phpenv rehash -- git clone git://github.com/silverstripe/silverstripe-travis-support.git ~/travis-support -- php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss -- cd ~/builds/ss -script: -- vendor/bin/phpunit vendor/colymba/silverstripe-restfulapi/tests/ --exclude-group CORSPreflight diff --git a/README.md b/README.md index bf12f27..9f0515d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # SilverStripe RESTful API -[![Build Status](https://travis-ci.org/colymba/silverstripe-restfulapi.png?branch=master)](https://travis-ci.org/colymba/silverstripe-restfulapi) +[![Build Status](https://github.com/colymba/silverstripe-restfulapi/actions/workflows/ci.yml/badge.svg)](https://github.com/colymba/silverstripe-restfulapi/actions/workflows/ci.yml) This module implements a RESTful API for read/write access to your SilverStripe Models. It comes bundled with a default Token Authenticator, Query Handler and JSON Serializers, and can be extended to your need and to return XML or other content type via custom components. diff --git a/composer.json b/composer.json index c663e5b..3b4d390 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "silverstripe/framework": "~4.1" }, "require-dev": { - "phpunit/phpunit": "~5.7@stable" + "phpunit/phpunit": "^9.5" }, "autoload": { "psr-4": { diff --git a/phpunit.xml b/phpunit.xml index 4867919..898d651 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -15,9 +15,11 @@ - http://doc.silverstripe.org/framework/en/topics/testing/#configuration --> - - tests - + + + tests + + CORSPreflight diff --git a/src/PermissionManagers/DefaultPermissionManager.php b/src/PermissionManagers/DefaultPermissionManager.php index bf5f4b1..80a1615 100644 --- a/src/PermissionManagers/DefaultPermissionManager.php +++ b/src/PermissionManagers/DefaultPermissionManager.php @@ -28,7 +28,7 @@ class DefaultPermissionManager implements PermissionManager * @param string $httpMethod API request HTTP method * @return Boolean true or false if permission was given or not */ - public function checkPermission($model, $member = null, $httpMethod) + public function checkPermission($model, $member = null, $httpMethod = 'GET') { if (is_string($model)) { $model = singleton($model); diff --git a/src/PermissionManagers/PermissionManager.php b/src/PermissionManagers/PermissionManager.php index 1f8acc7..677c157 100644 --- a/src/PermissionManagers/PermissionManager.php +++ b/src/PermissionManagers/PermissionManager.php @@ -24,5 +24,5 @@ interface PermissionManager * @param string $httpMethod API request HTTP method * @return Boolean true or false if permission was given or not */ - public function checkPermission($model, $member = null, $httpMethod); + public function checkPermission($model, $member = null, $httpMethod = 'GET'); } diff --git a/src/QueryHandlers/DefaultQueryHandler.php b/src/QueryHandlers/DefaultQueryHandler.php index b800477..2a6a6fb 100644 --- a/src/QueryHandlers/DefaultQueryHandler.php +++ b/src/QueryHandlers/DefaultQueryHandler.php @@ -248,7 +248,7 @@ public function parseQueryParameters(array $params) * @param HTTPRequest $request The original HTTP request * @return DataObject|DataList Result of the search (note: DataList can be empty) */ - public function findModel($model, $id = false, $queryParams, HTTPRequest $request) + public function findModel($model, $id = false, $queryParams = [], HTTPRequest $request = null) { if ($id) { $return = DataObject::get_by_id($model, $id); diff --git a/src/RESTfulAPI.php b/src/RESTfulAPI.php index 21713ea..4e57526 100644 --- a/src/RESTfulAPI.php +++ b/src/RESTfulAPI.php @@ -440,7 +440,7 @@ private function setAnswerCORS(HTTPResponse $answer) //allowed headers $allowedHeaders = ''; $requestHeaders = $this->request->getHeader('Access-Control-Request-Headers'); - if ($cors['Allow-Headers'] === '*') { + if ($cors['Allow-Headers'] === '*' && $requestHeaders != null) { $allowedHeaders = $requestHeaders; } else { $allowedHeaders = $cors['Allow-Headers']; diff --git a/src/ThirdParty/Inflector/Inflector.php b/src/ThirdParty/Inflector/Inflector.php index 6036911..665241a 100644 --- a/src/ThirdParty/Inflector/Inflector.php +++ b/src/ThirdParty/Inflector/Inflector.php @@ -273,7 +273,7 @@ protected static function _cache($type, $key, $value = false) public static function reset() { if (empty(self::$_initialState)) { - self::$_initialState = get_class_vars('Inflector'); + self::$_initialState = get_class_vars(Inflector::class); return; } foreach (self::$_initialState as $key => $val) { diff --git a/tests/Authenticators/TokenAuthenticatorTest.php b/tests/Authenticators/TokenAuthenticatorTest.php index 1df8b20..77780e1 100644 --- a/tests/Authenticators/TokenAuthenticatorTest.php +++ b/tests/Authenticators/TokenAuthenticatorTest.php @@ -40,7 +40,7 @@ protected function getAuthenticator() return $auth; } - public static function setUpBeforeClass() + public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); diff --git a/tests/PermissionManagers/DefaultPermissionManagerTest.php b/tests/PermissionManagers/DefaultPermissionManagerTest.php index d51f46e..09d78b1 100644 --- a/tests/PermissionManagers/DefaultPermissionManagerTest.php +++ b/tests/PermissionManagers/DefaultPermissionManagerTest.php @@ -34,7 +34,7 @@ class DefaultPermissionManagerTest extends RESTfulAPITester ApiTestLibrary::class, ); - public static function setUpBeforeClass() + public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); @@ -83,25 +83,27 @@ public function testReadPermissions() 'Enabled' => false, )); + $id = ApiTestLibrary::get()->first()->ID; + // GET with permission = OK $requestHeaders = $this->getRequestHeaders(); $requestHeaders['X-Silverstripe-Apitoken'] = $this->getAdminToken(); - $response = Director::test('api/apitestlibrary/1', null, null, 'GET', null, $requestHeaders); + $response = Director::test('api/apitestlibrary/' . $id, null, null, 'GET', null, $requestHeaders); $this->assertEquals( - $response->getStatusCode(), 200, + $response->getStatusCode(), "Member of 'restfulapi-administrators' Group should be able to READ records." ); // GET with NO Permission = BAD $requestHeaders = $this->getRequestHeaders(); $requestHeaders['X-Silverstripe-Apitoken'] = $this->getStrangerToken(); - $response = Director::test('api/apitestlibrary/1', null, null, 'GET', null, $requestHeaders); + $response = Director::test('api/apitestlibrary/' . $id, null, null, 'GET', null, $requestHeaders); $this->assertEquals( - $response->getStatusCode(), 403, + $response->getStatusCode(), "Member without permission should NOT be able to READ records." ); } @@ -116,25 +118,27 @@ public function testEditPermissions() 'Enabled' => false, )); + $id = ApiTestLibrary::get()->first()->ID; + // PUT with permission = OK $requestHeaders = $this->getRequestHeaders(); $requestHeaders['X-Silverstripe-Apitoken'] = $this->getAdminToken(); - $response = Director::test('api/apitestlibrary/1', null, null, 'PUT', '{"Name":"Api"}', $requestHeaders); + $response = Director::test('api/apitestlibrary/' . $id, null, null, 'PUT', '{"Name":"Api"}', $requestHeaders); $this->assertEquals( - $response->getStatusCode(), 200, + $response->getStatusCode(), "Member of 'restfulapi-administrators' Group should be able to EDIT records." ); // PUT with NO Permission = BAD $requestHeaders = $this->getRequestHeaders(); $requestHeaders['X-Silverstripe-Apitoken'] = $this->getStrangerToken(); - $response = Director::test('api/apitestlibrary/1', null, null, 'PUT', '{"Name":"Api"}', $requestHeaders); + $response = Director::test('api/apitestlibrary/' . $id, null, null, 'PUT', '{"Name":"Api"}', $requestHeaders); $this->assertEquals( - $response->getStatusCode(), 403, + $response->getStatusCode(), "Member without permission should NOT be able to EDIT records." ); } @@ -155,8 +159,8 @@ public function testCreatePermissions() $response = Director::test('api/apitestlibrary', null, null, 'POST', '{"Name":"Api"}', $requestHeaders); $this->assertEquals( - $response->getStatusCode(), 200, + $response->getStatusCode(), "Member of 'restfulapi-administrators' Group should be able to CREATE records." ); @@ -166,8 +170,8 @@ public function testCreatePermissions() $response = Director::test('api/apitestlibrary', null, null, 'POST', '{"Name":"Api"}', $requestHeaders); $this->assertEquals( - $response->getStatusCode(), 403, + $response->getStatusCode(), "Member without permission should NOT be able to CREATE records." ); } @@ -182,25 +186,27 @@ public function testDeletePermissions() 'Enabled' => false, )); + $id = ApiTestLibrary::get()->first()->ID; + // DELETE with permission = OK $requestHeaders = $this->getRequestHeaders(); $requestHeaders['X-Silverstripe-Apitoken'] = $this->getAdminToken(); - $response = Director::test('api/apitestlibrary/1', null, null, 'DELETE', null, $requestHeaders); + $response = Director::test('api/apitestlibrary/' . $id, null, null, 'DELETE', null, $requestHeaders); $this->assertEquals( - $response->getStatusCode(), 200, + $response->getStatusCode(), "Member of 'restfulapi-administrators' Group should be able to DELETE records." ); // DELETE with NO Permission = BAD $requestHeaders = $this->getRequestHeaders(); $requestHeaders['X-Silverstripe-Apitoken'] = $this->getStrangerToken(); - $response = Director::test('api/apitestlibrary/1', null, null, 'DELETE', null, $requestHeaders); + $response = Director::test('api/apitestlibrary/' . $id, null, null, 'DELETE', null, $requestHeaders); $this->assertEquals( - $response->getStatusCode(), 403, + $response->getStatusCode(), "Member without permission should NOT be able to DELETE records." ); } diff --git a/tests/QueryHandlers/DefaultQueryHandlerTest.php b/tests/QueryHandlers/DefaultQueryHandlerTest.php index e2450c8..45b41f0 100644 --- a/tests/QueryHandlers/DefaultQueryHandlerTest.php +++ b/tests/QueryHandlers/DefaultQueryHandlerTest.php @@ -43,7 +43,7 @@ class DefaultQueryHandlerTest extends RESTfulAPITester /** * Turn on API access for the book and widget fixtures by default */ - public function setUp() + public function setUp(): void { parent::setUp(); @@ -81,7 +81,7 @@ protected function getQueryHandler() return $qh; } - public static function setUpBeforeClass() + public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); @@ -147,7 +147,10 @@ public function testAPIDisabled() public function testFindSingleModel() { $qh = $this->getQueryHandler(); - $request = $this->getHTTPRequest('GET', ApiTestBook::class, '1'); + + $id = ApiTestBook::get()->first()->ID; + + $request = $this->getHTTPRequest('GET', ApiTestBook::class, $id); $result = $qh->handleQuery($request); $this->assertContainsOnlyInstancesOf( @@ -156,7 +159,7 @@ public function testFindSingleModel() 'Single model request should return a DataObject of class model' ); $this->assertEquals( - 1, + $id, $result->ID, 'IDs mismatch. DataObject is not the record requested' ); diff --git a/tests/RESTfulAPITester.php b/tests/RESTfulAPITester.php index e49aa20..bf191f9 100644 --- a/tests/RESTfulAPITester.php +++ b/tests/RESTfulAPITester.php @@ -150,7 +150,7 @@ public function getRequestHeaders($site = null) ); } - public static function setUpBeforeClass() + public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); @@ -159,7 +159,7 @@ public static function setUpBeforeClass() } } - public function setUp() + public function setUp(): void { parent::setUp();