I need to publish a feature service using python from an arcgis pro map to arcgis enterprise (with a federated server). The feature service should be referenced to enterprise geodatabase (not hosted).
I’ve written a script with the following steps/logic but at the end it doesn’t give me what I need. Do you suggest a better solution?
( this is not the code, just the logic. I can share it if it is needed.)
- Publish map service:
o Sign in to the portal (arcpy.SignInToPortal)
o Create a sharing draft form arcgis pro map:
o sharing_draft = m.getWebLayerSharingDraft(server_type="FEDERATED_SERVER", service_type="MAP_IMAGE", service_name=service)
o Export to SD Draft: sharing_draft.exportToSDDraft(sddraft_output_filename)
o Staging: arcpy.server.StageService(in_service_definition_draft=sddraft_output_filename, out_service_definition=sd_output_filename)
o arcpy.server.UploadServiceDefinition
- Enable Feature Access:
o Connect to service api with arcgis Rest API and change the json file:
o response = requests.post(service_url, data=params, verify=True)
o data = response.json()
o data['extensions'][i]['enabled'] = 'true'
o updated_svc_json = json.dumps(data)
o edit_svc_url = service_url + "/edit"
o params = {'token': token, 'f': 'pjson', 'service': updated_svc_json}
o response = requests.post(edit_svc_url, data=params, verify=True)
The script publishes map service from arcgis pro map, referencing to enterprise geodatabase (not hosted), enables the feature access BUT since the server is federated with the portal the script DOESN’T create feature layer item in portal which is the missing part in my workflow. Please advise.
Solved! Go to Solution.
I was able to finish the script. I am sharing it here in case you have same question.
import os
import arcpy
from arcgis.gis import GIS
import xml.dom.minidom as dom
def publish_service(portal_url=None, server_url=None, input_aprx=None, sddraft_folder=None):
# get the PSA account information
username, password = REDACTED
# Sign in to portal
arcpy.SignInToPortal(portal_url, username, password)
# Reference map to publish
aprx = arcpy.mp.ArcGISProject(input_aprx)
m = aprx.listMaps(wildcard="REDACTED")[0]
# get the service name (equal to db name in our case)
first_layer = m.listLayers()[0]
dict = first_layer.connectionProperties
service = dict['connection_info']['database']
sddraft_filename = service + ".sddraft"
sddraft_output_filename = os.path.join(sddraft_folder, sddraft_filename)
# Create FeatureSharingDraft and set service properties
sharing_draft = m.getWebLayerSharingDraft(server_type="FEDERATED_SERVER",
service_type="MAP_IMAGE",
service_name=service)
sharing_draft.copyDataToServer = False
sharing_draft.credits = "REDACTED"
sharing_draft.description = ""
sharing_draft.federatedServerUrl = server_url
sharing_draft.portalFolder = "REDACTED"
sharing_draft.serverFolder = "REDACTED"
sharing_draft.summary = service.replace("_", " ") + " REDACTED"
prj_name_list = service.replace("_", " ").split(" ")
sharing_draft.tags = prj_name_list[0] + ", " + prj_name_list[1] + ", " + "REDACTED1, REDACTED2, REDACTED3"
# Create Service Definition Draft file
sharing_draft.exportToSDDraft(sddraft_output_filename)
# Read the sddraft xml.
print("Modifying xml file to enable feature access...")
doc = dom.parse(sddraft_output_filename)
# Find all elements named TypeName. This is where the server object extension
# (SOE) names are defined.
typeNames = doc.getElementsByTagName('TypeName')
for typeName in typeNames:
# Get the TypeName we want to enable.
if typeName.firstChild.data == "FeatureServer":
extension = typeName.parentNode
for extElement in extension.childNodes:
# Enable Feature Access.
if extElement.tagName == 'Enabled':
extElement.firstChild.data = 'true'
print("Modifying xml file to enable Z Defaults...")
# Find all elements named Key.
typeNames = doc.getElementsByTagName('Key')
for typeName in typeNames:
# Get the TypeName we want to enable.
if typeName.firstChild.data == "enableZDefaults":
parent = typeName.parentNode
for extElement in parent.childNodes:
if extElement.tagName == 'Value':
extElement.firstChild.data = 'true'
# Output to a new sddraft.
sddraft_mod_xml = service + '_mod_xml' + '.sddraft'
sddraft_mod_xml_file = os.path.join(sddraft_folder, sddraft_mod_xml)
f = open(sddraft_mod_xml_file, 'w')
doc.writexml(f)
f.close()
# Stage Service
sd_filename = service + ".sd"
sd_output_filename = os.path.join(sddraft_folder, sd_filename)
arcpy.server.StageService(in_service_definition_draft=sddraft_mod_xml_file,
out_service_definition=sd_output_filename)
# Share to portal
print("Uploading service definition...")
arcpy.server.UploadServiceDefinition(in_sd_file=sd_output_filename,
in_server=server_url)
print("Successfully uploaded service.")
# Sharing portal items
## Sign in to Portal with ArcGIS API for Python
print(f"Sign in to Portal: {portal_url} ...")
from arcgis.gis import GIS
username, password = REDACTED
gis = GIS(url=portal_url, username=username, password=password)
item_name = service.split("/")[-1]
items = gis.content.search(query=f"title:{item_name}",
max_items=2)
for item in items:
print(f"Sharing {item.title} {item.type}...")
item.share(everyone=True)
feature_service_url = f"{server_url}/rest/services/REDACTED/{service}/FeatureServer"
print(f"Feature Service URL is: {feature_service_url}")
return feature_service_url
My recommendation would be to use Fiddler or some other network traffic logger. I did something similar for ArcMap and a hosted feature service in ArcGIS Online. I suspect there is a REST call to create the item by ArcGIS Pro. If not, look in the SD draft XML.
I would use the arcgis python api instead of arcpy for authentication and publishing.
https://developers.arcgis.com/python/api-reference/
I was able to finish the script. I am sharing it here in case you have same question.
import os
import arcpy
from arcgis.gis import GIS
import xml.dom.minidom as dom
def publish_service(portal_url=None, server_url=None, input_aprx=None, sddraft_folder=None):
# get the PSA account information
username, password = REDACTED
# Sign in to portal
arcpy.SignInToPortal(portal_url, username, password)
# Reference map to publish
aprx = arcpy.mp.ArcGISProject(input_aprx)
m = aprx.listMaps(wildcard="REDACTED")[0]
# get the service name (equal to db name in our case)
first_layer = m.listLayers()[0]
dict = first_layer.connectionProperties
service = dict['connection_info']['database']
sddraft_filename = service + ".sddraft"
sddraft_output_filename = os.path.join(sddraft_folder, sddraft_filename)
# Create FeatureSharingDraft and set service properties
sharing_draft = m.getWebLayerSharingDraft(server_type="FEDERATED_SERVER",
service_type="MAP_IMAGE",
service_name=service)
sharing_draft.copyDataToServer = False
sharing_draft.credits = "REDACTED"
sharing_draft.description = ""
sharing_draft.federatedServerUrl = server_url
sharing_draft.portalFolder = "REDACTED"
sharing_draft.serverFolder = "REDACTED"
sharing_draft.summary = service.replace("_", " ") + " REDACTED"
prj_name_list = service.replace("_", " ").split(" ")
sharing_draft.tags = prj_name_list[0] + ", " + prj_name_list[1] + ", " + "REDACTED1, REDACTED2, REDACTED3"
# Create Service Definition Draft file
sharing_draft.exportToSDDraft(sddraft_output_filename)
# Read the sddraft xml.
print("Modifying xml file to enable feature access...")
doc = dom.parse(sddraft_output_filename)
# Find all elements named TypeName. This is where the server object extension
# (SOE) names are defined.
typeNames = doc.getElementsByTagName('TypeName')
for typeName in typeNames:
# Get the TypeName we want to enable.
if typeName.firstChild.data == "FeatureServer":
extension = typeName.parentNode
for extElement in extension.childNodes:
# Enable Feature Access.
if extElement.tagName == 'Enabled':
extElement.firstChild.data = 'true'
print("Modifying xml file to enable Z Defaults...")
# Find all elements named Key.
typeNames = doc.getElementsByTagName('Key')
for typeName in typeNames:
# Get the TypeName we want to enable.
if typeName.firstChild.data == "enableZDefaults":
parent = typeName.parentNode
for extElement in parent.childNodes:
if extElement.tagName == 'Value':
extElement.firstChild.data = 'true'
# Output to a new sddraft.
sddraft_mod_xml = service + '_mod_xml' + '.sddraft'
sddraft_mod_xml_file = os.path.join(sddraft_folder, sddraft_mod_xml)
f = open(sddraft_mod_xml_file, 'w')
doc.writexml(f)
f.close()
# Stage Service
sd_filename = service + ".sd"
sd_output_filename = os.path.join(sddraft_folder, sd_filename)
arcpy.server.StageService(in_service_definition_draft=sddraft_mod_xml_file,
out_service_definition=sd_output_filename)
# Share to portal
print("Uploading service definition...")
arcpy.server.UploadServiceDefinition(in_sd_file=sd_output_filename,
in_server=server_url)
print("Successfully uploaded service.")
# Sharing portal items
## Sign in to Portal with ArcGIS API for Python
print(f"Sign in to Portal: {portal_url} ...")
from arcgis.gis import GIS
username, password = REDACTED
gis = GIS(url=portal_url, username=username, password=password)
item_name = service.split("/")[-1]
items = gis.content.search(query=f"title:{item_name}",
max_items=2)
for item in items:
print(f"Sharing {item.title} {item.type}...")
item.share(everyone=True)
feature_service_url = f"{server_url}/rest/services/REDACTED/{service}/FeatureServer"
print(f"Feature Service URL is: {feature_service_url}")
return feature_service_url
Thanks, Azin! When I move my Python 2 code to Python 3, I will rewrite the code to use the latest OOTB functionality. I see how so much of my custom code is now OOTB.
update:
enabling feature access is possible now by:
# Create MapImageSharingDraft, set copyDataToServer property to False to reference registered data, set CIM symbols, and map operations. Include feature layer and enable capabilities.
server_type = "FEDERATED_SERVER"
federated_server_url = "https://MyFederatedServer.esri.com/serverWebadaptor"
sddraft = m.getWebLayerSharingDraft(server_type, "MAP_IMAGE", service_name)
sddraft.federatedServerUrl = federated_server_url
sddraft.copyDataToServer = False
sddraft.useCIMSymbols = True
sddraft.mapOperations = "Map,Data"
sddraft.extension.feature.isEnabled = True
sddraft.extension.feature.featureCapabilities = "Create,Sync,Query"