-
Notifications
You must be signed in to change notification settings - Fork 124
feat: add render template API support #922
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f5e0c98
9f80a19
4eb181b
6a11352
2167056
a30d487
afaec4b
666ca08
60e0753
63f4bff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Meilisearch\Contracts; | ||
|
|
||
| class TemplateRenderQuery | ||
| { | ||
| /** | ||
| * @var array{kind: 'inlineDocumentTemplate', inline: string}|array{kind: 'documentTemplate', indexUid: non-empty-string, embedder: non-empty-string}|array{kind: 'documentTemplate', indexUid: non-empty-string, templateUid: non-empty-string} | ||
| */ | ||
| private array $template; | ||
|
|
||
| /** | ||
| * @var array{kind: 'inlineDocument', inline: array<string, mixed>}|array{kind: 'indexDocument', indexUid: non-empty-string, id: string|int}|null | ||
| */ | ||
| private ?array $input = null; | ||
|
|
||
| private bool $inputSet = false; | ||
|
|
||
| /** | ||
| * @param array{kind: 'inlineDocumentTemplate', inline: string}|array{kind: 'documentTemplate', indexUid: non-empty-string, embedder: non-empty-string}|array{kind: 'documentTemplate', indexUid: non-empty-string, templateUid: non-empty-string} $template | ||
| * @param array{kind: 'inlineDocument', inline: array<string, mixed>}|array{kind: 'indexDocument', indexUid: non-empty-string, id: string|int}|null $input | ||
| */ | ||
| public function __construct(array $template, ?array $input = null) | ||
| { | ||
| $this->template = $template; | ||
|
|
||
| if (\func_num_args() >= 2) { | ||
| $this->input = $input; | ||
| $this->inputSet = true; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Set the template to render. | ||
| * | ||
| * Supports two kinds: | ||
| * - inlineDocumentTemplate: ['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.name }}'] | ||
| * - documentTemplate: ['kind' => 'documentTemplate', 'indexUid' => 'movies', 'embedder' => 'myEmbedder'] | ||
| * or ['kind' => 'documentTemplate', 'indexUid' => 'movies', 'templateUid' => 'myTemplate'] | ||
|
Comment on lines
+39
to
+41
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if there are only these three options then it could be properly documented via phpdoc: |
||
| * | ||
| * @param array{kind: 'inlineDocumentTemplate', inline: string}|array{kind: 'documentTemplate', indexUid: non-empty-string, embedder: non-empty-string}|array{kind: 'documentTemplate', indexUid: non-empty-string, templateUid: non-empty-string} $template | ||
| */ | ||
| public function setTemplate(array $template): self | ||
| { | ||
| $this->template = $template; | ||
|
|
||
| return $this; | ||
| } | ||
|
|
||
| /** | ||
| * Set the input document for template rendering. | ||
| * | ||
| * Supports two kinds: | ||
| * - inlineDocument: ['kind' => 'inlineDocument', 'inline' => ['name' => 'John']] | ||
| * - indexDocument: ['kind' => 'indexDocument', 'indexUid' => 'movies', 'id' => '2'] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if there are only these two options then it could be properly documented via phpdoc: |
||
| * | ||
| * Pass null to explicitly send null input (API returns rendered: null). | ||
| * Omit this call entirely to not include input in the request. | ||
| * | ||
| * @param array{kind: 'inlineDocument', inline: array<string, mixed>}|array{kind: 'indexDocument', indexUid: non-empty-string, id: string|int}|null $input | ||
| */ | ||
| public function setInput(?array $input): self | ||
| { | ||
| $this->input = $input; | ||
| $this->inputSet = true; | ||
|
|
||
| return $this; | ||
| } | ||
|
|
||
| /** | ||
| * @return array{template: array{kind: 'inlineDocumentTemplate', inline: string}|array{kind: 'documentTemplate', indexUid: non-empty-string, embedder: non-empty-string}|array{kind: 'documentTemplate', indexUid: non-empty-string, templateUid: non-empty-string}, input?: array{kind: 'inlineDocument', inline: array<string, mixed>}|array{kind: 'indexDocument', indexUid: non-empty-string, id: string|int}|null} | ||
| */ | ||
| public function toArray(): array | ||
| { | ||
| $result = [ | ||
| 'template' => $this->template, | ||
| ]; | ||
|
|
||
| if ($this->inputSet) { | ||
| $result['input'] = $this->input; | ||
| } | ||
|
|
||
| return $result; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Meilisearch\Contracts; | ||
|
|
||
| class TemplateRenderResult | ||
| { | ||
| private string $template; | ||
| private ?string $rendered; | ||
|
|
||
| /** | ||
| * @param array{template: string, rendered: string|null} $data | ||
| */ | ||
| public function __construct(array $data) | ||
| { | ||
| $this->template = $data['template']; | ||
| $this->rendered = $data['rendered']; | ||
| } | ||
|
|
||
| public function getTemplate(): string | ||
| { | ||
| return $this->template; | ||
| } | ||
|
|
||
| public function getRendered(): ?string | ||
| { | ||
| return $this->rendered; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Meilisearch\Endpoints\Delegates; | ||
|
|
||
| use Meilisearch\Contracts\TemplateRenderQuery; | ||
| use Meilisearch\Contracts\TemplateRenderResult; | ||
| use Meilisearch\Endpoints\Templates; | ||
|
|
||
| trait HandlesTemplates | ||
| { | ||
| protected Templates $templates; | ||
|
|
||
| public function renderTemplate(TemplateRenderQuery $query): TemplateRenderResult | ||
| { | ||
| return $this->templates->render($query); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Meilisearch\Endpoints; | ||
|
|
||
| use Meilisearch\Contracts\Endpoint; | ||
| use Meilisearch\Contracts\TemplateRenderQuery; | ||
| use Meilisearch\Contracts\TemplateRenderResult; | ||
|
|
||
| final class Templates extends Endpoint | ||
| { | ||
| protected const PATH = '/render-template'; | ||
|
|
||
| public function render(TemplateRenderQuery $query): TemplateRenderResult | ||
| { | ||
| $response = $this->http->post(self::PATH, $query->toArray()); | ||
|
|
||
| return new TemplateRenderResult($response); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Tests\Contracts; | ||
|
|
||
| use Meilisearch\Contracts\TemplateRenderQuery; | ||
| use PHPUnit\Framework\TestCase; | ||
|
|
||
| final class TemplateRenderQueryTest extends TestCase | ||
| { | ||
| public function testToArrayWithInlineTemplateAndInlineInput(): void | ||
| { | ||
| $query = new TemplateRenderQuery( | ||
| ['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.name }}'], | ||
| ['kind' => 'inlineDocument', 'inline' => ['name' => 'John']], | ||
| ); | ||
|
|
||
| $result = $query->toArray(); | ||
|
|
||
| self::assertSame([ | ||
| 'template' => ['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.name }}'], | ||
| 'input' => ['kind' => 'inlineDocument', 'inline' => ['name' => 'John']], | ||
| ], $result); | ||
| } | ||
|
|
||
| public function testToArrayWithDocumentTemplateAndIndexDocumentInput(): void | ||
| { | ||
| $query = new TemplateRenderQuery( | ||
| ['kind' => 'documentTemplate', 'indexUid' => 'movies', 'embedder' => 'myEmbedder'], | ||
| ['kind' => 'indexDocument', 'indexUid' => 'movies', 'id' => '2'], | ||
| ); | ||
|
|
||
| $result = $query->toArray(); | ||
|
|
||
| self::assertSame([ | ||
| 'template' => ['kind' => 'documentTemplate', 'indexUid' => 'movies', 'embedder' => 'myEmbedder'], | ||
| 'input' => ['kind' => 'indexDocument', 'indexUid' => 'movies', 'id' => '2'], | ||
| ], $result); | ||
| } | ||
|
|
||
| public function testToArrayOmitsInputWhenNotSet(): void | ||
| { | ||
| $query = new TemplateRenderQuery( | ||
| ['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.name }}'], | ||
| // input omitted — not passed | ||
| ); | ||
|
|
||
| $result = $query->toArray(); | ||
|
|
||
| self::assertArrayNotHasKey('input', $result); | ||
| self::assertSame([ | ||
| 'template' => ['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.name }}'], | ||
| ], $result); | ||
| } | ||
|
|
||
| public function testToArrayIncludesNullInputWhenExplicitlySet(): void | ||
| { | ||
| $query = new TemplateRenderQuery( | ||
| ['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.name }}'], | ||
| null, | ||
| ); | ||
|
|
||
| $result = $query->toArray(); | ||
|
|
||
| self::assertArrayHasKey('input', $result); | ||
| self::assertNull($result['input']); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Tests\Endpoints; | ||
|
|
||
| use Meilisearch\Contracts\TemplateRenderQuery; | ||
| use Meilisearch\Http\Client; | ||
| use Tests\TestCase; | ||
|
|
||
| final class TemplatesTest extends TestCase | ||
| { | ||
| protected function setUp(): void | ||
| { | ||
| parent::setUp(); | ||
|
|
||
| $http = new Client($this->host, getenv('MEILISEARCH_API_KEY')); | ||
| $http->patch('/experimental-features', ['renderRoute' => true]); | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| protected function tearDown(): void | ||
| { | ||
| $http = new Client($this->host, getenv('MEILISEARCH_API_KEY')); | ||
| $http->patch('/experimental-features', ['renderRoute' => false]); | ||
|
|
||
| parent::tearDown(); | ||
| } | ||
|
|
||
| public function testCanRenderInlineTemplate(): void | ||
| { | ||
| $query = new TemplateRenderQuery( | ||
| ['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.breed }} called {{ doc.name }}'], | ||
| ['kind' => 'inlineDocument', 'inline' => ['breed' => 'Jack Russell', 'name' => 'Iko']], | ||
| ); | ||
|
|
||
| $response = $this->client->renderTemplate($query); | ||
|
|
||
| self::assertSame('{{ doc.breed }} called {{ doc.name }}', $response->getTemplate()); | ||
| self::assertSame('Jack Russell called Iko', $response->getRendered()); | ||
| } | ||
|
|
||
| public function testCanRenderTemplateWithNullInput(): void | ||
| { | ||
| $query = new TemplateRenderQuery( | ||
| ['kind' => 'inlineDocumentTemplate', 'inline' => '{{ doc.breed }} called {{ doc.name }}'], | ||
| null, | ||
| ); | ||
|
|
||
| $response = $this->client->renderTemplate($query); | ||
|
|
||
| self::assertSame('{{ doc.breed }} called {{ doc.name }}', $response->getTemplate()); | ||
| self::assertNull($response->getRendered()); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.