Integration service handles issue syncing between the Testlio platform and various client bug-trackers.
Testlio integrations depend of the following services:
issue-service
- API for storing allISSUES
,COMMENTS
andATTACHMENTS
.integration-service
- responsible for detecting and syncing resources between Testlio and client bug trackers.*-mapper-service
- formatting Testlio CRUD requests sent byintegration-service
to client API.
integration-service
is a micro-service that is built with lambda-tools framework.
Currently integration-service supports the sync of following types of resources:
ISSUE
COMMENT
ATTACHMENT
COMMENT ATTACHMENT
Issues are kept in sync the following way:
- Periodic CloudWatch event triggers a lambda that invokes a
project-lambda
for each active integration. - The
project-lambda
queries allSYNCED
ISSUE
remoteId
s from theintegrated-resources
DynamoDB table and makes a request to*-mapper-service
. *-mapper-service
detects new and changed issues from the last query and returns to the href provided by theintegration-service
.integrations-callback-post
is triggered once the result of changed and new issues is returned by the mapper.- lambda iterates over the
issue-service
collection and detects changes on Testlio's side as well. - then all the changed resources (from testlio's side and mapper side) are changed in
integrated-resources
table to UNSYNCED state
- lambda iterates over the
integrated-resources
stream event triggersintegrations-resource-lambda
on every change.- The lambda then validates if the event should be synced (if the state is
UNSYCNED
) - Valid stream events trigger one of
GET
,DELETE
orPOST
against the mapper.
- The lambda then validates if the event should be synced (if the state is
- Mapper executes the request and returns the result to given callback.
-
POST
,PUT
andDELETE
methods are returned toresources-callback-lambda
:- the resources are then marked as SYNCED and the syncing of these resources is complete.
2.
GET
ISSUE
requests are returned toresources-conflict-lambda
. - The issue is retrieved from the
issue-service
and matched to it's remote equivalent. - The changes are calculated. Local changes are executed against issue-service and a
PUT
request is sent to the mapper.
- Mapper executes the PUT requests on the clients API
resources-callback-lambda
is triggered, marking theISSUE
as synced.
Starting point: periodic CloudWatch event (every 5 minutes)
1. Fetch all enabled integrations from `integrations` table
2. Invoke **integrations-project-lambda** for each integration
Starting point: invoked by periodic-integrations-lambda
Sends all remote issue id's and their remoteRevisions to mapper.
Input parameters:
- Specific integration guid
- Querys
integrated-resources
table for all integration resources - Filter all issues of the integration that have a remoteId
- Make bulk get (MODIFIED) request against mapper in format of:
{
"integration": {
"href": "https://api.testlio.com/...",
"integration": {... }
},
"method": "MODIFIED",
"payload": [
{
"remoteId": "231471949776582",
"remoteRevision": "2016-12-15T14:55:34.469Z"
},
{
"remoteId": "231434493693728",
"remoteRevision": "2016-12-15T10:17:56.819Z"
},
...
],
"resource": {
"type": "ISSUE"
}
}
/integrations/{integrationGuid}/resources/{resourceGuid}/callbacks/{callbackGuid}
Expected request body format:
{
"remoteRevision": "2017-01-18T14:09:07.746Z",
"resource": {
"type": "ISSUE"
},
"response": {
"new": [
"249090811512816",
"249086133471133"
],
"changed": [
"2490908115",
"2490861334"
],
"syncedAt": "2017-05-16 09:37"
},
"success": true
}
Starting point: [endpoint]integrations-callbacks-post
{
"resource": {
"type": "ISSUE"
},
"response": [
"249090811512816",
"249086133471133",
...
],
"success": true
}
The resources changed in remote system are in response array.
- Consumes callback, invokes itself
- Fetches collection page by page, changes state to UNSYNCED for remote or local updates
Makes HTTP POST requests against mapper with similar data.
{
"integration": {
"href": "/integrations/a42f5225-e787-40fc-80b1-b0fb363fdda9",
"integration": {
"collectionHref": "http://local.testlio:3055/collections/b0424c1c-d9f5-49ff-8d47-e89af802e4cb",
"connection": {
"username": "[email protected]",
"password": "xxx"
},
"enabled": true,
"guid": "a42f5225-e787-40fc-80b1-b0fb363fdda9",
"mapperPath": "integration-mapper.salesforce.v1",
"meta": {
"environment": "Mobile2",
"externalIdPrefix": "a",
"mapperPath": "integration-mapper.salesforce.v1"
},
"remoteId": "sales-remote"
}
},
"method": "POST",
"payload": {
"state": "open",
"issueData": {
"labels": [],
"assignedTo": null,
"isStarred": false,
"severity": "high",
"reportQualityHasTitle": true,
"reportFeedback": "Awesome! You did a great job!",
"number": "24305",
"isDeleted": false,
"fixVersion": null,
"closeExplanation": null,
"title": "This is another test for Testlio integration."
"testCycleId": null,
"description": "description",
"reportQualityHasAttachments": true,
"feature": null,
"reportQualityHasBody": true,
"isApproved": true,
"isClosed": true,
"reportQuality": "5",
"closeReason": "Works as Designed",
"feedbackSeen": false,
"buildVersion": "160.store.3"
}
},
"resource": {
"id": "remote_id2",
"parent": {
"id": "remote_id1",
"type": "COLLECTION"
},
"type": "ISSUE"
}
}
Input parameters:
integrated-resources
table stream events
- Sits on top of the
integrated-resources
table - Expects INSERT or MODIFY events
- Only processes rows in UNSYNCED state
- Generates a callback and updates
integrated-resources
row with callback - Builds request, including integration data, callback data, method, resource and payload
- For NEW transactions, makes POST request against mapper
- For UPDATE transactions, make GET request against mapper
/integrations/{integrationGuid}/resources/{resourceGuid}/callbacks/{callbackGuid}
Expected request body format:
{
"resource": {
"id": "18",
"type": "ISSUE",
"parent": {
"id": "400",
"type": "COLLECTION"
}
},
"response": {
"state": "open",
"issueData": {
"title": "some title",
"description": "some description",
"severity": "low"
}
},
"subresources": [
{
"id": "37318.40555",
"type": "COMMENT",
"createdAt": 1494743052
},
{
"id": "37318.30931",
"type": "ATTACHMENT"
}
],
"remoteRevision": "2017-05-15T08:52:25.665-0400",
"success": true
}
Starting point: [endpoint]callback-post
Consume callbacks from integration-mapper. Callback may be from POST|UPDATE|DELETE requests.
POST|UPDATE|DELETE
request callbacks are the final step in flow, and require updating integrated-resources
row to SYNCED state
- Check if callback exists and is current.
- Update requests state in Callbacks table
- update
integrated-resources
with remoteId and synced state - in case of
POST
, check for eligible children fromintegrated-resources
, and push them to request-sync-lambda - if
GET
call integrationts-conflict-lambda
/integrations/{integrationGuid}/resources/{resourceGuid}/callbacks/{callbackGuid}/conflict/
{
"resource": {
"id": "18",
"type": "ISSUE",
"parent": {
"id": "400",
"type": "COLLECTION"
}
},
"response": {
"state": "open",
"issueData": {
"title": "some title",
"description": "some description",
"severity": "low"
}
},
"subresources": [
{
"id": "37318.40555",
"type": "COMMENT",
"createdAt": 1494743052
},
{
"id": "37318.30931",
"type": "ATTACHMENT"
}
],
"remoteRevision": "2017-05-15T08:52:25.665-0400",
"success": true
}
Starting point: [endpoint]callback-conflict-post
A GET
request callback means that client service has gotten back to us with its version of the data. We need to compare it against our version
and calculate changes to be applied first to our system and then the clients. Clients changes must be prioritized over local changes.
Input parameters: issue from remote system in Testlios issue-service format
- Fetches the last known synced state of the issue (head)
- Fetches the current state of the resource in issue-service
- Diffs out fields updated in remote system and in our system since the last sync
- Updates local data with remote changes
- Update remote system with changes on issue-service side (remote system changes have priority over issue-service)
mapper-service is the connecting link between clients remote system and integration-service. It's task is to take in CRUD requests in issue-service format and carry them out on the remote systems API and then return to integration-service with results.
- Every single CRUD request has its own unique callback href for returning results
- communication between integration-service and mapper must be authenticated with mapper secrets so that they couldn't be used by other services directly
- with integration mappers we always MUST assume that everything is completely asynchronous, even issue fetching from clients system.
holds info about integrations that have been set up
It is important to note here, that connection data is encrypted using Amazon KMS before it's stored to the database.
- guid (guid, require)
- type (enum[jira, asana, github...], required)
- collection href (guid, required)
- connection (object, required)
- enabled (boolean, required)
holds data about testlio resources (about to be) synced
- guid (guid, required)
- integrationGuid (guid, required)
- resourceHref (issue, collection or attachment href) (string, optional)
- parentResourceHref (href of a parent resource) (href, required)
- remoteId (string, optional)
- remoteParentId (string, optional)
- callbackGuid (guid, optional)
- type (enum[ISSUE, COMMENT, ATTACHMENT], required)
- transaction (enum[NEW, UPDATE, DELETE], required)
- state (enum[SYNCED, UNSYNCED, INPROGRESS, ERROR, CONFLICT])
- headRevisionHref (last successfully synced version of data) (href, optional)
- lastRevisionHref (current requests version of data) (href, optional)
holds info about callbacks
- guid (guid, required)
- resourceGuid (guid, required)
- consumed (boolean, true)
- method (string, required)
- request (schema of the request made against mapper) (object, required)
- requestResponse (mappers response to the request) (string, required)
- response (mappers async response for the request containing data and success) (string, required)
- targetHash (guid, required)
- data (object, required)
- isDeleted (boolean, required)
- targetHash (guid, required)
- data (object, required)