Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.2.1
current_version = 0.2.2
commit = True
tag = False

Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## [0.2.2] - 2024-06-27
### Changed
- Schema.nodes resolver now handles all InversionSolution types
- AutomationTask.inversion_solution resolver now handles all InversionSolution types

## [0.2.1] - 2024-06-20

### Changed
Expand Down
2 changes: 1 addition & 1 deletion graphql_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

__author__ = 'GNS Science'
__email__ = 'nshm@gns.cri.nz'
__version__ = '0.2.1'
__version__ = '0.2.2'
24 changes: 19 additions & 5 deletions graphql_api/schema/custom/automation_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ class Meta:
model_type = ModelType()
task_type = TaskSubType()
inversion_solution = graphene.Field(
'graphql_api.schema.custom.inversion_solution.InversionSolution',
description="the primary result of this task (only for task_type == INVERSION.",
'graphql_api.schema.custom.inversion_solution_union.InversionSolutionUnion',
description="the result of this task. NB only available for task_types:"
"INVERSION, SCALE_SOLUTION, AGGREGATE_SOLUTION, TIME_DEPENDENT_SOLUTION.",
)

@staticmethod
Expand All @@ -56,16 +57,27 @@ def from_json(jsondata):

@staticmethod
def resolve_inversion_solution(root, info, **args):

log.info(f"resolve_inversion_solution {root.task_type}")
resolvable_types = [
TaskSubType.INVERSION.value,
TaskSubType.SCALE_SOLUTION.value,
TaskSubType.AGGREGATE_SOLUTION.value,
TaskSubType.TIME_DEPENDENT_SOLUTION.value,
]

if not len(root.files):
return
if not root.task_type == TaskSubType.INVERSION.value:
if root.task_type not in resolvable_types:
log.info(f"Cannot resove inversion_soluton for {root.task_type}")
return

t0 = dt.utcnow()
res = None

# TODO this is an ugly hack....
# - It gets the inversion solution by traversing the file_relations until it finds an InversionSolution.
# - It gets the inversion solution by traversing the file_relations until it finds
# an InversionSolution subtype.
# - Instead this attribute needs to be a first-class one-to-one relationship
for file_id in root.files:
if isinstance(file_id, dict): # new form, files is list of objects
Expand All @@ -79,9 +91,11 @@ def resolve_inversion_solution(root, info, **args):
if not file_relation.role == FileRole.WRITE.value:
continue
file = get_data_manager().file.get_one(file_relation.file_id)
if file.__class__.__name__ == 'InversionSolution':
if 'InversionSolution' in file.__class__.__name__:
res = file
log.info(f"resolved inversion_solution file {file}")
break

db_metrics.put_duration(__name__, 'AutomationTask.resolve_inversion_solution', dt.utcnow() - t0)
return res

Expand Down
17 changes: 17 additions & 0 deletions graphql_api/schema/custom/inversion_solution_union.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# !inversion_solution_union.py
import graphene

from .aggregate_inversion_solution import AggregateInversionSolution
from .inversion_solution import InversionSolution
from .scaled_inversion_solution import ScaledInversionSolution
from .time_dependent_inversion_solution import TimeDependentInversionSolution


class InversionSolutionUnion(graphene.Union):
class Meta:
types = (
InversionSolution,
ScaledInversionSolution,
AggregateInversionSolution,
TimeDependentInversionSolution,
)
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ def test_task_product_query(self, mocked_api_DB):
id
created
inversion_solution {
id
file_name
... on Node { id }
... on FileInterface { file_name }
}
files {
total_count
Expand Down Expand Up @@ -183,8 +183,8 @@ def test_example_failing_product_query(self, mocked_api_DB):
id
created
inversion_solution {
id
file_name
... on Node { id }
... on FileInterface { file_name }
}
files {
total_count
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ def test_task_product_query(self, mocked_api):
id
created
inversion_solution {
id
file_name
... on Node { id }
... on FileInterface { file_name }
}
files {
total_count
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
"""
Test API function for GeneralTask
using moto mocking re issue #223
"""

import datetime as dt
import unittest

import boto3
from dateutil.tz import tzutc
from graphene.test import Client
from graphql_relay import from_global_id
from moto import mock_dynamodb, mock_s3
from pynamodb.connection.base import Connection # for mocking

from graphql_api.config import REGION, S3_BUCKET_NAME
from graphql_api.data import data_manager
from graphql_api.dynamodb.models import ToshiFileObject, ToshiIdentity, ToshiThingObject
from graphql_api.schema import root_schema
from graphql_api.schema.search_manager import SearchManager

from .hazard.setup_helpers import SetupHelpersMixin


@mock_dynamodb
@mock_s3
class TestScaledInversionSolution(unittest.TestCase, SetupHelpersMixin):
def setUp(self):
self.client = Client(root_schema)

# S3
self._s3 = boto3.resource('s3', region_name=REGION)
self._s3.create_bucket(Bucket=S3_BUCKET_NAME)

# Dynamo
self._connection = Connection(region=REGION)

ToshiThingObject.create_table()
ToshiFileObject.create_table()
ToshiIdentity.create_table()

self._data_manager = data_manager.DataManager(search_manager=SearchManager('test', 'test', {'fake': 'auth'}))

upstream_sid = self.create_source_solution()
self.new_gt = self.create_general_task()
self.at_id = self.create_automation_task("SCALE_SOLUTION")
self.create_gt_relation(self.new_gt, self.at_id)

result = self.create_scaled_solution(upstream_sid, self.at_id)

ss = result['data']['create_scaled_inversion_solution']['solution']
self.scaled_solution_id = ss['id']

# def create_task_file(self, task_id, file_id, role):
qry2 = '''
mutation create_file_relation(
$thing_id:ID!
$file_id:ID!
$role:FileRole!) {
create_file_relation(
file_id:$file_id
thing_id:$thing_id
role:$role
)
{
ok
}
}'''
variables = dict(thing_id=self.at_id, file_id=self.at_id, role='WRITE')
executed = self.client.execute(qry2, variable_values=variables)
print('created file relation', executed)

def test_general_task_query(self):
print("self.new_gt", self.new_gt)

qry = '''
query GeneralTaskChildrenTabQuery($id: ID!) {
node(id: $id) {
... on GeneralTask {
id
model_type
children {
edges {
node {
child {
__typename
... on Node {
id
}
...on AutomationTask {
task_type
inversion_solution {
__typename
... on Node {
id
}
}
}
... on AutomationTaskInterface {
state
result
created
duration
arguments {
k
v
}
}
}
}
}
}
}
}
}

'''

print(qry)
executed = self.client.execute(qry, variable_values=dict(id=self.new_gt))
print(executed)

node = executed['data']['node']
assert node['id'] == self.new_gt
assert node['children']['edges'][0]['node']['child']['__typename'] == "AutomationTask"
assert node['children']['edges'][0]['node']['child']['inversion_solution']['__typename']
assert node['children']['edges'][0]['node']['child']['inversion_solution']['id'] == self.scaled_solution_id
assert (
node['children']['edges'][0]['node']['child']['inversion_solution']['__typename']
== "ScaledInversionSolution"
)
20 changes: 12 additions & 8 deletions graphql_api/tests/test_inversion_solution_bug_93.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,15 +148,19 @@ def test_query_with_files(self, mocked_read_object, mocked_get_one):
task_type
id
inversion_solution {
id
file_name
meta {
k
v
... on Node { id }
... on FileInterface {
file_name
meta {
k
v
}
}
tables {
table_id
table_type
... on InversionSolutionInterface {
tables {
table_id
table_type
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nzshm22-toshi-api",
"version": "0.2.1",
"version": "0.2.2",
"description": "A graphql API for NZSHM22 shared resources using AWS lambda, S3, Dynamodb and ElasticSearch",
"directories": {
"lib": "lib"
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "nshm-toshi-api"
version = "0.2.1"
version = "0.2.2"
description = "the object store used by NZSHM project"
authors = ["Chris Chamberlain <chrisbc@artisan.co.nz>"]
license = "AGPL3"
Expand Down