├── .docker
├── nginx
│ └── conf.d
│ │ └── api.conf
└── php
│ ├── Dockerfile
│ └── conf
│ ├── xdebug.ini
│ └── zz-docker.conf
├── .editorconfig
├── .env.example
├── .gitattributes
├── .github
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .php_cs.dist
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── SECURITY.md
├── app
├── Console
│ ├── Commands
│ │ ├── ApiResponseCacheBustCommand.php
│ │ ├── IpmaSurfaceObservationFetchCommand.php
│ │ ├── IpmaWarningFetchCommand.php
│ │ ├── ProCivOccurrenceCloseCommand.php
│ │ └── ProCivOccurrenceFetchCommand.php
│ └── Kernel.php
├── Exceptions
│ └── Handler.php
├── Filters
│ ├── AcronymFilter.php
│ ├── Concerns
│ │ └── GeoLocator.php
│ ├── Contracts
│ │ ├── AcronymFilter.php
│ │ ├── CountyFilter.php
│ │ ├── DistrictFilter.php
│ │ ├── EventFilter.php
│ │ ├── Filter.php
│ │ ├── GeoLocator.php
│ │ ├── OccurrenceFamilyFilter.php
│ │ ├── OccurrenceFilter.php
│ │ ├── OccurrenceSpeciesFilter.php
│ │ ├── OccurrenceStatusFilter.php
│ │ ├── OccurrenceTypeFilter.php
│ │ ├── ParishFilter.php
│ │ ├── UserFilter.php
│ │ └── WeatherObservationFilter.php
│ ├── CountyFilter.php
│ ├── DistrictFilter.php
│ ├── EventFilter.php
│ ├── Filter.php
│ ├── OccurrenceFamilyFilter.php
│ ├── OccurrenceFilter.php
│ ├── OccurrenceSpeciesFilter.php
│ ├── OccurrenceStatusFilter.php
│ ├── OccurrenceTypeFilter.php
│ ├── ParishFilter.php
│ ├── UserFilter.php
│ └── WeatherObservationFilter.php
├── Http
│ ├── Controllers
│ │ ├── AcronymController.php
│ │ ├── AuthController.php
│ │ ├── Controller.php
│ │ ├── CountyController.php
│ │ ├── DistrictController.php
│ │ ├── EventController.php
│ │ ├── FallbackController.php
│ │ ├── IpmaWarningController.php
│ │ ├── OccurrenceController.php
│ │ ├── OccurrenceFamilyController.php
│ │ ├── OccurrenceReportController.php
│ │ ├── OccurrenceSpeciesController.php
│ │ ├── OccurrenceStatusController.php
│ │ ├── OccurrenceTypeController.php
│ │ ├── ParishController.php
│ │ ├── UserController.php
│ │ ├── UserProfileController.php
│ │ ├── UserRoleController.php
│ │ └── WeatherObservationController.php
│ ├── Kernel.php
│ ├── Middleware
│ │ ├── CheckForMaintenanceMode.php
│ │ ├── TrimStrings.php
│ │ ├── TrustProxies.php
│ │ └── ValidateJsonApiRequest.php
│ ├── Requests
│ │ ├── Acronym
│ │ │ ├── Create.php
│ │ │ ├── Delete.php
│ │ │ ├── Index.php
│ │ │ ├── Update.php
│ │ │ └── View.php
│ │ ├── Auth
│ │ │ ├── Authenticate.php
│ │ │ ├── Refresh.php
│ │ │ └── Verify.php
│ │ ├── County
│ │ │ ├── Index.php
│ │ │ └── View.php
│ │ ├── District
│ │ │ ├── Index.php
│ │ │ └── View.php
│ │ ├── Event
│ │ │ ├── Create.php
│ │ │ ├── Delete.php
│ │ │ ├── Index.php
│ │ │ ├── Update.php
│ │ │ └── View.php
│ │ ├── Occurrence
│ │ │ ├── GenerateReport.php
│ │ │ ├── Index.php
│ │ │ ├── Update.php
│ │ │ └── View.php
│ │ ├── OccurrenceFamily
│ │ │ └── Index.php
│ │ ├── OccurrenceSpecies
│ │ │ └── Index.php
│ │ ├── OccurrenceStatus
│ │ │ └── Index.php
│ │ ├── OccurrenceType
│ │ │ └── Index.php
│ │ ├── Parish
│ │ │ ├── Index.php
│ │ │ └── View.php
│ │ ├── Request.php
│ │ ├── User
│ │ │ ├── Create.php
│ │ │ ├── Index.php
│ │ │ ├── RetrieveRoles.php
│ │ │ ├── Update.php
│ │ │ ├── UpdateProfile.php
│ │ │ ├── View.php
│ │ │ └── ViewProfile.php
│ │ ├── Warning
│ │ │ └── Index.php
│ │ └── WeatherObservation
│ │ │ ├── Index.php
│ │ │ └── View.php
│ └── Serializers
│ │ ├── AcronymSerializer.php
│ │ ├── CountySerializer.php
│ │ ├── DistrictSerializer.php
│ │ ├── EventSerializer.php
│ │ ├── EventTypeSerializer.php
│ │ ├── OccurrenceFamilySerializer.php
│ │ ├── OccurrenceSerializer.php
│ │ ├── OccurrenceSpeciesSerializer.php
│ │ ├── OccurrenceStatusSerializer.php
│ │ ├── OccurrenceTypeSerializer.php
│ │ ├── ParishSerializer.php
│ │ ├── ProCivOccurrenceSerializer.php
│ │ ├── RoleSerializer.php
│ │ ├── UserSerializer.php
│ │ ├── WarningSerializer.php
│ │ ├── WeatherObservationSerializer.php
│ │ └── WeatherStationSerializer.php
├── Jobs
│ ├── Api
│ │ └── ResponseCacheBuster.php
│ ├── Ipma
│ │ ├── SurfaceObservationFetcher.php
│ │ └── WarningFetcher.php
│ ├── ProCiv
│ │ ├── OccurrenceCloser.php
│ │ └── OccurrenceFetcher.php
│ └── Report
│ │ └── ReportGenerator.php
├── Models
│ ├── Acronym.php
│ ├── Concerns
│ │ └── Cacheable.php
│ ├── County.php
│ ├── District.php
│ ├── Event.php
│ ├── EventType.php
│ ├── Observers
│ │ └── CacheableObserver.php
│ ├── Occurrence.php
│ ├── OccurrenceFamily.php
│ ├── OccurrenceSpecies.php
│ ├── OccurrenceStatus.php
│ ├── OccurrenceType.php
│ ├── Parish.php
│ ├── ProCivOccurrence.php
│ ├── ProCivOccurrenceLog.php
│ ├── Role.php
│ ├── User.php
│ ├── WeatherObservation.php
│ ├── WeatherSensor.php
│ └── WeatherStation.php
├── Policies
│ ├── AcronymPolicy.php
│ ├── EventPolicy.php
│ ├── OccurrencePolicy.php
│ └── UserPolicy.php
├── Providers
│ ├── AppServiceProvider.php
│ ├── AuthServiceProvider.php
│ ├── EventServiceProvider.php
│ ├── FilterServiceProvider.php
│ ├── HttpClientServiceProvider.php
│ ├── RepositoryServiceProvider.php
│ ├── ResponseServiceProvider.php
│ ├── RouteServiceProvider.php
│ └── ServiceClientServiceProvider.php
├── Reports
│ ├── Contracts
│ │ └── Report.php
│ ├── OccurrenceReport.php
│ └── Report.php
├── Repositories
│ ├── AcronymRepository.php
│ ├── AcronymRepositoryDecorator.php
│ ├── Concerns
│ │ └── Paginator.php
│ ├── Contracts
│ │ ├── AcronymRepository.php
│ │ ├── CountyRepository.php
│ │ ├── DistrictRepository.php
│ │ ├── EventRepository.php
│ │ ├── OccurrenceFamilyRepository.php
│ │ ├── OccurrenceRepository.php
│ │ ├── OccurrenceSpeciesRepository.php
│ │ ├── OccurrenceStatusRepository.php
│ │ ├── OccurrenceTypeRepository.php
│ │ ├── Paginator.php
│ │ ├── ParishRepository.php
│ │ ├── ProCivOccurrenceRepository.php
│ │ ├── Repository.php
│ │ ├── UserRepository.php
│ │ ├── WeatherObservationRepository.php
│ │ └── WeatherStationRepository.php
│ ├── CountyRepository.php
│ ├── CountyRepositoryDecorator.php
│ ├── DistrictRepository.php
│ ├── DistrictRepositoryDecorator.php
│ ├── EventRepository.php
│ ├── EventRepositoryDecorator.php
│ ├── OccurrenceFamilyRepository.php
│ ├── OccurrenceFamilyRepositoryDecorator.php
│ ├── OccurrenceRepository.php
│ ├── OccurrenceRepositoryDecorator.php
│ ├── OccurrenceSpeciesRepository.php
│ ├── OccurrenceSpeciesRepositoryDecorator.php
│ ├── OccurrenceStatusRepository.php
│ ├── OccurrenceStatusRepositoryDecorator.php
│ ├── OccurrenceTypeRepository.php
│ ├── OccurrenceTypeRepositoryDecorator.php
│ ├── ParishRepository.php
│ ├── ParishRepositoryDecorator.php
│ ├── ProCivOccurrenceRepository.php
│ ├── Repository.php
│ ├── UserRepository.php
│ ├── UserRepositoryDecorator.php
│ ├── WeatherObservationRepository.php
│ ├── WeatherObservationRepositoryDecorator.php
│ └── WeatherStationRepository.php
├── Rules
│ └── ValidRole.php
├── ServiceClients
│ ├── Contracts
│ │ ├── IpmaApiServiceClient.php
│ │ ├── ProCivWebsiteServiceClient.php
│ │ └── ServiceClient.php
│ ├── IpmaApiServiceClient.php
│ ├── ProCivWebsiteServiceClient.php
│ └── ServiceClient.php
└── Support
│ ├── ResponseFactory.php
│ └── helpers.php
├── artisan
├── bootstrap
├── app.php
└── cache
│ └── .gitignore
├── composer.json
├── composer.lock
├── config
├── app.php
├── auth.php
├── broadcasting.php
├── cache.php
├── database.php
├── filesystems.php
├── hashing.php
├── jwt.php
├── logging.php
├── mail.php
├── queue.php
├── services.php
├── session.php
└── view.php
├── database
├── .gitignore
├── factories
│ ├── AcronymFactory.php
│ ├── CountyFactory.php
│ ├── DistrictFactory.php
│ ├── EventFactory.php
│ ├── EventTypeFactory.php
│ ├── OccurrenceFactory.php
│ ├── OccurrenceFamilyFactory.php
│ ├── OccurrenceSpeciesFactory.php
│ ├── OccurrenceStatusFactory.php
│ ├── OccurrenceTypeFactory.php
│ ├── ParishFactory.php
│ ├── ProCivOccurrenceFactory.php
│ ├── ProCivOccurrenceLogFactory.php
│ ├── UserFactory.php
│ ├── WeatherObservationFactory.php
│ ├── WeatherSensorFactory.php
│ └── WeatherStationFactory.php
├── migrations
│ ├── 2019_04_08_185908_create_users_table.php
│ ├── 2019_04_09_095403_create_bouncer_tables.php
│ ├── 2019_04_09_095426_create_districts_table.php
│ ├── 2019_04_11_145226_create_counties_table.php
│ ├── 2019_04_12_074212_create_parishes_table.php
│ ├── 2019_04_12_224617_create_acronyms_table.php
│ ├── 2019_04_16_222648_create_event_types_table.php
│ ├── 2019_04_16_222657_create_events_table.php
│ ├── 2019_04_17_203846_create_occurrence_families_table.php
│ ├── 2019_04_17_203847_create_occurrence_species_table.php
│ ├── 2019_04_17_203847_create_occurrence_statuses_table.php
│ ├── 2019_04_17_203848_create_occurrence_types_table.php
│ ├── 2019_04_17_203849_create_occurrences_table.php
│ ├── 2019_04_17_203850_create_prociv_occurrences_table.php
│ ├── 2019_04_17_203851_create_prociv_occurrence_logs_table.php
│ ├── 2019_08_13_084800_create_failed_jobs_table.php
│ ├── 2019_09_04_133750_create_weather_stations_table.php
│ ├── 2019_09_04_133751_create_weather_sensors_table.php
│ └── 2019_09_04_133752_create_weather_observations_table.php
└── seeds
│ ├── AcronymSeeder.php
│ ├── CountySeeder.php
│ ├── DatabaseSeeder.php
│ ├── DistrictSeeder.php
│ ├── EventTypeSeeder.php
│ ├── IpmaStationAndSensorSeeder.php
│ ├── OccurrenceFamilySeeder.php
│ ├── OccurrenceSpeciesSeeder.php
│ ├── OccurrenceStatusSeeder.php
│ ├── OccurrenceTypeSeeder.php
│ ├── ParishSeeder.php
│ ├── RoleSeeder.php
│ └── data
│ ├── acronyms.php
│ ├── counties.php
│ ├── districts.php
│ ├── event_types.php
│ ├── ipma_sensors.php
│ ├── occurrence_families.php
│ ├── occurrence_species.php
│ ├── occurrence_statuses.php
│ ├── occurrence_types.php
│ └── parishes.php
├── docker-compose.yml
├── phpunit.xml.dist
├── pre-commit.sh
├── public
├── .htaccess
├── documentation
│ ├── acronyms
│ │ ├── endpoints.yaml
│ │ └── schemas.yaml
│ ├── auth
│ │ ├── endpoints.yaml
│ │ └── schemas.yaml
│ ├── common
│ │ ├── parameters
│ │ │ ├── accept.yaml
│ │ │ ├── authorization.yaml
│ │ │ ├── content_type.yaml
│ │ │ ├── exact.yaml
│ │ │ ├── ids.yaml
│ │ │ ├── latitude.yaml
│ │ │ ├── longitude.yaml
│ │ │ ├── order.yaml
│ │ │ ├── page_number.yaml
│ │ │ ├── page_size.yaml
│ │ │ ├── radius.yaml
│ │ │ └── search.yaml
│ │ └── responses
│ │ │ ├── 400.yaml
│ │ │ ├── 401.yaml
│ │ │ ├── 403.yaml
│ │ │ ├── 404.yaml
│ │ │ ├── 406.yaml
│ │ │ ├── 415.yaml
│ │ │ ├── 422.yaml
│ │ │ └── 429.yaml
│ ├── counties
│ │ ├── endpoints.yaml
│ │ └── schemas.yaml
│ ├── districts
│ │ ├── endpoints.yaml
│ │ └── schemas.yaml
│ ├── events
│ │ ├── endpoints.yaml
│ │ └── schemas.yaml
│ ├── index.php
│ ├── ipma
│ │ ├── endpoints.yaml
│ │ └── schemas.yaml
│ ├── logo.png
│ ├── occurrences
│ │ ├── endpoints.yaml
│ │ └── schemas.yaml
│ ├── openapi.yaml
│ ├── parishes
│ │ ├── endpoints.yaml
│ │ └── schemas.yaml
│ ├── roles
│ │ └── schemas.yaml
│ ├── users
│ │ ├── endpoints.yaml
│ │ └── schemas.yaml
│ └── weather
│ │ ├── endpoints.yaml
│ │ └── schemas.yaml
├── favicon.ico
├── index.php
├── robots.txt
└── web.config
├── resources
└── lang
│ └── en
│ ├── auth.php
│ ├── pagination.php
│ ├── passwords.php
│ └── validation.php
├── routes
├── api
│ ├── acronyms.php
│ ├── auth.php
│ ├── counties.php
│ ├── districts.php
│ ├── events.php
│ ├── ipma.php
│ ├── occurrences.php
│ ├── parishes.php
│ ├── users.php
│ └── weather.php
├── console.php
└── web
│ └── fallback.php
├── server.php
├── storage
├── app
│ ├── .gitignore
│ └── public
│ │ └── .gitignore
├── framework
│ ├── .gitignore
│ ├── cache
│ │ ├── .gitignore
│ │ └── data
│ │ │ └── .gitignore
│ ├── sessions
│ │ └── .gitignore
│ ├── testing
│ │ └── .gitignore
│ └── views
│ │ └── .gitignore
└── logs
│ └── .gitignore
└── tests
├── Integration
├── Commands
│ ├── Api
│ │ └── ResponseCacheBustCommandTest.php
│ ├── Ipma
│ │ ├── SurfaceObservationFetchCommandTest.php
│ │ └── WarningFetchCommandTest.php
│ └── ProCiv
│ │ ├── OccurrenceCloseCommandTest.php
│ │ └── OccurrenceFetchCommandTest.php
├── Controllers
│ ├── AcronymController
│ │ ├── CreateEndpointTest.php
│ │ ├── DeleteEndpointTest.php
│ │ ├── IndexEndpointTest.php
│ │ ├── UpdateEndpointTest.php
│ │ └── ViewEndpointTest.php
│ ├── AuthController
│ │ ├── AuthenticateEndpointTest.php
│ │ ├── RefreshEndpointTest.php
│ │ └── VerifyEndpointTest.php
│ ├── CountyController
│ │ ├── IndexEndpointTest.php
│ │ └── ViewEndpointTest.php
│ ├── DistrictController
│ │ ├── IndexEndpointTest.php
│ │ └── ViewEndpointTest.php
│ ├── EventController
│ │ ├── CreateEndpointTest.php
│ │ ├── DeleteEndpointTest.php
│ │ ├── IndexEndpointTest.php
│ │ ├── UpdateEndpointTest.php
│ │ └── ViewEndpointTest.php
│ ├── FallbackController
│ │ └── DocumentationEndpointTest.php
│ ├── IpmaWarningController
│ │ └── IndexEndpointTest.php
│ ├── OccurrenceController
│ │ ├── IndexEndpointTest.php
│ │ ├── UpdateEndpointTest.php
│ │ └── ViewEndpointTest.php
│ ├── OccurrenceFamilyController
│ │ └── IndexEndpointTest.php
│ ├── OccurrenceReportController
│ │ ├── DownloadReportEndpointTest.php
│ │ └── GenerateReportEndpointTest.php
│ ├── OccurrenceSpeciesController
│ │ └── IndexEndpointTest.php
│ ├── OccurrenceStatusController
│ │ └── IndexEndpointTest.php
│ ├── OccurrenceTypeController
│ │ └── IndexEndpointTest.php
│ ├── ParishController
│ │ ├── IndexEndpointTest.php
│ │ └── ViewEndpointTest.php
│ ├── UserController
│ │ ├── CreateEndpointTest.php
│ │ ├── IndexEndpointTest.php
│ │ ├── UpdateEndpointTest.php
│ │ └── ViewEndpointTest.php
│ ├── UserProfileController
│ │ ├── UpdateEndpointTest.php
│ │ └── ViewEndpointTest.php
│ ├── UserRoleController
│ │ └── IndexEndpointTest.php
│ └── WeatherObservationController
│ │ ├── IndexEndpointTest.php
│ │ └── ViewEndpointTest.php
├── CreatesApplication.php
├── HttpClientMocker.php
├── RefreshDatabaseWithRoles.php
└── TestCase.php
├── Unit
└── TestCase.php
└── data
├── Ipma
├── SurfaceObservations.json
└── Warnings.json
└── ProCiv
├── MainOccurrences.json
└── OccurrencesByLocation.json
/.docker/nginx/conf.d/api.conf:
--------------------------------------------------------------------------------
1 | server {
2 | server_name api.vost.test;
3 | listen 80;
4 | index index.php index.html;
5 |
6 | error_log /var/log/nginx/error.log;
7 | access_log /var/log/nginx/access.log;
8 |
9 | root /var/www/api.vost.test/public;
10 |
11 | location / {
12 | try_files $uri $uri/ /index.php?$query_string;
13 | gzip_static on;
14 | }
15 |
16 | location ~ \.php$ {
17 | try_files $uri =404;
18 | fastcgi_split_path_info ^(.+\.php)(/.+)$;
19 | fastcgi_pass vost_api:9000;
20 | fastcgi_index index.php;
21 | include fastcgi_params;
22 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
23 | fastcgi_param PATH_INFO $fastcgi_path_info;
24 |
25 | #
26 | # CORS
27 | #
28 | add_header 'Access-Control-Allow-Origin' '*' always;
29 | add_header 'Access-Control-Allow-Credentials' 'true';
30 | add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PUT';
31 | add_header 'Access-Control-Allow-Headers' 'Version,Accept,Accept-Encoding,Accept-Language,Connection,Cookie,Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.docker/php/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:7.3.11-fpm
2 |
3 | RUN apt-get update && apt-get install -y \
4 | build-essential \
5 | curl \
6 | git \
7 | libmcrypt-dev \
8 | locales \
9 | unzip \
10 | vim \
11 | libzip-dev \
12 | zip \
13 | --no-install-recommends && \
14 | apt-get clean && rm -rf /var/lib/apt/lists/*
15 |
16 | RUN docker-php-ext-install \
17 | pcntl \
18 | pdo_mysql \
19 | zip
20 |
21 | RUN pecl channel-update pecl.php.net \
22 | && printf "\n" | pecl install mcrypt-1.0.3 \
23 | && printf "\n" | pecl install xdebug-2.9.0 \
24 | && printf "\n" | pecl install redis-5.0.2 \
25 | && docker-php-ext-enable mcrypt xdebug redis
26 |
27 | COPY conf/xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
28 | COPY conf/zz-docker.conf /usr/local/etc/php-fpm.d/zz-docker.conf
29 |
30 | COPY --from=composer /usr/bin/composer /usr/bin/composer
31 |
32 | WORKDIR /var/www/api.vost.test
33 |
--------------------------------------------------------------------------------
/.docker/php/conf/xdebug.ini:
--------------------------------------------------------------------------------
1 | zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20180731/xdebug.so
2 | xdebug.remote_connect_back=1
3 | xdebug.remote_enable=1
4 | xdebug.remote_port=9000
5 |
--------------------------------------------------------------------------------
/.docker/php/conf/zz-docker.conf:
--------------------------------------------------------------------------------
1 | [global]
2 | daemonize = no
3 |
4 | [www]
5 | listen = 9000
6 |
7 | pm = dynamic
8 | pm.max_children = 64
9 | pm.process_idle_timeout = 2s
10 | pm.max_requests = 1000
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [*.yml]
15 | indent_size = 2
16 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME="VOST Portugal API"
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_URL=http://api.vost.test
6 |
7 | #LOCAL_STORAGE_PATH=/path
8 |
9 | #
10 | # JWT secret
11 | #
12 | JWT_SECRET=
13 |
14 | LOG_CHANNEL=stack
15 |
16 | DB_CONNECTION=mariadb
17 | DB_HOST=vost_mariadb
18 | DB_PORT=3306
19 | DB_DATABASE=vost_api
20 | DB_USERNAME=root
21 | DB_PASSWORD=root
22 |
23 | BROADCAST_DRIVER=log
24 | CACHE_DRIVER=redis
25 | QUEUE_CONNECTION=redis
26 | SESSION_DRIVER=redis
27 | SESSION_LIFETIME=120
28 |
29 | REDIS_HOST=vost_redis
30 | REDIS_PASSWORD=null
31 | REDIS_PORT=6379
32 |
33 | MAIL_DRIVER=smtp
34 | MAIL_HOST=smtp.mailtrap.io
35 | MAIL_PORT=2525
36 | MAIL_USERNAME=null
37 | MAIL_PASSWORD=null
38 | MAIL_ENCRYPTION=null
39 |
40 | #
41 | # Service Clients
42 | #
43 |
44 | IPMA_API_HOSTNAME=https://api.ipma.pt
45 |
46 | PROCIV_WEBSITE_HOSTNAME=http://www.prociv.pt
47 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.css linguist-vendored
3 | *.scss linguist-vendored
4 | *.js linguist-vendored
5 | CHANGELOG.md export-ignore
6 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | ## Description
6 | This pull request addresses issue [API-XYZ](https://github.com/vostpt/api/issues/XYZ).
7 |
8 | ## Task items:
9 | - [ ] Fixed issue X;
10 | - [ ] Added feature Y;
11 | - [ ] Updated the documentation for Z;
12 | - [ ] Increased test coverage for W;
13 | - [ ] Other task related item;
14 | - [ ] ...
15 |
16 | ## Requirements (optional)
17 | Requirements when deploying the current work to an environment (`dev`, `staging` or `production`):
18 |
19 | - Migrations need to be executed;
20 | - An `ENV` variable needs to be added;
21 | - Permissions need to be set for `X`, `Y` and `Z`;
22 | - ...
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /node_modules
3 | /public/hot
4 | /public/storage
5 | /storage/*.key
6 | /vendor
7 | /.idea
8 | /.vscode
9 | /.php_cs.cache
10 | .DS_Store
11 | .env
12 | .phpunit.result.cache
13 | Homestead.json
14 | Homestead.yaml
15 | npm-debug.log
16 | yarn-error.log
17 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | dist: bionic
4 |
5 | env:
6 | - DB_HOST=localhost DB_USERNAME=travis
7 |
8 | addons:
9 | mariadb: '10.4'
10 |
11 | php:
12 | - '7.3'
13 |
14 | matrix:
15 | fast_finish: true
16 |
17 | sudo: true
18 |
19 | before_install:
20 | - echo "extension = redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
21 |
22 | before_script:
23 | # Database setup
24 | - sudo mysql -e 'create database vost_api;'
25 | - sudo mysql -e 'CREATE USER IF NOT EXISTS travis@localhost; GRANT ALL ON *.* TO travis@localhost;'
26 |
27 | # Install project dependencies
28 | - composer install --no-interaction
29 |
30 | # Clear everything
31 | - php artisan optimize:clear
32 |
33 | # Create optimised classmap
34 | - composer dump-autoload -o
35 |
36 | # Check coding standards
37 | - composer cs-check app
38 | - composer cs-check config
39 | - composer cs-check database
40 | - composer cs-check tests
41 |
42 | script:
43 | - mkdir -p build/logs
44 | - vendor/bin/phpunit --dump-xdebug-filter build/xdebug-filter.php
45 | - vendor/bin/phpunit --testdox --prepend build/xdebug-filter.php --coverage-text --coverage-clover build/logs/clover.xml
46 |
47 | after_success:
48 | - vendor/bin/php-coveralls -v
49 |
50 | cache:
51 | directories:
52 | - vendor
53 | - $HOME/.composer/cache
54 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | ### The MIT License (MIT)
2 |
3 | Copyright (C) 2019 VOST Portugal.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Use this section to tell people about which versions of your project are
6 | currently being supported with security updates.
7 |
8 | | Version | Supported |
9 | | ------- | ------------------ |
10 | | 5.1.x | :white_check_mark: |
11 | | 5.0.x | :x: |
12 | | 4.0.x | :white_check_mark: |
13 | | < 4.0 | :x: |
14 |
15 | ## Reporting a Vulnerability
16 |
17 | Use this section to tell people how to report a vulnerability.
18 |
19 | Tell them where to go, how often they can expect to get an update on a
20 | reported vulnerability, what to expect if the vulnerability is accepted or
21 | declined, etc.
22 |
--------------------------------------------------------------------------------
/app/Console/Commands/ApiResponseCacheBustCommand.php:
--------------------------------------------------------------------------------
1 | dispatchNow(new ResponseCacheBuster());
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/Console/Commands/IpmaSurfaceObservationFetchCommand.php:
--------------------------------------------------------------------------------
1 | dispatchNow(new SurfaceObservationFetcher());
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/Console/Commands/IpmaWarningFetchCommand.php:
--------------------------------------------------------------------------------
1 | dispatchNow(new WarningFetcher());
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/Console/Commands/ProCivOccurrenceCloseCommand.php:
--------------------------------------------------------------------------------
1 | dispatchNow(new OccurrenceCloser());
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/Console/Commands/ProCivOccurrenceFetchCommand.php:
--------------------------------------------------------------------------------
1 | dispatchNow(new OccurrenceFetcher());
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/Console/Kernel.php:
--------------------------------------------------------------------------------
1 | job(new OccurrenceFetcher())->everyFiveMinutes();
29 | $schedule->job(new OccurrenceCloser())->everyThirtyMinutes();
30 |
31 | // Fetch IPMA warnings and surface observations
32 | $schedule->job(new WarningFetcher())->everyThirtyMinutes();
33 | $schedule->job(new SurfaceObservationFetcher())->everyThirtyMinutes();
34 |
35 | // Bust the API response cache
36 | $schedule->job(new ResponseCacheBuster())->everyFiveMinutes();
37 | }
38 |
39 | /**
40 | * {@inheritDoc}
41 | */
42 | protected function commands(): void
43 | {
44 | $this->load(__DIR__.'/Commands');
45 |
46 | require base_path('routes/console.php');
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/Exceptions/Handler.php:
--------------------------------------------------------------------------------
1 | getMessage(), $exception);
23 | }
24 |
25 | if ($exception instanceof HttpException) {
26 | return response()->error($exception);
27 | }
28 |
29 | if ($exception instanceof ValidationException) {
30 | return response()->validation($exception);
31 | }
32 |
33 | return response()->json([
34 | 'errors' => [
35 | [
36 | 'id' => $exception->getCode(),
37 | 'detail' => $exception->getMessage(),
38 | ],
39 | ],
40 | ], 500);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/Filters/AcronymFilter.php:
--------------------------------------------------------------------------------
1 | collection(new Collection($cache->get('ipma_warnings', [])), new WarningSerializer(), [
27 | 'county',
28 | 'county.district',
29 | ]);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/Http/Controllers/UserRoleController.php:
--------------------------------------------------------------------------------
1 | collection(Role::all(), new RoleSerializer());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/Http/Middleware/CheckForMaintenanceMode.php:
--------------------------------------------------------------------------------
1 | header('Content-Type');
30 |
31 | if ($contentTypeHeader && $contentTypeHeader !== self::MEDIA_TYPE && \mb_stripos($contentTypeHeader, self::MEDIA_TYPE) !== false) {
32 | throw new UnsupportedMediaTypeHttpException('Unsupported media type');
33 | }
34 |
35 | $acceptHeader = $request->header('Accept');
36 |
37 | if ($acceptHeader && $acceptHeader !== self::MEDIA_TYPE && \mb_stripos($acceptHeader, self::MEDIA_TYPE) !== false) {
38 | throw new NotAcceptableHttpException('Not acceptable');
39 | }
40 |
41 | return $next($request);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/Http/Requests/Acronym/Create.php:
--------------------------------------------------------------------------------
1 | user()->can('create', Acronym::class);
19 | }
20 |
21 | /**
22 | * {@inheritDoc}
23 | */
24 | public function rules(): array
25 | {
26 | return [
27 | 'initials' => [
28 | 'required',
29 | 'string',
30 | 'max:16',
31 | Rule::unique('acronyms', 'initials'),
32 | ],
33 | 'meaning' => [
34 | 'required',
35 | 'string',
36 | 'max:255',
37 | ],
38 | ];
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/Http/Requests/Acronym/Delete.php:
--------------------------------------------------------------------------------
1 | route('Acronym');
17 |
18 | return $this->user()->can('delete', $acronym);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/Http/Requests/Acronym/Index.php:
--------------------------------------------------------------------------------
1 | [
20 | 'integer',
21 | ],
22 | 'page.size' => [
23 | 'integer',
24 | ],
25 | 'ids' => [
26 | 'array',
27 | ],
28 | 'ids.*' => [
29 | Rule::exists('acronyms', 'id'),
30 | ],
31 | 'search' => [
32 | 'string',
33 | ],
34 | 'exact' => [
35 | 'boolean',
36 | ],
37 | 'sort' => [
38 | Rule::in(AcronymFilter::getSortableColumns()),
39 | ],
40 | 'order' => [
41 | Rule::in(AcronymFilter::getOrderValues()),
42 | ],
43 | ];
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/Http/Requests/Acronym/Update.php:
--------------------------------------------------------------------------------
1 | route('Acronym');
18 |
19 | return $this->user()->can('update', $acronym);
20 | }
21 |
22 | /**
23 | * {@inheritDoc}
24 | */
25 | public function rules(): array
26 | {
27 | return [
28 | 'initials' => [
29 | 'string',
30 | 'max:16',
31 | Rule::unique('acronyms', 'initials')
32 | ->ignoreModel($this->route('Acronym')),
33 | ],
34 | 'meaning' => [
35 | 'string',
36 | 'max:255',
37 | ],
38 | ];
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/Http/Requests/Acronym/View.php:
--------------------------------------------------------------------------------
1 | [
18 | 'required',
19 | 'email',
20 | ],
21 | 'password' => [
22 | 'required',
23 | ],
24 | ];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/Http/Requests/Auth/Refresh.php:
--------------------------------------------------------------------------------
1 | user()->can('refresh', User::class);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/Http/Requests/Auth/Verify.php:
--------------------------------------------------------------------------------
1 | user()->can('verify', User::class);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/Http/Requests/County/Index.php:
--------------------------------------------------------------------------------
1 | [
20 | 'integer',
21 | ],
22 | 'page.size' => [
23 | 'integer',
24 | ],
25 | 'ids' => [
26 | 'array',
27 | ],
28 | 'ids.*' => [
29 | Rule::exists('counties', 'id'),
30 | ],
31 | 'search' => [
32 | 'string',
33 | ],
34 | 'exact' => [
35 | 'boolean',
36 | ],
37 | 'districts' => [
38 | 'array',
39 | ],
40 | 'districts.*' => [
41 | Rule::exists('districts', 'id'),
42 | ],
43 | 'sort' => [
44 | Rule::in(CountyFilter::getSortableColumns()),
45 | ],
46 | 'order' => [
47 | Rule::in(CountyFilter::getOrderValues()),
48 | ],
49 | ];
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/Http/Requests/County/View.php:
--------------------------------------------------------------------------------
1 | [
20 | 'integer',
21 | ],
22 | 'page.size' => [
23 | 'integer',
24 | ],
25 | 'ids' => [
26 | 'array',
27 | ],
28 | 'ids.*' => [
29 | Rule::exists('districts', 'id'),
30 | ],
31 | 'search' => [
32 | 'string',
33 | ],
34 | 'exact' => [
35 | 'boolean',
36 | ],
37 | 'sort' => [
38 | Rule::in(DistrictFilter::getSortableColumns()),
39 | ],
40 | 'order' => [
41 | Rule::in(DistrictFilter::getOrderValues()),
42 | ],
43 | ];
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/Http/Requests/District/View.php:
--------------------------------------------------------------------------------
1 | route('Event');
17 |
18 | return $this->user()->can('delete', $event);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/Http/Requests/Event/View.php:
--------------------------------------------------------------------------------
1 | route('Occurrence');
18 |
19 | return $this->user()->can('update', $occurrence);
20 | }
21 |
22 | /**
23 | * {@inheritDoc}
24 | */
25 | public function rules(): array
26 | {
27 | return [
28 | 'event' => [
29 | 'nullable',
30 | Rule::exists('events', 'id'),
31 | ],
32 | ];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/Http/Requests/Occurrence/View.php:
--------------------------------------------------------------------------------
1 | [
20 | 'integer',
21 | ],
22 | 'page.size' => [
23 | 'integer',
24 | ],
25 | 'ids' => [
26 | 'array',
27 | ],
28 | 'ids.*' => [
29 | Rule::exists('occurrence_families', 'id'),
30 | ],
31 | 'search' => [
32 | 'string',
33 | ],
34 | 'exact' => [
35 | 'boolean',
36 | ],
37 | 'sort' => [
38 | Rule::in(OccurrenceFamilyFilter::getSortableColumns()),
39 | ],
40 | 'order' => [
41 | Rule::in(OccurrenceFamilyFilter::getOrderValues()),
42 | ],
43 | ];
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/Http/Requests/OccurrenceSpecies/Index.php:
--------------------------------------------------------------------------------
1 | [
20 | 'integer',
21 | ],
22 | 'page.size' => [
23 | 'integer',
24 | ],
25 | 'ids' => [
26 | 'array',
27 | ],
28 | 'ids.*' => [
29 | Rule::exists('occurrence_species', 'id'),
30 | ],
31 | 'search' => [
32 | 'string',
33 | ],
34 | 'exact' => [
35 | 'boolean',
36 | ],
37 | 'families' => [
38 | 'array',
39 | ],
40 | 'families.*' => [
41 | Rule::exists('occurrence_families', 'id'),
42 | ],
43 | 'sort' => [
44 | Rule::in(OccurrenceSpeciesFilter::getSortableColumns()),
45 | ],
46 | 'order' => [
47 | Rule::in(OccurrenceSpeciesFilter::getOrderValues()),
48 | ],
49 | ];
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/Http/Requests/OccurrenceStatus/Index.php:
--------------------------------------------------------------------------------
1 | [
20 | 'integer',
21 | ],
22 | 'page.size' => [
23 | 'integer',
24 | ],
25 | 'ids' => [
26 | 'array',
27 | ],
28 | 'ids.*' => [
29 | Rule::exists('occurrence_statuses', 'id'),
30 | ],
31 | 'search' => [
32 | 'string',
33 | ],
34 | 'exact' => [
35 | 'boolean',
36 | ],
37 | 'sort' => [
38 | Rule::in(OccurrenceStatusFilter::getSortableColumns()),
39 | ],
40 | 'order' => [
41 | Rule::in(OccurrenceStatusFilter::getOrderValues()),
42 | ],
43 | ];
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/Http/Requests/OccurrenceType/Index.php:
--------------------------------------------------------------------------------
1 | [
20 | 'integer',
21 | ],
22 | 'page.size' => [
23 | 'integer',
24 | ],
25 | 'ids' => [
26 | 'array',
27 | ],
28 | 'ids.*' => [
29 | Rule::exists('occurrence_types', 'id'),
30 | ],
31 | 'search' => [
32 | 'string',
33 | ],
34 | 'exact' => [
35 | 'boolean',
36 | ],
37 | 'species' => [
38 | 'array',
39 | ],
40 | 'species.*' => [
41 | Rule::exists('occurrence_species', 'id'),
42 | ],
43 | 'sort' => [
44 | Rule::in(OccurrenceTypeFilter::getSortableColumns()),
45 | ],
46 | 'order' => [
47 | Rule::in(OccurrenceTypeFilter::getOrderValues()),
48 | ],
49 | ];
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/Http/Requests/Parish/Index.php:
--------------------------------------------------------------------------------
1 | [
20 | 'integer',
21 | ],
22 | 'page.size' => [
23 | 'integer',
24 | ],
25 | 'ids' => [
26 | 'array',
27 | ],
28 | 'ids.*' => [
29 | Rule::exists('parishes', 'id'),
30 | ],
31 | 'search' => [
32 | 'string',
33 | ],
34 | 'exact' => [
35 | 'boolean',
36 | ],
37 | 'counties' => [
38 | 'array',
39 | ],
40 | 'counties.*' => [
41 | Rule::exists('counties', 'id'),
42 | ],
43 | 'sort' => [
44 | Rule::in(ParishFilter::getSortableColumns()),
45 | ],
46 | 'order' => [
47 | Rule::in(ParishFilter::getOrderValues()),
48 | ],
49 | ];
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/Http/Requests/Parish/View.php:
--------------------------------------------------------------------------------
1 | [
19 | 'required',
20 | 'string',
21 | 'max:255',
22 | ],
23 | 'surname' => [
24 | 'required',
25 | 'string',
26 | 'max:255',
27 | ],
28 | 'email' => [
29 | 'required',
30 | 'email',
31 | 'max:255',
32 | Rule::unique('users', 'email'),
33 | ],
34 | 'password' => [
35 | 'required',
36 | 'confirmed',
37 | ],
38 | 'password_confirmation' => [
39 | 'required',
40 | ],
41 | ];
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/Http/Requests/User/Index.php:
--------------------------------------------------------------------------------
1 | user()->can('index', User::class);
21 | }
22 |
23 | /**
24 | * {@inheritDoc}
25 | */
26 | public function rules(): array
27 | {
28 | return [
29 | 'page.number' => [
30 | 'integer',
31 | ],
32 | 'page.size' => [
33 | 'integer',
34 | ],
35 | 'ids' => [
36 | 'array',
37 | ],
38 | 'ids.*' => [
39 | Rule::exists('users', 'id'),
40 | ],
41 | 'search' => [
42 | 'string',
43 | ],
44 | 'exact' => [
45 | 'boolean',
46 | ],
47 | 'sort' => [
48 | Rule::in(UserFilter::getSortableColumns()),
49 | ],
50 | 'order' => [
51 | Rule::in(UserFilter::getOrderValues()),
52 | ],
53 | 'roles' => [
54 | 'array',
55 | ],
56 | 'roles.*' => [
57 | new ValidRole(),
58 | ],
59 | ];
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/Http/Requests/User/RetrieveRoles.php:
--------------------------------------------------------------------------------
1 | user()->can('retrieveRoles', User::class);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/Http/Requests/User/Update.php:
--------------------------------------------------------------------------------
1 | route('User');
18 |
19 | return $this->user()->can('update', $userToUpdate);
20 | }
21 |
22 | /**
23 | * {@inheritDoc}
24 | */
25 | public function rules(): array
26 | {
27 | return [
28 | 'name' => [
29 | 'string',
30 | 'max:255',
31 | ],
32 | 'surname' => [
33 | 'string',
34 | 'max:255',
35 | ],
36 | 'email' => [
37 | 'email',
38 | 'max:255',
39 | Rule::unique('users', 'email')
40 | ->ignoreModel($this->route('User')),
41 | ],
42 | 'password' => [
43 | 'required_with:password_confirmation',
44 | 'confirmed',
45 | ],
46 | 'password_confirmation' => [
47 | 'required_with:password',
48 | ],
49 | 'roles' => [
50 | 'array',
51 | ],
52 | 'roles.*' => [
53 | Rule::exists('roles', 'name'),
54 | ],
55 | ];
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/Http/Requests/User/UpdateProfile.php:
--------------------------------------------------------------------------------
1 | user()->can('updateProfile', User::class);
19 | }
20 |
21 | /**
22 | * {@inheritDoc}
23 | */
24 | public function rules(): array
25 | {
26 | return [
27 | 'name' => [
28 | 'string',
29 | 'max:255',
30 | ],
31 | 'surname' => [
32 | 'string',
33 | 'max:255',
34 | ],
35 | 'email' => [
36 | 'email',
37 | 'max:255',
38 | Rule::unique('users', 'email')
39 | ->ignoreModel($this->user()),
40 | ],
41 | 'password' => [
42 | 'required_with:password_confirmation',
43 | 'confirmed',
44 | ],
45 | 'password_confirmation' => [
46 | 'required_with:password',
47 | ],
48 | ];
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/Http/Requests/User/View.php:
--------------------------------------------------------------------------------
1 | user()->can('view', User::class);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/Http/Requests/User/ViewProfile.php:
--------------------------------------------------------------------------------
1 | user()->can('viewProfile', User::class);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/Http/Requests/Warning/Index.php:
--------------------------------------------------------------------------------
1 | $model->initials,
23 | 'meaning' => $model->meaning,
24 | 'created_at' => $model->created_at->toDateTimeString(),
25 | 'updated_at' => $model->updated_at->toDateTimeString(),
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/Http/Serializers/CountySerializer.php:
--------------------------------------------------------------------------------
1 | route('counties::view', [
26 | 'County' => $model->getKey(),
27 | ]),
28 | ];
29 | }
30 |
31 | /**
32 | * {@inheritDoc}
33 | */
34 | public function getAttributes($model, array $fields = null): array
35 | {
36 | return [
37 | 'code' => $model->code,
38 | 'name' => $model->name,
39 | 'created_at' => $model->created_at->toDateTimeString(),
40 | 'updated_at' => $model->updated_at->toDateTimeString(),
41 | ];
42 | }
43 |
44 | /**
45 | * Associated District.
46 | *
47 | * @param County $county
48 | *
49 | * @return \Tobscure\JsonApi\Relationship
50 | */
51 | public function district(County $county): Relationship
52 | {
53 | return new Relationship(new Resource($county->district()->first(), new DistrictSerializer()));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/Http/Serializers/DistrictSerializer.php:
--------------------------------------------------------------------------------
1 | route('districts::view', [
23 | 'District' => $model->getKey(),
24 | ]),
25 | ];
26 | }
27 |
28 | /**
29 | * {@inheritDoc}
30 | */
31 | public function getAttributes($model, array $fields = null): array
32 | {
33 | return [
34 | 'code' => $model->code,
35 | 'name' => $model->name,
36 | 'created_at' => $model->created_at->toDateTimeString(),
37 | 'updated_at' => $model->updated_at->toDateTimeString(),
38 | ];
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/Http/Serializers/EventTypeSerializer.php:
--------------------------------------------------------------------------------
1 | $model->name,
23 | 'created_at' => $model->created_at->toDateTimeString(),
24 | 'updated_at' => $model->updated_at->toDateTimeString(),
25 | ];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Http/Serializers/OccurrenceFamilySerializer.php:
--------------------------------------------------------------------------------
1 | $model->code,
23 | 'name' => $model->name,
24 | 'created_at' => $model->created_at->toDateTimeString(),
25 | 'updated_at' => $model->updated_at->toDateTimeString(),
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/Http/Serializers/OccurrenceSpeciesSerializer.php:
--------------------------------------------------------------------------------
1 | $model->code,
26 | 'name' => $model->name,
27 | 'created_at' => $model->created_at->toDateTimeString(),
28 | 'updated_at' => $model->updated_at->toDateTimeString(),
29 | ];
30 | }
31 |
32 | /**
33 | * Associated Family.
34 | *
35 | * @param OccurrenceSpecies $occurrenceSpecies
36 | *
37 | * @return \Tobscure\JsonApi\Relationship
38 | */
39 | public function family(OccurrenceSpecies $occurrenceSpecies): Relationship
40 | {
41 | return new Relationship(new Resource($occurrenceSpecies->family()->first(), new OccurrenceFamilySerializer()));
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/Http/Serializers/OccurrenceStatusSerializer.php:
--------------------------------------------------------------------------------
1 | $model->code,
23 | 'name' => $model->name,
24 | 'created_at' => $model->created_at->toDateTimeString(),
25 | 'updated_at' => $model->updated_at->toDateTimeString(),
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/Http/Serializers/OccurrenceTypeSerializer.php:
--------------------------------------------------------------------------------
1 | $model->code,
26 | 'name' => $model->name,
27 | 'created_at' => $model->created_at->toDateTimeString(),
28 | 'updated_at' => $model->updated_at->toDateTimeString(),
29 | ];
30 | }
31 |
32 | /**
33 | * Associated Species.
34 | *
35 | * @param OccurrenceType $occurrenceType
36 | *
37 | * @return \Tobscure\JsonApi\Relationship
38 | */
39 | public function species(OccurrenceType $occurrenceType): Relationship
40 | {
41 | return new Relationship(new Resource($occurrenceType->species()->first(), new OccurrenceSpeciesSerializer()));
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/Http/Serializers/ParishSerializer.php:
--------------------------------------------------------------------------------
1 | route('parishes::view', [
26 | 'Parish' => $model->getKey(),
27 | ]),
28 | ];
29 | }
30 |
31 | /**
32 | * {@inheritDoc}
33 | */
34 | public function getAttributes($model, array $fields = null): array
35 | {
36 | return [
37 | 'code' => $model->code,
38 | 'name' => $model->name,
39 | 'created_at' => $model->created_at->toDateTimeString(),
40 | 'updated_at' => $model->updated_at->toDateTimeString(),
41 | ];
42 | }
43 |
44 | /**
45 | * Associated County.
46 | *
47 | * @param Parish $parish
48 | *
49 | * @return \Tobscure\JsonApi\Relationship
50 | */
51 | public function county(Parish $parish): Relationship
52 | {
53 | return new Relationship(new Resource($parish->county()->first(), new CountySerializer()));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/Http/Serializers/ProCivOccurrenceSerializer.php:
--------------------------------------------------------------------------------
1 | $model->remote_id,
23 | 'ground_assets' => (int) $model->ground_assets,
24 | 'ground_operatives' => (int) $model->ground_operatives,
25 | 'aerial_assets' => (int) $model->aerial_assets,
26 | 'aerial_operatives' => (int) $model->aerial_operatives,
27 | 'created_at' => $model->created_at->toDateTimeString(),
28 | 'updated_at' => $model->updated_at->toDateTimeString(),
29 | ];
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/Http/Serializers/RoleSerializer.php:
--------------------------------------------------------------------------------
1 | $model->name,
23 | 'title' => $model->title,
24 | 'created_at' => $model->created_at->toDateTimeString(),
25 | 'updated_at' => $model->updated_at->toDateTimeString(),
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/Http/Serializers/UserSerializer.php:
--------------------------------------------------------------------------------
1 | route('users::view', [
26 | 'User' => $model->getKey(),
27 | ]),
28 | ];
29 | }
30 |
31 | /**
32 | * {@inheritDoc}
33 | */
34 | public function getAttributes($model, array $fields = null): array
35 | {
36 | return [
37 | 'email' => $model->email,
38 | 'name' => $model->name,
39 | 'surname' => $model->surname,
40 | 'created_at' => $model->created_at->toDateTimeString(),
41 | 'updated_at' => $model->updated_at->toDateTimeString(),
42 | ];
43 | }
44 |
45 | /**
46 | * Associated Roles.
47 | *
48 | * @param User $user
49 | *
50 | * @return \Tobscure\JsonApi\Relationship
51 | */
52 | public function roles(User $user): Relationship
53 | {
54 | return new Relationship(new Collection($user->roles()->get(), new RoleSerializer()));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/Http/Serializers/WarningSerializer.php:
--------------------------------------------------------------------------------
1 | $warning['text'],
33 | 'awareness_type_name' => $warning['awarenessTypeName'],
34 | 'awareness_level' => $warning['awarenessLevelID'],
35 | 'started_at' => $warning['started_at']->toDateTimeString(),
36 | 'ended_at' => $warning['ended_at']->toDateTimeString(),
37 | ];
38 | }
39 |
40 | /**
41 | * Associated County.
42 | *
43 | * @param array $warning
44 | *
45 | * @return \Tobscure\JsonApi\Relationship
46 | */
47 | public function county(array $warning): Relationship
48 | {
49 | return new Relationship(new Resource($warning['county'], new CountySerializer()));
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/Http/Serializers/WeatherStationSerializer.php:
--------------------------------------------------------------------------------
1 | $model->entity,
26 | 'name' => $model->name,
27 | 'serial' => $model->serial,
28 | 'created_at' => $model->created_at->toDateTimeString(),
29 | 'updated_at' => $model->updated_at->toDateTimeString(),
30 | ];
31 | }
32 |
33 | /**
34 | * Associated County.
35 | *
36 | * @param WeatherStation $weatherStation
37 | *
38 | * @return \Tobscure\JsonApi\Relationship
39 | */
40 | public function county(WeatherStation $weatherStation): Relationship
41 | {
42 | return new Relationship(new Resource($weatherStation->county()->first(), new CountySerializer()));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/Jobs/Api/ResponseCacheBuster.php:
--------------------------------------------------------------------------------
1 | info(\sprintf('Busting API response cache for: %s', \implode(', ', $tags)));
34 |
35 | if ($cache->tags($tags)->flush()) {
36 | $logger->info(\sprintf('Clearing bust tags for: %s', \implode(', ', $tags)));
37 |
38 | return clear_cache_bust_tags();
39 | }
40 | }
41 |
42 | return false;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/Jobs/Report/ReportGenerator.php:
--------------------------------------------------------------------------------
1 | report = $report;
35 | }
36 |
37 | /**
38 | * Execute the job.
39 | *
40 | * @param \Psr\Log\LoggerInterface $logger
41 | *
42 | * @return bool
43 | */
44 | public function handle(LoggerInterface $logger): bool
45 | {
46 | return $this->report->isReadyForDownload() || $this->report->generate($logger);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/Models/Acronym.php:
--------------------------------------------------------------------------------
1 | 16) {
31 | throw new LengthException('The initials cannot have more than 16 characters');
32 | }
33 |
34 | $this->attributes['initials'] = \mb_strtoupper(\trim($initials));
35 | }
36 |
37 | /**
38 | * Set the meaning.
39 | *
40 | * @param string $meaning
41 | *
42 | * @throws \LengthException
43 | *
44 | * @return void
45 | */
46 | public function setMeaningAttribute(string $meaning): void
47 | {
48 | if (\mb_strlen($meaning) > 255) {
49 | throw new LengthException('The meaning cannot have more than 255 characters');
50 | }
51 |
52 | $this->attributes['meaning'] = $meaning;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/Models/Concerns/Cacheable.php:
--------------------------------------------------------------------------------
1 | hasMany(Event::class);
26 | }
27 |
28 | /**
29 | * Set the name.
30 | *
31 | * @param string $name
32 | *
33 | * @throws \LengthException
34 | *
35 | * @return void
36 | */
37 | public function setNameAttribute(string $name): void
38 | {
39 | if (\mb_strlen($name) > 255) {
40 | throw new LengthException('The name cannot exceed 255 characters');
41 | }
42 |
43 | $this->attributes['name'] = $name;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/Models/Observers/CacheableObserver.php:
--------------------------------------------------------------------------------
1 | markForCacheBust($model);
23 | }
24 |
25 | /**
26 | * Handle the updated event.
27 | *
28 | * @param Model $model
29 | *
30 | * @throws \Exception
31 | *
32 | * @return void
33 | */
34 | public function updated(Model $model): void
35 | {
36 | $this->markForCacheBust($model);
37 | }
38 |
39 | /**
40 | * Handle the deleted event.
41 | *
42 | * @param Model $model
43 | *
44 | * @throws \Exception
45 | *
46 | * @return void
47 | */
48 | public function deleted(Model $model): void
49 | {
50 | $this->markForCacheBust($model);
51 | }
52 |
53 | /**
54 | * Mark cache for bust.
55 | *
56 | * @param Model $model
57 | *
58 | * @throws \Exception
59 | *
60 | * @return void
61 | */
62 | private function markForCacheBust(Model $model): void
63 | {
64 | add_cache_bust_tag(\get_class($model));
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/Models/OccurrenceFamily.php:
--------------------------------------------------------------------------------
1 | hasMany(OccurrenceSpecies::class);
28 | }
29 |
30 | /**
31 | * Set the name.
32 | *
33 | * @param string $name
34 | *
35 | * @throws \LengthException
36 | *
37 | * @return void
38 | */
39 | public function setNameAttribute(string $name): void
40 | {
41 | if (\mb_strlen($name) > 255) {
42 | throw new LengthException('The name cannot exceed 255 characters');
43 | }
44 |
45 | $this->attributes['name'] = $name;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/Models/OccurrenceSpecies.php:
--------------------------------------------------------------------------------
1 | belongsTo(OccurrenceFamily::class, 'family_id');
29 | }
30 |
31 | /**
32 | * Associated Types.
33 | *
34 | * @return \Illuminate\Database\Eloquent\Relations\HasMany
35 | */
36 | public function types(): HasMany
37 | {
38 | return $this->hasMany(OccurrenceType::class);
39 | }
40 |
41 | /**
42 | * Set the name.
43 | *
44 | * @param string $name
45 | *
46 | * @throws \LengthException
47 | *
48 | * @return void
49 | */
50 | public function setNameAttribute(string $name): void
51 | {
52 | if (\mb_strlen($name) > 255) {
53 | throw new LengthException('The name cannot exceed 255 characters');
54 | }
55 |
56 | $this->attributes['name'] = $name;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/Models/Parish.php:
--------------------------------------------------------------------------------
1 | belongsTo(County::class);
28 | }
29 |
30 | /**
31 | * Set the code.
32 | *
33 | * @param string $code
34 | *
35 | * @throws \LengthException
36 | *
37 | * @return void
38 | */
39 | public function setCodeAttribute(string $code): void
40 | {
41 | if (\mb_strlen($code) !== 6) {
42 | throw new LengthException('The code must have 6 characters');
43 | }
44 |
45 | $this->attributes['code'] = $code;
46 | }
47 |
48 | /**
49 | * Set the name.
50 | *
51 | * @param string $name
52 | *
53 | * @throws \LengthException
54 | *
55 | * @return void
56 | */
57 | public function setNameAttribute(string $name): void
58 | {
59 | if (\mb_strlen($name) > 255) {
60 | throw new LengthException('The name cannot exceed 255 characters');
61 | }
62 |
63 | $this->attributes['name'] = $name;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/Models/ProCivOccurrence.php:
--------------------------------------------------------------------------------
1 | 'int',
23 | 'aerial_operatives_involved' => 'int',
24 | 'ground_assets_involved' => 'int',
25 | 'ground_operatives_involved' => 'int',
26 | ];
27 |
28 | /**
29 | * Parent occurrence.
30 | *
31 | * @return \Illuminate\Database\Eloquent\Relations\MorphOne
32 | */
33 | public function parent(): MorphOne
34 | {
35 | return $this->morphOne(Occurrence::class, 'source');
36 | }
37 |
38 | /**
39 | * Associated logs.
40 | *
41 | * @return \Illuminate\Database\Eloquent\Relations\HasMany
42 | */
43 | public function logs(): HasMany
44 | {
45 | return $this->hasMany(ProCivOccurrenceLog::class, 'occurrence_id');
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/Models/ProCivOccurrenceLog.php:
--------------------------------------------------------------------------------
1 | belongsTo(ProCivOccurrence::class, 'occurrence_id');
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/Models/Role.php:
--------------------------------------------------------------------------------
1 | belongsTo(County::class);
26 | }
27 |
28 | /**
29 | * Associated WeatherObservations.
30 | *
31 | * @return \Illuminate\Database\Eloquent\Relations\HasMany
32 | */
33 | public function observations(): HasMany
34 | {
35 | return $this->hasMany(WeatherObservation::class, 'station_id');
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Policies/AcronymPolicy.php:
--------------------------------------------------------------------------------
1 | isAn(Role::ADMINISTRATOR);
23 | }
24 |
25 | /**
26 | * Determine whether the User can update Acronyms.
27 | *
28 | * @param User $user
29 | * @param Acronym $acronym
30 | *
31 | * @return bool
32 | */
33 | public function update(User $user, Acronym $acronym): bool
34 | {
35 | return $user->isAn(Role::ADMINISTRATOR);
36 | }
37 |
38 | /**
39 | * Determine whether the User can delete Acronyms.
40 | *
41 | * @param User $user
42 | * @param Acronym $acronym
43 | *
44 | * @return bool
45 | */
46 | public function delete(User $user, Acronym $acronym): bool
47 | {
48 | return $user->isAn(Role::ADMINISTRATOR);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/Policies/OccurrencePolicy.php:
--------------------------------------------------------------------------------
1 | isAn(Role::ADMINISTRATOR, Role::MODERATOR);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | \VOSTPT\Models\ProCivOccurrence::class,
29 | ]);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/Providers/AuthServiceProvider.php:
--------------------------------------------------------------------------------
1 | \VOSTPT\Policies\AcronymPolicy::class,
16 | \VOSTPT\Models\Event::class => \VOSTPT\Policies\EventPolicy::class,
17 | \VOSTPT\Models\Occurrence::class => \VOSTPT\Policies\OccurrencePolicy::class,
18 | \VOSTPT\Models\User::class => \VOSTPT\Policies\UserPolicy::class,
19 | ];
20 |
21 | /**
22 | * Register any authentication / authorization services.
23 | *
24 | * @return void
25 | */
26 | public function boot(): void
27 | {
28 | $this->registerPolicies();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/Providers/EventServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->bind(ClientInterface::class, static function () {
22 | $client = new GuzzleClient([
23 | // Do not throw exceptions on HTTP 4xx/5xx status
24 | RequestOptions::HTTP_ERRORS => false,
25 | ]);
26 |
27 | return new GuzzleAdapter($client);
28 | });
29 | }
30 |
31 | /**
32 | * {@inheritDoc}
33 | */
34 | public function provides(): array
35 | {
36 | return [
37 | ClientInterface::class,
38 | ];
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/Providers/ResponseServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->singleton(FactoryContract::class, static function ($app) {
20 | return new ResponseFactory($app[ViewFactory::class], $app['redirect']);
21 | });
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/Reports/Contracts/Report.php:
--------------------------------------------------------------------------------
1 | next = $next;
29 | }
30 |
31 | /**
32 | * {@inheritdoc}
33 | */
34 | public function findById(int $id): ?Model
35 | {
36 | return $this->next->findById($id);
37 | }
38 |
39 | /**
40 | * {@inheritDoc}
41 | */
42 | public function createQueryBuilder(): Builder
43 | {
44 | return $this->next->createQueryBuilder();
45 | }
46 |
47 | /**
48 | * {@inheritdoc}
49 | */
50 | public function getPaginator(Filter $filter): LengthAwarePaginator
51 | {
52 | return Cache::tags(Acronym::class)->rememberForever($filter->getSignature(), function () use ($filter) {
53 | return $this->next->getPaginator($filter);
54 | });
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/Repositories/Concerns/Paginator.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder();
18 |
19 | $filter->apply($builder);
20 |
21 | return $builder->paginate(
22 | $filter->getPageSize(),
23 | $filter->getColumns(),
24 | 'page[number]',
25 | $filter->getPageNumber()
26 | )->appends($filter->getUrlParameters());
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/Repositories/Contracts/AcronymRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder()
28 | ->where('code', $code)
29 | ->first();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/Repositories/DistrictRepository.php:
--------------------------------------------------------------------------------
1 | next = $next;
29 | }
30 |
31 | /**
32 | * {@inheritdoc}
33 | */
34 | public function findById(int $id): ?Model
35 | {
36 | return $this->next->findById($id);
37 | }
38 |
39 | /**
40 | * {@inheritDoc}
41 | */
42 | public function createQueryBuilder(): Builder
43 | {
44 | return $this->next->createQueryBuilder();
45 | }
46 |
47 | /**
48 | * {@inheritdoc}
49 | */
50 | public function getPaginator(Filter $filter): LengthAwarePaginator
51 | {
52 | return Cache::tags(District::class)->rememberForever($filter->getSignature(), function () use ($filter) {
53 | return $this->next->getPaginator($filter);
54 | });
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/Repositories/EventRepository.php:
--------------------------------------------------------------------------------
1 | next = $next;
29 | }
30 |
31 | /**
32 | * {@inheritdoc}
33 | */
34 | public function findById(int $id): ?Model
35 | {
36 | return $this->next->findById($id);
37 | }
38 |
39 | /**
40 | * {@inheritDoc}
41 | */
42 | public function createQueryBuilder(): Builder
43 | {
44 | return $this->next->createQueryBuilder();
45 | }
46 |
47 | /**
48 | * {@inheritdoc}
49 | */
50 | public function getPaginator(Filter $filter): LengthAwarePaginator
51 | {
52 | return Cache::tags(Event::class)->rememberForever($filter->getSignature(), function () use ($filter) {
53 | return $this->next->getPaginator($filter);
54 | });
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/Repositories/OccurrenceFamilyRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder()
29 | ->stalled()
30 | ->get();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/Repositories/OccurrenceSpeciesRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder()
28 | ->where('code', $code)
29 | ->first();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/Repositories/OccurrenceTypeRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder()
28 | ->where('code', $code)
29 | ->first();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/Repositories/ParishRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder()
28 | ->where('code', $code)
29 | ->first();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/Repositories/ProCivOccurrenceRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder()
26 | ->where('remote_id', $id)
27 | ->first();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/Repositories/Repository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder()
17 | ->where('id', $id)
18 | ->first();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/Repositories/UserRepository.php:
--------------------------------------------------------------------------------
1 | next = $next;
29 | }
30 |
31 | /**
32 | * {@inheritdoc}
33 | */
34 | public function findById(int $id): ?Model
35 | {
36 | return $this->next->findById($id);
37 | }
38 |
39 | /**
40 | * {@inheritDoc}
41 | */
42 | public function createQueryBuilder(): Builder
43 | {
44 | return $this->next->createQueryBuilder();
45 | }
46 |
47 | /**
48 | * {@inheritdoc}
49 | */
50 | public function getPaginator(Filter $filter): LengthAwarePaginator
51 | {
52 | return Cache::tags(User::class)->rememberForever($filter->getSignature(), function () use ($filter) {
53 | return $this->next->getPaginator($filter);
54 | });
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/Repositories/WeatherObservationRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder()
30 | ->where('station_id', $station->getKey())
31 | ->where('timestamp', $timestamp)
32 | ->first();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/Repositories/WeatherStationRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder()
26 | ->where('entity', $entity)
27 | ->where('serial', $serial)
28 | ->first();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/Rules/ValidRole.php:
--------------------------------------------------------------------------------
1 | first() !== null;
18 | }
19 |
20 | /**
21 | * {@inheritDoc}
22 | */
23 | public function message(): string
24 | {
25 | return \sprintf('The :attribute value must be one of: %s', \implode(', ', Role::pluck('name')->all()));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/ServiceClients/Contracts/IpmaApiServiceClient.php:
--------------------------------------------------------------------------------
1 | get('json/warnings_www.json');
15 | }
16 |
17 | /**
18 | * {@inheritDoc}
19 | */
20 | public function getSurfaceObservations(): array
21 | {
22 | $results = $this->get('open-data/observation/meteorology/stations/observations.json');
23 |
24 | // Results come out of order, so we sort them out before returning them
25 | \ksort($results);
26 |
27 | return $results;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/ServiceClients/ProCivWebsiteServiceClient.php:
--------------------------------------------------------------------------------
1 | 'application/json;charset=utf-8',
16 | 'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:71.0) Gecko/20100101 Firefox/71.0',
17 | ];
18 | }
19 |
20 | /**
21 | * {@inheritDoc}
22 | */
23 | public function getMainOccurrences(): array
24 | {
25 | $response = $this->post('_vti_bin/ARM.ANPC.UI/ANPC_SituacaoOperacional.svc/GetMainOccurrences', [
26 | 'allData' => true,
27 | ]);
28 |
29 | return $response['GetMainOccurrencesResult']['ArrayInfo'][0] ?? [
30 | 'Data' => [],
31 | 'Total' => 0,
32 | ];
33 | }
34 |
35 | /**
36 | * {@inheritDoc}
37 | */
38 | public function getOccurrenceHistory(): array
39 | {
40 | $response = $this->post('_vti_bin/ARM.ANPC.UI/ANPC_SituacaoOperacional.svc/GetHistoryOccurrencesByLocation', [
41 | 'allData' => true,
42 | ]);
43 |
44 | return $response['GetHistoryOccurrencesByLocationResult']['ArrayInfo'][0] ?? [
45 | 'Data' => [],
46 | 'Total' => 0,
47 | ];
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/bootstrap/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/config/services.php:
--------------------------------------------------------------------------------
1 | [
8 | 'api' => [
9 | 'hostname' => env('IPMA_API_HOSTNAME'),
10 | ],
11 | ],
12 |
13 | 'prociv' => [
14 | 'website' => [
15 | 'hostname' => env('PROCIV_WEBSITE_HOSTNAME'),
16 | ],
17 | ],
18 |
19 | ];
20 |
--------------------------------------------------------------------------------
/config/view.php:
--------------------------------------------------------------------------------
1 | [
19 | resource_path('views'),
20 | ],
21 |
22 | /*
23 | |--------------------------------------------------------------------------
24 | | Compiled View Path
25 | |--------------------------------------------------------------------------
26 | |
27 | | This option determines where all the compiled Blade templates will be
28 | | stored for your application. Typically, this is within the storage
29 | | directory. However, as usual, you are free to change this value.
30 | |
31 | */
32 |
33 | 'compiled' => env(
34 | 'VIEW_COMPILED_PATH',
35 | \realpath(storage_path('framework/views'))
36 | ),
37 |
38 | ];
39 |
--------------------------------------------------------------------------------
/database/.gitignore:
--------------------------------------------------------------------------------
1 | *.sqlite
2 |
--------------------------------------------------------------------------------
/database/factories/AcronymFactory.php:
--------------------------------------------------------------------------------
1 | define(Acronym::class, static function (Faker $faker) {
16 | return [
17 | 'initials' => $faker->unique()->lexify(),
18 | 'meaning' => $faker->unique()->sentence(),
19 | ];
20 | });
21 |
--------------------------------------------------------------------------------
/database/factories/CountyFactory.php:
--------------------------------------------------------------------------------
1 | define(County::class, static function (Faker $faker) {
17 | return [
18 | 'district_id' => static function () {
19 | return factory(District::class)->create()->getKey();
20 | },
21 | 'code' => $faker->unique()->numerify('######'),
22 | 'name' => \sprintf('%s County', $faker->unique()->name),
23 | ];
24 | });
25 |
--------------------------------------------------------------------------------
/database/factories/DistrictFactory.php:
--------------------------------------------------------------------------------
1 | define(District::class, static function (Faker $faker) {
16 | return [
17 | 'code' => $faker->unique()->numerify('######'),
18 | 'name' => \sprintf('%s District', $faker->unique()->name),
19 | ];
20 | });
21 |
--------------------------------------------------------------------------------
/database/factories/EventFactory.php:
--------------------------------------------------------------------------------
1 | define(Event::class, static function (Faker $faker) {
18 | return [
19 | 'type_id' => static function () {
20 | return factory(EventType::class)->create()->getKey();
21 | },
22 | 'parish_id' => static function () {
23 | return factory(Parish::class)->create()->getKey();
24 | },
25 | 'name' => $faker->sentence(),
26 | 'description' => $faker->paragraph,
27 | 'latitude' => $faker->latitude,
28 | 'longitude' => $faker->longitude,
29 | 'started_at' => $faker->dateTime,
30 | 'ended_at' => $faker->dateTime,
31 | ];
32 | });
33 |
--------------------------------------------------------------------------------
/database/factories/EventTypeFactory.php:
--------------------------------------------------------------------------------
1 | define(EventType::class, static function (Faker $faker) {
16 | return [
17 | 'name' => $faker->unique()->sentence(),
18 | ];
19 | });
20 |
--------------------------------------------------------------------------------
/database/factories/OccurrenceFactory.php:
--------------------------------------------------------------------------------
1 | define(Occurrence::class, static function (Faker $faker) {
20 | return [
21 | 'event_id' => static function () {
22 | return factory(Event::class)->create()->getKey();
23 | },
24 | 'type_id' => static function () {
25 | return factory(OccurrenceType::class)->create()->getKey();
26 | },
27 | 'status_id' => static function () {
28 | return factory(OccurrenceStatus::class)->create()->getKey();
29 | },
30 | 'parish_id' => static function () {
31 | return factory(Parish::class)->create()->getKey();
32 | },
33 | 'locality' => $faker->sentence(),
34 | 'latitude' => $faker->latitude,
35 | 'longitude' => $faker->longitude,
36 | 'started_at' => $faker->dateTime,
37 | 'ended_at' => $faker->dateTime,
38 | ];
39 | });
40 |
--------------------------------------------------------------------------------
/database/factories/OccurrenceFamilyFactory.php:
--------------------------------------------------------------------------------
1 | define(OccurrenceFamily::class, static function (Faker $faker) {
16 | return [
17 | 'code' => $faker->unique()->numberBetween(1000, 9999),
18 | 'name' => $faker->unique()->sentence(),
19 | ];
20 | });
21 |
--------------------------------------------------------------------------------
/database/factories/OccurrenceSpeciesFactory.php:
--------------------------------------------------------------------------------
1 | define(OccurrenceSpecies::class, static function (Faker $faker) {
17 | return [
18 | 'family_id' => static function () {
19 | return factory(OccurrenceFamily::class)->create()->getKey();
20 | },
21 | 'code' => $faker->unique()->numberBetween(1000, 9999),
22 | 'name' => $faker->unique()->sentence(),
23 | ];
24 | });
25 |
--------------------------------------------------------------------------------
/database/factories/OccurrenceStatusFactory.php:
--------------------------------------------------------------------------------
1 | define(OccurrenceStatus::class, static function (Faker $faker) {
16 | return [
17 | 'code' => $faker->unique()->numberBetween(1, 255),
18 | 'name' => $faker->unique()->sentence(),
19 | ];
20 | });
21 |
--------------------------------------------------------------------------------
/database/factories/OccurrenceTypeFactory.php:
--------------------------------------------------------------------------------
1 | define(OccurrenceType::class, static function (Faker $faker) {
17 | return [
18 | 'species_id' => static function () {
19 | return factory(OccurrenceSpecies::class)->create()->getKey();
20 | },
21 | 'code' => $faker->unique()->numberBetween(1000, 9999),
22 | 'name' => $faker->unique()->sentence(),
23 | ];
24 | });
25 |
--------------------------------------------------------------------------------
/database/factories/ParishFactory.php:
--------------------------------------------------------------------------------
1 | define(Parish::class, static function (Faker $faker) {
17 | return [
18 | 'county_id' => static function () {
19 | return factory(County::class)->create()->getKey();
20 | },
21 | 'code' => $faker->unique()->numerify('######'),
22 | 'name' => \sprintf('%s Parish', $faker->unique()->name),
23 | ];
24 | });
25 |
--------------------------------------------------------------------------------
/database/factories/ProCivOccurrenceFactory.php:
--------------------------------------------------------------------------------
1 | define(ProCivOccurrence::class, static function (Faker $faker) {
16 | return [
17 | 'remote_id' => $faker->unique()->numerify('#############'),
18 | 'ground_assets' => $faker->numberBetween(1, 8),
19 | 'ground_operatives' => $faker->numberBetween(2, 8),
20 | 'aerial_assets' => $faker->numberBetween(0, 8),
21 | 'aerial_operatives' => $faker->numberBetween(0, 8),
22 | ];
23 | });
24 |
--------------------------------------------------------------------------------
/database/factories/UserFactory.php:
--------------------------------------------------------------------------------
1 | define(User::class, static function (Faker $faker) {
16 | return [
17 | 'name' => $faker->firstName,
18 | 'surname' => $faker->lastName,
19 | 'email' => $faker->unique()->safeEmail,
20 | 'password' => 'secret',
21 | ];
22 | });
23 |
--------------------------------------------------------------------------------
/database/factories/WeatherObservationFactory.php:
--------------------------------------------------------------------------------
1 | define(WeatherObservation::class, static function (Faker $faker) {
17 | return [
18 | 'station_id' => static function () {
19 | return factory(WeatherStation::class)->create()->getKey();
20 | },
21 | 'temperature' => $faker->randomFloat(1, 1, 50),
22 | 'humidity' => $faker->randomFloat(1, 2, 100),
23 | 'wind_speed' => $faker->randomFloat(1, 0, 120),
24 | 'wind_direction' => $faker->randomElement(WeatherObservation::WIND_DIRECTIONS),
25 | 'precipitation' => $faker->randomFloat(1, 0, 2000),
26 | 'atmospheric_pressure' => $faker->randomFloat(1, 1010, 1030),
27 | 'radiation' => $faker->randomFloat(1, 0, 3600),
28 | 'timestamp' => $faker->dateTime(),
29 | ];
30 | });
31 |
--------------------------------------------------------------------------------
/database/factories/WeatherSensorFactory.php:
--------------------------------------------------------------------------------
1 | define(WeatherSensor::class, static function (Faker $faker) {
17 | return [
18 | 'station_id' => static function () {
19 | return factory(WeatherStation::class)->create()->getKey();
20 | },
21 | 'type' => $faker->name,
22 | 'latitude' => $faker->latitude,
23 | 'longitude' => $faker->longitude,
24 | 'altitude' => $faker->numberBetween(1, 1800),
25 | 'started_at' => $faker->date(),
26 | ];
27 | });
28 |
--------------------------------------------------------------------------------
/database/factories/WeatherStationFactory.php:
--------------------------------------------------------------------------------
1 | define(WeatherStation::class, static function (Faker $faker) {
17 | return [
18 | 'county_id' => static function () {
19 | return factory(County::class)->create()->getKey();
20 | },
21 | 'entity' => $faker->unique()->company,
22 | 'name' => \sprintf('%s Station', $faker->unique()->name),
23 | 'serial' => $faker->unique()->numerify('###'),
24 | ];
25 | });
26 |
--------------------------------------------------------------------------------
/database/migrations/2019_04_08_185908_create_users_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
20 |
21 | $table->string('name');
22 | $table->string('surname');
23 | $table->string('email')->unique();
24 | $table->string('password');
25 |
26 | $table->timestamps();
27 | });
28 | }
29 |
30 | /**
31 | * Reverse the migrations.
32 | *
33 | * @return void
34 | */
35 | public function down(): void
36 | {
37 | Schema::drop('users');
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/database/migrations/2019_04_09_095426_create_districts_table.php:
--------------------------------------------------------------------------------
1 | tinyIncrements('id');
20 |
21 | $table->string('code')->unique();
22 | $table->string('name')->unique();
23 |
24 | $table->timestamps();
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | *
31 | * @return void
32 | */
33 | public function down(): void
34 | {
35 | Schema::drop('districts');
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/database/migrations/2019_04_11_145226_create_counties_table.php:
--------------------------------------------------------------------------------
1 | smallIncrements('id');
20 | $table->unsignedTinyInteger('district_id');
21 |
22 | $table->string('code')->unique();
23 | $table->string('name');
24 |
25 | $table->timestamps();
26 |
27 | $table->unique([
28 | 'district_id',
29 | 'name',
30 | ]);
31 |
32 | $table->foreign('district_id')
33 | ->references('id')
34 | ->on('districts')
35 | ->onUpdate('cascade');
36 | });
37 | }
38 |
39 | /**
40 | * Reverse the migrations.
41 | *
42 | * @return void
43 | */
44 | public function down(): void
45 | {
46 | Schema::drop('counties');
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/database/migrations/2019_04_12_074212_create_parishes_table.php:
--------------------------------------------------------------------------------
1 | smallIncrements('id');
20 | $table->unsignedSmallInteger('county_id');
21 |
22 | $table->string('code')->unique();
23 | $table->string('name');
24 |
25 | $table->timestamps();
26 |
27 | $table->unique([
28 | 'county_id',
29 | 'name',
30 | ]);
31 |
32 | $table->foreign('county_id')
33 | ->references('id')
34 | ->on('counties')
35 | ->onUpdate('cascade');
36 | });
37 | }
38 |
39 | /**
40 | * Reverse the migrations.
41 | *
42 | * @return void
43 | */
44 | public function down(): void
45 | {
46 | Schema::drop('parishes');
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/database/migrations/2019_04_12_224617_create_acronyms_table.php:
--------------------------------------------------------------------------------
1 | smallIncrements('id');
20 |
21 | $table->string('initials')->unique();
22 | $table->string('meaning');
23 |
24 | $table->timestamps();
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | *
31 | * @return void
32 | */
33 | public function down(): void
34 | {
35 | Schema::drop('acronyms');
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/database/migrations/2019_04_16_222648_create_event_types_table.php:
--------------------------------------------------------------------------------
1 | tinyIncrements('id');
20 |
21 | $table->string('name')->unique();
22 |
23 | $table->timestamps();
24 | });
25 | }
26 |
27 | /**
28 | * Reverse the migrations.
29 | *
30 | * @return void
31 | */
32 | public function down(): void
33 | {
34 | Schema::drop('event_types');
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/database/migrations/2019_04_17_203846_create_occurrence_families_table.php:
--------------------------------------------------------------------------------
1 | tinyIncrements('id');
20 |
21 | $table->unsignedSmallInteger('code')->unique();
22 | $table->string('name')->unique();
23 |
24 | $table->timestamps();
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | *
31 | * @return void
32 | */
33 | public function down(): void
34 | {
35 | Schema::drop('occurrence_families');
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/database/migrations/2019_04_17_203847_create_occurrence_species_table.php:
--------------------------------------------------------------------------------
1 | tinyIncrements('id');
20 | $table->unsignedTinyInteger('family_id');
21 |
22 | $table->unsignedSmallInteger('code')->unique();
23 | $table->string('name');
24 |
25 | $table->timestamps();
26 |
27 | $table->unique([
28 | 'family_id',
29 | 'name',
30 | ]);
31 |
32 | $table->foreign('family_id')
33 | ->references('id')
34 | ->on('occurrence_families')
35 | ->onUpdate('cascade');
36 | });
37 | }
38 |
39 | /**
40 | * Reverse the migrations.
41 | *
42 | * @return void
43 | */
44 | public function down(): void
45 | {
46 | Schema::drop('occurrence_species');
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/database/migrations/2019_04_17_203847_create_occurrence_statuses_table.php:
--------------------------------------------------------------------------------
1 | tinyIncrements('id');
20 |
21 | $table->unsignedTinyInteger('code')->unique();
22 | $table->string('name')->unique();
23 |
24 | $table->timestamps();
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | *
31 | * @return void
32 | */
33 | public function down(): void
34 | {
35 | Schema::drop('occurrence_statuses');
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/database/migrations/2019_04_17_203848_create_occurrence_types_table.php:
--------------------------------------------------------------------------------
1 | tinyIncrements('id');
20 | $table->unsignedTinyInteger('species_id');
21 |
22 | $table->unsignedSmallInteger('code')->unique();
23 | $table->string('name');
24 |
25 | $table->timestamps();
26 |
27 | $table->unique([
28 | 'species_id',
29 | 'name',
30 | ]);
31 |
32 | $table->foreign('species_id')
33 | ->references('id')
34 | ->on('occurrence_species')
35 | ->onUpdate('cascade');
36 | });
37 | }
38 |
39 | /**
40 | * Reverse the migrations.
41 | *
42 | * @return void
43 | */
44 | public function down(): void
45 | {
46 | Schema::drop('occurrence_types');
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/database/migrations/2019_04_17_203850_create_prociv_occurrences_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
20 | $table->string('remote_id')->unique();
21 |
22 | $table->unsignedSmallInteger('ground_assets');
23 | $table->unsignedSmallInteger('ground_operatives');
24 |
25 | $table->unsignedSmallInteger('aerial_assets');
26 | $table->unsignedSmallInteger('aerial_operatives');
27 |
28 | $table->timestamps();
29 | });
30 | }
31 |
32 | /**
33 | * Reverse the migrations.
34 | *
35 | * @return void
36 | */
37 | public function down(): void
38 | {
39 | Schema::drop('prociv_occurrences');
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/database/migrations/2019_08_13_084800_create_failed_jobs_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
20 | $table->text('connection');
21 | $table->text('queue');
22 | $table->longText('payload');
23 | $table->longText('exception');
24 | $table->timestamp('failed_at')->useCurrent();
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | *
31 | * @return void
32 | */
33 | public function down(): void
34 | {
35 | Schema::drop('failed_jobs');
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/database/migrations/2019_09_04_133750_create_weather_stations_table.php:
--------------------------------------------------------------------------------
1 | smallIncrements('id');
20 | $table->unsignedSmallInteger('county_id');
21 |
22 | $table->string('entity');
23 | $table->string('name');
24 | $table->string('serial');
25 |
26 | $table->timestamps();
27 |
28 | $table->unique([
29 | 'entity',
30 | 'name',
31 | ]);
32 |
33 | $table->unique([
34 | 'entity',
35 | 'serial',
36 | ]);
37 |
38 | $table->foreign('county_id')
39 | ->references('id')
40 | ->on('counties')
41 | ->onUpdate('cascade');
42 | });
43 | }
44 |
45 | /**
46 | * Reverse the migrations.
47 | *
48 | * @return void
49 | */
50 | public function down(): void
51 | {
52 | Schema::drop('weather_stations');
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/database/migrations/2019_09_04_133751_create_weather_sensors_table.php:
--------------------------------------------------------------------------------
1 | smallIncrements('id');
20 | $table->unsignedSmallInteger('station_id');
21 |
22 | $table->string('type');
23 |
24 | $table->decimal('latitude', 10, 8);
25 | $table->decimal('longitude', 11, 8);
26 |
27 | $table->unsignedSmallInteger('altitude');
28 |
29 | $table->date('started_at')->nullable();
30 |
31 | $table->timestamps();
32 |
33 | $table->unique([
34 | 'station_id',
35 | 'type',
36 | ]);
37 |
38 | $table->foreign('station_id')
39 | ->references('id')
40 | ->on('weather_stations')
41 | ->onUpdate('cascade');
42 | });
43 | }
44 |
45 | /**
46 | * Reverse the migrations.
47 | *
48 | * @return void
49 | */
50 | public function down(): void
51 | {
52 | Schema::drop('weather_sensors');
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/database/seeds/AcronymSeeder.php:
--------------------------------------------------------------------------------
1 | create($acronym);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/database/seeds/CountySeeder.php:
--------------------------------------------------------------------------------
1 | $counties) {
21 | $district = District::where('code', $districtCode)->first();
22 |
23 | foreach ($counties as $county) {
24 | factory(County::class)->create(\array_merge($county, [
25 | 'district_id' => $district->getKey(),
26 | ]));
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/database/seeds/DatabaseSeeder.php:
--------------------------------------------------------------------------------
1 | call(RoleSeeder::class);
17 | $this->call(DistrictSeeder::class);
18 | $this->call(CountySeeder::class);
19 | $this->call(ParishSeeder::class);
20 | $this->call(AcronymSeeder::class);
21 | $this->call(EventTypeSeeder::class);
22 | $this->call(OccurrenceStatusSeeder::class);
23 | $this->call(OccurrenceFamilySeeder::class);
24 | $this->call(OccurrenceSpeciesSeeder::class);
25 | $this->call(OccurrenceTypeSeeder::class);
26 | $this->call(IpmaStationAndSensorSeeder::class);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/database/seeds/DistrictSeeder.php:
--------------------------------------------------------------------------------
1 | create($district);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/database/seeds/EventTypeSeeder.php:
--------------------------------------------------------------------------------
1 | create($attributes);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/database/seeds/OccurrenceFamilySeeder.php:
--------------------------------------------------------------------------------
1 | create($attributes);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/database/seeds/OccurrenceSpeciesSeeder.php:
--------------------------------------------------------------------------------
1 | $species) {
21 | $parent = OccurrenceFamily::where('code', $code)->first();
22 |
23 | foreach ($species as $attributes) {
24 | factory(OccurrenceSpecies::class)->create(\array_merge($attributes, [
25 | 'family_id' => $parent->getKey(),
26 | ]));
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/database/seeds/OccurrenceStatusSeeder.php:
--------------------------------------------------------------------------------
1 | create($occurrenceStatus);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/database/seeds/OccurrenceTypeSeeder.php:
--------------------------------------------------------------------------------
1 | $types) {
21 | $parent = OccurrenceSpecies::where('code', $code)->first();
22 |
23 | foreach ($types as $attributes) {
24 | factory(OccurrenceType::class)->create(\array_merge($attributes, [
25 | 'species_id' => $parent->getKey(),
26 | ]));
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/database/seeds/ParishSeeder.php:
--------------------------------------------------------------------------------
1 | $parishes) {
21 | $county = County::where('code', $countyCode)->first();
22 |
23 | foreach ($parishes as $parish) {
24 | factory(Parish::class)->create(\array_merge($parish, [
25 | 'county_id' => $county->getKey(),
26 | ]));
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/database/seeds/RoleSeeder.php:
--------------------------------------------------------------------------------
1 | Role::ADMINISTRATOR,
19 | 'title' => 'Administrator',
20 | ]);
21 |
22 | Role::create([
23 | 'name' => Role::MODERATOR,
24 | 'title' => 'Data Moderator',
25 | ]);
26 |
27 | Role::create([
28 | 'name' => Role::CONTRIBUTOR,
29 | 'title' => 'Data Contributor',
30 | ]);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/database/seeds/data/event_types.php:
--------------------------------------------------------------------------------
1 | 'Incêndio',
8 | ],
9 | [
10 | 'name' => 'Fenómeno Meteorológico Adverso',
11 | ],
12 | ];
13 |
--------------------------------------------------------------------------------
/database/seeds/data/occurrence_families.php:
--------------------------------------------------------------------------------
1 | 1000,
8 | 'name' => 'Riscos Naturais',
9 | ],
10 | [
11 | 'code' => 2000,
12 | 'name' => 'Riscos Tecnológicos',
13 | ],
14 | [
15 | 'code' => 3000,
16 | 'name' => 'Riscos Mistos',
17 | ],
18 | [
19 | 'code' => 4000,
20 | 'name' => 'Protecção e Assistência a Pessoas e Bens',
21 | ],
22 | [
23 | 'code' => 9000,
24 | 'name' => 'Operações e Estados de Alerta',
25 | ],
26 | ];
27 |
--------------------------------------------------------------------------------
/database/seeds/data/occurrence_statuses.php:
--------------------------------------------------------------------------------
1 | OccurrenceStatus::FALSE_ALERT,
10 | 'name' => 'Falso Alerta',
11 | ],
12 | [
13 | 'code' => OccurrenceStatus::SURVEILLANCE,
14 | 'name' => 'Vigilância',
15 | ],
16 | [
17 | 'code' => OccurrenceStatus::DISPATCH,
18 | 'name' => 'Despacho',
19 | ],
20 | [
21 | 'code' => OccurrenceStatus::FIRST_ALERT_DISPATCH,
22 | 'name' => 'Despacho de 1º Alerta',
23 | ],
24 | [
25 | 'code' => OccurrenceStatus::ONGOING,
26 | 'name' => 'Em Curso',
27 | ],
28 | [
29 | 'code' => OccurrenceStatus::ARRIVAL_AT_TO,
30 | 'name' => 'Chegada ao TO',
31 | ],
32 | [
33 | 'code' => OccurrenceStatus::RESOLVING,
34 | 'name' => 'Em Resolução',
35 | ],
36 | [
37 | 'code' => OccurrenceStatus::CONCLUSION,
38 | 'name' => 'Conclusão',
39 | ],
40 | [
41 | 'code' => OccurrenceStatus::CLOSED,
42 | 'name' => 'Encerrada',
43 | ],
44 | [
45 | 'code' => OccurrenceStatus::CLOSED_BY_VOST,
46 | 'name' => 'Encerrada pela VOST',
47 | ],
48 | ];
49 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.7'
2 |
3 | services:
4 | nginx:
5 | container_name: vost_nginx
6 | image: nginx:1.14.0-alpine
7 | ports:
8 | - 80:80
9 | volumes:
10 | - ./.docker/nginx/conf.d:/etc/nginx/conf.d:ro
11 | - .:/var/www/api.vost.test
12 | restart: unless-stopped
13 | networks:
14 | vost_network:
15 | aliases:
16 | - api.vost.test
17 |
18 | api:
19 | container_name: vost_api
20 | build: .docker/php
21 | expose:
22 | - 9000
23 | volumes:
24 | - .:/var/www/api.vost.test
25 | restart: unless-stopped
26 | networks:
27 | - vost_network
28 |
29 | mariadb:
30 | container_name: vost_mariadb
31 | image: mariadb:10.4.6
32 | ports:
33 | - 3306:3306
34 | environment:
35 | MYSQL_DATABASE: vost_api
36 | MYSQL_ROOT_PASSWORD: root
37 | restart: unless-stopped
38 | networks:
39 | - vost_network
40 |
41 | test_database:
42 | container_name: vost_test_database
43 | image: mariadb:10.4.6
44 | environment:
45 | MYSQL_DATABASE: vost_api
46 | MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
47 | MYSQL_ROOT_HOST: "%"
48 | restart: unless-stopped
49 | networks:
50 | - vost_network
51 |
52 | redis:
53 | container_name: vost_redis
54 | image: redis:4.0.9-alpine
55 | ports:
56 | - 6379:6379
57 | environment:
58 | - ALLOW_EMPTY_PASSWORD=yes
59 | restart: unless-stopped
60 | networks:
61 | - vost_network
62 |
63 | networks:
64 | vost_network:
65 |
--------------------------------------------------------------------------------
/pre-commit.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | EXIT_STATUS=0
4 | REGEX=".*php$"
5 |
6 | #
7 | # Coding style check per PHP file
8 | #
9 | for file in $(git diff --cached --name-only --diff-filter=ACM); do
10 | if [[ $file =~ $REGEX ]]; then
11 | composer cs-check $file
12 |
13 | EXIT_STATUS=$?
14 |
15 | if [ $EXIT_STATUS -ne 0 ]; then
16 | echo "Issues detected! To fix, execute: composer cs-fix $file"
17 |
18 | exit $EXIT_STATUS
19 | fi
20 | fi
21 | done
22 |
23 | if [ $EXIT_STATUS -eq 0 ]; then
24 | echo "All good! No coding style issues found :)"
25 | fi
26 |
27 | exit $EXIT_STATUS
28 |
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 |
2 |
3 | Options -MultiViews -Indexes
4 |
5 |
6 | RewriteEngine On
7 |
8 | # Handle Authorization Header
9 | RewriteCond %{HTTP:Authorization} .
10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
11 |
12 | # Redirect Trailing Slashes If Not A Folder...
13 | RewriteCond %{REQUEST_FILENAME} !-d
14 | RewriteCond %{REQUEST_URI} (.+)/$
15 | RewriteRule ^ %1 [L,R=301]
16 |
17 | # Handle Front Controller...
18 | RewriteCond %{REQUEST_FILENAME} !-d
19 | RewriteCond %{REQUEST_FILENAME} !-f
20 | RewriteRule ^ index.php [L]
21 |
22 |
--------------------------------------------------------------------------------
/public/documentation/acronyms/schemas.yaml:
--------------------------------------------------------------------------------
1 | Acronym:
2 | type: object
3 | properties:
4 | type:
5 | type: string
6 | default: acronyms
7 | id:
8 | type: string
9 | example: 1
10 | attributes:
11 | type: object
12 | properties:
13 | initials:
14 | type: string
15 | example: ATI
16 | meaning:
17 | type: string
18 | example: Ataque Inicial
19 | created_at:
20 | type: string
21 | format: date-time
22 | updated_at:
23 | type: string
24 | format: date-time
25 |
--------------------------------------------------------------------------------
/public/documentation/auth/schemas.yaml:
--------------------------------------------------------------------------------
1 | AccessToken:
2 | type: object
3 | properties:
4 | meta:
5 | type: object
6 | properties:
7 | access_token:
8 | type: string
9 | example: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJodHRwOlwvXC9sb2NhbGhvc3RcL3YxXC9hdXRoXC9yZWZyZXNoIiwiaWF0IjoxNTU0OTcwOTUzLCJleHAiOjE1NTQ5NzQ1NTMsIm5iZiI6MTU1NDk3MDk1MywianRpIjoiYTV1QmhSUXZXd3dYM2xWUiIsInN1YiI6MSwicHJ2IjoiN2IyOWQwZDhkMGU3ZDQ3ZjllM2FhNmQ3N2Q2NTJiNDZiMWEwNzUwOCIsImlkIjoxLCJuYW1lIjoiRGVzaGF3biIsInN1cm5hbWUiOiJCYXVjaCIsImVtYWlsIjoibW96ZWxsZS5zdHJvc2luQGV4YW1wbGUub3JnIiwicm9sZXMiOlsiYWRtaW5pc3RyYXRvciJdfQ.QmYfktTlX1sheCpZlb28KGnCClDv3Oe_FCz3rhPPXLOiwMxa2UYzkuqmXmUI9iwjXbPbSYUx8JckKWITi3kYww
10 | token_type:
11 | type: string
12 | default: bearer
13 | expires_in:
14 | type: integer
15 | example: 3600
16 |
--------------------------------------------------------------------------------
/public/documentation/common/parameters/accept.yaml:
--------------------------------------------------------------------------------
1 | name: Accept
2 | in: header
3 | schema:
4 | type: string
5 | default: application/vnd.api+json
6 |
--------------------------------------------------------------------------------
/public/documentation/common/parameters/authorization.yaml:
--------------------------------------------------------------------------------
1 | name: Authorization
2 | in: header
3 | required: true
4 | schema:
5 | type: string
6 | example: Bearer
7 |
--------------------------------------------------------------------------------
/public/documentation/common/parameters/content_type.yaml:
--------------------------------------------------------------------------------
1 | name: Content-Type
2 | in: header
3 | required: true
4 | schema:
5 | type: string
6 | default: application/vnd.api+json
7 |
--------------------------------------------------------------------------------
/public/documentation/common/parameters/exact.yaml:
--------------------------------------------------------------------------------
1 | name: exact
2 | in: query
3 | description: Enable exact match when searching
4 | schema:
5 | type: boolean
6 | default: 0
7 |
--------------------------------------------------------------------------------
/public/documentation/common/parameters/ids.yaml:
--------------------------------------------------------------------------------
1 | name: ids[]
2 | in: query
3 | description: Ids to filter by
4 | schema:
5 | type: array
6 | items:
7 | type: string
8 | example: 1
9 |
--------------------------------------------------------------------------------
/public/documentation/common/parameters/latitude.yaml:
--------------------------------------------------------------------------------
1 | name: latitude
2 | in: query
3 | description: Geolocation latitude
4 | schema:
5 | type: number
6 | format: double
7 | example: 38.166749
8 |
--------------------------------------------------------------------------------
/public/documentation/common/parameters/longitude.yaml:
--------------------------------------------------------------------------------
1 | name: longitude
2 | in: query
3 | description: Geolocation longitude
4 | schema:
5 | type: number
6 | format: double
7 | example: -7.891448
8 |
--------------------------------------------------------------------------------
/public/documentation/common/parameters/order.yaml:
--------------------------------------------------------------------------------
1 | name: order
2 | in: query
3 | description: Sorting order of the results
4 | schema:
5 | type: string
6 | enum:
7 | - asc
8 | - desc
9 | default: desc
10 |
--------------------------------------------------------------------------------
/public/documentation/common/parameters/page_number.yaml:
--------------------------------------------------------------------------------
1 | name: page[number]
2 | in: query
3 | description: Result page number
4 | schema:
5 | type: integer
6 | minimum: 1
7 | default: 1
8 |
--------------------------------------------------------------------------------
/public/documentation/common/parameters/page_size.yaml:
--------------------------------------------------------------------------------
1 | name: page[size]
2 | in: query
3 | description: Items per result page
4 | schema:
5 | type: integer
6 | minimum: 1
7 | default: 50
8 |
--------------------------------------------------------------------------------
/public/documentation/common/parameters/radius.yaml:
--------------------------------------------------------------------------------
1 | name: radius
2 | in: query
3 | description: Geolocation radius
4 | schema:
5 | type: integer
6 | minimum: 1
7 | maximum: 200
8 | default: 10
9 |
--------------------------------------------------------------------------------
/public/documentation/common/parameters/search.yaml:
--------------------------------------------------------------------------------
1 | name: search
2 | in: query
3 | description: Subject to search for
4 | schema:
5 | type: string
6 |
--------------------------------------------------------------------------------
/public/documentation/common/responses/400.yaml:
--------------------------------------------------------------------------------
1 | description: Bad Request
2 | content:
3 | application/vnd.api+json:
4 | schema:
5 | type: object
6 | properties:
7 | errors:
8 | type: array
9 | items:
10 | type: object
11 | properties:
12 | status:
13 | type: integer
14 | default: 400
15 | detail:
16 | type: string
17 |
--------------------------------------------------------------------------------
/public/documentation/common/responses/401.yaml:
--------------------------------------------------------------------------------
1 | description: Unauthorised
2 | content:
3 | application/vnd.api+json:
4 | schema:
5 | type: object
6 | properties:
7 | errors:
8 | type: array
9 | items:
10 | type: object
11 | properties:
12 | status:
13 | type: integer
14 | default: 401
15 | detail:
16 | type: string
17 |
--------------------------------------------------------------------------------
/public/documentation/common/responses/403.yaml:
--------------------------------------------------------------------------------
1 | description: Forbidden
2 | content:
3 | application/vnd.api+json:
4 | schema:
5 | type: object
6 | properties:
7 | errors:
8 | type: array
9 | items:
10 | type: object
11 | properties:
12 | status:
13 | type: integer
14 | default: 403
15 | detail:
16 | type: string
17 |
--------------------------------------------------------------------------------
/public/documentation/common/responses/404.yaml:
--------------------------------------------------------------------------------
1 | description: Not Found
2 | content:
3 | application/vnd.api+json:
4 | schema:
5 | type: object
6 | properties:
7 | errors:
8 | type: array
9 | items:
10 | type: object
11 | properties:
12 | status:
13 | type: integer
14 | default: 404
15 | detail:
16 | type: string
17 |
--------------------------------------------------------------------------------
/public/documentation/common/responses/406.yaml:
--------------------------------------------------------------------------------
1 | description: Not Acceptable
2 | content:
3 | application/vnd.api+json:
4 | schema:
5 | type: object
6 | properties:
7 | errors:
8 | type: array
9 | items:
10 | type: object
11 | properties:
12 | status:
13 | type: integer
14 | default: 406
15 | detail:
16 | type: string
17 |
--------------------------------------------------------------------------------
/public/documentation/common/responses/415.yaml:
--------------------------------------------------------------------------------
1 | description: Unsupported Media Type
2 | content:
3 | application/vnd.api+json:
4 | schema:
5 | type: object
6 | properties:
7 | errors:
8 | type: array
9 | items:
10 | type: object
11 | properties:
12 | status:
13 | type: integer
14 | default: 415
15 | detail:
16 | type: string
17 | default: Wrong media type
18 |
--------------------------------------------------------------------------------
/public/documentation/common/responses/422.yaml:
--------------------------------------------------------------------------------
1 | description: Unprocessable Entity
2 | content:
3 | application/vnd.api+json:
4 | schema:
5 | type: object
6 | properties:
7 | errors:
8 | type: array
9 | items:
10 | type: object
11 | properties:
12 | detail:
13 | type: string
14 | meta:
15 | type: object
16 | properties:
17 | field:
18 | type: string
19 |
--------------------------------------------------------------------------------
/public/documentation/common/responses/429.yaml:
--------------------------------------------------------------------------------
1 | description: Too Many Requests
2 | content:
3 | application/vnd.api+json:
4 | schema:
5 | type: object
6 | properties:
7 | errors:
8 | type: array
9 | items:
10 | type: object
11 | properties:
12 | detail:
13 | type: string
14 | meta:
15 | type: object
16 | properties:
17 | field:
18 | type: string
19 |
--------------------------------------------------------------------------------
/public/documentation/districts/schemas.yaml:
--------------------------------------------------------------------------------
1 | District:
2 | type: object
3 | properties:
4 | type:
5 | type: string
6 | default: districts
7 | id:
8 | type: string
9 | example: 1
10 | attributes:
11 | type: object
12 | properties:
13 | code:
14 | type: integer
15 | example: 080000
16 | name:
17 | type: string
18 | example: Faro
19 | created_at:
20 | type: string
21 | format: date-time
22 | updated_at:
23 | type: string
24 | format: date-time
25 | links:
26 | type: object
27 | properties:
28 | self:
29 | type: string
30 | description: Link to this page
31 |
--------------------------------------------------------------------------------
/public/documentation/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | VOST Portugal API
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/public/documentation/ipma/endpoints.yaml:
--------------------------------------------------------------------------------
1 | index:
2 | get:
3 | tags:
4 | - Warnings
5 | summary: Index warnings
6 | description: Index the available warnings.
7 | operationId: warnings::index
8 | parameters:
9 | - $ref: '../common/parameters/content_type.yaml'
10 | - $ref: '../common/parameters/accept.yaml'
11 |
12 | responses:
13 | 200:
14 | description: OK
15 | content:
16 | application/vnd.api+json:
17 | schema:
18 | type: object
19 | properties:
20 | data:
21 | type: array
22 | items:
23 | $ref: 'schemas.yaml#/WarningWithRelations'
24 | included:
25 | type: array
26 | items:
27 | - $ref: '../counties/schemas.yaml#/CountyWithRelations'
28 | - $ref: '../districts/schemas.yaml#/District'
29 | 406:
30 | $ref: '../common/responses/406.yaml'
31 | 415:
32 | $ref: '../common/responses/415.yaml'
33 |
--------------------------------------------------------------------------------
/public/documentation/ipma/schemas.yaml:
--------------------------------------------------------------------------------
1 | WarningWithRelations:
2 | type: object
3 | properties:
4 | type:
5 | type: string
6 | default: warnings
7 | id:
8 | type: string
9 | format: uuid
10 | example: 8b51f8c2-31bd-49fc-9beb-93a2e2fcb894
11 | attributes:
12 | type: object
13 | properties:
14 | description:
15 | type: string
16 | example: Rajadas até 75 km/h, em especial no litoral e nas terras altas.
17 | awareness_name:
18 | type: string
19 | example: Vento
20 | awareness_level:
21 | type: string
22 | example: yellow
23 | started_at:
24 | type: string
25 | format: date-time
26 | ended_at:
27 | type: string
28 | format: date-time
29 | relationships:
30 | type: object
31 | properties:
32 | county:
33 | type: object
34 | properties:
35 | data:
36 | type: object
37 | properties:
38 | type:
39 | type: string
40 | default: counties
41 | id:
42 | type: string
43 | example: 1
44 |
--------------------------------------------------------------------------------
/public/documentation/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vostpt/api/7acb11a53c58174fb007094a04b32dfaf2b26e3a/public/documentation/logo.png
--------------------------------------------------------------------------------
/public/documentation/roles/schemas.yaml:
--------------------------------------------------------------------------------
1 | Role:
2 | type: object
3 | properties:
4 | type:
5 | type: string
6 | default: roles
7 | id:
8 | type: string
9 | example: 1
10 | attributes:
11 | type: object
12 | properties:
13 | name:
14 | type: string
15 | example: contributor
16 | title:
17 | type: string
18 | example: Data Contributor
19 | created_at:
20 | type: string
21 | format: date-time
22 | updated_at:
23 | type: string
24 | format: date-time
25 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vostpt/api/7acb11a53c58174fb007094a04b32dfaf2b26e3a/public/favicon.ico
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/public/web.config:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/resources/lang/en/auth.php:
--------------------------------------------------------------------------------
1 | 'These credentials do not match our records.',
18 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
19 |
20 | ];
21 |
--------------------------------------------------------------------------------
/resources/lang/en/pagination.php:
--------------------------------------------------------------------------------
1 | '« Previous',
18 | 'next' => 'Next »',
19 |
20 | ];
21 |
--------------------------------------------------------------------------------
/resources/lang/en/passwords.php:
--------------------------------------------------------------------------------
1 | 'Passwords must be at least eight characters and match the confirmation.',
18 | 'reset' => 'Your password has been reset!',
19 | 'sent' => 'We have e-mailed your password reset link!',
20 | 'token' => 'This password reset token is invalid.',
21 | 'user' => "We can't find a user with that e-mail address.",
22 |
23 | ];
24 |
--------------------------------------------------------------------------------
/routes/api/acronyms.php:
--------------------------------------------------------------------------------
1 | name('acronyms::')->group(static function (): void {
16 | Route::get('/', [
17 | AcronymController::class,
18 | 'index',
19 | ])->name('index');
20 |
21 | Route::post('/', [
22 | AcronymController::class,
23 | 'create',
24 | ])->name('create')->middleware('jwt-auth');
25 |
26 | Route::get('/{Acronym}', [
27 | AcronymController::class,
28 | 'view',
29 | ])->name('view');
30 |
31 | Route::patch('/{Acronym}', [
32 | AcronymController::class,
33 | 'update',
34 | ])->name('update')->middleware('jwt-auth');
35 |
36 | Route::delete('/{Acronym}', [
37 | AcronymController::class,
38 | 'delete',
39 | ])->name('delete')->middleware('jwt-auth');
40 | });
41 |
--------------------------------------------------------------------------------
/routes/api/auth.php:
--------------------------------------------------------------------------------
1 | name('auth::')->group(static function (): void {
16 | Route::post('/', [
17 | AuthController::class,
18 | 'authenticate',
19 | ])->name('authenticate');
20 |
21 | Route::get('/verify', [
22 | AuthController::class,
23 | 'verify',
24 | ])->name('verify')->middleware('jwt-auth');
25 |
26 | Route::get('/refresh', [
27 | AuthController::class,
28 | 'refresh',
29 | ])->name('refresh')->middleware('jwt-auth');
30 | });
31 |
--------------------------------------------------------------------------------
/routes/api/counties.php:
--------------------------------------------------------------------------------
1 | name('counties::')->group(static function (): void {
16 | Route::get('/', [
17 | CountyController::class,
18 | 'index',
19 | ])->name('index');
20 |
21 | Route::get('/{County}', [
22 | CountyController::class,
23 | 'view',
24 | ])->name('view');
25 | });
26 |
--------------------------------------------------------------------------------
/routes/api/districts.php:
--------------------------------------------------------------------------------
1 | name('districts::')->group(static function (): void {
16 | Route::get('/', [
17 | DistrictController::class,
18 | 'index',
19 | ])->name('index');
20 |
21 | Route::get('/{District}', [
22 | DistrictController::class,
23 | 'view',
24 | ])->name('view');
25 | });
26 |
--------------------------------------------------------------------------------
/routes/api/events.php:
--------------------------------------------------------------------------------
1 | name('events::')->group(static function (): void {
16 | Route::get('/', [
17 | EventController::class,
18 | 'index',
19 | ])->name('index');
20 |
21 | Route::post('/', [
22 | EventController::class,
23 | 'create',
24 | ])->name('create')->middleware('jwt-auth');
25 |
26 | Route::get('/{Event}', [
27 | EventController::class,
28 | 'view',
29 | ])->name('view');
30 |
31 | Route::patch('/{Event}', [
32 | EventController::class,
33 | 'update',
34 | ])->name('update')->middleware('jwt-auth');
35 |
36 | Route::delete('/{Event}', [
37 | EventController::class,
38 | 'delete',
39 | ])->name('delete')->middleware('jwt-auth');
40 | });
41 |
--------------------------------------------------------------------------------
/routes/api/ipma.php:
--------------------------------------------------------------------------------
1 | name('ipma::')->group(static function (): void {
16 | Route::get('/warnings', [
17 | IpmaWarningController::class,
18 | 'index',
19 | ])->name('warnings::index');
20 | });
21 |
--------------------------------------------------------------------------------
/routes/api/parishes.php:
--------------------------------------------------------------------------------
1 | name('parishes::')->group(static function (): void {
16 | Route::get('/', [
17 | ParishController::class,
18 | 'index',
19 | ])->name('index');
20 |
21 | Route::get('/{Parish}', [
22 | ParishController::class,
23 | 'view',
24 | ])->name('view');
25 | });
26 |
--------------------------------------------------------------------------------
/routes/api/weather.php:
--------------------------------------------------------------------------------
1 | name('weather::')->group(static function (): void {
16 | Route::prefix('observations')->name('observations::')->group(static function (): void {
17 | Route::get('/', [
18 | WeatherObservationController::class,
19 | 'index',
20 | ])->name('index');
21 |
22 | Route::get('/{WeatherObservation}', [
23 | WeatherObservationController::class,
24 | 'view',
25 | ])->name('view');
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/routes/console.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | $uri = urldecode(
11 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
12 | );
13 |
14 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the
15 | // built-in PHP web server. This provides a convenient way to test a Laravel
16 | // application without having installed a "real" web server software here.
17 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) {
18 | return false;
19 | }
20 |
21 | require_once __DIR__.'/public/index.php';
22 |
--------------------------------------------------------------------------------
/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !public/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/storage/app/public/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/.gitignore:
--------------------------------------------------------------------------------
1 | config.php
2 | routes.php
3 | schedule-*
4 | compiled.php
5 | services.json
6 | events.scanned.php
7 | routes.scanned.php
8 | down
9 |
--------------------------------------------------------------------------------
/storage/framework/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !data/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/storage/framework/cache/data/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/sessions/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/testing/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/views/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/tests/Integration/Commands/Api/ResponseCacheBustCommandTest.php:
--------------------------------------------------------------------------------
1 | assertFalse(Cache::has('tags_for_cache_busting'));
23 |
24 | $this->artisan('api:bust:response-cache');
25 |
26 | $this->assertFalse(Cache::has('tags_for_cache_busting'));
27 | }
28 |
29 | /**
30 | * @test
31 | */
32 | public function itSuccessfullyBustsResponseCache(): void
33 | {
34 | $this->assertFalse(Cache::has('tags_for_cache_busting'));
35 |
36 | add_cache_bust_tag('foo');
37 |
38 | $this->assertTrue(Cache::has('tags_for_cache_busting'));
39 |
40 | $this->artisan('api:bust:response-cache');
41 |
42 | $this->assertFalse(Cache::has('tags_for_cache_busting'));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/Integration/Controllers/FallbackController/DocumentationEndpointTest.php:
--------------------------------------------------------------------------------
1 | json('GET', Str::random());
18 |
19 | $response->assertStatus(302);
20 | $response->assertRedirect('/documentation');
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/Integration/CreatesApplication.php:
--------------------------------------------------------------------------------
1 | make(Kernel::class)->bootstrap();
21 |
22 | // Clear the cache
23 | $app['cache.store']->flush();
24 |
25 | return $app;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/Integration/HttpClientMocker.php:
--------------------------------------------------------------------------------
1 | addResponse($response);
25 | }
26 |
27 | return $httpClient;
28 | }
29 |
30 | /**
31 | * @param string $path
32 | * @param int $status
33 | * @param array $headers
34 | *
35 | * @return ResponseInterface
36 | */
37 | protected function createHttpResponse(string $path = null, int $status = 200, array $headers = []): ResponseInterface
38 | {
39 | return new Response($status, $headers, $path ? \file_get_contents(base_path($path)) : null);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tests/Integration/RefreshDatabaseWithRoles.php:
--------------------------------------------------------------------------------
1 | artisan('db:seed', [
19 | '--class' => 'RoleSeeder',
20 | ]);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/Integration/TestCase.php:
--------------------------------------------------------------------------------
1 | 'application/vnd.api+json;charset=utf-8',
13 | ];
14 |
15 | protected const INVALID_CONTENT_TYPE_HEADER = [
16 | 'Content-Type' => 'application/vnd.api+json;charset=utf-8',
17 | ];
18 |
19 | protected const VALID_CONTENT_TYPE_HEADER = [
20 | 'Content-Type' => 'application/vnd.api+json',
21 | ];
22 |
23 | /**
24 | * {@inheritDoc}
25 | */
26 | protected function setUp(): void
27 | {
28 | parent::setUp();
29 |
30 | // Make sure tests don't get throttled when hitting the endpoints
31 | $this->withoutMiddleware(\Illuminate\Routing\Middleware\ThrottleRequests::class);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tests/Unit/TestCase.php:
--------------------------------------------------------------------------------
1 |