Skip to content

Commit

Permalink
Merge branch 'alloy-avoid-parent-calls' into staging
Browse files Browse the repository at this point in the history
  • Loading branch information
neprune committed Feb 8, 2025
2 parents 74b12fe + 0f2b015 commit 420fc31
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 66 deletions.
11 changes: 10 additions & 1 deletion perllib/Integrations/AlloyV2.pm
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ sub design_attributes_to_hash {
sub search {
my ($self, $body_base, $skip_count) = @_;

my $stats = { result => 1 };
my $stats = { results => [{ value => { value => 1 } } ] };
unless ($skip_count) {
my $stats_body = { %$body_base };
$stats_body->{type} = 'MathAggregation';
Expand Down Expand Up @@ -217,7 +217,12 @@ sub search {
for my $jr ( @$join_results ) {
my $item_id = $jr->{itemId};

my @joined_item_ids;

for my $jq ( @{ $jr->{joinQueries} } ) {

my $joined_item_id = $jq->{item}->{itemId};
push @joined_item_ids, $joined_item_id;
# Make sure attribute code is unique.
# E.g. Attribute code may originally be something like
# 'attributes_itemsTitle' but this doesn't make it clear
Expand All @@ -230,7 +235,11 @@ sub search {

# Append to top-level attribute list
push @{ $id_to_res{$item_id}{attributes} }, $attr;

}

# Push the IDs of the joined items to a top-level field.
$id_to_res{$item_id}{joinedItemIDs} = \@joined_item_ids;
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions perllib/Open311/Endpoint/Integration/AlloyV2.pm
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,10 @@ It fetches the item's parents to find any that have a C<rfs_design> designCode,
assuming that's the 'inspection' associated with the 'defect' (or indeed, the
defect associated with the job).
If C<use_joins_rather_than_parent_calls> is set, C<inspection_to_defect_attribute_link>
and C<inspection_specific_attribute> will be used in a join to get C<rfs_design> parents
in the query for updated resources, rather than querying the parents of each defect separately.
It uses the C<defect_attribute_mapping> status entry to find the attribute
containing the status, and then uses C<defect_status_mapping> to map that
Alloy value to a status.
Expand Down Expand Up @@ -859,6 +863,10 @@ fetch any new reports in those designs. As with updates, it fetches any
resources updated in the time window. It ignores any in C<ignored_defect_types>
or any with a C<rfs_design> parent.
If C<use_joins_rather_than_parent_calls> is set, C<inspection_to_defect_attribute_link>
and C<inspection_specific_attribute> will be used in a join to get C<rfs_design> parents
in the query for updated resources, rather than querying the parents of each defect separately.
It uses C<defect_sourcetype_category_mapping> on the designCode to fetch a
default and a possible types mapping, which it uses to set a category if it
finds an attribute with a particular fixed list of matches. It also might
Expand Down Expand Up @@ -995,6 +1003,12 @@ sub fetch_updated_resources {
}]
};

if ($self->config->{use_joins_rather_than_parent_calls}) {
my $inspection_to_defect_attribute_link = $self->config->{inspection_to_defect_attribute_link};
my $inspection_specific_attribute = $self->config->{inspection_specific_attribute};
$body_base->{joinAttributes} = ["root^$inspection_to_defect_attribute_link.$inspection_specific_attribute"]
}

return $self->alloy->search( $body_base );
}

Expand Down Expand Up @@ -1204,6 +1218,12 @@ sub _get_defect_inspection {
sub _get_defect_inspection_parents {
my ($self, $defect) = @_;

if ($self->config->{use_joins_rather_than_parent_calls}) {
# We should have already fetched the parents by joining in the initial query
# so we can return these IDs.
return @{ $defect->{joinedItemIDs} // [] };
}

my $parents = $self->alloy->api_call(call => "item/$defect->{itemId}/parents")->{results};
my @linked_defects;
foreach (@$parents) {
Expand Down
64 changes: 64 additions & 0 deletions t/integrations/alloyv2.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package Integrations::AlloyV2::Dummy;
use Moo;

extends 'Integrations::AlloyV2';
sub _build_config_file { path(__FILE__)->sibling("alloyv2.yml")->stringify }

package main;

use strict;
use warnings;
use utf8;

use Test::More;
use Test::MockModule;
use Encode;
use JSON::MaybeXS;
use Path::Tiny;

my $integration = Integrations::AlloyV2::Dummy->new;
my $mocked_integration = Test::MockModule->new('Integrations::AlloyV2');
$mocked_integration->mock('api_call', sub {
my ($self, %args) = @_;
my $call = $args{call};
if ($call =~ 'aqs/join') {
return decode_json(encode_utf8(path(__FILE__)->sibling('json/alloyv2/join_response.json')->slurp));
}
fail "Got unexpected call: $call";
});

subtest "Join results are parsed correctly" => sub {
my $query = {
properties => {
joinAttributes => [ 'root^parent_attribute.child_attribute' ]
}
};
my $res = $integration->search($query, 1);
is_deeply $res, [
{
'attributes' => [
{
'attributeCode' => 'normal_attribute',
'value' => 'n1'
},
{
'attributeCode' => 'root.parent_attribute.child_attribute',
'value' => 'c1'
}
],
'itemId' => 'r1',
'joinedItemIDs' => ['p1']
},
{
'itemId' => 'r2',
'attributes' => [
{
'value' => 'n2',
'attributeCode' => 'normal_attribute'
}
]
}
];
};

done_testing;
6 changes: 6 additions & 0 deletions t/integrations/alloyv2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"api_key": "api_key",
"api_url": "http://localhost/api/",
"rfs_design": 'rfs_design',
"parent_attribute_name": "parent_attribute_name"
}
43 changes: 43 additions & 0 deletions t/integrations/json/alloyv2/join_response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"joinResults": [
{
"itemId": "r1",
"joinQueries": [
{
"item": {
"itemId": "p1",
"attributes": [
{
"attributeCode": "child_attribute",
"value": "c1"
}
]
},
"joinAttributes": [
"root.parent_attribute.child_attribute"
]
}
]
}
],
"results": [
{
"itemId": "r1",
"attributes": [
{
"attributeCode": "normal_attribute",
"value": "n1"
}
]
},
{
"itemId": "r2",
"attributes": [
{
"attributeCode": "normal_attribute",
"value": "n2"
}
]
}
]
}
133 changes: 68 additions & 65 deletions t/open311/endpoint/alloyv2.t
Original file line number Diff line number Diff line change
Expand Up @@ -492,73 +492,76 @@ subtest "create problem with no resource_id" => sub {
restore_time;
};

subtest "check fetch updates" => sub {
my $res = $endpoint->run_test_request(
GET => '/servicerequestupdates.json?jurisdiction_id=dummy&start_date=2019-01-01T00:00:00Z&end_date=2019-03-01T02:00:00Z',
);
for my $use_joins_rather_than_parent_calls (1, 0) {
subtest "check fetch updates (using join: $use_joins_rather_than_parent_calls)" => sub {
$endpoint->config->{use_joins_rather_than_parent_calls} = $use_joins_rather_than_parent_calls;
my $res = $endpoint->run_test_request(
GET => '/servicerequestupdates.json?jurisdiction_id=dummy&start_date=2019-01-01T00:00:00Z&end_date=2019-03-01T02:00:00Z',
);

my $sent = pop @sent;
ok $res->is_success, 'valid request'
or diag $res->content;
my $sent = pop @sent;
ok $res->is_success, 'valid request'
or diag $res->content;

is_deeply decode_json($res->content),
[ {
status => 'investigating',
service_request_id => '3027029',
description => 'This is an updated customer response',
updated_datetime => '2019-01-01T00:32:40Z',
update_id => '3027029_20190101003240',
media_url => '',
extras => { latest_data_only => 1 },
},
{
status => 'investigating',
service_request_id => '3027030',
description => '',
updated_datetime => '2019-01-01T01:42:40Z',
update_id => '3027030_20190101014240',
media_url => '',
extras => { latest_data_only => 1 },
},
{
status => 'not_councils_responsibility',
service_request_id => '3027031',
description => '',
updated_datetime => '2019-01-01T01:43:40Z',
update_id => '3027031_20190101014340',
media_url => '',
external_status_code => '01b51bb5c0de101a004154b5',
extras => { latest_data_only => 1 },
},
{
status => 'action_scheduled',
service_request_id => '3027032',
description => '',
updated_datetime => '2019-01-01T01:48:13Z',
update_id => '4947501_20190101014813',
media_url => '',
extras => { latest_data_only => 1 },
},
{
status => 'investigating',
service_request_id => '3027034',
description => '',
updated_datetime => '2019-01-01T01:49:13Z',
update_id => '3027034_20190101014913',
media_url => '',
extras => { latest_data_only => 1 },
},
{
status => 'open',
service_request_id => '4947502',
description => '',
updated_datetime => '2019-01-01T01:51:08Z',
update_id => '4947502_20190101015108',
media_url => '',
extras => { latest_data_only => 1 },
}
], 'correct json returned';
};
is_deeply decode_json($res->content),
[ {
status => 'investigating',
service_request_id => '3027029',
description => 'This is an updated customer response',
updated_datetime => '2019-01-01T00:32:40Z',
update_id => '3027029_20190101003240',
media_url => '',
extras => { latest_data_only => 1 },
},
{
status => 'investigating',
service_request_id => '3027030',
description => '',
updated_datetime => '2019-01-01T01:42:40Z',
update_id => '3027030_20190101014240',
media_url => '',
extras => { latest_data_only => 1 },
},
{
status => 'not_councils_responsibility',
service_request_id => '3027031',
description => '',
updated_datetime => '2019-01-01T01:43:40Z',
update_id => '3027031_20190101014340',
media_url => '',
external_status_code => '01b51bb5c0de101a004154b5',
extras => { latest_data_only => 1 },
},
{
status => 'action_scheduled',
service_request_id => '3027032',
description => '',
updated_datetime => '2019-01-01T01:48:13Z',
update_id => '4947501_20190101014813',
media_url => '',
extras => { latest_data_only => 1 },
},
{
status => 'investigating',
service_request_id => '3027034',
description => '',
updated_datetime => '2019-01-01T01:49:13Z',
update_id => '3027034_20190101014913',
media_url => '',
extras => { latest_data_only => 1 },
},
{
status => 'open',
service_request_id => '4947502',
description => '',
updated_datetime => '2019-01-01T01:51:08Z',
update_id => '4947502_20190101015108',
media_url => '',
extras => { latest_data_only => 1 },
}
], 'correct json returned';
};
}

subtest "check fetch updates with cobrand skipping update where job has unchanged parent defect" => sub {
my $res = $oxfordshire_endpoint->run_test_request(
Expand Down
4 changes: 4 additions & 0 deletions t/open311/endpoint/alloyv2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
"irg_config_code": "ASSET_INSPECTIONS",
"resource_attachment_attribute_id": "attributes_filesAttachableAttachments",

"use_joins_rather_than_parent_calls": 0,
"inspection_to_defect_attribute_link": "defect_to_inspection_link_attribute",
"inspection_specific_attribute": "inspection_only_attribute",

"category_list_code": "designs_listFixMyStreetCategories1001257_5d3210e1fe2ad806f8df98c1",
"category_title_attribute": "title",
# List of categories/subcategories and parent attribute id
Expand Down
40 changes: 40 additions & 0 deletions t/open311/endpoint/json/alloyv2/defect_search.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,46 @@
{
"page": 1,
"pageSize": 20,
"joinResults": [
{
"itemId": "4947501",
"joinQueries": [
{
"item": {
"itemId": "3027032"
},
"attributes": [
{
"attributeCode": "inspection_only_attribute",
"value": "doesn't matter"
}
]
}
],
"joinAttributes": [
"root.defect_to_inspection_link_attribute.inspection_only_attribute"
]
},
{
"itemId": "0203824fc0de101a008fbb4f",
"joinQueries": [
{
"item": {
"itemId": "irrelevant"
},
"attributes": [
{
"attributeCode": "inspection_only_attribute",
"value": "doesn't matter"
}
]
}
],
"joinAttributes": [
"root.defect_to_inspection_link_attribute.inspection_only_attribute"
]
}
],
"results": [
{
"itemId": "4947501",
Expand Down

0 comments on commit 420fc31

Please sign in to comment.