From b45a66fb0c5e28ab07f0c1366f6e917992ef8fc8 Mon Sep 17 00:00:00 2001 From: Nik Gupta Date: Tue, 9 Jul 2024 17:18:23 +0100 Subject: [PATCH] [TfL] Add passthrough integration for Atlas backend. --- conf/council-tfl.yml-example | 8 +++ .../Endpoint/Integration/Passthrough.pm | 13 +++- .../Open311/Endpoint/Integration/UK/TfL.pm | 67 +++++++++++++++++++ 3 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 conf/council-tfl.yml-example create mode 100644 perllib/Open311/Endpoint/Integration/UK/TfL.pm diff --git a/conf/council-tfl.yml-example b/conf/council-tfl.yml-example new file mode 100644 index 000000000..dd274a9a1 --- /dev/null +++ b/conf/council-tfl.yml-example @@ -0,0 +1,8 @@ +endpoint: https://localhost:4000/ +api_key: 123 + +oauth2_url: https://localhost:5000/ +oauth2_client_id: client_id +oauth2_client_secret: client_secret +oauth2_tenant_id: tenant_id +ocp_apim_subscription_key: subscription_key diff --git a/perllib/Open311/Endpoint/Integration/Passthrough.pm b/perllib/Open311/Endpoint/Integration/Passthrough.pm index 2b40241a4..bcc0ae19a 100644 --- a/perllib/Open311/Endpoint/Integration/Passthrough.pm +++ b/perllib/Open311/Endpoint/Integration/Passthrough.pm @@ -102,13 +102,14 @@ the function to die. sub _request { my ($self, $method, $url, $params) = @_; $url = URI->new($self->endpoint . $url); + my $headers = $self->_headers(); my $resp; if ($method eq 'POST') { $params->{api_key} = $self->api_key; - $resp = $self->ua->post($url, $params); + $resp = $self->ua->post($url, %$headers, Content => $params); } else { $url->query_form(%$params); - $resp = $self->ua->get($url); + $resp = $self->ua->get($url, %$headers); } my $content = $resp->decoded_content; my $xml = $self->pt_xml->XMLin(\$content); @@ -116,6 +117,14 @@ sub _request { return $xml; } +=head2 _headers + +This can be overriden to return headers used by C<_request>. + +=cut + +sub _headers { {} } + =head2 services Transparently passed through to the backend to fetch a list of services. diff --git a/perllib/Open311/Endpoint/Integration/UK/TfL.pm b/perllib/Open311/Endpoint/Integration/UK/TfL.pm new file mode 100644 index 000000000..c51e42561 --- /dev/null +++ b/perllib/Open311/Endpoint/Integration/UK/TfL.pm @@ -0,0 +1,67 @@ +=head1 NAME + +Open311::Endpoint::Integration::UK::TfL - Tfl Atlas backend. + +=head1 SUMMARY + +This is a TfL-specific Passthrough intergration for their Atlas backend. +It is a standard Open311 server apart from it passes a bearer token +fetched from their OAuth2 API and a 'Ocp-Apim-Subscription-Key' header. + +=cut + +package Open311::Endpoint::Integration::UK::TfL; + +use HTTP::Request::Common; +use JSON::MaybeXS; +use LWP::UserAgent; +use Moo; +extends 'Open311::Endpoint::Integration::Passthrough'; + +around BUILDARGS => sub { + my ($orig, $class, %args) = @_; + $args{jurisdiction_id} = 'tfl'; + return $class->$orig(%args); +}; + +has oauth2_url => ( is => 'ro' ); +has oauth2_client_id => ( is => 'ro' ); +has oauth2_client_secret => ( is => 'ro' ); +has oauth2_tenant_id => ( is => 'ro' ); +has ocp_apim_subscription_key => ( is => 'ro' ); + +has oauth2_token => ( + is => 'lazy', + default => sub { + my $self = shift; + + my $token = $self->memcache->get('tfl_atlas_oauth2_token'); + unless ($token) { + my $url = $self->oauth2_url . $self->oauth2_tenant_id . "/oauth2/token"; + my $req = POST $url, [ + grant_type => "client_credentials", + client_id => $self->oauth2_client_id, + client_secret => $self->oauth2_client_secret, + ]; + my $response = $self->ua->request($req); + unless ($response->is_success) { + $self->logger->warn("Getting OAuth2 token failed: $url"); + return; + } + my $content = decode_json($response->content); + $token = $content->{access_token}; + $self->memcache->set('tfl_atlas_oauth2_token', $token, time() + $content->{expires_in} - 10); + } + return $token; + }, +); + +sub _headers { + my $self = shift; + return { + 'Ocp-Apim-Subscription-Key' => $self->ocp_apim_subscription_key, + 'Authorization' => 'Bearer ' . $self->oauth2_token, + }; +} + +1;