port-ocean 0.16.0__py3-none-any.whl → 0.16.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of port-ocean might be problematic. Click here for more details.

@@ -34,6 +34,7 @@ class MappedEntity:
34
34
  entity: dict[str, Any] = field(default_factory=dict)
35
35
  did_entity_pass_selector: bool = False
36
36
  raw_data: Optional[dict[str, Any]] = None
37
+ misconfigurations: dict[str, str] = field(default_factory=dict)
37
38
 
38
39
 
39
40
  class JQEntityProcessor(BaseEntityProcessor):
@@ -95,21 +96,37 @@ class JQEntityProcessor(BaseEntityProcessor):
95
96
  )
96
97
 
97
98
  async def _search_as_object(
98
- self, data: dict[str, Any], obj: dict[str, Any]
99
+ self,
100
+ data: dict[str, Any],
101
+ obj: dict[str, Any],
102
+ misconfigurations: dict[str, str] | None = None,
99
103
  ) -> dict[str, Any | None]:
104
+ """
105
+ Identify and extract the relevant value for the chosen key and populate it into the entity
106
+ :param data: the property itself that holds the key and the value, it is being passed to the task and we get back a task item,
107
+ if the data is a dict, we will recursively call this function again.
108
+ :param obj: the key that we want its value to be mapped into our entity.
109
+ :param misconfigurations: due to the recursive nature of this function,
110
+ we aim to have a dict that represents all of the misconfigured properties and when used recursively,
111
+ we pass this reference to misfoncigured object to add the relevant misconfigured keys.
112
+ :return: Mapped object with found value.
113
+ """
114
+
100
115
  search_tasks: dict[
101
116
  str, Task[dict[str, Any | None]] | list[Task[dict[str, Any | None]]]
102
117
  ] = {}
103
118
  for key, value in obj.items():
104
119
  if isinstance(value, list):
105
120
  search_tasks[key] = [
106
- asyncio.create_task(self._search_as_object(data, obj))
121
+ asyncio.create_task(
122
+ self._search_as_object(data, obj, misconfigurations)
123
+ )
107
124
  for obj in value
108
125
  ]
109
126
 
110
127
  elif isinstance(value, dict):
111
128
  search_tasks[key] = asyncio.create_task(
112
- self._search_as_object(data, value)
129
+ self._search_as_object(data, value, misconfigurations)
113
130
  )
114
131
  else:
115
132
  search_tasks[key] = asyncio.create_task(self._search(data, value))
@@ -118,12 +135,20 @@ class JQEntityProcessor(BaseEntityProcessor):
118
135
  for key, task in search_tasks.items():
119
136
  try:
120
137
  if isinstance(task, list):
121
- result[key] = [await task for task in task]
138
+ result_list = []
139
+ for task in task:
140
+ task_result = await task
141
+ if task_result is None and misconfigurations is not None:
142
+ misconfigurations[key] = obj[key]
143
+ result_list.append(task_result)
144
+ result[key] = result_list
122
145
  else:
123
- result[key] = await task
146
+ task_result = await task
147
+ if task_result is None and misconfigurations is not None:
148
+ misconfigurations[key] = obj[key]
149
+ result[key] = task_result
124
150
  except Exception:
125
151
  result[key] = None
126
-
127
152
  return result
128
153
 
129
154
  async def _get_mapped_entity(
@@ -135,11 +160,15 @@ class JQEntityProcessor(BaseEntityProcessor):
135
160
  ) -> MappedEntity:
136
161
  should_run = await self._search_as_bool(data, selector_query)
137
162
  if parse_all or should_run:
138
- mapped_entity = await self._search_as_object(data, raw_entity_mappings)
163
+ misconfigurations: dict[str, str] = {}
164
+ mapped_entity = await self._search_as_object(
165
+ data, raw_entity_mappings, misconfigurations
166
+ )
139
167
  return MappedEntity(
140
168
  mapped_entity,
141
169
  did_entity_pass_selector=should_run,
142
170
  raw_data=data if should_run else None,
171
+ misconfigurations=misconfigurations,
143
172
  )
144
173
 
145
174
  return MappedEntity()
@@ -221,7 +250,11 @@ class JQEntityProcessor(BaseEntityProcessor):
221
250
  passed_entities = []
222
251
  failed_entities = []
223
252
  examples_to_send: list[dict[str, Any]] = []
253
+ entity_misconfigurations: dict[str, str] = {}
254
+ missing_required_fields: bool = False
224
255
  for result in calculated_entities_results:
256
+ if len(result.misconfigurations) > 0:
257
+ entity_misconfigurations |= result.misconfigurations
225
258
  if result.entity.get("identifier") and result.entity.get("blueprint"):
226
259
  parsed_entity = Entity.parse_obj(result.entity)
227
260
  if result.did_entity_pass_selector:
@@ -233,6 +266,12 @@ class JQEntityProcessor(BaseEntityProcessor):
233
266
  examples_to_send.append(result.raw_data)
234
267
  else:
235
268
  failed_entities.append(parsed_entity)
269
+ else:
270
+ missing_required_fields = True
271
+ if len(entity_misconfigurations) > 0:
272
+ logger.info(
273
+ f"The mapping resulted with invalid values for{" identifier, blueprint," if missing_required_fields else " "} properties. Mapping result: {entity_misconfigurations}"
274
+ )
236
275
  if (
237
276
  not calculated_entities_results
238
277
  and raw_results
@@ -248,4 +287,5 @@ class JQEntityProcessor(BaseEntityProcessor):
248
287
  return CalculationResult(
249
288
  EntitySelectorDiff(passed=passed_entities, failed=failed_entities),
250
289
  errors,
290
+ misonfigured_entity_keys=entity_misconfigurations,
251
291
  )
@@ -185,7 +185,7 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
185
185
  send_raw_data_examples_amount = (
186
186
  SEND_RAW_DATA_EXAMPLES_AMOUNT if ocean.config.send_raw_data_examples else 0
187
187
  )
188
- all_entities, register_errors = await self._register_resource_raw(
188
+ all_entities, register_errors,_ = await self._register_resource_raw(
189
189
  resource_config,
190
190
  raw_results,
191
191
  user_agent_type,
@@ -202,7 +202,7 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
202
202
  0, send_raw_data_examples_amount - len(passed_entities)
203
203
  )
204
204
 
205
- entities, register_errors = await self._register_resource_raw(
205
+ entities, register_errors,_ = await self._register_resource_raw(
206
206
  resource_config,
207
207
  items,
208
208
  user_agent_type,
@@ -1,5 +1,13 @@
1
- from typing import TypedDict, Any, AsyncIterator, Callable, Awaitable, NamedTuple
2
-
1
+ from typing import (
2
+ TypedDict,
3
+ Any,
4
+ AsyncIterator,
5
+ Callable,
6
+ Awaitable,
7
+ NamedTuple,
8
+ )
9
+
10
+ from dataclasses import field
3
11
  from port_ocean.core.models import Entity
4
12
 
5
13
  RAW_ITEM = dict[Any, Any]
@@ -30,6 +38,7 @@ class EntitySelectorDiff(NamedTuple):
30
38
  class CalculationResult(NamedTuple):
31
39
  entity_selector_diff: EntitySelectorDiff
32
40
  errors: list[Exception]
41
+ misonfigured_entity_keys: dict[str, str] = field(default_factory=dict)
33
42
 
34
43
 
35
44
  class IntegrationEventsCallbacks(TypedDict):
@@ -269,3 +269,37 @@ class TestJQEntityProcessor:
269
269
  assert len(result.entity_selector_diff.passed) == 1
270
270
  assert result.entity_selector_diff.passed[0].properties.get("foo") == "bar"
271
271
  assert not result.errors
272
+
273
+ async def test_parse_items_wrong_mapping(
274
+ self, mocked_processor: JQEntityProcessor
275
+ ) -> None:
276
+ mapping = Mock()
277
+ mapping.port.entity.mappings.dict.return_value = {
278
+ "title": ".foo",
279
+ "identifier": ".ark",
280
+ "blueprint": ".baz",
281
+ "properties": {
282
+ "description": ".bazbar",
283
+ "url": ".foobar",
284
+ "defaultBranch": ".bar.baz",
285
+ },
286
+ }
287
+ mapping.port.items_to_parse = None
288
+ mapping.selector.query = "true"
289
+ raw_results = [
290
+ {
291
+ "foo": "bar",
292
+ "baz": "bazbar",
293
+ "bar": {"foobar": "barfoo", "baz": "barbaz"},
294
+ },
295
+ {"foo": "bar", "baz": "bazbar", "bar": {"foobar": "foobar"}},
296
+ ]
297
+ result = await mocked_processor._parse_items(mapping, raw_results)
298
+ assert len(result.misonfigured_entity_keys) > 0
299
+ assert len(result.misonfigured_entity_keys) == 4
300
+ assert result.misonfigured_entity_keys == {
301
+ "identifier": ".ark",
302
+ "description": ".bazbar",
303
+ "url": ".foobar",
304
+ "defaultBranch": ".bar.baz",
305
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: port-ocean
3
- Version: 0.16.0
3
+ Version: 0.16.1
4
4
  Summary: Port Ocean is a CLI tool for managing your Port projects.
5
5
  Home-page: https://app.getport.io
6
6
  Keywords: ocean,port-ocean,port
@@ -87,7 +87,7 @@ port_ocean/core/handlers/entities_state_applier/port/get_related_entities.py,sha
87
87
  port_ocean/core/handlers/entities_state_applier/port/order_by_entities_dependencies.py,sha256=lyv6xKzhYfd6TioUgR3AVRSJqj7JpAaj1LxxU2xAqeo,1720
88
88
  port_ocean/core/handlers/entity_processor/__init__.py,sha256=FvFCunFg44wNQoqlybem9MthOs7p1Wawac87uSXz9U8,156
89
89
  port_ocean/core/handlers/entity_processor/base.py,sha256=udR0w5TstTOS5xOfTjAZIEdldn4xr6Oyb3DylatYX3Q,1869
90
- port_ocean/core/handlers/entity_processor/jq_entity_processor.py,sha256=X0aYDqgvuZG813JM1qjWpK-V9ASgctTRNgz02wJ4vMY,8887
90
+ port_ocean/core/handlers/entity_processor/jq_entity_processor.py,sha256=X-up0HVdE8pkITxzvB1BC7W8Oq0C14WbT3WqV7p-wJc,11129
91
91
  port_ocean/core/handlers/port_app_config/__init__.py,sha256=8AAT5OthiVM7KCcM34iEgEeXtn2pRMrT4Dze5r1Ixbk,134
92
92
  port_ocean/core/handlers/port_app_config/api.py,sha256=6VbKPwFzsWG0IYsVD81hxSmfqtHUFqrfUuj1DBX5g4w,853
93
93
  port_ocean/core/handlers/port_app_config/base.py,sha256=4Nxt2g8voEIHJ4Y1Km5NJcaG2iSbCklw5P8-Kus7Y9k,3007
@@ -100,10 +100,10 @@ port_ocean/core/integrations/mixins/__init__.py,sha256=FA1FEKMM6P-L2_m7Q4L20mFa4
100
100
  port_ocean/core/integrations/mixins/events.py,sha256=Ddfx2L4FpghV38waF8OfVeOV0bHBxNIgjU-q5ffillI,2341
101
101
  port_ocean/core/integrations/mixins/handler.py,sha256=mZ7-0UlG3LcrwJttFbMe-R4xcOU2H_g33tZar7PwTv8,3771
102
102
  port_ocean/core/integrations/mixins/sync.py,sha256=B9fEs8faaYLLikH9GBjE_E61vo0bQDjIGQsQ1SRXOlA,3931
103
- port_ocean/core/integrations/mixins/sync_raw.py,sha256=FjmYIP-7fqQY9tlqN9tNyHwdN81Dn4sqxVMI6JJbRYE,20275
103
+ port_ocean/core/integrations/mixins/sync_raw.py,sha256=Wir4aTSCkIvG6Ny9Eo0Xf55OkSbh_6wHfNSaCffAKJQ,20279
104
104
  port_ocean/core/integrations/mixins/utils.py,sha256=oN4Okz6xlaefpid1_Pud8HPSw9BwwjRohyNsknq-Myg,2309
105
105
  port_ocean/core/models.py,sha256=O8nOKc4ORZz9tS5s6y5YgGLEBroXpvSPDqKuz48uKvs,1965
106
- port_ocean/core/ocean_types.py,sha256=3_d8-n626f1kWLQ_Jxw194LEyrOVupz05qs_Y1pvB-A,990
106
+ port_ocean/core/ocean_types.py,sha256=j_-or1VxDy22whLLxwxgzIsE4wAhFLH19Xff9l4oJA8,1124
107
107
  port_ocean/core/utils/entity_topological_sorter.py,sha256=MDUjM6OuDy4Xj68o-7InNN0w1jqjxeDfeY8U02vySNI,3081
108
108
  port_ocean/core/utils/utils.py,sha256=QSRuF9wlhbOw6cELlDlek_UIX6ciIuKWml8QhBmHU_k,3703
109
109
  port_ocean/debug_cli.py,sha256=gHrv-Ey3cImKOcGZpjoHlo4pa_zfmyOl6TUM4o9VtcA,96
@@ -131,7 +131,7 @@ port_ocean/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
131
131
  port_ocean/tests/clients/port/mixins/test_entities.py,sha256=A9myrnkLhKSQrnOLv1Zz2wiOVSxW65Q9RIUIRbn_V7w,1586
132
132
  port_ocean/tests/conftest.py,sha256=JXASSS0IY0nnR6bxBflhzxS25kf4iNaABmThyZ0mZt8,101
133
133
  port_ocean/tests/core/defaults/test_common.py,sha256=sR7RqB3ZYV6Xn6NIg-c8k5K6JcGsYZ2SCe_PYX5vLYM,5560
134
- port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py,sha256=Yv03P-LDcJCKZ21exiTFrcT1eu0zn6Z954dilxrb52Y,10842
134
+ port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py,sha256=C2nLgapTdXRrzP5B4xBuHcc14L-NztFpxLIv8Iuv6Gg,12046
135
135
  port_ocean/tests/core/handlers/mixins/test_sync_raw.py,sha256=RPrbw4Zs6bmhL9zMQviq7-qMfgP5_4nJDkfZiAukK-g,15782
136
136
  port_ocean/tests/core/test_utils.py,sha256=Z3kdhb5V7Svhcyy3EansdTpgHL36TL6erNtU-OPwAcI,2647
137
137
  port_ocean/tests/core/utils/test_entity_topological_sorter.py,sha256=zuq5WSPy_88PemG3mOUIHTxWMR_js1R7tOzUYlgBd68,3447
@@ -156,8 +156,8 @@ port_ocean/utils/repeat.py,sha256=0EFWM9d8lLXAhZmAyczY20LAnijw6UbIECf5lpGbOas,32
156
156
  port_ocean/utils/signal.py,sha256=K-6kKFQTltcmKDhtyZAcn0IMa3sUpOHGOAUdWKgx0_E,1369
157
157
  port_ocean/utils/time.py,sha256=pufAOH5ZQI7gXvOvJoQXZXZJV-Dqktoj9Qp9eiRwmJ4,1939
158
158
  port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
159
- port_ocean-0.16.0.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
160
- port_ocean-0.16.0.dist-info/METADATA,sha256=IonSZuh115163Dk9ISkmRGEHSnavV_0i58YgSuz2mek,6673
161
- port_ocean-0.16.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
162
- port_ocean-0.16.0.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
163
- port_ocean-0.16.0.dist-info/RECORD,,
159
+ port_ocean-0.16.1.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
160
+ port_ocean-0.16.1.dist-info/METADATA,sha256=6Zbwkb10_nBvr-Yan20BoYf42oD77C0je7Ft2UXmZHs,6673
161
+ port_ocean-0.16.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
162
+ port_ocean-0.16.1.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
163
+ port_ocean-0.16.1.dist-info/RECORD,,