sowiso / php-api-sdk

一个包装SOWISO API的SOWISO PHP库

v1.2.0 2024-04-17 13:07 UTC

This package is auto-updated.

Last update: 2024-09-17 14:38:24 UTC


README

这个PHP库包含了访问SOWISO API的SDK。它不应该被公开使用。

安装

composer require sowiso/php-api-sdk

使用

SDK由一些在本节中解释的功能组成。通用使用方法可在此(基本且不工作)示例中查看

$configuration = SowisoApiConfiguration::create();
$api = new SowisoApi($configuration);

$context = SowisoApiContext::create();
$response = $api->request($context, '{}');

SDK必须使用配置实例化,然后可以用来请求API。

配置

$configuration = SowisoApiConfiguration::create(
    baseUrl: 'SOWISO_API_BASE_URL',
    apiKey: 'SOWISO_API_KEY',
);

$api = new SowisoApi($configuration);

SOWISO API需要以下配置设置

  • API基础URL:要使用的SOWISO服务器的URL。示例:"https://cloud.sowiso.nl"(没有尾随反斜杠)
  • API密钥:用于验证所有请求的API密钥。仅由SOWISO发布。

PSR-17和PSR-18

SDK允许指定使用您自己的HTTP客户端(PSR-18)和您自己的HTTP消息工厂(PSR-17)。如果没有提供,SDK将使用包来自动发现项目中适用的包。

$api = new SowisoApi(
    configuration: SowisoApiConfiguration::create(), // The configuration is needed here
    httpClient: ...,
    httpRequestFactory: ...,
    httpStreamFactory: ...,
);

上下文

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$context = SowisoApiContext::create(
    data: ['anything-you-want' => 42],
    user: 'CURRENT_USER',
);

$response = $api->request($context, '{}'); // The JSON data from the request

上下文可用于将任意数据从请求传递到回调和钩子方法。此外,上下文还包含一些由某些端点所需的数据

  • PlayExerciseSet
    • SowisoApiContext#user

有效负载

SDK支持将附加有效负载从每个请求传递到回调和钩子。此附加有效负载可以通过请求JSON中的__additionalPayload字段指定。如果没有提供或字段为空,则SowisoApiPayload#getData()方法返回null

异常

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$context = SowisoApiContext::create();

try {
    $response = $api->request($context, '{}');
} catch (NoBaseUrlException $e) {
    // when no API base url is set
} catch (NoApiKeyException $e) {
    // when no API key is set
} catch (NoUserException $e) {
    // when no API user is set in the context (and the requested endpoint requires it)
} catch (NoEndpointException $e) {
    // when the API request is missing the endpoint to fetch
} catch (InvalidEndpointException $e) {
    // when the API request has specified an invalid endpoint to fetch
} catch (InvalidJsonDataException $e) {
    // when the SDK cannot handle JSON data
} catch (InvalidJsonRequestException $e) {
    // when the API request has invalid JSON data
} catch (InvalidJsonResponseException $e) {
    // when the API response has invalid JSON data
} catch (MissingDataException $e) {
    // when the API request or response is missing required data
} catch (InvalidDataException $e) {
    // when the API request or response contains invalid data
} catch (ResponseErrorException $e) {
    // when the API response has an error
} catch (FetchingFailedException $e) {
    // when the internal HTTP request to the API has failed for some reason
} catch (DataVerificationFailedException $e) {
    // when verifying data failed in the "DataVerificationHook"
} catch (SowisoApiException $e) {
    // when any other SDK-related error occurs
} catch (\Exception $e) {
    // when any other error occurs
}

注意:不建议像示例中那样捕获每个异常。

端点

SDK设计成以JSON数据为输入,以相同的格式返回API的响应。输入JSON必须是包含名为__endpoint的顶级字段的JSON对象,该字段包含端点名称。该字段确定要调用的实际SOWISO API端点。该JSON对象中的所有数据都传递给API端点,除了所有以__开头的字段。响应可以是JSON对象或JSON数组。

以下示例显示了SDK的基本用法以及如何指定API端点。

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$context = SowisoApiContext::create();

$response = $api->request($context, '{"__endpoint": "ENDPOINT_NAME"}'); // Use any actual endpoint name here, e.g., "play/set"

目前,SDK中提供以下端点

PlayExerciseSet

  • 名称:play/set
  • API端点:
    • GET /api/play/set/set_id/:set_id/username/:username/lang/:lang/view/:view
    • GET /api/play/set/try_id/:try_id/username/:username/lang/:lang/view/:view
  • API文档https://cloud.sowiso.nl/documentation/api/index.html#api-Player-PlaySet
  • 请求字段:
    • set_id(整数)try_id(整数)
    • lang(字符串)[可选]
    • view(字符串)(student|readonly) [可选,默认=student]
  • 响应字段:
    • {exercise_id, try_id}[]ExerciseTry[]
  • 必需的上下文字段:
    • username(字符串)

当请求的视图设置为readonly时,该组中的练习不会返回“尝试ID”。因此,处理“尝试ID”的相应钩子不会调用。

当请求包含try_id而不是set_id时,API会继续属于该try_id的练习组。不允许提供两个字段,否则会导致InvalidDataException异常。

PlayExercise

目前,此端点仅支持获取“尝试ID”的练习。在SDK的后续版本中,可能支持请求“练习ID”。

ReplayExerciseTry

EvaluateAnswer

PlayHint

PlaySolution

StoreAnswer

回调

SDK允许为每个端点指定回调实现。每个回调都提供四种方法(可选)实现

  • onRequest回调方法在请求转发到SOWISO API之前调用。其数据包含请求数据和可能的环境数据。
  • onResponse回调方法在从SOWISO API返回响应后调用。其数据包含响应数据,以及可能的环境数据。
  • onSuccess回调方法在请求和响应都成功时调用。其数据包含请求数据、响应数据,以及可能的环境数据。
  • onFailure回调方法在请求或响应未成功时调用。其数据包含异常以及可能的环境数据。

当使用同一端点的多个回调时,它们按指定顺序执行。

PlayExerciseSet

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useCallback(new class extends PlayExerciseSetCallback {
    public function onRequest(PlayExerciseSetOnRequestData $data): void {}
    public function onResponse(PlayExerciseSetOnResponseData $data): void {}
    public function onSuccess(PlayExerciseSetOnSuccessData $data): void {}
    public function onFailure(PlayExerciseSetOnFailureData $data): void {}
});

PlayExercise

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useCallback(new class extends PlayExerciseCallback {
    public function onRequest(PlayExerciseOnRequestData $data): void {}
    public function onResponse(PlayExerciseOnResponseData $data): void {}
    public function onSuccess(PlayExerciseOnSuccessData $data): void {}
    public function onFailure(PlayExerciseOnFailureData $data): void {}
});

ReplayExerciseTry

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useCallback(new class extends ReplayExerciseTryCallback {
    public function onRequest(ReplayExerciseTryOnRequestData $data): void {}
    public function onResponse(ReplayExerciseTryOnResponseData $data): void {}
    public function onSuccess(ReplayExerciseTryOnSuccessData $data): void {}
    public function onFailure(ReplayExerciseTryOnFailureData $data): void {}
});

EvaluateAnswer

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useCallback(new class extends EvaluateAnswerCallback {
    public function onRequest(EvaluateAnswerOnRequestData $data): void {}
    public function onResponse(EvaluateAnswerOnResponseData $data): void {}
    public function onSuccess(EvaluateAnswerOnSuccessData $data): void {}
    public function onFailure(EvaluateAnswerOnFailureData $data): void {}
});

PlayHint

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useCallback(new class extends PlayHintCallback {
    public function onRequest(PlayHintOnRequestData $data): void {}
    public function onResponse(PlayHintOnResponseData $data): void {}
    public function onSuccess(PlayHintOnSuccessData $data): void {}
    public function onFailure(PlayHintOnFailureData $data): void {}
});

PlaySolution

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useCallback(new class extends PlaySolutionCallback {
    public function onRequest(PlaySolutionOnRequestData $data): void {}
    public function onResponse(PlaySolutionOnResponseData $data): void {}
    public function onSuccess(PlaySolutionrOnSuccessData $data): void {}
    public function onFailure(PlaySolutionOnFailureData $data): void {}
});

StoreAnswer

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useCallback(new class extends StoreAnswerCallback {
    public function onRequest(StoreAnswerOnRequestData $data): void {}
    public function onResponse(StoreAnswerOnResponseData $data): void {}
    public function onSuccess(StoreAnswerOnSuccessData $data): void {}
    public function onFailure(StoreAnswerOnFailureData $data): void {}
});

请求处理器

SDK 允许为每个端点指定请求处理器。每个请求处理器都提供一个 handle 方法来(可选)实现。它的数据包含请求数据以及可能的环境数据。当 handle 方法返回任何数组时,请求不会传递给 API。如果 handle 方法没有返回 null,则请求将传递给 API。SDK 仍然运行端点的所有回调方法。

当为同一端点使用多个请求处理器时,只有最新的处理器用于处理请求。

PlayExerciseSet

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useRequestHandler(new class extends PlayExerciseSetRequestHandler {
    public function handle(SowisoApiContext $context, SowisoApiPayload $payload, PlayExerciseSetRequest $request): ?array
    {
        return null; // Or whatever should be the response
    }
});

PlayExercise

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useRequestHandler(new class extends PlayExerciseRequestHandler {
    public function handle(SowisoApiContext $context, SowisoApiPayload $payload, PlayExerciseRequest $request): ?array
    {
        return null; // Or whatever should be the response
    }
});

ReplayExerciseTry

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useRequestHandler(new class extends ReplayExerciseTryRequestHandler {
    public function handle(SowisoApiContext $context, SowisoApiPayload $payload, ReplayExerciseTryRequest $request): ?array
    {
        return null; // Or whatever should be the response
    }
});

EvaluateAnswer

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useRequestHandler(new class extends EvaluateAnswerRequestHandler {
    public function handle(SowisoApiContext $context, SowisoApiPayload $payload, EvaluateAnswerRequest $request): ?array
    {
        return null; // Or whatever should be the response
    }
});

PlayHint

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useRequestHandler(new class extends PlayHintRequestHandler {
    public function handle(SowisoApiContext $context, SowisoApiPayload $payload, PlayHintRequest $request): ?array
    {
        return null; // Or whatever should be the response
    }
});

PlaySolution

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useRequestHandler(new class extends PlaySolutionRequestHandler {
    public function handle(SowisoApiContext $context, SowisoApiPayload $payload, PlaySolutionRequest $request): ?array
    {
        return null; // Or whatever should be the response
    }
});

StoreAnswer

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useRequestHandler(new class extends StoreAnswerRequestHandler {
    public function handle(SowisoApiContext $context, SowisoApiPayload $payload, StoreAnswerRequest $request): ?array
    {
        return null; // Or whatever should be the response
    }
});

钩子

SDK 提供所谓的钩子,它们 挂钩到 一个或多个回调。它们用于提供一些 SOWISO 特定的功能。

数据验证

DataVerificationHook 简化了在将数据传递给 API 之前对请求数据的验证。此钩子的主要用例是确保在 SOWISO 播放器和 SDK 之间可能发生的任何请求数据篡改在传递给 API 之前被检测和阻止。

数据验证可以在每个端点级别进行。例如,所有要发送到 "play/set" 端点的请求在发送到 API 之前将通过 verifyPlayExerciseSetRequest() 方法进行验证。每次数据验证失败时,实现可以抛出 SowisoApiException.DataVerificationFailed 异常(或任何其他异常),这将中止请求堆栈(即,请求不会传递给 API)并退出 SowisoApi#request() 方法。

除了解析的、特定于端点的请求数据之外,所有请求数据对象都包含以下属性

  • context - 传递给 SowisoApi#request() 方法的上下文对象
  • payload - 传递在请求的 __additionalPayload 字段中的 JSON 数据
$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useHook(new class extends DataVerificationHook {
    public function verifyPlayExerciseSetRequest(PlayExerciseSetOnRequestData $data): void {}
    public function verifyPlayExerciseRequest(PlayExerciseOnRequestData $data): void {}
    public function verifyReplayExerciseTryRequest(ReplayExerciseTryOnRequestData $data): void {}
    public function verifyEvaluateAnswerRequest(EvaluateAnswerOnRequestData $data): void {}
    public function verifyPlayHintRequest(PlayHintOnRequestData $data): void {}
    public function verifyPlaySolutionRequest(PlaySolutionOnRequestData $data): void {}
    public function verifyStoreAnswerRequest(StoreAnswerOnRequestData $data): void {}
});

从 TryIdVerification 钩子迁移到 DataVerification 钩子

新的 DataVerificationHook 通过大量简化了整个验证过程;然而,它需要在钩子实现中添加一些逻辑。

首先,我们已经从 TryIdVerificationHook 中移除了 onRegisterTryId() 方法,因为它与 DataCaptureHook::onRegisterExerciseSet() 冲突。如果你没有在 DataCaptureHook 中使用此方法来注册新的练习尝试,请这样做。

其次,我们已移除 onCatchInvalidTryId() 方法。这是一个可选的辅助方法,可以用于捕获无效的 "Try ID" 的情况。同样,可以通过捕获 InvalidTryIdException 异常(它也将被移除)来实现相同的行为,并且可以通过捕获 DataVerificationFailedException 异常来获得。

最后,是 isValidTryId() 方法。这个方法在包含 "Try ID" 的每个请求上都被调用。使用新的 DataVerificationHook,将为每个端点提供新的 validate...Request() 方法,您可以在其中验证给定的 "Try ID"。

以下示例展示了 DataVerificationHook 的一个实现,其中私有的 verifyTryId() 方法类似于旧 isValidTryId() 方法(应抛出 DataVerificationFailedException 异常而不是返回 false)。如您所见,目前只需要对 "play/set" 端点给予特殊关注,因为该端点允许请求 "Try ID" 和 "Set ID"(但不能同时请求)。此外,此示例还显示了如何验证其他请求数据参数。

$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useHook(new class extends DataVerificationHook {
    public function verifyPlayExerciseSetRequest(PlayExerciseSetOnRequestData $data): void {
        if ($data->getRequest()->usesTryId()) {
            $this->verifyTryId($data->getContext(), $data->getPayload(), $data->getRequest()->getTryId());
        } else {
            // $this->verifySetId($data->getRequest()->getSetId());
        }

        // $this->verifyView($data->getRequest()->getView());
    }

    public function verifyPlayExerciseRequest(PlayExerciseOnRequestData $data): void {
        $this->verifyTryId($data->getContext(), $data->getPayload(), $data->getRequest()->getTryId());
        // $this->verifyView($data->getRequest()->getView());
    }

    public function verifyReplayExerciseTryRequest(ReplayExerciseTryOnRequestData $data): void {
        $this->verifyTryId($data->getContext(), $data->getPayload(), $data->getRequest()->getTryId());
    }

    public function verifyEvaluateAnswerRequest(EvaluateAnswerOnRequestData $data): void {
        $this->verifyTryId($data->getContext(), $data->getPayload(), $data->getRequest()->getTryId());
    }

    public function verifyPlayHintRequest(PlayHintOnRequestData $data): void {
        $this->verifyTryId($data->getContext(), $data->getPayload(), $data->getRequest()->getTryId());
    }

    public function verifyPlaySolutionRequest(PlaySolutionOnRequestData $data): void {
        $this->verifyTryId($data->getContext(), $data->getPayload(), $data->getRequest()->getTryId());
    }

    public function verifyStoreAnswerRequest(StoreAnswerOnRequestData $data): void {
        $this->verifyTryId($data->getContext(), $data->getPayload(), $data->getRequest()->getTryId());
    }

    private function verifyTryId(SowisoApiContext $context, SowisoApiPayload $payload, int $tryId): void {
        // ...
    }
});

数据捕获

DataCapture 钩子简化了接收常见、处理过的数据。它将来可以扩展以提供其他常见数据。

OnRegisterExerciseSetData 对象包含以下属性

  • setId - 包含练习的 "Set ID"
  • exerciseTries - 练习尝试的列表,每个尝试包括 "Exercise ID" 和 "Try ID"
  • context - 传递给 SowisoApi#request() 方法的上下文对象
  • payload - 传递在请求的 __additionalPayload 字段中的 JSON 数据
$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useHook(new class extends DataCaptureHook {
    public function onRegisterExerciseSet(OnRegisterExerciseSetData $data): void {}
});

得分捕获

ScoreCapture 钩子封装了所有返回用户某些形式得分的端点。OnScoreData 对象包含以下属性

  • tryId - 与得分相关的 "尝试 ID"
  • score - 表示得分的数字,介于 0.0 和 10.0 之间,考虑了历史得分
  • completed - 一个布尔值,表示答案是否已完成/最终
  • source - 一个枚举,表示哪个端点返回了这个得分
  • context - 传递给 SowisoApi#request() 方法的上下文对象
  • payload - 传递在请求的 __additionalPayload 字段中的 JSON 数据
$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useHook(new class extends ScoreCaptureHook {
    public function onScore(OnScoreData $data): void {}
});

测试模式

TestMode 钩子简化了与在 "测试" 模式下播放和评估相关的所有操作。通过实现和使用此钩子,您可以决定某个 "练习集" 或 "练习尝试" 是否应使用 "测试" 模式。

注意:当在 "测试" 模式下播放 "练习集" 时,返回的 "练习尝试" 也应在 "测试" 模式下评估。

所有数据对象都包含以下属性

  • context - 传递给 SowisoApi#request() 方法的上下文对象
  • payload - 传递在请求的 __additionalPayload 字段中的 JSON 数据
$api = new SowisoApi(SowisoApiConfiguration::create()); // The configuration is needed here

$api->useHook(new class extends TestModeHook {
    public function shouldExerciseSetBePlayedInTestMode(ShouldExerciseSetBePlayedInTestModeData $data): bool {}
    public function shouldExerciseTryBePlayedInTestMode(ShouldExerciseTryBePlayedInTestModeData $data): bool {}
    public function shouldExerciseTryBeEvaluatedInTestMode(ShouldExerciseTryBeEvaluatedInTestModeData $data): bool {}
});