Skip to content

Commit

Permalink
1859: Fixed merge
Browse files Browse the repository at this point in the history
  • Loading branch information
tuj committed Aug 16, 2024
2 parents cbbb9a1 + a6ee440 commit da336bc
Show file tree
Hide file tree
Showing 18 changed files with 482 additions and 17 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/php_upgrade.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ jobs:
MYSQL_PASSWORD: db
MYSQL_DATABASE: db_test
MYSQL_ROOT_PASSWORD: password
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
# https://mariadb.org/mariadb-server-docker-official-images-healthcheck-without-mysqladmin/
options: >-
--health-cmd="healthcheck.sh --connect --innodb_initialized"
--health-interval=5s
--health-timeout=2s
--health-retries=3
strategy:
fail-fast: false
matrix:
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,12 @@ jobs:
MYSQL_PASSWORD: db
MYSQL_DATABASE: db_test
MYSQL_ROOT_PASSWORD: password
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
# https://mariadb.org/mariadb-server-docker-official-images-healthcheck-without-mysqladmin/
options: >-
--health-cmd="healthcheck.sh --connect --innodb_initialized"
--health-interval=5s
--health-timeout=2s
--health-retries=3
strategy:
fail-fast: false
matrix:
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@ All notable changes to this project will be documented in this file.
- Adds support for interactive slides.
- Adds interactivity for creating quick bookings from a slide through Microsoft Graph.
- Adds KeyVaultService that can serve key-value entries from the environment for storing secrets.

## [2.0.6] - 2024-06-28

- [#208](https://github.com/os2display/display-api-service/pull/208)
- Removed feed items from Notified where image returns 403.
- Fixed phpunit github actions healthcheck for mariadb.
- [#207](https://github.com/os2display/display-api-service/pull/207)
- Fixed parameter not set error in (os2display) api container.

## [2.0.5] - 2024-05-21

- [#206](https://github.com/os2display/display-api-service/pull/206)
- Added support for Notified (Instagram) feed as replacement for SparkleIOFeedType.
- Deprecated SparkleIOFeedType. (getsparkle.io has shut down)

## [2.0.4] - 2024-04-25

- [#204](https://github.com/os2display/display-api-service/pull/204)
- Ensured real ip is logged in nginx.
- [#200](https://github.com/os2display/display-api-service/pull/200)
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@
"vendor/bin/phpunit --stop-on-failure"
],
"test-setup": [
"bin/console cache:clear --env=test --no-debug",
"bin/console --env=test cache:clear --no-debug",
"bin/console --env=test doctrine:database:drop --if-exists --force --quiet",
"bin/console --env=test doctrine:database:create --no-interaction --if-not-exists --quiet",
"bin/console --env=test doctrine:migrations:migrate --no-interaction --quiet"
Expand Down
9 changes: 9 additions & 0 deletions fixtures/feed.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,12 @@ App\Entity\Tenant\Feed:
createdAt (unique): '<dateTimeBetween("-2 years", "-2 days")>'
modifiedAt: '<dateTimeBetween($createdAt, "-1 days")>'
id: '<ulid($createdAt)>'
feed_abc_notified:
feedSource: '@feed_source_abc_notified'
slide: '@slide_abc_notified'
tenant: '@tenant_abc'
createdAt (unique): '<dateTimeBetween("-2 years", "-2 days")>'
modifiedAt: '<dateTimeBetween($createdAt, "-1 days")>'
id: '<ulid($createdAt)>'
configuration:
feeds: [12345]
9 changes: 8 additions & 1 deletion fixtures/feed_source.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ App\Entity\Tenant\FeedSource:
feed (template):
description: <text()>
feedType: "App\\Feed\\RssFeedType"
secrets: []
secrets: [ ]
supportedFeedOutputType: 'rss'
createdAt (unique): '<dateTimeBetween("-2 years", "-2 days")>'
modifiedAt: '<dateTimeBetween($createdAt, "-1 days")>'
Expand All @@ -14,3 +14,10 @@ App\Entity\Tenant\FeedSource:
feed_source_xyz_2 (extends feed):
title: 'feed_source_xyz_2'
tenant: '@tenant_xyz'
feed_source_abc_notified (extends feed):
title: 'feed_source_abc_notified'
feedType: "App\\Feed\\RssFeedType"
secrets:
token: '1234567890'
supportedFeedOutputType: 'instagram'
tenant: '@tenant_abc'
10 changes: 8 additions & 2 deletions fixtures/slide.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ App\Entity\Tenant\Slide:
theme: '@theme_abc_1'
feed: '@feed_abc_1'
tenant: '@tenant_abc'
media: [ '@media_abc_*', '@media_abc_*', '@media_abc_*']
media: [ '@media_abc_*', '@media_abc_*', '@media_abc_*' ]
slide_abc_{2..60} (extends slide):
title: 'slide_abc_<current()>'
theme: '@theme_abc_*'
tenant: '@tenant_abc'
media: [ '@media_abc_*', '@media_abc_*', '@media_abc_*']
media: [ '@media_abc_*', '@media_abc_*', '@media_abc_*' ]
slide_def_shared_to_abc (extends slide):
title: 'slide_def_shared_to_abc'
theme: '@theme_def'
Expand All @@ -28,3 +28,9 @@ App\Entity\Tenant\Slide:
title: 'slide_xyz_<current()>'
theme: '@theme_xyz'
tenant: '@tenant_xyz'
slide_abc_notified (extends slide):
title: 'slide_abc_notified'
template: '@template_notified'
content:
maxEntries: 6
tenant: '@tenant_abc'
7 changes: 7 additions & 0 deletions fixtures/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ App\Entity\Template:
createdAt (unique): '<dateTimeBetween("-2 years", "-2 days")>'
modifiedAt: '<dateTimeBetween($createdAt, "-1 days")>'
id: '<ulid($createdAt)>'
template_notified:
title: 'template_notified'
description: A template with different that serves notified data
resources: <templateResources()>
createdAt (unique): '<dateTimeBetween("-2 years", "-2 days")>'
modifiedAt: '<dateTimeBetween($createdAt, "-1 days")>'
id: '<ulid($createdAt)>'
3 changes: 2 additions & 1 deletion infrastructure/os2display/display-api-service/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,5 @@ RUN chmod +x /usr/local/bin/docker-entrypoint.sh

WORKDIR ${APP_PATH}

CMD [ "docker-entrypoint.sh" ]
CMD ["php-fpm"]
ENTRYPOINT [ "docker-entrypoint.sh" ]
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ if [ "${1#-}" != "$1" ]; then
fi

## Start the PHP FPM process.
echo "Starting PHP 8.2 FPM"
echo "Starting PHP 8.3 FPM"

exec php-fpm "$@"
exec "$@"
250 changes: 250 additions & 0 deletions src/Feed/NotifiedFeedType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
<?php

declare(strict_types=1);

namespace App\Feed;

use App\Entity\Tenant\Feed;
use App\Entity\Tenant\FeedSource;
use App\Service\FeedService;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Uid\Ulid;
use Symfony\Contracts\HttpClient\HttpClientInterface;

/**
* @see https://api.listen.notified.com/docs/index.html
*/
class NotifiedFeedType implements FeedTypeInterface
{
final public const string SUPPORTED_FEED_TYPE = 'instagram';
final public const int REQUEST_TIMEOUT = 10;

private const string BASE_URL = 'https://api.listen.notified.com';

public function __construct(
private readonly FeedService $feedService,
private readonly HttpClientInterface $client,
private readonly LoggerInterface $logger
) {}

public function getData(Feed $feed): array
{
try {
$secrets = $feed->getFeedSource()?->getSecrets();
if (!isset($secrets['token'])) {
return [];
}

$configuration = $feed->getConfiguration();
if (!isset($configuration['feeds']) || 0 === count($configuration['feeds'])) {
return [];
}

$slide = $feed->getSlide();
$slideContent = $slide?->getContent();

$pageSize = $slideContent['maxEntries'] ?? 10;

$token = $secrets['token'];

$data = $this->getMentions($token, 1, $pageSize, $configuration['feeds']);

$feedItems = array_map(fn (array $item) => $this->getFeedItemObject($item), $data);

$result = [];

// Check that image is accessible, otherwise leave out the feed element.
foreach ($feedItems as $feedItem) {
$response = $this->client->request(Request::METHOD_HEAD, $feedItem['mediaUrl']);
$statusCode = $response->getStatusCode();

if (200 == $statusCode) {
$result[] = $feedItem;
}
}

return $result;
} catch (\Throwable $throwable) {
$this->logger->error('{code}: {message}', [
'code' => $throwable->getCode(),
'message' => $throwable->getMessage(),
]);
}

return [];
}

/**
* {@inheritDoc}
*/
public function getAdminFormOptions(FeedSource $feedSource): array
{
$endpoint = $this->feedService->getFeedSourceConfigUrl($feedSource, 'feeds');

// @TODO: Translation.
return [
[
'key' => 'notified-selector',
'input' => 'multiselect-from-endpoint',
'endpoint' => $endpoint,
'name' => 'feeds',
'label' => 'Vælg feed',
'helpText' => 'Her vælger du hvilket feed der skal hentes indgange fra.',
'formGroupClasses' => 'col-md-6 mb-3',
],
];
}

/**
* {@inheritDoc}
*/
public function getConfigOptions(Request $request, FeedSource $feedSource, string $name): ?array
{
try {
if ('feeds' === $name) {
$secrets = $feedSource->getSecrets();

if (!isset($secrets['token'])) {
return [];
}

$token = $secrets['token'];

$data = $this->getSearchProfiles($token);

return array_map(fn (array $item) => [
'id' => Ulid::generate(),
'title' => $item['name'] ?? '',
'value' => $item['id'] ?? '',
], $data);
}
} catch (\Throwable $throwable) {
$this->logger->error('{code}: {message}', [
'code' => $throwable->getCode(),
'message' => $throwable->getMessage(),
]);
}

return null;
}

public function getMentions(string $token, int $page = 1, int $pageSize = 10, array $searchProfileIds = []): array
{
$body = [
'page' => $page,
'pageSize' => $pageSize,
'searchProfileIds' => $searchProfileIds,
];

$res = $this->client->request(
'POST',
self::BASE_URL.'/api/listen/mentions',
[
'timeout' => self::REQUEST_TIMEOUT,
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'Notified-Custom-Token' => $token,
],
'body' => json_encode($body),
]
);

return $res->toArray();
}

public function getSearchProfiles(string $token): array
{
$response = $this->client->request(
'GET',
self::BASE_URL.'/api/listen/searchprofiles',
[
'timeout' => self::REQUEST_TIMEOUT,
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'Notified-Custom-Token' => $token,
],
]
);

return $response->toArray();
}

/**
* {@inheritDoc}
*/
public function getRequiredSecrets(): array
{
return ['token'];
}

/**
* {@inheritDoc}
*/
public function getRequiredConfiguration(): array
{
return ['feeds'];
}

/**
* {@inheritDoc}
*/
public function getSupportedFeedOutputType(): string
{
return self::SUPPORTED_FEED_TYPE;
}

/**
* Parse feed item into object.
*/
private function getFeedItemObject(array $item): array
{
$description = $item['description'] ?? null;

return [
'text' => $description,
'textMarkup' => null !== $description ? $this->wrapTags($description) : null,
'mediaUrl' => $item['mediaUrl'] ?? null,
// Video is not supported by the Notified Listen API.
'videoUrl' => null,
'username' => $item['sourceName'] ?? null,
'createdTime' => $item['published'] ?? null,
];
}

private function wrapTags(string $input): string
{
$text = trim($input);

// Strip unicode zero-width-space.
$text = str_replace("\xE2\x80\x8B", '', $text);

// Collects trailing tags one by one.
$trailingTags = [];
$pattern = "/\s*#(?<tag>[^\s#]+)\n?$/u";
while (preg_match($pattern, (string) $text, $matches)) {
// We're getting tags in reverse order.
array_unshift($trailingTags, $matches['tag']);
$text = preg_replace($pattern, '', (string) $text);
}

// Wrap sections in p tags.
$text = preg_replace("/(.+)\n?/u", '<p>\1</p>', (string) $text);

// Wrap inline tags.
$pattern = '/(#(?<tag>[^\s#]+))/';

return implode('', [
'<div class="text">',
preg_replace($pattern, '<span class="tag">\1</span>', (string) $text),
'</div>',
'<div class="tags">',
implode(' ',
array_map(fn ($tag) => '<span class="tag">#'.$tag.'</span>', $trailingTags)
),
'</div>',
]);
}
}
Loading

0 comments on commit da336bc

Please sign in to comment.