multiple provider support
This commit is contained in:
parent
432d90fa1b
commit
3e89c654ec
@ -1,3 +1,4 @@
|
||||
#index:
|
||||
# path: /
|
||||
# controller: App\Controller\DefaultController::index
|
||||
api_v1:
|
||||
resource: ../src/Controller/Api/v1
|
||||
type: annotation
|
||||
prefix: /{provider}/api/v1
|
||||
|
@ -4,7 +4,7 @@
|
||||
# Put parameters here that don't need to change on each machine where the app is deployed
|
||||
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
|
||||
parameters:
|
||||
locale: 'en'
|
||||
locale: 'pl'
|
||||
|
||||
services:
|
||||
# default configuration for services in *this* file
|
||||
@ -15,6 +15,10 @@ services:
|
||||
# fetching services directly from the container via $container->get() won't work.
|
||||
# The best practice is to be explicit about your dependencies anyway.
|
||||
|
||||
_instanceof:
|
||||
App\Provider\Provider:
|
||||
tags: [ app.provider ]
|
||||
|
||||
# makes classes in src/ available to be used as services
|
||||
# this creates a service per class whose id is the fully-qualified class name
|
||||
App\:
|
||||
@ -46,8 +50,8 @@ services:
|
||||
proxy.config:
|
||||
class: 'ProxyManager\Configuration'
|
||||
calls:
|
||||
- ['setGeneratorStrategy', ['@proxy.strategy']]
|
||||
- ['setProxiesTargetDir', ['%kernel.cache_dir%/proxy']]
|
||||
- ['setGeneratorStrategy', ['@proxy.strategy']]
|
||||
- ['setProxiesTargetDir', ['%kernel.cache_dir%/proxy']]
|
||||
|
||||
ProxyManager\Configuration: '@proxy.config'
|
||||
|
||||
@ -60,3 +64,7 @@ services:
|
||||
App\Service\Normalizer\:
|
||||
resource: '../src/Service/Normalizer'
|
||||
tags: [serializer.normalizer]
|
||||
|
||||
# other servces
|
||||
App\Service\ProviderResolver:
|
||||
arguments: [!tagged app.provider]
|
||||
|
@ -4,8 +4,6 @@
|
||||
"author": "Kacper Donat <kadet1090@gmail.com>",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"webpack": "^4.17.0",
|
||||
"webpack-cli": "^3.1.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.4",
|
||||
"@fortawesome/pro-light-svg-icons": "^5.3.1",
|
||||
"@fortawesome/pro-regular-svg-icons": "^5.3.1",
|
||||
@ -30,7 +28,12 @@
|
||||
"vue": "^2.5.17",
|
||||
"vue-class-component": "^6.2.0",
|
||||
"vue-property-decorator": "^7.0.0",
|
||||
"webpack": "^4.17.0",
|
||||
"webpack-cli": "^3.1.0",
|
||||
"xmldom": "^0.1.27",
|
||||
"xpath": "^0.0.27"
|
||||
},
|
||||
"dependencies": {
|
||||
"mini-css-extract-plugin": "^0.4.2"
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,10 @@
|
||||
{{ departure.line.symbol }}
|
||||
</div>
|
||||
<div class="line__display">{{ departure.display }}</div>
|
||||
<div class="line__perks flex-space-left">
|
||||
<fa :icon="['fas', 'walking']" fixed-width v-if="departure.line.fast"/>
|
||||
<fa :icon="['fal', 'moon']" fixed-width v-if="departure.line.night"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="departure__time">
|
||||
|
@ -34,9 +34,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-warning" v-else-if="filter.length > 2">
|
||||
<fa :icon="['far', 'exclamation-triangle']"></fa>
|
||||
Nie znaleziono więcej przystanków, spełniających te kryteria.
|
||||
</div>
|
||||
<div class="alert alert-info" v-else>
|
||||
<fa :icon="['far', 'search']"></fa>
|
||||
Wprowadź zapytanie powyżej, aby wyszukać przystanek.
|
||||
</div>
|
||||
</div>
|
@ -13,3 +13,19 @@
|
||||
.flex-space-right {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.alert {
|
||||
border-width: 0;
|
||||
background: transparent;
|
||||
|
||||
@each $color, $value in $theme-colors {
|
||||
&.alert-#{$color} {
|
||||
border-bottom: 2px solid theme-color-level($color, $alert-color-level);
|
||||
transition: background-color ease 200ms;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(theme-color-level($color, $alert-bg-level), .5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
$border-radius: 2px;
|
||||
$border-radius: 0;
|
||||
$border-radius-lg: $border-radius;
|
||||
$border-radius-sm: $border-radius;
|
||||
|
||||
|
@ -23,7 +23,6 @@ export class Departures extends Vue {
|
||||
async update() {
|
||||
const response = await fetch(urls.prepare(urls.departures, {
|
||||
stop: this.stops.map(stop => stop.id),
|
||||
provider: 'gdansk'
|
||||
}));
|
||||
|
||||
if (response.ok) {
|
||||
|
@ -9,26 +9,11 @@ import stop = require('../../components/stop.html');
|
||||
|
||||
import { Prop, Watch } from "vue-property-decorator";
|
||||
import { filter, map } from "../utils";
|
||||
import { debounce, throttle } from "../decorators";
|
||||
import { Departures } from "./departures";
|
||||
import { debounce } from "../decorators";
|
||||
|
||||
@Component({ template: picker })
|
||||
export class PickerComponent extends Vue {
|
||||
protected stops?: Stop[] = [{
|
||||
"id": 2001,
|
||||
"name": "Dworzec Główny",
|
||||
"description": null,
|
||||
"location": [54.35544, 18.64565],
|
||||
"variant": "01",
|
||||
"onDemand": false
|
||||
}, {
|
||||
"id": 2002,
|
||||
"name": "Dworzec Główny",
|
||||
"description": null,
|
||||
"location": [54.35541, 18.64548],
|
||||
"variant": "02",
|
||||
"onDemand": false
|
||||
}];
|
||||
protected stops?: Stop[] = [];
|
||||
|
||||
private remove(stop: Stop) {
|
||||
this.stops = this.stops.filter(s => s != stop);
|
||||
@ -70,10 +55,7 @@ export class FinderComponent extends Vue {
|
||||
|
||||
this.state = 'fetching';
|
||||
|
||||
const response = await fetch(urls.prepare(urls.stops.search, {
|
||||
name: this.filter,
|
||||
provider: 'gdansk'
|
||||
}));
|
||||
const response = await fetch(urls.prepare(urls.stops.search, { name: this.filter }));
|
||||
|
||||
if (response.ok) {
|
||||
this.found = await response.json();
|
||||
|
@ -45,12 +45,14 @@ export function prepare(url: string, params: UrlParams = { }) {
|
||||
return Object.keys(params).length > 0 ? `${url}?${query(params)}` : url;
|
||||
}
|
||||
|
||||
const base = '/{provider}/api/v1';
|
||||
|
||||
export default {
|
||||
departures: '/{provider}/departures',
|
||||
departures: `${base}/departures`,
|
||||
stops: {
|
||||
all: '/{provider}/stops',
|
||||
search: '/{provider}/stops/search',
|
||||
get: '/{provider}/stop/{id}'
|
||||
all: `${base}/stops`,
|
||||
search: `${base}/stops/search`,
|
||||
get: `${base}/stops/{id}`
|
||||
},
|
||||
prepare
|
||||
prepare: (url: string, params: UrlParams = { }) => prepare(url, Object.assign({}, { provider: window['app'].provider }, params))
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace App\Controller;
|
||||
namespace App\Controller\Api\v1;
|
||||
|
||||
use App\Controller\Controller;
|
||||
use App\Model\Departure;
|
||||
use App\Provider\DepartureRepository;
|
||||
use App\Provider\StopRepository;
|
||||
@ -12,7 +13,7 @@ use Symfony\Component\Routing\Annotation\Route;
|
||||
/**
|
||||
* Class DeparturesController
|
||||
*
|
||||
* @Route("/{provider}/departures")
|
||||
* @Route("/departures")
|
||||
*/
|
||||
class DeparturesController extends Controller
|
||||
{
|
@ -1,13 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
namespace App\Controller\Api\v1;
|
||||
|
||||
|
||||
use App\Controller\Controller;
|
||||
use App\Provider\MessageRepository;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
|
||||
/**
|
||||
* @Route("/{provider}/messages")
|
||||
* @Route("/messages")
|
||||
*/
|
||||
class MessagesController extends Controller
|
||||
{
|
@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace App\Controller;
|
||||
namespace App\Controller\Api\v1;
|
||||
|
||||
use App\Controller\Controller;
|
||||
use App\Provider\StopRepository;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
@ -11,7 +12,7 @@ use Symfony\Component\Routing\Annotation\Route;
|
||||
* Class StopsController
|
||||
*
|
||||
* @package App\Controller
|
||||
* @Route("/{provider}/stops")
|
||||
* @Route("/stops")
|
||||
*/
|
||||
class StopsController extends Controller
|
||||
{
|
@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class HomepageController extends Controller
|
||||
{
|
||||
/**
|
||||
* @Route("/", name="home")
|
||||
*/
|
||||
public function homepage()
|
||||
{
|
||||
return $this->render('base.html.twig');
|
||||
}
|
||||
}
|
27
src/Controller/MainController.php
Normal file
27
src/Controller/MainController.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
|
||||
use App\Provider\Provider;
|
||||
use App\Service\ProviderResolver;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class MainController extends Controller
|
||||
{
|
||||
/**
|
||||
* @Route("/", name="choose")
|
||||
*/
|
||||
public function choose(ProviderResolver $resolver)
|
||||
{
|
||||
return $this->render('choose.html.twig', ['providers' => $resolver->all()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{provider}", name="app")
|
||||
*/
|
||||
public function app(Provider $provider)
|
||||
{
|
||||
return $this->render('app.html.twig', ['provider' => $provider]);
|
||||
}
|
||||
}
|
@ -8,4 +8,7 @@ interface Provider
|
||||
public function getLineRepository(): LineRepository;
|
||||
public function getStopRepository(): StopRepository;
|
||||
public function getMessageRepository(): MessageRepository;
|
||||
|
||||
public function getName();
|
||||
public function getIdentifier();
|
||||
}
|
@ -27,6 +27,7 @@ class ZtmGdanskDataUpdateSubscriber implements EventSubscriberInterface
|
||||
private $em;
|
||||
private $ids;
|
||||
private $logger;
|
||||
private $provider;
|
||||
|
||||
/**
|
||||
* ZtmGdanskDataUpdateSubscriber constructor.
|
||||
@ -34,19 +35,24 @@ class ZtmGdanskDataUpdateSubscriber implements EventSubscriberInterface
|
||||
* @param $provider
|
||||
* @param $em
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $em, IdUtils $ids, LoggerInterface $logger)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->ids = $ids;
|
||||
$this->logger = $logger;
|
||||
public function __construct(
|
||||
EntityManagerInterface $em,
|
||||
IdUtils $ids,
|
||||
LoggerInterface $logger,
|
||||
ZtmGdanskProvider $provider
|
||||
) {
|
||||
$this->em = $em;
|
||||
$this->ids = $ids;
|
||||
$this->logger = $logger;
|
||||
$this->provider = $provider;
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
$provider = ProviderEntity::createFromArray([
|
||||
'name' => 'ZTM Gdańsk',
|
||||
'name' => $this->provider->getName(),
|
||||
'class' => ZtmGdanskProvider::class,
|
||||
'id' => 'ztm-gdansk',
|
||||
'id' => $this->provider->getIdentifier(),
|
||||
]);
|
||||
|
||||
$this->em->persist($provider);
|
||||
@ -94,7 +100,7 @@ class ZtmGdanskDataUpdateSubscriber implements EventSubscriberInterface
|
||||
OperatorEntity::class,
|
||||
$this->ids->generate($provider, $line['agencyId'])
|
||||
);
|
||||
$type = [
|
||||
$type = [
|
||||
2 => LineModel::TYPE_TRAM,
|
||||
5 => LineModel::TYPE_TROLLEYBUS,
|
||||
];
|
||||
|
@ -16,13 +16,23 @@ class ZtmGdanskProvider implements Provider
|
||||
private $stops;
|
||||
private $messages;
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'MZKZG Trójmiasto';
|
||||
}
|
||||
|
||||
public function getIdentifier()
|
||||
{
|
||||
return 'trojmiasto';
|
||||
}
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $em,
|
||||
GenericLineRepository $lines,
|
||||
GenericStopRepository $stops,
|
||||
ZtmGdanskMessageRepository $messages
|
||||
) {
|
||||
$provider = $em->getReference(ProviderEntity::class, 'ztm-gdansk');
|
||||
$provider = $em->getReference(ProviderEntity::class, $this->getIdentifier());
|
||||
|
||||
$lines = $lines->withProvider($provider);
|
||||
$stops = $stops->withProvider($provider);
|
||||
|
@ -64,6 +64,8 @@ final class EntityConverter
|
||||
'symbol' => $entity->getSymbol(),
|
||||
'type' => $entity->getType(),
|
||||
'operator' => $convert($entity->getOperator()),
|
||||
'night' => $entity->isNight(),
|
||||
'fast' => $entity->isFast(),
|
||||
'tracks' => $this->collection($entity->getTracks(), $convert),
|
||||
]);
|
||||
break;
|
||||
|
@ -3,30 +3,18 @@
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
|
||||
use App\Exception\NonExistentServiceException;
|
||||
use App\Provider\Provider;
|
||||
use App\Provider\ZtmGdanskProvider;
|
||||
use Kadet\Functional\Transforms as t;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
|
||||
class ProviderResolver
|
||||
{
|
||||
private const PROVIDER = [
|
||||
'gdansk' => ZtmGdanskProvider::class
|
||||
];
|
||||
private $providers;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* ProviderResolver constructor.
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||
*/
|
||||
public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container)
|
||||
public function __construct($providers)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->providers = collect($providers)->keyBy(t\property('identifier'));
|
||||
}
|
||||
|
||||
/**\
|
||||
@ -37,10 +25,17 @@ class ProviderResolver
|
||||
*/
|
||||
public function resolve(string $name): Provider
|
||||
{
|
||||
if (!array_key_exists($name, static::PROVIDER)) {
|
||||
throw new NonExistentServiceException();
|
||||
if (!$this->providers->has($name)) {
|
||||
$message = sprintf("Provider '%s' doesn't exist, you can choose from: %s", $name, $this->providers->keys()->implode(', '));
|
||||
throw new NonExistentServiceException($message);
|
||||
}
|
||||
|
||||
return $this->container->get(static::PROVIDER[$name]);
|
||||
return $this->providers->get($name);
|
||||
}
|
||||
|
||||
/** @return Provider[] */
|
||||
public function all(): Collection
|
||||
{
|
||||
return clone $this->providers;
|
||||
}
|
||||
}
|
13
templates/app.html.twig
Normal file
13
templates/app.html.twig
Normal file
@ -0,0 +1,13 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% block title "#{parent()} - #{provider.name}" %}
|
||||
{% block body %}
|
||||
<stop-picker></stop-picker>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
<script>
|
||||
window.app = {
|
||||
provider: {{ provider.identifier|json_encode|raw }}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
@ -1,16 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}Welcome!{% endblock %}</title>
|
||||
{% block stylesheets %}{% endblock %}
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="main.css" />
|
||||
|
||||
<title>{% block title %}Czy dojadę?{% endblock %}</title>
|
||||
</head>
|
||||
<body>
|
||||
{% block body %}
|
||||
<main id="app" class="container">
|
||||
<stop-picker></stop-picker>
|
||||
</main>
|
||||
{% endblock %}
|
||||
<main role="main" class="container" id="app">
|
||||
{% block body %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<script src="bundle.js"></script>
|
||||
{% block javascripts %}{% endblock %}
|
||||
</body>
|
||||
|
11
templates/choose.html.twig
Normal file
11
templates/choose.html.twig
Normal file
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block body %}
|
||||
<div class="alert alert-primary">
|
||||
<fa :icon="['fal', 'info-circle']"></fa>
|
||||
Wybierz źródło danych
|
||||
</div>
|
||||
{% for provider in providers %}
|
||||
<a href="{{ path('app', { provider: provider.identifier }) }}">{{ provider.name }}</a>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
@ -1,4 +1,6 @@
|
||||
const path = require('path');
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
|
||||
const config = {
|
||||
entry: {
|
||||
main: ['./resources/ts/app.ts'],
|
||||
@ -24,7 +26,9 @@ const config = {
|
||||
}]
|
||||
},{
|
||||
test: /\.s[ac]ss$/,
|
||||
use: ["style-loader", "css-loader?sourceMap", "sass-loader?sourceMap"]
|
||||
use: [{
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
}, "css-loader?sourceMap", "sass-loader?sourceMap"]
|
||||
}, {
|
||||
test: /\.css$/,
|
||||
use: ["style-loader", "css-loader"]
|
||||
@ -46,6 +50,9 @@ const config = {
|
||||
use: 'raw-loader'
|
||||
}]
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({ filename: '[name].css' })
|
||||
],
|
||||
};
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
|
@ -1967,6 +1967,14 @@ mimic-fn@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
|
||||
|
||||
mini-css-extract-plugin@^0.4.2:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.2.tgz#b3ecc0d6b1bbe5ff14add42b946a7b200cf78651"
|
||||
dependencies:
|
||||
loader-utils "^1.1.0"
|
||||
schema-utils "^1.0.0"
|
||||
webpack-sources "^1.1.0"
|
||||
|
||||
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
|
||||
|
Loading…
Reference in New Issue
Block a user