Skip to content

Commit f582a81

Browse files
rdeshmukh15narenderqlikPagadala Narenderatttiwari
authored
GL-Work (#24)
* New streams (#19) * changes for the rasing the exception if not have the key * changes for the org_id name change * added parent stream for the child in the metadata * chnages for the review * pagination code changes * added methos names in the abstrct file * changes for the retun * changes for the none * changes for the duplicte records handling * chnages for the project key * none type issue with fixes changes * changes for the adding parent id into the key properties * SAC-29741: integration fix --------- Co-authored-by: Pagadala Narender <ext.pagadala.narender@globallogic.com> Co-authored-by: Rutuja Deshmukh <107538720+rdeshmukh15@users.noreply.github.com> Co-authored-by: atttiwari <atul.tiwari@qlik.com> * SAC-29742: Removed previous merged code causing issue --------- Co-authored-by: narenderqlik <Narender.Pagadala@qlik.com> Co-authored-by: Pagadala Narender <ext.pagadala.narender@globallogic.com> Co-authored-by: atttiwari <atul.tiwari@qlik.com>
1 parent 98f7cb0 commit f582a81

37 files changed

Lines changed: 2123 additions & 93 deletions

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,4 @@ workflows:
6464
- master
6565
jobs:
6666
- build:
67-
context: circleci-user
67+
context: circleci-user

.github/copilot-instructions.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Instructions for Building a Singer Tap/Target
2+
3+
This document provides guidance for implementing a high-quality Singer Tap (or Target) in compliance with the Singer specification and community best practices. Use it in conjunction with GitHub Copilot or your preferred IDE.
4+
5+
---
6+
7+
## 1. Rate Limiting
8+
9+
- Respect API rate limits (e.g., daily quotas or per-second limits).
10+
- For short-term rate limits, detect HTTP 429 or similar errors and implement retries with sleep/delay.
11+
- Use Singer’s built-in rate-limiting utilities where available.
12+
13+
14+
## 2. Memory Efficiency
15+
16+
- Minimize RAM usage by streaming data.
17+
Example: Use generators or iterators instead of loading entire datasets into memory.
18+
19+
20+
## 3. Consistent Date Handling
21+
22+
- Use RFC 3339 format (including time zone offset). UTC (Z) is preferred.
23+
Examples:
24+
Good: 2017-01-01T00:00:00Z, 2017-01-01T00:00:00-05:00
25+
Bad: 2017-01-01 00:00:00
26+
Use pytz for timezone-aware conversions.
27+
28+
29+
## 4. Logging & Exception Handling
30+
31+
- Log every API request (URL + parameters), omitting sensitive info (e.g., API keys).
32+
- Log progress updates (e.g., “Starting stream X”).
33+
- On API errors, log status code and response body.
34+
35+
For fatal errors:
36+
- Log at CRITICAL or FATAL level.
37+
- Exit with non-zero status.
38+
- Omit stack trace for known, user-triggered conditions.
39+
- Include full trace for unexpected exceptions.
40+
- For recoverable errors, implement retries with exponential backoff (e.g., using the backoff library).
41+
42+
43+
## 5. Module Structure
44+
45+
- Organize code into a proper Python module (directory with __init__.py), not a single script file.
46+
47+
48+
## 6. Schema Management
49+
50+
- For static schemas, store them as .json files in a schemas/ directory—not as inline Python dicts.
51+
Prefer explicit schemas:
52+
- Avoid additionalProperties: true or vague typing.
53+
- Use clear field names and types.
54+
- Set additionalProperties: false when schemas must be strict.
55+
- Be cautious when tightening schemas in new versions—it may require a major version bump per semantic versioning.
56+
57+
58+
## 7. JSON Schema Guidelines
59+
60+
- All files under schemas/*.json must follow the JSON Schema standard.
61+
- Any fields named created_time, modified_time, ending in _time or ending in _date must use the date-time format.
62+
- Any fields looks like date-time field, give suggestion to validate the fields should have date-time format.
63+
- Avoid using additionalProperties at the root level. It's allowed in nested fields only.
64+
65+
Example:
66+
{
67+
"type": "object",
68+
"properties": {
69+
"created_time": {
70+
"type": ["null", "string"],
71+
"format": "date-time"
72+
},
73+
"last_access_time": {
74+
"type": ["null", "string"],
75+
"format": "date-time"
76+
}
77+
}
78+
}
79+
80+
81+
## 8. Validating Bookmarking
82+
83+
We use the singer.bookmarks module to read from and write to the bookmark state file.
84+
To ensure correctness, always validate the structure of the bookmark state before processing or committing any changes.
85+
- In abstract.py, we use get_bookmark() and write_bookmark() to manage bookmarks for streams.
86+
- The write_bookmark() function overrides the one from the singer module to apply custom behavior.
87+
- Always confirm that the state structure matches the expected format before writing.
88+
89+
Format Example:
90+
{
91+
"bookmarks": {
92+
"stream_name": {
93+
"replication_key": "2024-01-01T00:00:00Z"
94+
}
95+
}
96+
}
97+
98+
99+
Optional validation function:
100+
def is_valid_bookmark_state(state):
101+
return isinstance(state, dict) and \
102+
"bookmarks" in state and \
103+
isinstance(state["bookmarks"], dict)
104+
105+
106+
## 9. Code Quality
107+
108+
- Use pylint and aim for zero error-level messages.
109+
- CI pipelines (e.g., CircleCI) should enforce linting.
110+
- Fix or explicitly disable warnings when appropriate.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ __pycache__/
44

55
sample_config.json
66
catalog.json
7+
config.json

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Changelog
2+
23
## 1.1.0
4+
* Added New streams into the tap [#19](https://github.com/singer-io/tap-circle-ci/pull/19)
35
* Library version Upgrade [#20](https://github.com/singer-io/tap-circle-ci/pull/20)
46

57
## 1.0.0

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ This tap:
1111
- [Pipelines](https://circleci.com/docs/api/v2/#get-all-pipelines)
1212
- [Workflows](https://circleci.com/docs/api/v2/#get-a-pipeline-39-s-workflows)
1313
- [Jobs](https://circleci.com/docs/api/v2/#get-a-workflow-39-s-jobs)
14+
- [Context](https://circleci.com/docs/api/v2/#tag/Context)
15+
- [Deploy](https://circleci.com/docs/api/v2/#tag/Deploy)
16+
- [Groups](https://circleci.com/docs/api/v2/#tag/Groups)
17+
- [Pipeline-Definition](https://circleci.com/docs/api/v2/#tag/Pipeline-Definition)
18+
- [Project](https://circleci.com/docs/api/v2/#tag/Project)
19+
- [Schedule](https://circleci.com/docs/api/v2/#tag/Schedule)
20+
- [Trigger](https://circleci.com/docs/api/v2/#tag/Trigger)
21+
- [Collaborations](https://circleci.com/docs/api/v2/#tag/User/operation/getCollaborations)
1422
- Outputs the schema for each resource
1523
- Incrementally pulls data based on the input state
1624

tap_circle_ci/client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,11 @@ def __make_request(self, method: str, endpoint: str, **kwargs) -> Optional[Mappi
9797
Dict,List,None: Returns a `Json Parsed` HTTP Response or None if exception
9898
"""
9999
response = self._session.request(method, endpoint, **kwargs)
100+
if response.status_code == 201:
101+
return response
100102
if response.status_code != 200:
101103
try:
102-
logger.error("Status: %s Message: %s", response.status_code, response.text)
104+
logger.error("Status : %s Message: %s", response.status_code, response.text)
103105
except AttributeError:
104106
pass
105107
try:
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"type": "object",
3+
"additionalProperties": false,
4+
"properties": {
5+
"id": {
6+
"type": "string"
7+
},
8+
"vcs_type": {
9+
"type": [
10+
"null",
11+
"string"
12+
]
13+
},
14+
"name": {
15+
"type": [
16+
"null",
17+
"string"
18+
]
19+
},
20+
"avatar_url": {
21+
"type": [
22+
"null",
23+
"string"
24+
]
25+
},
26+
"slug": {
27+
"type": [
28+
"null",
29+
"string"
30+
]
31+
}
32+
}
33+
}

tap_circle_ci/schemas/context.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"type": "object",
3+
"additionalProperties": false,
4+
"properties": {
5+
"id": {
6+
"type": "string"
7+
},
8+
"name": {
9+
"type": [
10+
"null",
11+
"string"
12+
]
13+
},
14+
"created_at": {
15+
"type": [
16+
"null",
17+
"string"
18+
],
19+
"format": "date-time"
20+
},
21+
"organization_id": {
22+
"type": [
23+
"null",
24+
"string"
25+
]
26+
}
27+
}
28+
}

tap_circle_ci/schemas/deploy.json

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"type": "object",
3+
"additionalProperties": false,
4+
"properties": {
5+
"id": {
6+
"type": "string"
7+
},
8+
"name": {
9+
"type": [
10+
"null",
11+
"string"
12+
]
13+
},
14+
"description": {
15+
"type": [
16+
"null",
17+
"string"
18+
]
19+
},
20+
"organization_id": {
21+
"type": [
22+
"null",
23+
"string"
24+
]
25+
},
26+
"created_at": {
27+
"type": [
28+
"null",
29+
"string"
30+
],
31+
"format": "date-time"
32+
},
33+
"updated_at": {
34+
"type": [
35+
"null",
36+
"string"
37+
],
38+
"format": "date-time"
39+
},
40+
"labels": {
41+
"type": [
42+
"null",
43+
"array"
44+
],
45+
"items": {
46+
"type": "object",
47+
"additionalProperties": false,
48+
"properties": {
49+
"key": {
50+
"type": [
51+
"null",
52+
"string"
53+
]
54+
},
55+
"value": {
56+
"type": [
57+
"null",
58+
"string"
59+
]
60+
}
61+
}
62+
}
63+
}
64+
}
65+
}

tap_circle_ci/schemas/groups.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"type": "object",
3+
"additionalProperties": false,
4+
"properties": {
5+
"id": {
6+
"type": "string"
7+
},
8+
"name": {
9+
"type": [
10+
"null",
11+
"string"
12+
]
13+
},
14+
"description": {
15+
"type": [
16+
"null",
17+
"string"
18+
]
19+
},
20+
"organization_id": {
21+
"type": [
22+
"null",
23+
"string"
24+
]
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)