GithubHelp home page GithubHelp logo

5pm-hdh / churchtools-api Goto Github PK

View Code? Open in Web Editor NEW
18.0 6.0 8.0 1010 KB

The Churchtools-API Client is a wrapper for the churchtools-api written in PHP.

License: MIT License

PHP 100.00%
churchtools-api churchtools-client churchtools php api api-client

churchtools-api's Issues

Bug: Retrieve Group - Weekday

Property Weekday come sometimes in as array, and sometimes as non-array-type.

Stacktrace:

PHP Fatal error:  Uncaught TypeError: Cannot assign int to property CTApi\Models\GroupInformation::$weekday of type ?array in C:\Users\ldumberger\projects\5pm-ct-export\vendor\5pm-hdh\churchtools-api\src\Models\Traits\FillWithData.php:22
Stack trace:
#0 C:\Users\ldumberger\projects\5pm-ct-export\vendor\5pm-hdh\churchtools-api\src\Models\Traits\FillWithData.php(15): CTApi\Models\GroupInformation->fillNonArrayType('weekday', 3)
#1 C:\Users\ldumberger\projects\5pm-ct-export\vendor\5pm-hdh\churchtools-api\src\Models\Traits\FillWithData.php(63): CTApi\Models\GroupInformation->fillWithData(Array)
#2 C:\Users\ldumberger\projects\5pm-ct-export\vendor\5pm-hdh\churchtools-api\src\Models\Group.php(48): CTApi\Models\GroupInformation::createModelFromData(Array)
#3 C:\Users\ldumberger\projects\5pm-ct-export\vendor\5pm-hdh\churchtools-api\src\Models\Traits\FillWithData.php(13): CTApi\Models\Group->fillArrayType('information', Array)
#4 C:\Users\ldumberger\projects\5pm-ct-export\vendor\5pm-hdh\churchtools-api\src\Models\Traits\FillWithData.php(63): CTApi\Models\Group->fillWithData(Array)
#5 C:\Users\ldumberger\projects\5pm-ct-export\vendor\5pm-hdh\churchtools-api\src\Models\Traits\FillWithData.php(44): CTApi\Models\Group::createModelFromData(Array)
#6 [internal function]: CTApi\Models\Group::CTApi\Models\Traits\{closure}(Array)
#7 C:\Users\ldumberger\projects\5pm-ct-export\vendor\5pm-hdh\churchtools-api\src\Models\Traits\FillWithData.php(43): array_map(Object(Closure), Array)
#8 C:\Users\ldumberger\projects\5pm-ct-export\vendor\5pm-hdh\churchtools-api\src\Requests\AbstractRequestBuilder.php(24): CTApi\Models\Group::createModelsFromArray(Array)

security fix for guzzlehttp/guzzle

Some days ago, guzzlehttp/guzzle released v7.4.3 containing a fix for a vulnerability with severity score 8/10. For more details see here: GHSA-cwmx-hcrq-mhc3

Issue

churchtools-api requires "guzzlehttp/guzzle": "7.3.*" in composer.json, preventing the dependency to incorporate this fix.

Possible fix

Please consider to require "guzzlehttp/guzzle:^7" - if compatible - to get all fixes for the 7.x major version.

Pagination does not work

I am trying to load persons in a paginated style. But it looks like this does not work.

$persons = PersonRequest::where('page', 1)
            ->where('limit', 10)
            ->get();

It always loads all persons. As far as I understand the reason is that the conditions are not added as query params to the request.

Edit
Right after sending this issue, I noticed that the Pagination trait iterates over all pages and merges them.
Isn't there a way to limit the result to one page?

Refactor: Delete Person

In #91 a method was defined to delete models. The current code breaks with the builder pattern and has to be refactored.

The PersonRequest currently contains the whole delete logic:

public static function delete(Person $person): void
{
    $id = $person->getId();

    if (is_null($id)) {
        throw new CTModelException("ID of Person cannot be null.");
    }

    (new PersonRequestBuilder())->delete($id);
}
  • Target state: The static delete-method in PersonRequest referes to the PersonRequestBuilder:
public static function delete(Person ($person): void
{
    (new PersonRequestBuilder())->delete($person);
}
  • The current defined delete-method in AbstractRequestBuilder should be renamed to "deleteData" (similar to updateData-method)
  • The deleteData(string $modelId)-method must have the visiblity protected (this is a technical api that should not be accessed from outside)
  • The delete(UpdatableModel $model)-method must have the visibility public

Meta Informations of Wiki

Meta information ModifiedPerson is not stored in person form, but with resource identifier. Transform the given resource identifier to a person model.

Refactor: Check HTTP-Status in CTClient instead of RequestBuilder

The AbstractRequestBuilder contains code to validate the Http-Status-Code:

// AbstractRequestBuilder.php
public function delete(string $modelId): void
{
    $url = $this->getApiEndpoint() . '/' . $modelId;

    $client = CTClient::getClient();
    $response = $client->delete($url);

    if (!in_array($response->getStatusCode(), [200, 204])) {
        throw CTRequestException::ofErrorResponse($response);
    }
}

and

// AbstractRequestBuilder.php
protected function updateData(string $objectId, array $data): void
{
    $url = $this->getApiEndpoint() . '/' . $objectId;

    $client = CTClient::getClient();
    $response = $client->patch($url, ['json' => $data]);

    if ($response->getStatusCode() !== 200) {
        throw CTRequestException::ofErrorResponse($response);
    }
}

I think it would be better to handle the HTTP-Status Code in the CTClient itself. For this reason i created the handleResponse function that is called by every POST, GET, PATCH and DELETE-Method:

// CTClient.php
private function handleResponse(ResponseInterface $response): ResponseInterface
{
    switch ($response->getStatusCode()) {
        case 401:
            throw new CTAuthException("Unauthorized.", 401);
    }
    return $response;
}

If neccessary we could also add a second "HTTP-Method" parameter to handle distinguish HTTP-Methods differently:

private function handleResponse(ResponseInterface $reponse, string $httpMethod): ResponseInterface {}

But i think a good first shot could be to throw a CTRequestException::ofErrorResponse($response) for every status code that is not between 200 and 299.

PublicGroup - Image

There are diffrent Image-Sizes:

  • Heading Image: 86ace43925d8cbc6f67a42559e7860884aae0e1002672f49d28a15092a585a2f?p=group-tile
  • Thumbnail: 86ace43925d8cbc6f67a42559e7860884aae0e1002672f49d28a15092a585a2f

Request diffrent Image Types.

AbstractModel-Class for Id getter

The Id-Property is available in all Models with nullable string property. Create AbstractModel with ID-Property and Getter. Add convenience Getter:

  • getIdOrFail: return ID as String or throw CTModel-Exception (Nullsafe getter)
  • getIdAsInteger: return ID as Integer or null if not castable
  • getIdAsIntegerOrFail: return ID as Integer or throw CTModel-Exception

⚠️ Nullable checks for getId can be replaced with nullsafe getter getIdOrFail

  • Group
$id = $person->getId();
if (is_null($id)) {
    throw new CTModelException("ID of Person cannot be null.");
} else {
    $this->updateDataForModel($person, $id, $attributesToUpdate);
}
  • WikiCategory
if (!is_null($this->getId())) {
    return WikiPageRequestBuilder::requestPageFromCategoryAndIdentifier( (string) $this->getId(), $identifier);
} else {
    return null;
}

⚠️ Integer-Casts can be replaced with getIdAsInteger

  • Event
public function requestAgenda(): EventAgenda
{
    return (new EventAgendaRequestBuilder((int)$this->getId()))->get();
}

Service & ServiceGroups

  • Models: Service / ServiceGroup
  • Requests: ServiceRequest
  • Event: process Data "eventServices"

Document Error handling / expected exceptions

It would be nice if you can write down under doc/, or as docstring, what exceptions one should could expect, when

  1. a request times out
  2. the domain cannot be resolved
  3. the authenticated user is not allowed to see the requested ressource
  4. the session cookie/ login token is invalid

The "GuzzleHttp\Client" class is considered final

I am getting this deprecation notice:

User Deprecated: The "GuzzleHttp\Client" class is considered final. It may change without further notice as of its next major version. You should not extend it from "CTApi\CTClient".

One should switch from inheritence to composition in the client.

Wiki: build page-tree

  • build tree of wiki-pages by parsing the page content for subpages
  • subpages are marked with [[The title of the Subpage]]

Update Request

Error in PersonUpdateRequestTest:
Response for this Integration Test:

{
  "message": "Forbidden to edit the field statusId",
  "translatedMessage": "Keine Berechtigung um das Feld statusId zu sehen oder zu editieren",
  "messageKey": "error.forbidden.edit.field",
  "args": {
    "field": "statusId"
  },
  "errors": []
}
  • Prevent "statusId" from being sent to updateRequest
  • Write the message and translatedMessage to the CTPermissionException for better readability

@Naitsirch Do you run into the same issue with this Integration-Test? How we can handle this Error? Should we create a "blacklist" for update attributes? Or split the getModifiableAttributes into getModifiableAttributesUpdate and getModifiableAttributesCreate?

Refactor CURL-Request of File-Upload

Upload-Method uses pure CURL for Request because Guzzle cant handle combination of header "Content-type:multipart/form-data" and option "form_params".

// Upload file with pure CURL
$ch = curl_init(CTConfig::getApiUrl() . $this->getApiEndpoint() . "?login_token=" . CTConfig::getApiKey());
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"content-type:multipart/form-data",
"csrf-token:" . $csrfToken
]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, ["files[]" => curl_file_create($filePath)]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$resultString = (string)curl_exec($ch);
$curlInfo = curl_getinfo($ch);
CTLog::getLog()->debug("Upload-File Url: " . CTUtil::arrayPathGet($curlInfo, "url"));
CTLog::getLog()->debug("Upload-File Http-Code: " . CTUtil::arrayPathGet($curlInfo, "http_code"));
curl_close($ch);
try {
$data = json_decode($resultString, true);
} catch (\Exception $e) {
CTLog::getLog()->warning("Could not convert upload response to JSON: ". $resultString);
$data = [];
}
$statusCode = array_key_exists("http_code", $curlInfo) ? $curlInfo["http_code"] : 400;
if ($statusCode >= 200 && $statusCode <= 299) {
if (empty($data)) {
return null;
} else {
return File::createModelFromData(CTUtil::arrayPathGet($data, "data"));
}
} else {
$ctResponse = CTResponse::createEmpty();
$ctResponse->withBody(new CTMessageBody($data ?? []));
throw CTRequestException::ofErrorResponse($ctResponse);
}

TODO: Check if there is a workaround and use Guzzle again.

FileDownload

  • disable caching on file download
  • make "requestFileContent" as public method
  • fix file request

Changelog

  • add changelog
  • create tags and releases

GitHub Actions

before pull request / on master branch:

  • composer validation

execute test suite:

  • copy "tests/testdata.example.ini" to "tests/testdata.ini"
  • replace values with repository secrets
  • run phpunit tests

Replace Ajax-Api with REST-API

The ChurchTools API has not yet implemented all functions. To use different update functions anyway, the JavaScript AJAX API is used, which is not documented. The AJAX request calls have been reverese-engineered.

Replace these calls with the REST API as soon as it provides the respective function:

  • SongArrangement (src/Requests/SongArrangementUpdateRequest.php)
  • Song (src/Requests/SongUpdateRequestBuilder.php)

See all usages for: "AjaxApi"

Model "requestXYZ"-Method - one-to-many vs. one-to-one

Currently implemented:

//one-to-one relation (event to agenda)
$event->requestAgenda()->get();

//one-to-many relation (event to songs)
$event->requestSongs()->where('should_practice', true)->get();

The one-to-one relation does not make sense! There will be alway just one returned instance. With the implementation above there could be nonsense code like: $event->requestAgenda()->where('id', [23, 32])->orderBy('id')->get() and you would neet to "unpack" the first array-element always using: $event->requestAgenda()->get()[0]

⚠New design rule:⚠

  • all one-to-one relations returns a model instance (or null)
// the method requestCreator returns a "Person"-Model
$creator = $song->getMeta()->requestCreator()
  • all one-to-many relations returns a RequestBuilder
$songs = $agenda->requestSongs()->where('should_practice', true)->get();

Person requestEvents

  • add requestXYZ-method to Person model: "requestEvents"
  • requests all events that the Person is involved in

Pseudo-Fields in Models

You can fill Models with Attributes, that they don't have defined as Properties.

Example-Book Model:

class BookModel
{
    use FillWithData;

    protected ?string $id = null;
    protected ?string $title = null;
}

Then fill with data, that don't exists on BookModel e.q. Author:

$bookWithPseudoField = BookModel::createModelFromData([
    "id" => 21,
    "title" => "One Example Title",
    "author" => "Pseudo Author"         // this field is not defined in the class
]);

var_dump($bookWithPseudoField);

The FillWithData-Trait will create Author-Property on the Objekt als public member. See the result of the var_dump:

object(Tests\Unit\Requests\BookModel)#21 (3) {
  ["id":protected]=>
  string(2) "21"
  ["title":protected]=>
  string(17) "One Example Title"
  ["author"]=>
  string(13) "Pseudo Author"
}

⚠️I'm not sure if we should prevent the creation of pseudo-fields.

CTConfig: session based auth

Handle diffrent logged in users as session. Enable switching between these sessions, to use the diffrent ct permissons of the users

JSON-Serializer

  • add toJson() to the FillWithData-trait to enable json-export

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.