Skip to content

Commit 9d8a822

Browse files
authored
Merge pull request #488 from unity-sds/develop
release/9.4.0
2 parents 296c403 + 75f029d commit 9d8a822

File tree

21 files changed

+610
-181
lines changed

21 files changed

+610
-181
lines changed

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [9.4.0] - 2024-12-16
9+
### Changed
10+
- [#485](https://github.com/unity-sds/unity-data-services/pull/485) feat: delete granule endpoint
11+
12+
## [9.3.0] - 2024-12-11
13+
### Changed
14+
- [#482](https://github.com/unity-sds/unity-data-services/pull/482) feat: updated name spaces and naming of resources
15+
16+
## [9.2.0] - 2024-12-09
17+
### Changed
18+
- [#478](https://github.com/unity-sds/unity-data-services/pull/478) feat: uds lib update
19+
20+
## [9.1.2] - 2024-12-09
21+
### Fixed
22+
- [#448](https://github.com/unity-sds/unity-data-services/pull/448) fix: wrong location archive keys
23+
24+
## [9.1.1] - 2024-12-09
25+
### Fixed
26+
- [#475](https://github.com/unity-sds/unity-data-services/pull/475) fix: index to es is not setting bbox correctly
27+
828
## [9.1.0] - 2024-12-03
929
### Changed
1030
- [#472](https://github.com/unity-sds/unity-data-services/pull/472) feat: amalgamation download type

cumulus_lambda_functions/cumulus_wrapper/query_granules.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def __init__(self, cumulus_base: str, cumulus_token: str):
2121
super().__init__(cumulus_base, cumulus_token)
2222
self._conditions.append('status=completed')
2323
self._item_transformer = ItemTransformer()
24+
self.__collection_id = None
2425

2526
def with_filter(self, filter_key, filter_values: list):
2627
if len(filter_values) < 1:
@@ -34,6 +35,7 @@ def with_filter(self, filter_key, filter_values: list):
3435

3536
def with_collection_id(self, collection_id: str):
3637
self._conditions.append(f'{self.__collection_id_key}={collection_id}')
38+
self.__collection_id = collection_id
3739
return self
3840

3941
def with_bbox(self):
@@ -130,6 +132,48 @@ def query_direct_to_private_api(self, private_api_prefix: str, transform=True):
130132
return {'server_error': f'error while invoking:{str(e)}'}
131133
return {'results': stac_list}
132134

135+
def delete_entry(self, private_api_prefix: str, granule_id: str):
136+
payload = {
137+
'httpMethod': 'DELETE',
138+
'resource': '/{proxy+}',
139+
'path': f'/{self.__granules_key}/{self.__collection_id}/{granule_id}',
140+
'queryStringParameters': {**{k[0]: k[1] for k in [k1.split('=') for k1 in self._conditions]}},
141+
# 'queryStringParameters': {'limit': '30'},
142+
'headers': {
143+
'Content-Type': 'application/json',
144+
},
145+
# 'body': json.dumps({"action": "removeFromCmr"})
146+
}
147+
LOGGER.debug(f'payload: {payload}')
148+
try:
149+
query_result = self._invoke_api(payload, private_api_prefix)
150+
"""
151+
{'statusCode': 200, 'body': '{"meta":{"name":"cumulus-api","stack":"am-uds-dev-cumulus","table":"granule","limit":3,"page":1,"count":0},"results":[]}', 'headers': {'x-powered-by': 'Express', 'access-control-allow-origin': '*', 'strict-transport-security': 'max-age=31536000; includeSubDomains', 'content-type': 'application/json; charset=utf-8', 'content-length': '120', 'etag': 'W/"78-YdHqDNIH4LuOJMR39jGNA/23yOQ"', 'date': 'Tue, 07 Jun 2022 22:30:44 GMT', 'connection': 'close'}, 'isBase64Encoded': False}
152+
"""
153+
if query_result['statusCode'] >= 500:
154+
LOGGER.error(f'server error status code: {query_result.statusCode}. details: {query_result}')
155+
return {'server_error': query_result}
156+
if query_result['statusCode'] >= 400:
157+
LOGGER.error(f'client error status code: {query_result.statusCode}. details: {query_result}')
158+
return {'client_error': query_result}
159+
query_result = json.loads(query_result['body'])
160+
LOGGER.info(f'json query_result: {query_result}')
161+
"""
162+
{
163+
"detail": "Record deleted"
164+
}
165+
"""
166+
if 'detail' not in query_result:
167+
LOGGER.error(f'missing key: detail. invalid response json: {query_result}')
168+
return {'server_error': f'missing key: detail. invalid response json: {query_result}'}
169+
if query_result['detail'] != 'Record deleted':
170+
LOGGER.error(f'Wrong Message: {query_result}')
171+
return {'server_error': f'Wrong Message: {query_result}'}
172+
except Exception as e:
173+
LOGGER.exception('error while invoking')
174+
return {'server_error': f'error while invoking:{str(e)}'}
175+
return {}
176+
133177
def query(self, transform=True):
134178
conditions_str = '&'.join(self._conditions)
135179
LOGGER.info(f'cumulus_base: {self.cumulus_base}')

cumulus_lambda_functions/granules_to_es/granules_indexer.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def __get_potential_files(self):
4747
potential_files = []
4848
self.__input_file_list = self.__cumulus_record['files']
4949
for each_file in self.__input_file_list:
50-
if 'type' in each_file and each_file['type'].strip().lower() != self.__valid_filetype_name:
50+
if 'type' in each_file and self.__valid_filetype_name not in each_file['type'].strip().lower():
5151
LOGGER.debug(f'Not metadata. skipping {each_file}')
5252
continue
5353
if 'fileName' not in each_file and 'name' in each_file: # add fileName if there is only name
@@ -93,6 +93,8 @@ def start(self):
9393
else:
9494
LOGGER.warning(f'unable to find STAC JSON file in {potential_files}')
9595
stac_item = ItemTransformer().to_stac(self.__cumulus_record)
96+
if stac_input_meta is not None and stac_input_meta.bbox is not None:
97+
stac_item['bbox'] = stac_input_meta.bbox
9698
if 'bbox' in stac_item:
9799
stac_item['bbox'] = GranulesDbIndex.to_es_bbox(stac_item['bbox'])
98100
collection_identifier = UdsCollections.decode_identifier(self.__cumulus_record['collectionId'])

cumulus_lambda_functions/lib/uds_db/granules_db_index.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,23 @@ def get_entry(self, tenant: str, tenant_venue: str, doc_id: str, ):
201201
raise ValueError(f"no such granule: {doc_id}")
202202
return result
203203

204+
def delete_entry(self, tenant: str, tenant_venue: str, doc_id: str, ):
205+
read_alias_name = f'{DBConstants.granules_read_alias_prefix}_{tenant}_{tenant_venue}'.lower().strip()
206+
result = self.__es.query({
207+
'size': 9999,
208+
'query': {'term': {'_id': doc_id}}
209+
}, read_alias_name)
210+
if result is None:
211+
raise ValueError(f"no such granule: {doc_id}")
212+
for each_granule in result['hits']['hits']:
213+
delete_result = self.__es.delete_by_query({
214+
'query': {'term': {'_id': each_granule['_id']}}
215+
}, each_granule['_index'])
216+
LOGGER.debug(f'delete_result: {delete_result}')
217+
if delete_result is None:
218+
raise ValueError(f"error deleting {each_granule}")
219+
return result
220+
204221
def update_entry(self, tenant: str, tenant_venue: str, json_body: dict, doc_id: str, ):
205222
write_alias_name = f'{DBConstants.granules_write_alias_prefix}_{tenant}_{tenant_venue}'.lower().strip()
206223
json_body['event_time'] = TimeUtils.get_current_unix_milli()

cumulus_lambda_functions/metadata_stac_generate_cmr/stac_input_metadata.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,20 @@ def __init__(self, input_stac_dict: dict):
1717
self.__prod_dt = None
1818
self.__insert_dt = None
1919
self.__custom_properties = {}
20+
self.__bbox = None
21+
22+
@property
23+
def bbox(self):
24+
return self.__bbox
25+
26+
@bbox.setter
27+
def bbox(self, val):
28+
"""
29+
:param val:
30+
:return: None
31+
"""
32+
self.__bbox = val
33+
return
2034

2135
@property
2236
def custom_properties(self):
@@ -132,6 +146,7 @@ def start(self) -> GranuleMetadataProps:
132146
stac_item: Item = ItemTransformer().from_stac(self.__input_stac_dict)
133147
self.__custom_properties = deepcopy(stac_item.properties)
134148
self.__remove_default_keys_in_custom_props()
149+
self.__bbox = stac_item.bbox
135150
# self.__custom_properties['collection_id'] = stac_item.collection_id # TODO version is included
136151
# collection_led_granule_id = stac_item.id if stac_item.id.startswith(stac_item.collection_id) else f'{stac_item.collection_id}:{stac_item.id}'
137152
# self.__custom_properties['granule_id'] = collection_led_granule_id # This needs to be start with collection_id to be consistent with cumulus granule_id which starts with collection

cumulus_lambda_functions/uds_api/dapa/granules_dapa_query_es.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ def __get_time_range_terms(self):
7575
]
7676

7777
def __create_pagination_links(self, page_marker_str):
78+
if self.__pagination_link_obj is None:
79+
return []
7880
new_queries = deepcopy(self.__pagination_link_obj.org_query_params)
7981
new_queries['limit'] = int(new_queries['limit'] if 'limit' in new_queries else self.__limit)
8082
current_page = f"{self.__pagination_link_obj.requesting_base_url}?{'&'.join([f'{k}={v}' for k, v in new_queries.items()])}"
@@ -114,8 +116,20 @@ def archive_single_granule(self, granule_id):
114116
daac_archiver.send_to_daac_internal(cnm_response)
115117
return
116118

119+
def __restructure_each_granule_result(self, each_granules_query_result_stripped):
120+
if 'event_time' in each_granules_query_result_stripped:
121+
each_granules_query_result_stripped.pop('event_time')
122+
if 'bbox' in each_granules_query_result_stripped:
123+
each_granules_query_result_stripped['bbox'] = GranulesDbIndex.from_es_bbox(each_granules_query_result_stripped['bbox'])
124+
for each_archiving_key in GranulesIndexMapping.archiving_keys:
125+
if each_archiving_key in each_granules_query_result_stripped:
126+
each_granules_query_result_stripped['properties'][each_archiving_key] = each_granules_query_result_stripped.pop(each_archiving_key)
127+
return
128+
117129
def get_single_granule(self, granule_id):
118130
granules_query_dsl = {
131+
'size': 1,
132+
'sort': [{'id': {'order': 'asc'}}],
119133
'query': {'bool': {'must': [{
120134
'term': {'id': granule_id}
121135
}]}}
@@ -132,10 +146,7 @@ def get_single_granule(self, granule_id):
132146
each_granules_query_result_stripped = granules_query_result['hits']['hits'][0]['_source']
133147
self_link = Link(rel='self', target=f'{self.__base_url}/{WebServiceConstants.COLLECTIONS}/{self.__collection_id}/items/{each_granules_query_result_stripped["id"]}', media_type='application/json', title=each_granules_query_result_stripped["id"]).to_dict(False)
134148
each_granules_query_result_stripped['links'].append(self_link)
135-
if 'event_time' in each_granules_query_result_stripped:
136-
each_granules_query_result_stripped.pop('event_time')
137-
if 'bbox' in each_granules_query_result_stripped:
138-
each_granules_query_result_stripped['bbox'] = GranulesDbIndex.from_es_bbox(each_granules_query_result_stripped['bbox'])
149+
self.__restructure_each_granule_result(each_granules_query_result_stripped)
139150
return each_granules_query_result_stripped
140151

141152
def start(self):
@@ -152,13 +163,7 @@ def start(self):
152163
for each_granules_query_result_stripped in granules_query_result_stripped:
153164
self_link = Link(rel='self', target=f'{self.__base_url}/{WebServiceConstants.COLLECTIONS}/{self.__collection_id}/items/{each_granules_query_result_stripped["id"]}', media_type='application/json', title=each_granules_query_result_stripped["id"]).to_dict(False)
154165
each_granules_query_result_stripped['links'].append(self_link)
155-
if 'event_time' in each_granules_query_result_stripped:
156-
each_granules_query_result_stripped.pop('event_time')
157-
if 'bbox' in each_granules_query_result_stripped:
158-
each_granules_query_result_stripped['bbox'] = GranulesDbIndex.from_es_bbox(each_granules_query_result_stripped['bbox'])
159-
for each_archiving_key in GranulesIndexMapping.archiving_keys:
160-
if each_archiving_key in each_granules_query_result_stripped:
161-
each_granules_query_result_stripped['properties'][each_archiving_key] = each_granules_query_result_stripped.pop(each_archiving_key)
166+
self.__restructure_each_granule_result(each_granules_query_result_stripped)
162167
pagination_link = '' if len(granules_query_result['hits']['hits']) < self.__limit else ','.join(granules_query_result['hits']['hits'][-1]['sort'])
163168
return {
164169
'statusCode': 200,

cumulus_lambda_functions/uds_api/granules_api.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
import os
33
from typing import Union
44

5+
from mdps_ds_lib.lib.aws.aws_s3 import AwsS3
6+
from pystac import Item
7+
8+
from cumulus_lambda_functions.cumulus_wrapper.query_granules import GranulesQuery
9+
510
from cumulus_lambda_functions.daac_archiver.daac_archiver_logic import DaacArchiverLogic
611
from cumulus_lambda_functions.uds_api.dapa.daac_archive_crud import DaacArchiveCrud, DaacDeleteModel, DaacAddModel, \
712
DaacUpdateModel
@@ -239,6 +244,51 @@ async def get_single_granule_dapa(request: Request, collection_id: str, granule_
239244
raise HTTPException(status_code=500, detail=str(e))
240245
return granules_result
241246

247+
@router.delete("/{collection_id}/items/{granule_id}")
248+
@router.delete("/{collection_id}/items/{granule_id}/")
249+
async def delete_single_granule_dapa(request: Request, collection_id: str, granule_id: str):
250+
authorizer: UDSAuthorizorAbstract = UDSAuthorizerFactory() \
251+
.get_instance(UDSAuthorizerFactory.cognito,
252+
es_url=os.getenv('ES_URL'),
253+
es_port=int(os.getenv('ES_PORT', '443'))
254+
)
255+
auth_info = FastApiUtils.get_authorization_info(request)
256+
collection_identifier = UdsCollections.decode_identifier(collection_id)
257+
if not authorizer.is_authorized_for_collection(DBConstants.delete, collection_id,
258+
auth_info['ldap_groups'],
259+
collection_identifier.tenant,
260+
collection_identifier.venue):
261+
LOGGER.debug(f'user: {auth_info["username"]} is not authorized for {collection_id}')
262+
raise HTTPException(status_code=403, detail=json.dumps({
263+
'message': 'not authorized to execute this action'
264+
}))
265+
try:
266+
LOGGER.debug(f'deleting granule: {granule_id}')
267+
cumulus_lambda_prefix = os.getenv('CUMULUS_LAMBDA_PREFIX')
268+
cumulus = GranulesQuery('https://na/dev', 'NA')
269+
cumulus.with_collection_id(collection_id)
270+
cumulus_delete_result = cumulus.delete_entry(cumulus_lambda_prefix, granule_id) # TODO not sure it is correct granule ID
271+
LOGGER.debug(f'cumulus_delete_result: {cumulus_delete_result}')
272+
es_delete_result = GranulesDbIndex().delete_entry(collection_identifier.tenant,
273+
collection_identifier.venue,
274+
granule_id
275+
)
276+
LOGGER.debug(f'es_delete_result: {es_delete_result}')
277+
# es_delete_result = [Item.from_dict(k['_source']) for k in es_delete_result['hits']['hits']]
278+
# if delete_files is False:
279+
# LOGGER.debug(f'Not deleting files as it is set to false in the request')
280+
# return {}
281+
# s3 = AwsS3()
282+
# for each_granule in es_delete_result:
283+
# s3_urls = [v.href for k, v in each_granule.assets.items()]
284+
# LOGGER.debug(f'deleting S3 for {each_granule.id} - s3_urls: {s3_urls}')
285+
# delete_result = s3.delete_multiple(s3_urls=s3_urls)
286+
# LOGGER.debug(f'delete_result for {each_granule.id} - delete_result: {delete_result}')
287+
except Exception as e:
288+
LOGGER.exception('failed during get_granules_dapa')
289+
raise HTTPException(status_code=500, detail=str(e))
290+
return {}
291+
242292

243293
@router.put("/{collection_id}/archive/{granule_id}")
244294
@router.put("/{collection_id}/archive/{granule_id}/")

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jsonschema==4.23.0
1515
jsonschema-specifications==2023.12.1
1616
lark==0.12.0
1717
mangum==0.18.0
18-
mdps-ds-lib==1.1.0
18+
mdps-ds-lib==1.1.1.dev000200
1919
pydantic==2.9.2
2020
pydantic_core==2.23.4
2121
pygeofilter==0.2.4

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
setup(
1414
name="cumulus_lambda_functions",
15-
version="9.1.0",
15+
version="9.4.0",
1616
packages=find_packages(),
1717
install_requires=install_requires,
1818
package_data={

0 commit comments

Comments
 (0)