ucapi-framework 1.4.0b8__tar.gz → 1.4.0b11__tar.gz

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.
Files changed (22) hide show
  1. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/PKG-INFO +1 -1
  2. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/pyproject.toml +1 -1
  3. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/tests/test_setup.py +49 -0
  4. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/ucapi_framework/setup.py +44 -7
  5. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/ucapi_framework.egg-info/PKG-INFO +1 -1
  6. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/README.md +0 -0
  7. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/setup.cfg +0 -0
  8. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/tests/test_config.py +0 -0
  9. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/tests/test_device.py +0 -0
  10. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/tests/test_discovery.py +0 -0
  11. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/tests/test_driver.py +0 -0
  12. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/tests/test_migration.py +0 -0
  13. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/ucapi_framework/__init__.py +0 -0
  14. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/ucapi_framework/config.py +0 -0
  15. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/ucapi_framework/device.py +0 -0
  16. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/ucapi_framework/discovery.py +0 -0
  17. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/ucapi_framework/driver.py +0 -0
  18. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/ucapi_framework/migration.py +0 -0
  19. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/ucapi_framework.egg-info/SOURCES.txt +0 -0
  20. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/ucapi_framework.egg-info/dependency_links.txt +0 -0
  21. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/ucapi_framework.egg-info/requires.txt +0 -0
  22. {ucapi_framework-1.4.0b8 → ucapi_framework-1.4.0b11}/ucapi_framework.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ucapi-framework
3
- Version: 1.4.0b8
3
+ Version: 1.4.0b11
4
4
  Summary: ucapi framework that provides core functionality for building integrations.
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ucapi-framework"
3
- version = "1.4.0b8"
3
+ version = "1.4.0b11"
4
4
  description = "ucapi framework that provides core functionality for building integrations."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -1951,6 +1951,54 @@ class TestMigrationMethods:
1951
1951
  )
1952
1952
  assert migration_field is None
1953
1953
 
1954
+ # But migration_possible should NOT be present either
1955
+ # because ConcreteSetupFlow doesn't override get_migration_data
1956
+ migration_possible = next(
1957
+ (s for s in result.settings if s["id"] == "migration_possible"), None
1958
+ )
1959
+ assert migration_possible is None
1960
+
1961
+ @pytest.mark.asyncio
1962
+ async def test_migration_possible_when_get_migration_data_overridden(
1963
+ self, config_manager, mock_driver
1964
+ ):
1965
+ """Test that migration_possible field appears when get_migration_data is overridden."""
1966
+
1967
+ class FlowWithMigrationSupport(ConcreteSetupFlow):
1968
+ """Flow that has migration support."""
1969
+
1970
+ async def get_migration_data(self, previous_version, current_version):
1971
+ return {
1972
+ "previous_driver_id": "old_driver",
1973
+ "new_driver_id": "new_driver",
1974
+ "entity_mappings": [],
1975
+ }
1976
+
1977
+ setup_flow = FlowWithMigrationSupport(config_manager, driver=mock_driver)
1978
+
1979
+ # Simulate initial setup WITHOUT previous_version
1980
+ msg = DriverSetupRequest(reconfigure=False, setup_data={})
1981
+
1982
+ result = await setup_flow.handle_driver_setup(msg)
1983
+
1984
+ # Should get restore prompt screen
1985
+ assert isinstance(result, RequestUserInput)
1986
+
1987
+ # migration_required should NOT be present (no previous_version)
1988
+ migration_field = next(
1989
+ (s for s in result.settings if s["id"] == "migration_required"), None
1990
+ )
1991
+ assert migration_field is None
1992
+
1993
+ # But migration_possible SHOULD be present
1994
+ migration_possible = next(
1995
+ (s for s in result.settings if s["id"] == "migration_possible"), None
1996
+ )
1997
+ assert migration_possible is not None
1998
+ assert (
1999
+ "migration" in migration_possible["field"]["label"]["value"]["en"].lower()
2000
+ )
2001
+
1954
2002
  @pytest.mark.asyncio
1955
2003
  async def test_is_migration_required_default_returns_false(self, config_manager):
1956
2004
  """Test that default implementation of is_migration_required returns False."""
@@ -2125,6 +2173,7 @@ class TestMigrationMethods:
2125
2173
  remote_url="http://localhost",
2126
2174
  driver_id="mydriver",
2127
2175
  pin="1234",
2176
+ api_key=None,
2128
2177
  )
2129
2178
 
2130
2179
  # Should return success
@@ -714,6 +714,20 @@ class BaseSetupFlow(ABC, Generic[ConfigT]):
714
714
  self._pending_device_config = None
715
715
  return SetupError(error_type=IntegrationSetupError.OTHER)
716
716
 
717
+ def _has_migration_support(self) -> bool:
718
+ """
719
+ Check if this setup flow has migration support by detecting if
720
+ get_migration_data has been overridden from the base implementation.
721
+
722
+ :return: True if get_migration_data is overridden, False otherwise
723
+ """
724
+ # Get the method from this instance's class
725
+ this_method = self.__class__.get_migration_data
726
+ # Get the method from BaseSetupFlow
727
+ base_method = BaseSetupFlow.get_migration_data
728
+ # They're different if the subclass overrode it
729
+ return this_method is not base_method
730
+
717
731
  async def _build_restore_prompt_screen(self) -> RequestUserInput:
718
732
  """
719
733
  Build the restore prompt screen for initial setup.
@@ -745,6 +759,23 @@ class BaseSetupFlow(ABC, Generic[ConfigT]):
745
759
  "field": {"label": {"value": self._previous_version or ""}},
746
760
  }
747
761
  )
762
+ # If we can't determine migration requirement (no previous_version provided),
763
+ # check if get_migration_data is overridden to indicate migration support
764
+ elif self._migration_required is None and self._has_migration_support():
765
+ settings.append(
766
+ {
767
+ "id": "migration_possible",
768
+ "label": {"en": ""},
769
+ "field": {
770
+ "label": {
771
+ "value": {
772
+ "en": "This integration supports migration. "
773
+ "If upgrading, consult documentation for migration requirements."
774
+ }
775
+ }
776
+ },
777
+ }
778
+ )
748
779
 
749
780
  settings.append(
750
781
  {
@@ -1124,8 +1155,9 @@ class BaseSetupFlow(ABC, Generic[ConfigT]):
1124
1155
  """
1125
1156
  Handle migration execution request.
1126
1157
 
1127
- Expects previous_version, remote_url, and pin. If driver_id is configured,
1128
- current_version will be automatically fetched from the Remote.
1158
+ Expects previous_version, remote_url, and either pin or api_key.
1159
+ If driver_id is configured, current_version will be automatically
1160
+ fetched from the Remote.
1129
1161
  Calls get_migration_data() to get entity mappings, then calls
1130
1162
  migrate_entities_on_remote() to perform the migration on the Remote.
1131
1163
 
@@ -1136,6 +1168,7 @@ class BaseSetupFlow(ABC, Generic[ConfigT]):
1136
1168
  current_version = msg.input_values.get("current_version", "").strip()
1137
1169
  remote_url = msg.input_values.get("remote_url", "http://localhost").strip()
1138
1170
  pin = msg.input_values.get("pin", "").strip()
1171
+ api_key = msg.input_values.get("api_key", "").strip()
1139
1172
 
1140
1173
  # Ensure remote_url has protocol prefix
1141
1174
  if remote_url and not remote_url.startswith(("http://", "https://")):
@@ -1144,11 +1177,14 @@ class BaseSetupFlow(ABC, Generic[ConfigT]):
1144
1177
 
1145
1178
  # If driver has driver_id set and current_version is not provided, fetch it from Remote
1146
1179
  driver_id = self.driver.driver_id if self.driver else None
1147
- if driver_id and not current_version and remote_url and pin:
1180
+ if driver_id and not current_version and remote_url and (pin or api_key):
1148
1181
  _LOG.info("Fetching current version from Remote for driver %s", driver_id)
1149
1182
 
1150
1183
  fetched_version = await get_driver_version(
1151
- remote_url=remote_url, driver_id=driver_id, pin=pin
1184
+ remote_url=remote_url,
1185
+ driver_id=driver_id,
1186
+ pin=pin or None,
1187
+ api_key=api_key or None,
1152
1188
  )
1153
1189
 
1154
1190
  if fetched_version:
@@ -1163,8 +1199,8 @@ class BaseSetupFlow(ABC, Generic[ConfigT]):
1163
1199
  missing_fields.append("Current Version")
1164
1200
  if not remote_url:
1165
1201
  missing_fields.append("Remote URL")
1166
- if not pin:
1167
- missing_fields.append("Remote PIN")
1202
+ if not pin and not api_key:
1203
+ missing_fields.append("Remote PIN or API Key")
1168
1204
 
1169
1205
  if missing_fields:
1170
1206
  _LOG.warning(
@@ -1243,7 +1279,8 @@ class BaseSetupFlow(ABC, Generic[ConfigT]):
1243
1279
  success = await migrate_entities_on_remote(
1244
1280
  remote_url=remote_url,
1245
1281
  migration_data=migration_data,
1246
- pin=pin,
1282
+ pin=pin or None,
1283
+ api_key=api_key or None,
1247
1284
  )
1248
1285
 
1249
1286
  if not success:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ucapi-framework
3
- Version: 1.4.0b8
3
+ Version: 1.4.0b11
4
4
  Summary: ucapi framework that provides core functionality for building integrations.
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown