diff --git a/README.md b/README.md index f527cf4da..abe044870 100644 --- a/README.md +++ b/README.md @@ -791,6 +791,71 @@ variables: } } +#### Publish Generated Collection Variable Drafts + +For all supported arguments and columns, see [the schema](src/types/variable.graphql). + +CMR-GraphQL queries an earthdata-varinfo lambda in order to generate and publish collection variable drafts. The resulting variables can be returned as part of the Variable type response. + +`publishVariableDrafts` will return collection generated variables, using the earthdata-varinfo project(https://github.com/nasa/earthdata-varinfo) + +##### Example Mutation + + mutation PublishGeneratedVariables($conceptId: String!) { + publishGeneratedVariables(conceptId: $conceptId) { + count + items { + conceptId + dataType + definition + dimensions + longName + name + standardName + units + metadataSpecification + } + } + } + + variables: + { + "conceptId": "C1000000001-EXAMPLE" + } + +##### Example Response + + { + "data": { + "publishGeneratedVariables": { + "count": 1, + "items": [ + { + "conceptId": "V1000000001-EXAMPLE", + "dataType": "int32", + "definition": "Grid/time", + "dimensions": [ + { + "Name": "Grid/time", + "Size": 1, + "Type": "TIME_DIMENSION" + } + ], + "longName": "Grid/time", + "name": "Grid/time", + "standardName": "time", + "units": "seconds since 1970-01-01 00:00:00 UTC", + "metadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + } + ] + } + } + } + #### Drafts For all supported arguments and columns, see [the schema](src/types/draft.graphql). @@ -961,6 +1026,7 @@ variables: "nativeId": "tool-1", "ummVersion": "1.2.0" } + #### Local graph database: Normally running GraphQl with `serverless offline` will utilize the `(cmr.earthdata.nasa.gov/graphdb)` endpoint, to query against related collections and duplicate collections in the graph database. To send queries to a locally running graph database, we can use a docker gremlin-server that exposes an HTTP endpoint. This is launched by running diff --git a/requirements.txt b/requirements.txt index f03080e87..b0c5d459d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -earthdata-varinfo==1.0.1 +earthdata-varinfo==2.1.0 diff --git a/src/datasources/collectionVariableDrafts.js b/src/datasources/collectionVariableDrafts.js index e61c00bd0..f0b4530e0 100644 --- a/src/datasources/collectionVariableDrafts.js +++ b/src/datasources/collectionVariableDrafts.js @@ -16,11 +16,7 @@ import { parseError } from '../utils/parseError' export default async (params, context) => { const { headers } = context - const { authorization: authorizationHeader = '' } = downcaseKeys(headers) - - // Split the token by a space (Bearer xxx.xxx.xxx) and take the - // last element to get just the token without the 'Bearer' identifier - const token = authorizationHeader.split(' ').pop() + const { authorization: authHeader } = downcaseKeys(headers) const lambdaClient = new LambdaClient(getLambdaConfig()) @@ -30,7 +26,7 @@ export default async (params, context) => { LogType: LogType.Tail, Payload: JSON.stringify({ ...params, - token + authHeader }) }) diff --git a/src/earthdataVarinfo/handler.py b/src/earthdataVarinfo/handler.py index 4bcd63694..adff6bf6b 100644 --- a/src/earthdataVarinfo/handler.py +++ b/src/earthdataVarinfo/handler.py @@ -1,10 +1,13 @@ """Module providing access to environment variables""" import os -from tempfile import TemporaryDirectory -from varinfo import VarInfoFromNetCDF4 -from varinfo.cmr_search import get_granules, get_granule_link, download_granule -from varinfo.umm_var import get_all_umm_var +# Needed by serverless python requirements because we are setting zip to true +try: + import unzip_requirements +except ImportError: + pass + +from varinfo.generate_umm_var import generate_collection_umm_var def main(event, context): """Handler that calls the earthdata-varinfo library""" @@ -16,35 +19,29 @@ def main(event, context): collection_concept_id = event.get('conceptId') # Get token - token = event.get('token') + auth_header = event.get('authHeader') + + if event.get('publish') is None: + publish = False + else: + publish = event.get('publish') # These two arguments are required for varinfo, return an error if they are not provided - if collection_concept_id is None or token is None: + if collection_concept_id is None or auth_header is None: return { 'isBase64Encoded': False, 'statusCode': 500, 'body': { - 'error': 'Collection Concept ID and Token must be provided.' + 'error': 'Collection Concept ID and Authentication Header must be provided.' } } try: - # Retrieve a list of 10 granules for the collection - granules = get_granules(collection_concept_id, cmr_env=cmr_url, token=token) - - # Get the URL for the first granule (NetCDF-4 file): - granule_link = get_granule_link(granules) - - # Make a temporary directory: - with TemporaryDirectory() as temp_dir: - # Download file to lambda runtime environment - local_granule = download_granule(granule_link, token, out_directory=temp_dir) - - # Parse the granule with VarInfo: - var_info = VarInfoFromNetCDF4(local_granule) - - # Generate all the UMM-Var records: - all_variables = get_all_umm_var(var_info) + # Generate all the UMM-Var records: + all_variables = generate_collection_umm_var(collection_concept_id, + auth_header=auth_header, + cmr_env=cmr_url, + publish=publish) except Exception as error: return { 'isBase64Encoded': False, @@ -54,9 +51,12 @@ def main(event, context): } } + if publish == True: + all_variables = [{'conceptId': item} for item in all_variables] + # Return a successful response return { 'isBase64Encoded': False, 'statusCode': 200, - 'body': list(all_variables.values()) + 'body': list(all_variables) } diff --git a/src/earthdataVarinfo/test/expected_response.json b/src/earthdataVarinfo/test/expected_generate_response.json similarity index 100% rename from src/earthdataVarinfo/test/expected_response.json rename to src/earthdataVarinfo/test/expected_generate_response.json diff --git a/src/earthdataVarinfo/test/expected_publish_response.json b/src/earthdataVarinfo/test/expected_publish_response.json new file mode 100644 index 000000000..0d15be680 --- /dev/null +++ b/src/earthdataVarinfo/test/expected_publish_response.json @@ -0,0 +1,9 @@ +{ + "isBase64Encoded": false, + "statusCode": 200, + "body": [ + {"conceptId": "V0001-TEST"}, + {"conceptId": "V0002-TEST"} + ] +} + \ No newline at end of file diff --git a/src/earthdataVarinfo/test/sample.HDF5 b/src/earthdataVarinfo/test/sample.HDF5 deleted file mode 100644 index a73a26ead..000000000 Binary files a/src/earthdataVarinfo/test/sample.HDF5 and /dev/null differ diff --git a/src/earthdataVarinfo/test/test_handler.py b/src/earthdataVarinfo/test/test_handler.py index fe9002397..366d8e3c7 100644 --- a/src/earthdataVarinfo/test/test_handler.py +++ b/src/earthdataVarinfo/test/test_handler.py @@ -9,11 +9,11 @@ class HandlerTest(TestCase): ''' A class for testing main function of handler.py ''' - def test_missing_token(self): - ''' Test when main is called with a missing token in the event paramter + def test_missing_auth_header(self): + ''' Test when main is called with a missing auth_header in the event paramter ''' response = main({'conceptId': 'C1234-TEST'}, "") - expected_response = {'body': {'error': 'Collection Concept ID and Token must be provided.'}, + expected_response = {'body': {'error': 'Collection Concept ID and Authentication Header must be provided.'}, 'isBase64Encoded': False, 'statusCode': 500} self.assertEqual(response, expected_response) @@ -21,25 +21,53 @@ def test_missing_token(self): def test_missing_concept_id(self): ''' Test when main is called with a missing conceptId in the event paramter ''' - response = main({'token': 'faketoken'}, "") - expected_response = {'body': {'error': 'Collection Concept ID and Token must be provided.'}, + response = main({'authHeader': 'fake header'}, "") + expected_response = {'body': {'error': 'Collection Concept ID and Authentication Header must be provided.'}, 'isBase64Encoded': False, 'statusCode': 500} self.assertEqual(response, expected_response) - @patch('handler.get_granules') - @patch('handler.get_granule_link') - @patch('handler.download_granule') - def test_good_case(self, mock_download_granule, mock_get_granule_link, mock_get_granules): + @patch('handler.generate_collection_umm_var') + def test_generate_case(self, mock_generate_collection_umm_var): ''' Test when main is called successfully ''' - mock_download_granule.return_value = 'test/sample.HDF5' - mock_get_granule_link.return_value = 'Mock link' - mock_get_granules.return_value = 'Mock granules' + # Specify the path to your JSON file + file_path = 'test/variables.json' + + # Open the JSON file for reading + with open(file_path, 'r') as json_file: + # Use json.load() to parse the JSON data into a Python variable + mock_response = json.load(json_file) + + # Set the mock's return value + mock_generate_collection_umm_var.return_value = mock_response + + # Call the main function + response = main({'authHeader': 'fake header', 'conceptId': 'C1234-TEST'}, "") + + # Specify the path to your JSON file + file_path = 'test/expected_generate_response.json' + + # Open the JSON file for reading + with open(file_path, 'r') as json_file: + # Use json.load() to parse the JSON data into a Python variable + expected_response = json.load(json_file) + + self.maxDiff = None + self.assertEqual(response, expected_response) + + @patch('handler.generate_collection_umm_var') + def test_publish_case(self, mock_generate_collection_umm_var): + ''' Test when main is called successfully + ''' + # Set the mock's return value + mock_generate_collection_umm_var.return_value = ['V0001-TEST', 'V0002-TEST'] + + # Call the main function + response = main({'authHeader': 'fake header', 'conceptId': 'C1234-TEST', 'publish': True}, "") - response = main({'token': 'faketoken', 'conceptId': 'C1234-TEST'}, "") # Specify the path to your JSON file - file_path = 'test/expected_response.json' + file_path = 'test/expected_publish_response.json' # Open the JSON file for reading with open(file_path, 'r') as json_file: diff --git a/src/earthdataVarinfo/test/variables.json b/src/earthdataVarinfo/test/variables.json new file mode 100644 index 000000000..e954470dd --- /dev/null +++ b/src/earthdataVarinfo/test/variables.json @@ -0,0 +1,491 @@ +[ + { + "Name": "Grid/time", + "LongName": "Grid/time", + "StandardName": "time", + "Definition": "Grid/time", + "DataType": "int32", + "Dimensions": [ + { + "Name": "Grid/time", + "Size": 1, + "Type": "TIME_DIMENSION" + } + ], + "Units": "seconds since 1970-01-01 00:00:00 UTC", + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/lon", + "LongName": "Grid/lon", + "StandardName": "longitude", + "Definition": "Grid/lon", + "DataType": "float32", + "Dimensions": [ + { + "Name": "Grid/lon", + "Size": 3600, + "Type": "LONGITUDE_DIMENSION" + } + ], + "Units": "degrees_east", + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/lat", + "LongName": "Grid/lat", + "StandardName": "latitude", + "Definition": "Grid/lat", + "DataType": "float32", + "Dimensions": [ + { + "Name": "Grid/lat", + "Size": 1800, + "Type": "LATITUDE_DIMENSION" + } + ], + "Units": "degrees_north", + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/time_bnds", + "LongName": "Grid/time_bnds", + "Definition": "Grid/time_bnds", + "DataType": "int32", + "Dimensions": [ + { + "Name": "Grid/time", + "Size": 1, + "Type": "TIME_DIMENSION" + }, + { + "Name": "Grid/nv", + "Size": 2, + "Type": "OTHER" + } + ], + "Units": "seconds since 1970-01-01 00:00:00 UTC", + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/lon_bnds", + "LongName": "Grid/lon_bnds", + "Definition": "Grid/lon_bnds", + "DataType": "float32", + "Dimensions": [ + { + "Name": "Grid/lon", + "Size": 3600, + "Type": "LONGITUDE_DIMENSION" + }, + { + "Name": "Grid/lonv", + "Size": 2, + "Type": "OTHER" + } + ], + "Units": "degrees_east", + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/lat_bnds", + "LongName": "Grid/lat_bnds", + "Definition": "Grid/lat_bnds", + "DataType": "float32", + "Dimensions": [ + { + "Name": "Grid/lat", + "Size": 1800, + "Type": "LATITUDE_DIMENSION" + }, + { + "Name": "Grid/latv", + "Size": 2, + "Type": "OTHER" + } + ], + "Units": "degrees_north", + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/precipitationCal", + "LongName": "Grid/precipitationCal", + "Definition": "Grid/precipitationCal", + "DataType": "float32", + "Dimensions": [ + { + "Name": "Grid/time", + "Size": 1, + "Type": "TIME_DIMENSION" + }, + { + "Name": "Grid/lon", + "Size": 3600, + "Type": "LONGITUDE_DIMENSION" + }, + { + "Name": "Grid/lat", + "Size": 1800, + "Type": "LATITUDE_DIMENSION" + } + ], + "Units": "mm/hr", + "FillValues": [ + { + "Value": -9999.900390625, + "Type": "SCIENCE_FILLVALUE", + "Description": "Extracted from _FillValue metadata attribute" + } + ], + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/precipitationUncal", + "LongName": "Grid/precipitationUncal", + "Definition": "Grid/precipitationUncal", + "DataType": "float32", + "Dimensions": [ + { + "Name": "Grid/time", + "Size": 1, + "Type": "TIME_DIMENSION" + }, + { + "Name": "Grid/lon", + "Size": 3600, + "Type": "LONGITUDE_DIMENSION" + }, + { + "Name": "Grid/lat", + "Size": 1800, + "Type": "LATITUDE_DIMENSION" + } + ], + "Units": "mm/hr", + "FillValues": [ + { + "Value": -9999.900390625, + "Type": "SCIENCE_FILLVALUE", + "Description": "Extracted from _FillValue metadata attribute" + } + ], + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/randomError", + "LongName": "Grid/randomError", + "Definition": "Grid/randomError", + "DataType": "float32", + "Dimensions": [ + { + "Name": "Grid/time", + "Size": 1, + "Type": "TIME_DIMENSION" + }, + { + "Name": "Grid/lon", + "Size": 3600, + "Type": "LONGITUDE_DIMENSION" + }, + { + "Name": "Grid/lat", + "Size": 1800, + "Type": "LATITUDE_DIMENSION" + } + ], + "Units": "mm/hr", + "FillValues": [ + { + "Value": -9999.900390625, + "Type": "SCIENCE_FILLVALUE", + "Description": "Extracted from _FillValue metadata attribute" + } + ], + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/HQprecipitation", + "LongName": "Grid/HQprecipitation", + "Definition": "Grid/HQprecipitation", + "DataType": "float32", + "Dimensions": [ + { + "Name": "Grid/time", + "Size": 1, + "Type": "TIME_DIMENSION" + }, + { + "Name": "Grid/lon", + "Size": 3600, + "Type": "LONGITUDE_DIMENSION" + }, + { + "Name": "Grid/lat", + "Size": 1800, + "Type": "LATITUDE_DIMENSION" + } + ], + "Units": "mm/hr", + "FillValues": [ + { + "Value": -9999.900390625, + "Type": "SCIENCE_FILLVALUE", + "Description": "Extracted from _FillValue metadata attribute" + } + ], + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/HQprecipSource", + "LongName": "Grid/HQprecipSource", + "Definition": "Grid/HQprecipSource", + "DataType": "int16", + "Dimensions": [ + { + "Name": "Grid/time", + "Size": 1, + "Type": "TIME_DIMENSION" + }, + { + "Name": "Grid/lon", + "Size": 3600, + "Type": "LONGITUDE_DIMENSION" + }, + { + "Name": "Grid/lat", + "Size": 1800, + "Type": "LATITUDE_DIMENSION" + } + ], + "FillValues": [ + { + "Value": -9999, + "Type": "SCIENCE_FILLVALUE", + "Description": "Extracted from _FillValue metadata attribute" + } + ], + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/HQobservationTime", + "LongName": "Grid/HQobservationTime", + "Definition": "Grid/HQobservationTime", + "DataType": "int16", + "Dimensions": [ + { + "Name": "Grid/time", + "Size": 1, + "Type": "TIME_DIMENSION" + }, + { + "Name": "Grid/lon", + "Size": 3600, + "Type": "LONGITUDE_DIMENSION" + }, + { + "Name": "Grid/lat", + "Size": 1800, + "Type": "LATITUDE_DIMENSION" + } + ], + "Units": "minutes", + "FillValues": [ + { + "Value": -9999, + "Type": "SCIENCE_FILLVALUE", + "Description": "Extracted from _FillValue metadata attribute" + } + ], + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/IRprecipitation", + "LongName": "Grid/IRprecipitation", + "Definition": "Grid/IRprecipitation", + "DataType": "float32", + "Dimensions": [ + { + "Name": "Grid/time", + "Size": 1, + "Type": "TIME_DIMENSION" + }, + { + "Name": "Grid/lon", + "Size": 3600, + "Type": "LONGITUDE_DIMENSION" + }, + { + "Name": "Grid/lat", + "Size": 1800, + "Type": "LATITUDE_DIMENSION" + } + ], + "Units": "mm/hr", + "FillValues": [ + { + "Value": -9999.900390625, + "Type": "SCIENCE_FILLVALUE", + "Description": "Extracted from _FillValue metadata attribute" + } + ], + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/IRkalmanFilterWeight", + "LongName": "Grid/IRkalmanFilterWeight", + "Definition": "Grid/IRkalmanFilterWeight", + "DataType": "int16", + "Dimensions": [ + { + "Name": "Grid/time", + "Size": 1, + "Type": "TIME_DIMENSION" + }, + { + "Name": "Grid/lon", + "Size": 3600, + "Type": "LONGITUDE_DIMENSION" + }, + { + "Name": "Grid/lat", + "Size": 1800, + "Type": "LATITUDE_DIMENSION" + } + ], + "FillValues": [ + { + "Value": -9999, + "Type": "SCIENCE_FILLVALUE", + "Description": "Extracted from _FillValue metadata attribute" + } + ], + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/probabilityLiquidPrecipitation", + "LongName": "Grid/probabilityLiquidPrecipitation", + "Definition": "Grid/probabilityLiquidPrecipitation", + "DataType": "int16", + "Dimensions": [ + { + "Name": "Grid/time", + "Size": 1, + "Type": "TIME_DIMENSION" + }, + { + "Name": "Grid/lon", + "Size": 3600, + "Type": "LONGITUDE_DIMENSION" + }, + { + "Name": "Grid/lat", + "Size": 1800, + "Type": "LATITUDE_DIMENSION" + } + ], + "Units": "percent", + "FillValues": [ + { + "Value": -9999, + "Type": "SCIENCE_FILLVALUE", + "Description": "Extracted from _FillValue metadata attribute" + } + ], + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + }, + { + "Name": "Grid/precipitationQualityIndex", + "LongName": "Grid/precipitationQualityIndex", + "Definition": "Grid/precipitationQualityIndex", + "DataType": "float32", + "Dimensions": [ + { + "Name": "Grid/time", + "Size": 1, + "Type": "TIME_DIMENSION" + }, + { + "Name": "Grid/lon", + "Size": 3600, + "Type": "LONGITUDE_DIMENSION" + }, + { + "Name": "Grid/lat", + "Size": 1800, + "Type": "LATITUDE_DIMENSION" + } + ], + "FillValues": [ + { + "Value": -9999.900390625, + "Type": "SCIENCE_FILLVALUE", + "Description": "Extracted from _FillValue metadata attribute" + } + ], + "MetadataSpecification": { + "URL": "https://cdn.earthdata.nasa.gov/umm/variable/v1.8.2", + "Name": "UMM-Var", + "Version": "1.8.2" + } + } +] \ No newline at end of file diff --git a/src/resolvers/__tests__/variable.test.js b/src/resolvers/__tests__/variable.test.js index 5366d502a..cf6431df9 100644 --- a/src/resolvers/__tests__/variable.test.js +++ b/src/resolvers/__tests__/variable.test.js @@ -1,9 +1,18 @@ import nock from 'nock' +import { mockClient } from 'aws-sdk-client-mock' +import { InvokeCommand, LambdaClient } from '@aws-sdk/client-lambda' + import { buildContextValue, server } from './__mocks__/mockServer' const contextValue = buildContextValue() +const lambdaClientMock = mockClient(LambdaClient) + +beforeEach(() => { + lambdaClientMock.reset() +}) + describe('Variable', () => { const OLD_ENV = process.env @@ -418,6 +427,71 @@ describe('Variable', () => { }) describe('Mutation', () => { + test('publishGeneratedVariables calls earthdata-varinfo lambda to publish variable drafts', async () => { + nock(/example-cmr/) + .defaultReplyHeaders({ + 'CMR-Took': 7, + 'CMR-Request-Id': 'abcd-1234-efgh-5678' + }) + .post(/collections\.json/) + .reply(200, { + feed: { + entry: [{ + id: 'C100000-EDSC' + }] + } + }) + + nock(/example-cmr/) + .defaultReplyHeaders({ + 'CMR-Took': 7, + 'CMR-Request-Id': 'abcd-1234-efgh-5678', + 'CMR-Hits': 1 + }) + .post(/variables\.json/) + .reply(200, { + items: [{ + concept_id: 'V10000-EDSC' + }] + }) + + const variableConceptIdList = [{ + conceptId: 'V10000-EDSC' + }] + + lambdaClientMock.on(InvokeCommand).resolves({ + Payload: Buffer.from(JSON.stringify({ + isBase64Encoded: false, + statusCode: 200, + body: variableConceptIdList + })) + }) + + const response = await server.executeOperation({ + variables: { + conceptId: 'C100000-EDSC' + }, + query: `mutation PublishGeneratedVariables($conceptId: String!) { + publishGeneratedVariables(conceptId: $conceptId) { + count + items { + conceptId + } + } + }` + }, { + contextValue + }) + + const { data } = response.body.singleResult + expect(data).toEqual({ + publishGeneratedVariables: { + count: 1, + items: variableConceptIdList + } + }) + }) + test('deleteVariable', async () => { nock(/example-cmr/) .defaultReplyHeaders({ diff --git a/src/resolvers/variable.js b/src/resolvers/variable.js index 9565a49f3..65684bcf9 100644 --- a/src/resolvers/variable.js +++ b/src/resolvers/variable.js @@ -4,6 +4,39 @@ import { handlePagingParams } from '../utils/handlePagingParams' import { isDraftConceptId } from '../utils/isDraftConceptId' export default { + Mutation: { + publishGeneratedVariables: async (source, args, context, info) => { + const { conceptId } = args + + const { dataSources } = context + + const results = await dataSources.collectionVariableDraftsSource({ + conceptId, + publish: true + }, context, parseResolveInfo) + + // Pull out the variable concept ids from the source to use as parameters later + const conceptIds = { + params: { conceptId: results.map((item) => item.conceptId) } + } + + return dataSources.variableSourceFetch( + handlePagingParams(conceptIds), + context, + parseResolveInfo(info) + ) + }, + + deleteVariable: async (source, args, context, info) => { + const { dataSources } = context + + return dataSources.variableSourceDelete( + handlePagingParams(args), + context, + parseResolveInfo(info) + ) + } + }, Query: { variables: async (source, args, context, info) => { const { dataSources } = context @@ -26,18 +59,6 @@ export default { } }, - Mutation: { - deleteVariable: async (source, args, context, info) => { - const { dataSources } = context - - return dataSources.variableSourceDelete( - handlePagingParams(args), - context, - parseResolveInfo(info) - ) - } - }, - Variable: { collections: async (source, args, context, info) => { const { dataSources } = context diff --git a/src/types/variable.graphql b/src/types/variable.graphql index c4c7f3c75..44ff48a01 100644 --- a/src/types/variable.graphql +++ b/src/types/variable.graphql @@ -112,6 +112,11 @@ type Query { } type Mutation { + publishGeneratedVariables ( + "The collection concept Id." + conceptId: String! + ): VariableList + deleteVariable ( "Provider ID of the variable." providerId: String! diff --git a/src/utils/parseRequestedFields.js b/src/utils/parseRequestedFields.js index 9e1b51fa2..81f3cc71b 100644 --- a/src/utils/parseRequestedFields.js +++ b/src/utils/parseRequestedFields.js @@ -113,7 +113,8 @@ export const parseRequestedFields = (parsedInfo, keyMap, conceptName) => { } if (name === 'collection' || name === 'collections') { - // If a user has requested granules, subscriptions, relatedCollections, duplicateCollections or generateVariableDrafts from + // If a user has requested granules, subscriptions, relatedCollections, + // duplicateCollections or generateVariableDrafts from // within a collection request the resolver will pull the conceptId and provide // it to the granules request but if a user doesn't explicity ask for the // collection concept id we need to request it