Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 85 additions & 6 deletions ProExporter/ContextCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
{
Expand All @@ -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
{
Expand All @@ -359,7 +365,7 @@ private static void CollectFeatureLayerInfo(FeatureLayer featureLayer, LayerInfo
}

// Selection count
if (!exportFastSchema)
if (!exportFastSchema && !IsServiceLayer(info.DataSourceKind))
{
try
{
Expand All @@ -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
{
Expand Down Expand Up @@ -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;
}

/// <summary>
/// Collect notebooks from the project
/// </summary>
Expand Down
3 changes: 2 additions & 1 deletion ProExporter/Serializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
33 changes: 33 additions & 0 deletions cli/tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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