Skip to main content

SAP Analytics Cloud

Overview​

SAP Analytics Cloud is a business intelligence and analytics platform. Learn more in the official SAP Analytics Cloud documentation.

The DataHub integration for SAP Analytics Cloud covers BI entities such as dashboards, charts, datasets, and related ownership context. It also captures table-level lineage and stateful deletion detection.

Concept Mapping​

SAP Analytics CloudDataHub
StoryDashboard
ApplicationDashboard
Live Data ModelDataset
Import Data ModelDataset
ModelDataset

Module sac​

Beta

Important Capabilities​

CapabilityStatusNotes
Descriptions✅Enabled by default.
Detect Deleted Entities✅Enabled by default via stateful ingestion.
Platform Instance✅Enabled by default.
Schema Metadata✅Enabled by default for Import Data Models; acquired Data Models are opt-in via ingest_acquired_data_model_schema_metadata.
Table-Level Lineage✅Enabled by default (only for Live Data Models).

Overview​

The sac module ingests metadata from SAP Analytics Cloud (SAC) into DataHub. It is intended for production ingestion workflows and module-specific capabilities are documented below.

Prerequisites​

Before running ingestion, ensure network connectivity to the source, valid authentication credentials, and read permissions for metadata APIs required by this module.

  1. Refer to Manage OAuth Clients to create an OAuth client in SAP Analytics Cloud. The OAuth client is required to have the following properties:

    • Purpose: API Access
    • Access:
      • Story Listing
      • Data Import Service
      • Data Export Service (only required when the opt-in ingest_acquired_data_model_schema_metadata is enabled - the schema of acquired Data Models is then read via the Data Export Service)
      • File Repository Read (only required when the opt-in ingest_private_content is enabled - used to read each resource's folderType so public and private root folders are labeled correctly)
    • Authorization Grant: Client Credentials
  2. Maintain connection mappings (optional):

To map individual connections in SAP Analytics Cloud to platforms, platform instances and environments, the connection_mapping configuration can be used within the recipe:

connection_mapping:
MY_BW_CONNECTION:
platform: bw
platform_instance: PROD_BW
env: PROD
MY_HANA_CONNECTION:
platform: hana
platform_instance: PROD_HANA
env: PROD

The key in the connection mapping dictionary represents the name of the connection created in SAP Analytics Cloud.

Content scope: public vs. non-public content​

By default the connector ingests only public content (stories and analytic applications saved in SAC's Public folder). This matches what most users browse under Files → My Files → Public and requires only the Story Listing access grant. If that is all you need, no further configuration is required.

To also ingest non-public content (private My Files and team folders), enable ingest_private_content. What you actually get back depends on the OAuth client's permissions, because SAC scopes results to the requesting principal:

ModeRecipeOAuth client permissionsWhat is ingested
Public only (default)(none)Story ListingAll public stories and applications.
What the client can seeingest_private_content: trueStory Listing + File Repository ReadPublic content plus any private/team content directly shared with the OAuth client. A client-credentials client owns no personal content, so in practice this adds only content explicitly shared with it.
Tenant-wideingest_private_content: true
apply_manage_privilege: true
Story Listing + File Repository Read + a role with the Manage privilege on Private Files and/or Public FilesPublic content plus all private and team content on the tenant (SAC's administrative System view).

apply_manage_privilege appends applyManagePrivilege=true to the resource requests. Without the Manage privilege on the OAuth client it has no effect, so combine it with the permission grant above.

When ingest_private_content is enabled the connector reads each resource's folderType from the SAC File Repository API (/api/v1/filerepository/Resources) so the built-in root folders are labeled by type rather than by their language-localized display name. The canonical labels are configurable via public_root_folder_name (default Public) and private_root_folder_name (default My Files). Team folders and other roots keep their given names.

All private content shares a single private_root_folder_name root. When apply_manage_privilege surfaces other users' private content, their stories are grouped under the same My Files browse tree rather than a per-user folder; each asset's owner is still recorded individually via its createdBy ownership.

If the File Repository API is unavailable, or a public resource is missing from it (a permission skew between Story Listing and File Repository Read, or paging truncation), public roots are still canonicalized from each resource's own isPublic flag; only non-public roots then fall back to their raw localized names, and ingestion continues.

Install the Plugin​

pip install 'acryl-datahub[sac]'

Starter Recipe​

Check out the following recipe to get started with ingestion! See below for full configuration options.

For general pointers on writing and running a recipe, see our main recipe guide.

source:
type: sac
config:
stateful_ingestion:
enabled: true

tenant_url: # Your SAP Analytics Cloud tenant URL, e.g. https://company.eu10.sapanalytics.cloud or https://company.eu10.hcs.cloud.sap
token_url: # The Token URL of your SAP Analytics Cloud tenant, e.g. https://company.eu10.hana.ondemand.com/oauth/token.

# Add secret in Secrets Tab with relevant names for each variable
client_id: "${SAC_CLIENT_ID}" # Your SAP Analytics Cloud client id
client_secret: "${SAC_CLIENT_SECRET}" # Your SAP Analytics Cloud client secret

# ingest stories
ingest_stories: true

# ingest applications
ingest_applications: true

resource_id_pattern:
allow:
- .*

resource_name_pattern:
allow:
- .*

# SAC returns its built-in public root folder in each resource's browse path
# localized to the resource language (e.g. "Public" / "Öffentlich"). The connector
# rewrites that first segment to this canonical label so all public content shares
# one browse tree; set to null to keep the raw localized name.
public_root_folder_name: Public

# Only a first browse-path segment matching one of these labels (case-insensitive)
# is treated as the public root and rewritten. Other shared roots that SAC can also
# return (e.g. a team/workspace root like "Default") are left untouched. Add your
# tenant language's public-root label if it is not already listed.
public_root_folder_aliases:
- Public
- Öffentlich

# folder_pattern is matched against the canonicalized browse path, so write rules
# against public_root_folder_name (e.g. Public/...) rather than a localized root.
folder_pattern:
allow:
- .*

connection_mapping:
MY_BW_CONNECTION:
platform: bw
platform_instance: PROD_BW
env: PROD
MY_HANA_CONNECTION:
platform: hana
platform_instance: PROD_HANA
env: PROD
# SAP Datasphere (DWC) connection: SAC does not expose the Datasphere space,
# so set it here to build the upstream sap-datasphere lineage (<space>.<model_name>).
MY_DATASPHERE_CONNECTION:
datasphere_space: my_space
# platform_instance / env / convert_urns_to_lowercase must match how the
# SAP Datasphere connector ingested the assets so the urns stitch.
env: PROD
convert_urns_to_lowercase: true

# Emit column-level lineage to SAP Datasphere by mirroring the upstream dataset's
# schema from DataHub (requires a datahub_api/graph connection and SAP Datasphere
# to be ingested first). Falls back to table-level lineage when unavailable.
resolve_datasphere_column_lineage: true

Config Details​

Note that a . is used to denote nested fields in the YAML recipe.

FieldDescription
client_id ✅
string
Client ID for the OAuth authentication
client_secret ✅
string(password)
Client secret for the OAuth authentication
tenant_url ✅
string
URL of the SAP Analytics Cloud tenant
token_url ✅
string
URL of the OAuth token endpoint of the SAP Analytics Cloud tenant
apply_manage_privilege
boolean
Append applyManagePrivilege=true to resource requests, which asks SAC to return all content on the tenant (the administrative System view) instead of only what the OAuth client can see directly. This requires the OAuth client to be assigned a role with the Manage privilege on Private Files and/or Public Files; without that privilege the parameter has no effect. Intended to be combined with ingest_private_content to ingest non-public content tenant-wide.
Default: False
incremental_lineage
boolean
When enabled, emits lineage as incremental to existing lineage already in DataHub. When disabled, re-states lineage on each run.
Default: False
ingest_acquired_data_model_schema_metadata
boolean
Controls whether schema metadata of acquired (non-import) Data Models is ingested via the Data Export Service. Live Data Models keep their schema in the source system and are skipped. Ingesting this schema adds one metadata request per acquired model. Requires the 'Data Export Service' access grant on the SAC OAuth client (see Prerequisites).
Default: False
ingest_applications
boolean
Controls whether Analytic Applications should be ingested
Default: True
ingest_import_data_model_schema_metadata
boolean
Controls whether schema metadata of Import Data Models should be ingested (ingesting schema metadata of Import Data Models significantly increases overall ingestion time)
Default: True
ingest_private_content
boolean
By default the connector ingests only public content (isPublic eq 1). When enabled, the isPublic filter is dropped so private and team content the OAuth client can see is ingested as well. With a client-credentials OAuth client (a technical user that owns no content), this alone typically surfaces little beyond public content unless apply_manage_privilege is also enabled and the client holds the Manage privilege on Private/Public Files. When enabled, the connector additionally reads each resource's folderType from the File Repository API to canonicalize root folders correctly (public vs. private) rather than assuming everything is public.
Default: False
ingest_stories
boolean
Controls whether Stories should be ingested
Default: True
platform_instance
One of string, null
The instance of the platform that all assets produced by this recipe belong to. This should be unique within the platform. See https://docs.datahub.com/docs/platform-instances/ for more details.
Default: None
private_root_folder_name
One of string, null
Canonical display name for SAC's built-in private (My Files) root folder. Only relevant when ingest_private_content is enabled; the first ancestorPath segment of a resource whose folderType is PRIVATE is rewritten to this value (the same localization dedup as public_root_folder_name, applied to the private root). Set to null to keep the raw localized name.
Default: My Files
public_root_folder_name
One of string, null
Canonical display name for SAC's built-in public root folder. SAC returns this root in each resource's ancestorPath localized to the resource's language (e.g. 'Public' in English, 'Öffentlich' in German), which would otherwise split the same physical root into one browse-path tree per language. Only a first ancestorPath segment matching public_root_folder_aliases is rewritten to this value; other roots are left untouched. User-created sub-folders keep their given name. Set to null to disable rewriting and keep the raw localized name.
Default: Public
query_name_template
One of string, null
Template for generating dataset urns of consumed queries, the placeholder {query} can be used within the template for inserting the name of the query
Default: QUERY/{name}
resolve_datasphere_column_lineage
boolean
In addition to table-level DWC lineage, emit column-level lineage to the backing SAP Datasphere dataset. SAC does not expose columns for Live Data Models, so the field list is resolved from the upstream Datasphere dataset's schema in DataHub (requires a datahub_api/graph connection) and mirrored onto the SAC dataset, since the live model is a passthrough. Falls back to table-level lineage when the graph or upstream schema is unavailable. No effect unless resolve_datasphere_lineage is also enabled.
Default: True
resolve_datasphere_lineage
boolean
For SAC Live Data Models backed by SAP Datasphere (Data Warehouse Cloud / 'DWC' connections), emit upstream lineage to the backing SAP Datasphere dataset. The Datasphere object's technical name is derived from the SAC model name; the Datasphere space is not exposed by SAC and must be supplied via connection_mapping.<connection_id>.datasphere_space. The upstream urn is built as <space>.<model_name>. SAC disambiguates repeated links to the same Datasphere object with a trailing numeric suffix that the real object does not carry; when a datahub_api/graph connection is available this is corrected by matching the suffix-trimmed name against an ingested Datasphere dataset. Models on connections without a configured datasphere_space are skipped with a warning.
Default: True
env
string
The environment that all assets produced by this connector belong to
Default: PROD
connection_mapping
map(str,ConnectionMappingConfig)
connection_mapping.key.env
string
The environment that this connection mapping belongs to
Default: PROD
connection_mapping.key.convert_urns_to_lowercase
boolean
Whether to lower-case identifiers when constructing the upstream dataset urn for this connection. Must match the convert_urns_to_lowercase setting used by the corresponding upstream connector recipe so the urns stitch. Currently applied to SAP Datasphere ('DWC') upstreams only; BW/HANA upstreams preserve case as before. Defaults to True (matching the SAP Datasphere connector default).
Default: True
connection_mapping.key.datasphere_space
One of string, null
For SAP Datasphere ('DWC') connections only: the Datasphere space id that backs this connection (e.g. bdap_sac). SAC does not expose the space for Datasphere-backed live models, so it must be supplied here to build the upstream sap-datasphere dataset urn (<space>.<model_name>). Leave unset for non-Datasphere connections.
Default: None
connection_mapping.key.platform
One of string, null
The platform that this connection mapping belongs to
Default: None
connection_mapping.key.platform_instance
One of string, null
The instance of the platform that this connection mapping belongs to
Default: None
folder_pattern
AllowDenyPattern
A class to store allow deny regexes.

Patterns are matched against the start of the string only, not the entire
string - a pattern does not need to match to the end to be considered a match.
For example, the pattern "prod" matches "prod", "prod_east", and "production".
To require an exact match, anchor your pattern explicitly, e.g. "^prod$".
folder_pattern.ignoreCase
One of boolean, null
Whether to ignore case sensitivity during pattern matching.
Default: True
public_root_folder_aliases
array
Root folder display names that identify SAC's built-in public root across tenant languages. isPublic eq 1 can also return other shared roots (e.g. a team/workspace root such as 'Default'), so only a first ancestorPath segment matching one of these labels (case-insensitive) is rewritten to public_root_folder_name — any other root is preserved rather than merged into the public tree. Add your tenant language's label if it is not listed. When ingest_private_content is enabled and a resource's folderType is known from the File Repository, that type is authoritative and the public root is canonicalized regardless of this list; the aliases only gate the fallback where folderType is unavailable.
Default: ['Public', 'Öffentlich']
public_root_folder_aliases.string
string
resource_id_pattern
AllowDenyPattern
A class to store allow deny regexes.

Patterns are matched against the start of the string only, not the entire
string - a pattern does not need to match to the end to be considered a match.
For example, the pattern "prod" matches "prod", "prod_east", and "production".
To require an exact match, anchor your pattern explicitly, e.g. "^prod$".
resource_id_pattern.ignoreCase
One of boolean, null
Whether to ignore case sensitivity during pattern matching.
Default: True
resource_name_pattern
AllowDenyPattern
A class to store allow deny regexes.

Patterns are matched against the start of the string only, not the entire
string - a pattern does not need to match to the end to be considered a match.
For example, the pattern "prod" matches "prod", "prod_east", and "production".
To require an exact match, anchor your pattern explicitly, e.g. "^prod$".
resource_name_pattern.ignoreCase
One of boolean, null
Whether to ignore case sensitivity during pattern matching.
Default: True
stateful_ingestion
One of StatefulStaleMetadataRemovalConfig, null
Stateful ingestion related configs
Default: None
stateful_ingestion.enabled
boolean
Whether or not to enable stateful ingest. Default: True if a pipeline_name is set and either a datahub-rest sink or datahub_api is specified, otherwise False
Default: False
stateful_ingestion.fail_safe_threshold
number
Prevents large amount of soft deletes & the state from committing from accidental changes to the source configuration if the relative change percent in entities compared to the previous state is above the 'fail_safe_threshold'.
Default: 75.0
stateful_ingestion.remove_stale_metadata
boolean
Soft-deletes the entities present in the last successful run but missing in the current run with stateful_ingestion enabled.
Default: True

Capabilities​

Use the Important Capabilities table above as the source of truth for supported features and whether additional configuration is required.

Limitations​

  • Only models which are used in a Story or an Application will be ingested because there is no dedicated API to retrieve models (only for Stories and Applications).
  • SAC does not return a folder for models, so they are grouped under a single synthetic Models root (which the connector invents) rather than the meaningless t > 4 tree GMS would otherwise derive from the dotted internal dataset name. folder_pattern is not applied to model browse paths — it is matched only against story and application paths.
  • Browse Paths for stories and applications start at SAC's built-in public root folder, which the API returns localized to the resource language (e.g. Public / Öffentlich). The connector canonicalizes that first segment to a single label (Public by default, configurable via public_root_folder_name, or null to keep the raw localized name) so the same physical root does not split into one browse tree per language. Only a first segment matching public_root_folder_aliases (defaults to Public / Öffentlich, case-insensitive) is rewritten; other shared roots SAC can return (e.g. a team/workspace root such as Default) are left unchanged. folder_pattern is matched against this canonicalized path.
  • Schema metadata is ingested for Import Data Models by default. For acquired (SAC-stored) models it can additionally be ingested via the Data Export Service $metadata document by enabling the opt-in ingest_acquired_data_model_schema_metadata flag (off by default; requires the "Data Export Service" OAuth grant). Live Data Models (e.g. BW/HANA/DWC) keep their schema in the source system, so it cannot be retrieved from SAC.
  • Lineages for Import Data Models cannot be ingested because the API is not providing any information about it.
  • SAP BW, SAP HANA, and SAP Datasphere (Data Warehouse Cloud / DWC connections) are supported for ingesting the upstream lineages of Live Data Models - a warning is logged for all other connection types, please feel free to open an issue on GitHub with the warning message to have this fixed.
  • For SAP Datasphere-backed Live Data Models, SAC exposes the underlying object's name but not its Datasphere space. Configure the space per connection via connection_mapping.<connection_id>.datasphere_space so the upstream sap-datasphere dataset urn (<space>.<model_name>) can be built. Set connection_mapping.<connection_id>.convert_urns_to_lowercase (default true) to match the casing used by your SAP Datasphere connector recipe. Models on connections without a configured datasphere_space are skipped with a warning (or set resolve_datasphere_lineage: false to disable this entirely).
  • Column-level lineage to SAP Datasphere is emitted when resolve_datasphere_column_lineage is enabled (default true). SAC does not expose columns for Live Data Models, so the field list is resolved from the upstream SAP Datasphere dataset's schema in DataHub — this requires a datahub_api/graph connection and that SAP Datasphere has already been ingested. Because the live model is a passthrough, that schema is mirrored onto the SAC dataset and each field is mapped to itself. When the graph or upstream schema is unavailable it falls back to table-level lineage only.
  • Ingest SAP Datasphere before SAC. SAC disambiguates repeated links to the same Datasphere object with a trailing numeric suffix (e.g. ...STATEMENT2) that the real object does not carry. The connector corrects this against the graph, but only once the real Datasphere dataset is present. If SAC runs first, the model's upstream edge is written against the as-is (suffixed) name and is corrected on the next SAC run after Datasphere is ingested — upstreamLineage is overwritten, so no duplicate edge remains. Column-level lineage has the same ordering requirement.
  • Upgrading from a connector version without suffix correction does not clean up datasetKey stubs that earlier runs may have emitted for the uncorrected suffixed urns. They are written as non-primary sources, so stale-entity removal leaves them behind as orphan Datasphere nodes; remove them manually (e.g. via datahub delete) if they are a concern.
  • For some models (e.g., builtin models) it cannot be detected whether the models are Live Data or Import Data Models. Therefore, these models will be ingested only with the Story subtype.

Limitations​

Module behavior is constrained by source APIs, permissions, and metadata exposed by the platform. Refer to capability notes for unsupported or conditional features.

Troubleshooting​

If ingestion fails, validate credentials, permissions, connectivity, and scope filters first. Then review ingestion logs for source-specific errors and adjust configuration accordingly.

Compatibility across tenant generations​

The connector reads stories, applications, and models from the Resources OData data endpoints (for example api/v1/Resources) directly, rather than discovering them from the tenant's $metadata document. This keeps ingestion working across SAP Analytics Cloud tenant generations: newer (CAP-based) tenants no longer describe the Resources entity set in $metadata (it is replaced there by a non-queryable RESOURCES_INDEX catalog), but the Resources data endpoints remain available and are what the connector uses.

Code Coordinates​

  • Class Name: datahub.ingestion.source.sac.sac.SACSource
  • Browse on GitHub
Questions?

If you've got any questions on configuring ingestion for SAP Analytics Cloud, feel free to ping us on our Slack.

💡 Contributing to this documentation

This page is auto-generated from the underlying source code. To make changes, please edit the relevant source files in the metadata-ingestion directory.

Tip: For quick typo fixes or documentation updates, you can click the ✏️ Edit icon directly in the GitHub UI to open a Pull Request. For larger changes and PR naming conventions, please refer to our Contributing Guide.