Skip to content
Open
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
8 changes: 8 additions & 0 deletions integrations/servicenow/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

<!-- towncrier release notes start -->

## 0.3.38 (2026-04-02)


### Improvements

- Fix selector and kind propagation


## 0.3.37 (2026-03-30)


Expand Down
91 changes: 84 additions & 7 deletions integrations/servicenow/integration.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Literal
from typing import Any, ClassVar, Literal

from port_ocean.core.handlers import APIPortAppConfig
from port_ocean.core.handlers.port_app_config.models import (
Expand All @@ -23,20 +23,24 @@ class APIQueryParams(BaseModel):
sysparm_display_value: Literal["true", "false", "all"] | None = Field(
alias="sysparmDisplayValue",
default="true",
title="Display Value",
description="Determines the type of data returned, either the actual values from the database or the display values of the fields",
)
sysparm_fields: str | None = Field(
alias="sysparmFields",
description="Comma-separated list of fields to return in the response",
default=None,
title="Fields",
description="Comma-separated list of fields to return in the response (e.g. 'sys_id,name,description,state')",
)
sysparm_exclude_reference_link: Literal["true", "false"] | None = Field(
alias="sysparmExcludeReferenceLink",
default="false",
title="Exclude Reference Link",
description="Flag that indicates whether to exclude Table API links for reference fields",
)
sysparm_query: str | None = Field(
alias="sysparmQuery",
title="Query",
description=(
"Encoded query used to filter the result set. Syntax: <col_name><operator><value>"
"<col_name>: Name of the table column to filter against"
Expand All @@ -60,20 +64,93 @@ class ResourceSelector(Selector):
api_query_params: APIQueryParams | None = Field(
alias="apiQueryParams",
default_factory=APIQueryParams,
title="API Query Params",
description="The query parameters used to filter resources from the ServiceNow API",
)


class ServiceNowResourceConfig(ResourceConfig):
selector: ResourceSelector
class IncidentResourceConfig(ResourceConfig):
selector: ResourceSelector = Field(
title="Incident Selector",
description="Selector for filtering ServiceNow incident records",
)
kind: Literal[ObjectKind.INCIDENT] = Field(
title="ServiceNow Incident",
description="A ServiceNow incident record from the incident table",
)


class ServiceNowPortAppConfig(PortAppConfig):
resources: list[ServiceNowResourceConfig | ResourceConfig] = Field(
default_factory=list
class UserGroupResourceConfig(ResourceConfig):
selector: ResourceSelector = Field(
title="User Group Selector",
description="Selector for filtering ServiceNow user group records",
)
kind: Literal[ObjectKind.USER_GROUP] = Field(
title="ServiceNow User Group",
description="A ServiceNow user group from the sys_user_group table",
)


class ServiceCatalogResourceConfig(ResourceConfig):
selector: ResourceSelector = Field(
title="Service Catalog Selector",
description="Selector for filtering ServiceNow service catalog records",
)
kind: Literal[ObjectKind.SERVICE_CATALOG] = Field(
title="ServiceNow Service Catalog",
description="A ServiceNow service catalog from the sc_catalog table",
)


class VulnerabilityResourceConfig(ResourceConfig):
selector: ResourceSelector = Field(
title="Vulnerability Selector",
description="Selector for filtering ServiceNow vulnerable item records",
)
kind: Literal[ObjectKind.VULNERABILITY] = Field(
title="ServiceNow Vulnerability",
description="A ServiceNow vulnerable item from the sn_vul_vulnerable_item table",
)


class ReleaseProjectResourceConfig(ResourceConfig):
selector: ResourceSelector = Field(
title="Release Project Selector",
description="Selector for filtering ServiceNow release project records",
)
kind: Literal[ObjectKind.RELEASE_PROJECT] = Field(
title="ServiceNow Release Project",
description="A ServiceNow release project from the release_project table",
)


class CustomResourceConfig(ResourceConfig):
selector: ResourceSelector = Field(
title="Custom Resource Selector",
description="Selector for filtering records from a custom ServiceNow table",
)
kind: str = Field(
title="Custom kind",
description="Use this to map additional ServiceNow resources by setting the kind name to any table available through the ServiceNow <a target='_blank' href='https://developer.servicenow.com/dev.do#!/reference/api/xanadu/rest/c_TableAPI#table-GET'>Table API</a>.\n\nExample: incident",
)


class ServiceNowPortAppConfig(PortAppConfig):
resources: list[
IncidentResourceConfig
| UserGroupResourceConfig
| ServiceCatalogResourceConfig
| VulnerabilityResourceConfig
| ReleaseProjectResourceConfig
| CustomResourceConfig
] = Field(
default_factory=list,
title="ServiceNow Resources",
description="Resource Configs for ServiceNow integration",
) # type: ignore[assignment]
allow_custom_kinds: ClassVar[bool] = True


class ServiceNowIntegration(BaseIntegration):
class AppConfigHandlerClass(APIPortAppConfig):
CONFIG_CLASS = ServiceNowPortAppConfig
9 changes: 5 additions & 4 deletions integrations/servicenow/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from webhook.processors.vulnerability_processor import (
VulnerabilityWebhookProcessor,
)
from integration import ServiceNowResourceConfig
from integration import ResourceSelector


@ocean.on_resync()
Expand All @@ -32,9 +32,10 @@ async def on_resources_resync(kind: str) -> ASYNC_GENERATOR_RESYNC_TYPE:
password=ocean.integration_config.get("servicenow_password"),
)
api_query_params = {}
selector = cast(ServiceNowResourceConfig, event.resource_config).selector
if selector.api_query_params:
api_query_params = selector.api_query_params.generate_request_params()
if event.resource_config:
selector = cast(ResourceSelector, event.resource_config.selector)
if selector.api_query_params:
api_query_params = selector.api_query_params.generate_request_params()
async for records in servicenow_client.get_paginated_resource(
resource_kind=kind, api_query_params=api_query_params
):
Expand Down
2 changes: 1 addition & 1 deletion integrations/servicenow/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "servicenow"
version = "0.3.37"
version = "0.3.38"
description = "ServiceNow Ocean Integration"
authors = ["Isaac Coffie <isaac@getport.io>"]

Expand Down
Loading