From a19d7a8ebcc4e4c5f715bfe140159c328e17cf84 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:33:49 +0000 Subject: [PATCH 1/2] Initial plan From 0b62c657270d4ec95bc397d7ae3ac0d255a51203 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:36:46 +0000 Subject: [PATCH 2/2] fix(addin): trim service layer exports Co-authored-by: danmaps <10421813+danmaps@users.noreply.github.com> --- ProExporter/ContextCollector.cs | 91 ++++++++++++++++++++++++++++++--- ProExporter/Serializer.cs | 3 +- cli/tests/test_cli.py | 33 ++++++++++++ 3 files changed, 120 insertions(+), 7 deletions(-) diff --git a/ProExporter/ContextCollector.cs b/ProExporter/ContextCollector.cs index 59a2b45..8e3ae03 100644 --- a/ProExporter/ContextCollector.cs +++ b/ProExporter/ContextCollector.cs @@ -304,8 +304,9 @@ private static void CollectFeatureLayerInfo(FeatureLayer featureLayer, LayerInfo info.DataSourcePath = GetDataStorePath(dataStore); info.DataSourceType = GetDataStoreType(dataStore); info.DataSourceKind = NormalizeDataSourceKind(info.DataSourceType, info.DataSourcePath, null); + var isServiceLayer = IsServiceLayer(info.DataSourceKind); - if (!exportFastSchema) + if (!exportFastSchema && !isServiceLayer) { // Feature count (can be slow for large datasets) try @@ -318,15 +319,20 @@ private static void CollectFeatureLayerInfo(FeatureLayer featureLayer, LayerInfo } } - // Fields (if enabled) - if (exportFields) + // Service layers can carry very large service definitions/field payloads. + // Keep snapshot exports concise by omitting heavyweight details. + if (isServiceLayer) + { + info.DataSourcePath = GetConciseServiceUrl(info.DataSourcePath); + } + else if (exportFields) { var fcDef = fc.GetDefinition(); info.Fields = exportFastSchema ? CollectFieldInfoFast(fcDef) : CollectFieldInfo(fcDef); } // Sample data (if enabled) - if (!exportFastSchema && sampleRowCount > 0) + if (!isServiceLayer && !exportFastSchema && sampleRowCount > 0) { try { @@ -339,7 +345,7 @@ private static void CollectFeatureLayerInfo(FeatureLayer featureLayer, LayerInfo } // Field statistics (if enabled) - if (options.ExportFieldStats && info.Fields != null && info.Fields.Count > 0) + if (!isServiceLayer && options.ExportFieldStats && info.Fields != null && info.Fields.Count > 0) { try { @@ -359,7 +365,7 @@ private static void CollectFeatureLayerInfo(FeatureLayer featureLayer, LayerInfo } // Selection count - if (!exportFastSchema) + if (!exportFastSchema && !IsServiceLayer(info.DataSourceKind)) { try { @@ -377,6 +383,11 @@ private static void CollectFeatureLayerInfo(FeatureLayer featureLayer, LayerInfo info.DataSourceKind = NormalizeDataSourceKind(info.DataSourceType, info.DataSourcePath, null); } + if (IsServiceLayer(info.DataSourceKind)) + { + info.DataSourcePath = GetConciseServiceUrl(info.DataSourcePath); + } + // Renderer info try { @@ -816,6 +827,74 @@ private static string NormalizeDataSourceKind(string dataSourceType, string data return "unknown"; } + private static bool IsServiceLayer(string dataSourceKind) + { + return string.Equals(dataSourceKind, "service", StringComparison.OrdinalIgnoreCase); + } + + private static string GetConciseServiceUrl(string dataSourcePath) + { + if (string.IsNullOrWhiteSpace(dataSourcePath)) + { + return dataSourcePath; + } + + var trimmed = dataSourcePath.Trim(); + if (TryGetServiceUri(trimmed, out var directUri)) + { + return directUri.GetLeftPart(UriPartial.Path); + } + + var httpIndex = trimmed.IndexOf("https://", StringComparison.OrdinalIgnoreCase); + if (httpIndex < 0) + { + httpIndex = trimmed.IndexOf("http://", StringComparison.OrdinalIgnoreCase); + } + + if (httpIndex < 0) + { + return trimmed; + } + + var candidate = trimmed.Substring(httpIndex); + var delimiters = new[] { ';', '\r', '\n', '\t', '"', '\'', ' ', ')', '}', ']' }; + var endIndex = candidate.Length; + foreach (var delimiter in delimiters) + { + var delimiterIndex = candidate.IndexOf(delimiter); + if (delimiterIndex >= 0 && delimiterIndex < endIndex) + { + endIndex = delimiterIndex; + } + } + + candidate = candidate.Substring(0, endIndex).TrimEnd(',', ';'); + if (TryGetServiceUri(candidate, out var embeddedUri)) + { + return embeddedUri.GetLeftPart(UriPartial.Path); + } + + return candidate; + } + + private static bool TryGetServiceUri(string value, out Uri uri) + { + uri = null; + if (!Uri.TryCreate(value, UriKind.Absolute, out var parsed)) + { + return false; + } + + if (!string.Equals(parsed.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) && + !string.Equals(parsed.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + uri = parsed; + return true; + } + /// /// Collect notebooks from the project /// diff --git a/ProExporter/Serializer.cs b/ProExporter/Serializer.cs index 07381dc..13d7489 100644 --- a/ProExporter/Serializer.cs +++ b/ProExporter/Serializer.cs @@ -512,7 +512,7 @@ arcgis context # Full markdown summary ├── context/ │ ├── project.json # Project metadata │ ├── maps.json # All maps with extents/scales - │ ├── layers.json # All layers with field schemas + │ ├── layers.json # All layers (service layers are metadata-only) │ ├── tables.json # Standalone tables │ ├── connections.json # Data connections │ ├── layouts.json # Print layouts @@ -552,6 +552,7 @@ arcgis context # Full markdown summary - `fields[]` — Array of field definitions - `sampleData[]` — Up to `sampleRowCount` sample rows (attributes only by default; geometry included only when `sampleGeometry: true`) +- Service-backed layers omit heavyweight field/sample payloads and keep only concise source metadata ### maps.json - `isActiveMap` — true = user is currently viewing this map diff --git a/cli/tests/test_cli.py b/cli/tests/test_cli.py index bea01c9..6780ec1 100644 --- a/cli/tests/test_cli.py +++ b/cli/tests/test_cli.py @@ -162,3 +162,36 @@ def test_layer_shows_active_map_flag(): result = runner.invoke(main, ["layer", "Layer 1"]) assert result.exit_code == 0 assert "Active map: Yes" in result.output + + +def test_service_layer_metadata_only_still_displays(): + runner = CliRunner() + with runner.isolated_filesystem(): + from pathlib import Path + + _write_json( + Path(".arcgispro/context/maps.json"), + [{"name": "Map A", "isActiveMap": True}], + ) + _write_json( + Path(".arcgispro/context/layers.json"), + [ + { + "name": "Hosted Parcels", + "mapName": "Map A", + "layerType": "FeatureLayer", + "geometryType": "Polygon", + "isVisible": True, + "isEditable": False, + "isBroken": False, + "dataSourceKind": "service", + "dataSourcePath": "https://example.arcgis.com/FeatureServer/0", + } + ], + ) + + result = runner.invoke(main, ["layer", "Hosted Parcels"]) + assert result.exit_code == 0 + assert "Hosted Parcels" in result.output + assert "Source: " in result.output + assert "https://example.arcgis.com/FeatureServer/0" in result.output