Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port 11015 add azure dev ops releases #1

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
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
70 changes: 69 additions & 1 deletion integrations/azure-devops/.port/resources/blueprints.json
Original file line number Diff line number Diff line change
Expand Up @@ -265,5 +265,73 @@
"many": false
}
}
},
{
"identifier": "release",
"title": "Release",
"icon": "AzureDevops",
"schema": {
"properties": {
"status": {
"title": "Status",
"type": "string",
"icon": "DefaultProperty",
"description": "The current status of the release"
},
"reason": {
"title": "Reason",
"type": "string",
"description": "The reason for the release creation"
},
"createdDate": {
"title": "Created Date",
"type": "string",
"format": "date-time",
"description": "The date and time when the release was created"
},
"modifiedDate": {
"title": "Modified Date",
"type": "string",
"format": "date-time",
"description": "The date and time when the release was last modified"
},
"createdBy": {
"title": "Created By",
"type": "string",
"icon": "User",
"description": "The person who created the release"
},
"modifiedBy": {
"title": "Modified By",
"type": "string",
"icon": "User",
"description": "The person who last modified the release"
},
"definitionName": {
"title": "Definition Name",
"type": "string",
"description": "The name of the release definition"
},
"link": {
"title": "Link",
"type": "string",
"format": "url",
"icon": "AzureDevops",
"description": "Link to the release in Azure DevOps"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"aggregationProperties": {},
"relations": {
"project": {
"title": "Project",
"target": "project",
"required": true,
"many": false
}
}
}
]
]
20 changes: 20 additions & 0 deletions integrations/azure-devops/.port/resources/port-app-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,23 @@ resources:
blueprint: '"column"'
relations:
board: .__board.id | gsub(" "; "")
- kind: release
selector:
query: 'true'
port:
entity:
mappings:
identifier: .id | tostring | gsub(" "; "")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

combine the project id (.projectReference.id | gsub(" "; "")) with the release id to make it unique, so that it doesn't get overridden by other releases in other projects

title: .name
blueprint: '"release"'
properties:
status: .status
reason: .reason
createdDate: .createdOn
modifiedDate: .modifiedOn
createdBy: .createdBy.displayName
modifiedBy: .modifiedBy.displayName
Comment on lines +120 to +121
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's use the unique name, which will be the email of the user

a sample response I saw from the docs

"modifiedBy": {
        "id": "4adb1680-0eac-6149-b5ee-fc8b4f6ca227",
        "displayName": "Chuck Reinhart",
        "uniqueName": "[email protected]",
        "url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/4adb1680-0eac-6149-b5ee-fc8b4f6ca227",
        "imageUrl": "https://dev.azure.com/fabrikam/_api/_common/identityImage?id=4adb1680-0eac-6149-b5ee-fc8b4f6ca227"
      }

definitionName: .releaseDefinition.name
link: ._links.web.href | gsub("_release?releaseId="; "")
relations:
project: .projectReference.id | gsub(" "; "")
5 changes: 5 additions & 0 deletions integrations/azure-devops/.port/spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ configurations:
required: true
type: url
sensitive: true
- name: vsrmUrl
description: The URL of your Visual Studio Resource Manager, Azure DevOps organization (e.g., "https://vsrm.dev.azure.com/{your-organization}"). To find your organization URL, refer to the <a target="_blank" href="https://learn.microsoft.com/en-us/azure/devops/extend/develop/work-with-urls?view=azure-devops&tabs=http">Azure DevOps documentation</a>.
required: true
type: url
sensitive: true
Comment on lines +15 to +19
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we have the organization from the main ADO url (organizationUrl), why do we need this again? is the organization id in the ADO organizationUrl different from the one in the VSRM?

- name: personalAccessToken
description: A personal access token (PAT) with read permissions for the resources you want to sync. To create a PAT, see the <a target="_blank" href="https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops">Azure DevOps documentation</a>.
required: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,18 @@


class AzureDevopsClient(HTTPBaseClient):
def __init__(self, organization_url: str, personal_access_token: str) -> None:
def __init__(self, organization_url: str, vsrm_url, personal_access_token: str) -> None:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's remove this if the vsrm_url is the same as the ado enterprise url

super().__init__(personal_access_token)
self._organization_base_url = organization_url
self._vsrm_base_url = vsrm_url

@classmethod
def create_from_ocean_config(cls) -> "AzureDevopsClient":
if cache := event.attributes.get("azure_devops_client"):
return cache
azure_devops_client = cls(
ocean.integration_config["organization_url"],
ocean.integration_config["vsrm_url"],
ocean.integration_config["personal_access_token"],
)
event.attributes["azure_devops_client"] = azure_devops_client
Expand Down Expand Up @@ -124,6 +126,17 @@ async def generate_pipelines(self) -> AsyncGenerator[list[dict[Any, Any]], None]
for pipeline in pipelines:
pipeline["__projectId"] = project["id"]
yield pipelines

async def generate_releases(self) -> AsyncGenerator[list[dict[str, Any]], None]:
async for projects in self.generate_projects():
for project in projects:
releases_url = f"{self._vsrm_base_url}/{project['id']}/{API_URL_PREFIX}/release/releases"
async for releases in self._get_paginated_by_top_and_skip(releases_url):
for release in releases:
release["__projectId"] = project["id"]
release["__project"] = project
Comment on lines +136 to +137
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we already get this information from the release response so we may not need this

      "projectReference": {
        "id": "d07908bc-118f-47d2-8a13-ff75601a6b1a",
        "name": "MyFirstProject"
      },

yield releases


async def generate_repository_policies(
self,
Expand Down Expand Up @@ -401,3 +414,4 @@ async def get_file_by_commit(
return await self._get_item_content(
file_path, repository_id, "Commit", commit_id
)

1 change: 1 addition & 0 deletions integrations/azure-devops/azure_devops/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class Kind(StrEnum):
WORK_ITEM = "work-item"
BOARD = "board"
COLUMN = "column"
RELEASE = "release"


PULL_REQUEST_SEARCH_CRITERIA: list[dict[str, Any]] = [
Expand Down
11 changes: 9 additions & 2 deletions integrations/azure-devops/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,15 @@ async def resync_boards(kind: str) -> ASYNC_GENERATOR_RESYNC_TYPE:
async for boards in azure_devops_client.get_boards_in_organization():
logger.info(f"Resyncing {len(boards)} boards")
yield boards




@ocean.on_resync(Kind.RELEASE)
async def resync_releases(kind: str) -> ASYNC_GENERATOR_RESYNC_TYPE:
azure_devops_client = AzureDevopsClient.create_from_ocean_config()
async for releases in azure_devops_client.generate_releases():
logger.info(f"Resyncing {len(releases)} releases")
yield releases

@ocean.router.post("/webhook")
async def webhook(request: Request) -> dict[str, Any]:
body = await request.json()
Expand Down