Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

jobs:
ci:
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v2
with:
# disable auto detection of JS tests (remove if any JS tests are added)
js: false
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This module **does not** provide any method for performing searches on your engi
* [Installation](#installation)
* [Engine vs Index](#engine-vs-index)
* [Specify environment variables](#specify-environment-variables)
* [Understanding your engine prefix and suffix:](#understanding-your-engine-prefix-and-suffix)
* [Understanding your engine prefix and engine suffix:](#understanding-your-engine-prefix-and-engine-suffix)
* [Configuration](#configuration)
* [File attachments for content extraction](#file-attachments-for-content-extraction)
* [Additional documentation](#additional-documentation)
Expand Down Expand Up @@ -40,20 +40,20 @@ To integrate with Silverstripe Search, define environment variables containing y

```
BIFROST_ENDPOINT="https://abc.provided.domain"
BIFROST_ENGINE_PREFIX="<engine-prefix>" # See "Understanding your engine prefix and suffix" below
BIFROST_ENGINE_PREFIX="<enginePrefix>" # See "Understanding your engine prefix and engine suffix" below
BIFROST_MANAGEMENT_API_KEY="abc.123.xyz"
```

### Understanding your engine prefix and suffix:
### Understanding your engine prefix and engine suffix:

> [!IMPORTANT]
> **TL;DR:**
> - All Silverstripe Search engine names follow a 4 slug format like this: `search-<subscription>-<environment>-<suffix>`
> - Your `<engine-prefix>` is everything except `-<suffix>`; so, it's just `search-<subscription>-<environment>`
> - All Silverstripe Search engine names follow a 4 slug format like this: `search-<subscription>-<environment>-<engineSuffix>`
> - Your `<enginePrefix>` is everything except `-<engineSuffix>`; so, it's just `search-<subscription>-<environment>`

For example:

| Engine | Engine prefix | Engine suffix |
| Engine name | Engine prefix | Engine suffix |
|---------------------------|----------------------|---------------|
| search-acmecorp-prod-main | search-acmecorp-prod | main |
| search-acmecorp-prod-inc | search-acmecorp-prod | inc |
Expand All @@ -64,7 +64,7 @@ For example:

Because you probably have more than one environment type that you're running search on (e.g. Production and UAT), and (generally speaking) you should have different engines for each of those environments. So, you can't just hardcode the entire engine name into your project, because that code doesn't change between environments.

Whenever you make a query, Forager will ask you for the "index" name; you will actually want to provide only the `<suffix>`. We will then take `BIFROST_ENGINE_PREFIX` and your `<suffix>`, put them together, and that's what will be queried. This allows you to set `BIFROST_ENGINE_PREFIX` differently for each environment, while having your `<suffix>` hardcoded in your project.
Whenever you make a query, Forager will ask you for the "index" name; you will actually want to provide only the `<engineSuffix>`. We will then take `BIFROST_ENGINE_PREFIX` and your `<engineSuffix>`, put them together, and that's what will be queried. This allows you to set `BIFROST_ENGINE_PREFIX` differently for each environment, while having your `<engineSuffix>` hardcoded in your project.

## Configuration

Expand All @@ -84,7 +84,7 @@ You can specify these data types in the `options` node of your fields.
```yaml
SilverStripe\Forager\Service\IndexConfiguration:
indexes:
<suffix>:
<engineSuffix>:
includeClasses:
SilverStripe\CMS\Model\SiteTree:
fields:
Expand Down Expand Up @@ -123,7 +123,7 @@ This field needs to contain a base 64 encoded string of binary for the file you
```yaml
SilverStripe\Forager\Service\IndexConfiguration:
indexes:
<suffix>:
<engineSuffix>:
includeClasses:
SilverStripe\Assets\File:
fields:
Expand Down
36 changes: 4 additions & 32 deletions _config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,21 @@ Name: forager-bifrost
Only:
envvarset: 'BIFROST_MANAGEMENT_API_KEY'
After:
- 'silverstripe-forager-elastic-enterprise'
- 'search-forager-default'
---
SilverStripe\Core\Injector\Injector:
SilverStripe\Forager\Service\IndexConfiguration:
constructor:
index_variant: '`BIFROST_ENGINE_PREFIX`'
indexPrefix: '`BIFROST_ENGINE_PREFIX`'
SilverStripe\Forager\Interfaces\IndexingInterface:
class: SilverStripe\ForagerBifrost\Service\BifrostService
constructor:
client: '%$Elastic\EnterpriseSearch\Client.managementClient'
client: '%$Silverstripe\Search\Client\Client.managementClient'
configuration: '%$SilverStripe\Forager\Service\IndexConfiguration'
builder: '%$SilverStripe\Forager\Service\DocumentBuilder'
Elastic\EnterpriseSearch\Client.managementClient:
Silverstripe\Search\Client\Client.managementClient:
factory: SilverStripe\ForagerBifrost\Service\ClientFactory
constructor:
host: '`BIFROST_ENDPOINT`'
token: '`BIFROST_MANAGEMENT_API_KEY`'
http_client: '%$GuzzleHttp\Client'
Elastic\EnterpriseSearch\AppSearch\Request\CreateEngine:
class: SilverStripe\ForagerBifrost\Service\Requests\PostEngine
Elastic\EnterpriseSearch\AppSearch\Request\DeleteDocuments:
class: SilverStripe\ForagerBifrost\Service\Requests\DeleteDocuments
Elastic\EnterpriseSearch\AppSearch\Request\GetSchema:
class: SilverStripe\ForagerBifrost\Service\Requests\GetSchema
Elastic\EnterpriseSearch\AppSearch\Request\IndexDocuments:
class: SilverStripe\ForagerBifrost\Service\Requests\PostDocuments
Elastic\EnterpriseSearch\AppSearch\Request\ListDocuments:
class: SilverStripe\ForagerBifrost\Service\Requests\PostDocumentsList
Elastic\EnterpriseSearch\AppSearch\Request\ListEngines:
class: SilverStripe\ForagerBifrost\Service\Requests\PostEngines
Elastic\EnterpriseSearch\AppSearch\Request\PutSchema:
class: SilverStripe\ForagerBifrost\Service\Requests\PostSchema

SilverStripe\ForagerElasticEnterprise\Service\EnterpriseSearchService:
# Append to existing valid_field_types definition
valid_field_types:
binary: 'binary'
# Maximum search Document size: 25 MB
max_document_size: 26214400

## customise the dataobject fields for Bifrost compatability
SilverStripe\Forager\DataObject\DataObjectDocument:
id_field: record_id
base_class_field: record_base_class
page_content_field: page_content
httpClient: '%$GuzzleHttp\Client'
10 changes: 10 additions & 0 deletions _config/dataobject.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
Name: forager-bifrost-dataobject
Only:
envvarset: 'BIFROST_MANAGEMENT_API_KEY'
---
# Customise the dataobject fields for Bifrost compatability
SilverStripe\Forager\DataObject\DataObjectDocument:
id_field: record_id
base_class_field: record_base_class
page_content_field: page_content
9 changes: 9 additions & 0 deletions _config/extensions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,12 @@ SilverStripe\Assets\File:
SilverStripe\AssetAdmin\Forms\FileFormFactory:
extensions:
ForagerBifrostFileFormExtension: SilverStripe\ForagerBifrost\Extensions\FileFormExtension

---
Name: 'forager-bifrost-extensions-queuedjobs'
Only:
moduleexists: 'symbiote/silverstripe-queuedjobs'
---
Symbiote\QueuedJobs\Services\QueuedJobService:
extensions:
ForagerBifrostQueuedJobsExtension: SilverStripe\ForagerBifrost\Extensions\QueuedJobsExtension
16 changes: 1 addition & 15 deletions _config/synonyms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ Name: forager-bifrost-synonyms
Only:
envvarset: 'BIFROST_MANAGEMENT_API_KEY'
After:
- 'silverstripe-forager-elastic-enterprise-synonyms'
- 'silverstripe-forager-synonyms'
---
SilverStripe\Core\Injector\Injector:
# Adaptors provided by this module
SilverStripe\Forager\Interfaces\Requests\CreateSynonymRuleAdaptor:
class: SilverStripe\ForagerBifrost\Adaptors\Requests\CreateSynonymRuleAdaptor
SilverStripe\Forager\Interfaces\Requests\GetSynonymRuleAdaptor:
Expand All @@ -18,17 +16,5 @@ SilverStripe\Core\Injector\Injector:
class: SilverStripe\ForagerBifrost\Adaptors\Requests\UpdateSynonymRuleAdaptor
SilverStripe\Forager\Interfaces\Requests\DeleteSynonymRuleAdaptor:
class: SilverStripe\ForagerBifrost\Adaptors\Requests\DeleteSynonymRuleAdaptor
# Adaptors provided by the ElasticEnterprise dependency
SilverStripe\Forager\Interfaces\Requests\GetSynonymCollectionsAdaptor:
class: SilverStripe\ForagerElasticEnterprise\Adaptors\Requests\GetSynonymCollectionsAdaptor
# Request overrides to work with the Bifröst API
Elastic\EnterpriseSearch\AppSearch\Request\DeleteSynonymSet:
class: SilverStripe\ForagerBifrost\Service\Requests\DeleteSynonymRule
Elastic\EnterpriseSearch\AppSearch\Request\GetSynonymSet:
class: SilverStripe\ForagerBifrost\Service\Requests\GetSynonymRule
Elastic\EnterpriseSearch\AppSearch\Request\ListSynonymSets:
class: SilverStripe\ForagerBifrost\Service\Requests\GetSynonymRules
Elastic\EnterpriseSearch\AppSearch\Request\CreateSynonymSet:
class: SilverStripe\ForagerBifrost\Service\Requests\CreateSynonymRule
Elastic\EnterpriseSearch\AppSearch\Request\PutSynonymSet:
class: SilverStripe\ForagerBifrost\Service\Requests\UpdateSynonymRule
class: SilverStripe\ForagerBifrost\Adaptors\Requests\GetSynonymCollectionsAdaptor
34 changes: 15 additions & 19 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "silverstripe/silverstripe-forager-bifrost",
"description": "A Silverstripe Search add-on for silverstripe/silverstripe-forager",
"description": "A Silverstripe Search adaptor for silverstripe/silverstripe-forager",
"type": "silverstripe-vendormodule",
"license": "BSD-3-Clause",
"homepage": "https://github.com/silverstripeltd/silverstripe-forager-bifrost",
Expand All @@ -17,26 +17,17 @@
"Bifröst"
],
"require": {
"php": "^8.1",
"silverstripe/framework": "^5",
"silverstripe/reports": "^5",
"silverstripe/silverstripe-forager-elastic-enterprise": "^1.1",
"guzzlehttp/guzzle": "^7"
"php": "^8.3",
"silverstripe/framework": "^6",
"silverstripe/reports": "^6",
"silverstripe/silverstripe-forager": "2.0.x-dev",
"silverstripe/silverstripe-search-client-php": "0.0.7-alpha",
"guzzlehttp/guzzle": "^7.9"
},
"require-dev": {
"silverstripe/recipe-cms": "^5",
"phpunit/phpunit": "^9.6.19",
"slevomat/coding-standard": "^8.8"
},
"repositories": {
"silverstripe/silverstripe-forager": {
"type": "git",
"url": "https://github.com/silverstripeltd/silverstripe-forager.git"
},
"silverstripe/silverstripe-forager-elastic-enterprise": {
"type": "git",
"url": "https://github.com/silverstripeltd/silverstripe-forager-elastic-enterprise.git"
}
"silverstripe/recipe-cms": "^6",
"phpunit/phpunit": "^11.3",
"slevomat/coding-standard": "~8.18.0"
},
"minimum-stability": "dev",
"prefer-stable": true,
Expand All @@ -57,5 +48,10 @@
},
"scripts": {
"phpcs": "phpcs --standard=phpcs.xml --extensions=php --encoding=utf-8"
},
"extra": {
"branch-alias": {
"dev-main": "2.0.x-dev"
}
}
}
6 changes: 6 additions & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
<file>src</file>
<file>tests</file>

<!-- Don't sniff test classes, because they include PHP attributes that cause Slevomat to throw errors -->
<exclude-pattern>./tests/*</exclude-pattern>
<!-- Don't sniff third party libraries -->
<exclude-pattern>./vendor/*</exclude-pattern>
<exclude-pattern>*/thirdparty/*</exclude-pattern>
Expand Down Expand Up @@ -146,6 +148,10 @@
<exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification"/>
<exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingTraversableTypeHintSpecification"/>
<exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingTraversableTypeHintSpecification"/>
<!-- Leave group order up to us -->
<exclude name="SlevomatCodingStandard.Classes.ClassStructure.IncorrectGroupOrder"/>
<!-- Leave the order up to us to decide -->
<exclude name="SlevomatCodingStandard.Classes.ClassStructure.IncorrectGroupOrder"/>
</rule>

<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses">
Expand Down
18 changes: 11 additions & 7 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<source>
<include>
<directory suffix=".php">src/</directory>
<exclude>
<directory suffix=".php">tests/</directory>
</exclude>
</whitelist>
</filter>
</include>
<exclude>
<directory suffix=".php">tests/</directory>
</exclude>
</source>
<php>
<env name="BIFROST_ENDPOINT" value="https://fakeplace.com" force="true"/>
<env name="BIFROST_MANAGEMENT_API_KEY" value="fakeApiKey" force="true"/>
</php>
</phpunit>
26 changes: 18 additions & 8 deletions src/Adaptors/Requests/CreateSynonymRuleAdaptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

namespace SilverStripe\ForagerBifrost\Adaptors\Requests;

use Elastic\EnterpriseSearch\Client;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forager\Interfaces\Requests\CreateSynonymRuleAdaptor as PostSynonymRuleAdaptorInterface;
use SilverStripe\Forager\Service\IndexConfiguration;
use SilverStripe\Forager\Service\Query\SynonymRule as SynonymRuleQuery;
use SilverStripe\Forager\Service\Results\SynonymRule as SynonymRuleResult;
use SilverStripe\ForagerBifrost\Processors\SynonymRuleProcessor;
use SilverStripe\ForagerBifrost\Service\Requests\CreateSynonymRule;
use Silverstripe\Search\Client\Client;
use Silverstripe\Search\Client\Exception\SynonymRulePostNotFoundException;
use Silverstripe\Search\Client\Exception\SynonymRulePostUnprocessableEntityException;
use Silverstripe\Search\Client\Model\SynonymRuleRequest;

class CreateSynonymRuleAdaptor implements PostSynonymRuleAdaptorInterface
{
Expand All @@ -24,16 +26,24 @@ public function setClient(?Client $client): void
$this->client = $client;
}

/**
* @throws SynonymRulePostNotFoundException
* @throws SynonymRulePostUnprocessableEntityException
*/
public function process(int|string $synonymCollectionId, SynonymRuleQuery $synonymRule): SynonymRuleResult
{
$request = Injector::inst()->create(CreateSynonymRule::class, $synonymCollectionId, $synonymRule);
// Silverstripe Search simply uses the engine name as the Synonym Collection ID
$engineName = IndexConfiguration::singleton()->environmentizeIndex($synonymCollectionId);
// Convert the query into a Silverstripe Search synonym rule string
$synonyms = SynonymRuleProcessor::getStringFromQuery($synonymRule);
$request = new SynonymRuleRequest();
$request->setSynonyms($synonyms);

// Should either be successful or throw an exception, which we'll let fly
$body = $this->client->appSearch()->createSynonymSet($request)->asString();
$body = json_decode($body, true);
$response = $this->client->synonymRulePost($engineName, $request);

$synonymRuleResult = SynonymRuleResult::create($body['id']);
SynonymRuleProcessor::applyStringToResult($synonymRuleResult, $body['synonyms']);
$synonymRuleResult = SynonymRuleResult::create($response->getId());
SynonymRuleProcessor::applyStringToResult($synonymRuleResult, $response->getSynonyms());

return $synonymRuleResult;
}
Expand Down
19 changes: 12 additions & 7 deletions src/Adaptors/Requests/DeleteSynonymRuleAdaptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

namespace SilverStripe\ForagerBifrost\Adaptors\Requests;

use Elastic\EnterpriseSearch\AppSearch\Request\DeleteSynonymSet;
use Elastic\EnterpriseSearch\Client;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forager\Interfaces\Requests\DeleteSynonymRuleAdaptor as DeleteSynonymRuleAdaptorInterface;
use SilverStripe\Forager\Service\IndexConfiguration;
use Silverstripe\Search\Client\Client;
use Silverstripe\Search\Client\Exception\SynonymRuleDeleteNotFoundException;
use Silverstripe\Search\Client\Exception\SynonymRuleDeleteUnprocessableEntityException;

class DeleteSynonymRuleAdaptor implements DeleteSynonymRuleAdaptorInterface
{
Expand All @@ -21,15 +22,19 @@ public function setClient(?Client $client): void
$this->client = $client;
}

/**
* @throws SynonymRuleDeleteNotFoundException
* @throws SynonymRuleDeleteUnprocessableEntityException
*/
public function process(int|string $synonymCollectionId, int|string $synonymRuleId): bool
{
$request = Injector::inst()->create(DeleteSynonymSet::class, $synonymCollectionId, $synonymRuleId);
// Silverstripe Search simply uses the engine name as the Synonym Collection ID
$engineName = IndexConfiguration::singleton()->environmentizeIndex($synonymCollectionId);

// Should either be successful or throw an exception, which we'll let fly
$body = $this->client->appSearch()->deleteSynonymSet($request)->asString();
$body = json_decode($body, true);
$response = $this->client->synonymRuleDelete($synonymRuleId, $engineName);

return $body['success'];
return $response->getSuccess();
}

}
Loading