Skip to content

Commit 42558f0

Browse files
authored
Merge pull request #13 from planet-a-ventures/joscha/chores
chore: update CI, releasing, formatter, etc.
2 parents 124c5e8 + 06176aa commit 42558f0

File tree

14 files changed

+1309
-842
lines changed

14 files changed

+1309
-842
lines changed

README.md

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,20 @@ pipeline.run(affinity_data)
5757

5858
Resources that can be loaded using this verified source are:
5959

60-
| Name | Description | API version | [Permissions](https://developer.affinity.co/#section/Getting-Started/Permissions) needed |
60+
| Name | Description | API version | [Permissions](https://developer.affinity.co/#section/Getting-Started/Permissions) needed |
6161
| -- | -- | -- | -- |
62-
| [companies](https://developer.affinity.co/#tag/companies) | The stored companies | V2 | Requires the "Export All Organizations directory" permission. |
63-
| [persons](https://developer.affinity.co/#tag/persons) | The stored persons | V2 | Requires the "Export All People directory" permission. |
64-
| [opportunities](https://developer.affinity.co/#tag/opportunities) | The stored opportunities | V2 | Requires the "Export data from Lists" permission. |
65-
| [lists](https://developer.affinity.co/#tag/lists) | A given list and/or a saved view of a list | V2 | Requires the "Export data from Lists" permission. |
66-
| [notes](https://api-docs.affinity.co/#notes) | Notes attached to companies, persons, opportunities | Legacy | n/a |
62+
| [companies](https://developer.affinity.co/#tag/companies) | The stored companies | V2 | Requires the "Export All Organizations directory" permission. |
63+
| [persons](https://developer.affinity.co/#tag/persons) | The stored persons | V2 | Requires the "Export All People directory" permission. |
64+
| [opportunities](https://developer.affinity.co/#tag/opportunities) | The stored opportunities | V2 | Requires the "Export data from Lists" permission. |
65+
| [lists](https://developer.affinity.co/#tag/lists) | A given list and/or a saved view of a list | V2 | Requires the "Export data from Lists" permission. |
66+
| [notes](https://api-docs.affinity.co/#notes) | Notes attached to companies, persons, opportunities | Legacy | n/a |
6767

6868
## V1 vs V2
6969

7070
There are two versions of the Affinity API:
7171

7272
1. [Legacy](https://api-docs.affinity.co/) which is available for all plans.
73-
2. [V2](https://developer.affinity.co/) which is only available for customers
73+
1. [V2](https://developer.affinity.co/) which is only available for customers
7474
with an enterprise plan.
7575

7676
This verified source makes use of both API endpoints.
@@ -108,3 +108,21 @@ Run
108108
```sh
109109
generate-model
110110
```
111+
112+
## 🚀 Development Workflow
113+
114+
1. **Make changes** to your code
115+
1. **Format code** with `format` before committing
116+
1. **Commit changes** - pre-commit hooks will run automatically
117+
1. **Push to GitHub** - CI will run tests on multiple platforms
118+
1. **Create release** by pushing a tag (format: `vX.X.X`)
119+
120+
## 📦 Publishing
121+
122+
Publishing to PyPI is fully automated:
123+
124+
1. Create a new tag: `git tag v1.0.0`
125+
1. Push the tag: `git push origin v1.0.0`
126+
1. GitHub Actions will automatically build and publish to PyPI
127+
128+
The project uses trusted publishing, so no API keys are required.

affinity_pipeline.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import dlt
2+
23
from dlt_source_affinity import ListReference, source
34

45
DEV_MODE = True

devenv.lock

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
"devenv": {
44
"locked": {
55
"dir": "src/modules",
6-
"lastModified": 1733788855,
6+
"lastModified": 1761038840,
77
"owner": "cachix",
88
"repo": "devenv",
9-
"rev": "d59fee8696cd48f69cf79f65992269df9891ba86",
9+
"rev": "d815b7839fe595c336813f586c574459553e9ea1",
1010
"type": "github"
1111
},
1212
"original": {
@@ -34,10 +34,10 @@
3434
"flake-compat_2": {
3535
"flake": false,
3636
"locked": {
37-
"lastModified": 1733328505,
37+
"lastModified": 1747046372,
3838
"owner": "edolstra",
3939
"repo": "flake-compat",
40-
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
40+
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
4141
"type": "github"
4242
},
4343
"original": {
@@ -89,10 +89,10 @@
8989
},
9090
"nixpkgs": {
9191
"locked": {
92-
"lastModified": 1733477122,
92+
"lastModified": 1758532697,
9393
"owner": "cachix",
9494
"repo": "devenv-nixpkgs",
95-
"rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857",
95+
"rev": "207a4cb0e1253c7658c6736becc6eb9cace1f25f",
9696
"type": "github"
9797
},
9898
"original": {
@@ -110,10 +110,10 @@
110110
]
111111
},
112112
"locked": {
113-
"lastModified": 1733319315,
113+
"lastModified": 1760045435,
114114
"owner": "cachix",
115115
"repo": "nixpkgs-python",
116-
"rev": "01263eeb28c09f143d59cd6b0b7c4cc8478efd48",
116+
"rev": "5dd936b1ce299f6d230774a88b1052e47f71ab91",
117117
"type": "github"
118118
},
119119
"original": {
@@ -122,12 +122,28 @@
122122
"type": "github"
123123
}
124124
},
125+
"nixpkgs-unstable": {
126+
"locked": {
127+
"lastModified": 1760965567,
128+
"owner": "nixos",
129+
"repo": "nixpkgs",
130+
"rev": "cb82756ecc37fa623f8cf3e88854f9bf7f64af93",
131+
"type": "github"
132+
},
133+
"original": {
134+
"owner": "nixos",
135+
"ref": "nixpkgs-unstable",
136+
"repo": "nixpkgs",
137+
"type": "github"
138+
}
139+
},
125140
"root": {
126141
"inputs": {
127142
"devenv": "devenv",
128143
"git-hooks": "git-hooks",
129144
"nixpkgs": "nixpkgs",
130145
"nixpkgs-python": "nixpkgs-python",
146+
"nixpkgs-unstable": "nixpkgs-unstable",
131147
"pre-commit-hooks": [
132148
"git-hooks"
133149
]

devenv.nix

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
inputs,
66
...
77
}:
8-
8+
let
9+
pkgs-unstable = import inputs.nixpkgs-unstable { system = pkgs.stdenv.system; };
10+
in
911
{
1012
packages = [
1113
pkgs.git
1214
pkgs.bash
15+
pkgs.python312Packages.setuptools
1316
];
1417

1518
languages.python.enable = true;
@@ -26,18 +29,30 @@
2629

2730
git-hooks.hooks = {
2831
shellcheck.enable = true;
29-
black.enable = true;
32+
shellcheck.excludes = [
33+
".envrc"
34+
];
35+
ruff.enable = true;
36+
ruff-format.enable = true;
3037
typos.enable = true;
3138
yamllint.enable = true;
3239
yamlfmt.enable = true;
40+
yamlfmt.settings.lint-only = false;
3341
check-toml.enable = true;
3442
commitizen.enable = true;
43+
commitizen.package = pkgs-unstable.commitizen;
3544
nixfmt-rfc-style.enable = true;
3645
markdownlint.enable = true;
46+
mdformat.enable = true;
47+
mdformat.package = pkgs.mdformat.withPlugins (
48+
ps: with ps; [
49+
mdformat-frontmatter
50+
]
51+
);
52+
trufflehog.enable = true;
3753
};
3854

3955
scripts.format.exec = ''
40-
yamlfmt .
4156
markdownlint --fix .
4257
pre-commit run --all-files
4358
'';
@@ -46,6 +61,13 @@
4661
pytest -s -vv "$@"
4762
'';
4863

64+
scripts.deps-upgrade.exec = ''
65+
uv \
66+
sync \
67+
--all-extras \
68+
--upgrade
69+
'';
70+
4971
enterTest = ''
5072
test-all
5173
'';

devenv.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ inputs:
66
inputs:
77
nixpkgs:
88
follows: nixpkgs
9+
nixpkgs-unstable:
10+
url: github:nixos/nixpkgs/nixpkgs-unstable

dlt_source_affinity/__init__.py

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,75 @@
11
"""A source loading entities and lists from Affinity CRM (affinity.co)"""
22

3+
import logging
34
from copy import deepcopy
4-
from dataclasses import field
5+
from dataclasses import field as dataclass_field
56
from enum import StrEnum
6-
from typing import Any, Dict, Generator, Iterable, List, Optional, Sequence, Tuple
7-
import logging
7+
from typing import (
8+
Any,
9+
Dict,
10+
Generator,
11+
Iterable,
12+
List,
13+
Literal,
14+
Optional,
15+
Sequence,
16+
Tuple,
17+
)
18+
819
import dlt
9-
from dlt.common.typing import TDataItem
10-
from dlt.sources import DltResource
11-
from dlt.extract.items import DataItemWithMeta
20+
from dlt.common.libs.pydantic import DltConfig
1221
from dlt.common.logger import is_logging
1322
from dlt.common.schema.typing import TTableReferenceParam
14-
from dlt.common.libs.pydantic import DltConfig
15-
from pydantic_flatten_rootmodel import flatten_root_model
23+
from dlt.common.typing import TDataItem
24+
from dlt.extract.items import DataItemWithMeta
25+
from dlt.sources import DltResource
1626
from pydantic import BaseModel, TypeAdapter
1727
from pydantic.fields import FieldInfo
28+
from pydantic_flatten_rootmodel import flatten_root_model
29+
30+
from .helpers import ListReference, generate_list_entries_path
31+
from .model.v1 import InteractionTypeToLiteral, Note
32+
from .model.v2 import (
33+
Attendee,
34+
ChatMessage,
35+
CompaniesValue,
36+
Company,
37+
CompanyPaged,
38+
CompanyValue,
39+
DateValue,
40+
Dropdown,
41+
DropdownsValue,
42+
DropdownValue,
43+
FieldModel,
44+
FloatsValue,
45+
FloatValue,
46+
FormulaValue,
47+
Interaction,
48+
InteractionValue,
49+
ListModel,
50+
LocationsValue,
51+
LocationValue,
52+
Opportunity,
53+
OpportunityPaged,
54+
OpportunityWithFields,
55+
Person,
56+
PersonPaged,
57+
PersonsValue,
58+
PersonValue,
59+
RankedDropdown,
60+
RankedDropdownValue,
61+
TextsValue,
62+
TextValue,
63+
Type3,
64+
)
1865
from .rest_client import (
66+
MAX_PAGE_LIMIT_V1,
67+
MAX_PAGE_LIMIT_V2,
1968
get_v1_rest_client,
2069
get_v2_rest_client,
2170
hooks,
22-
MAX_PAGE_LIMIT_V1,
23-
MAX_PAGE_LIMIT_V2,
2471
)
25-
from .type_adapters import note_adapter, list_adapter
26-
from .model.v1 import Note, InteractionTypeToLiteral
27-
from .model.v2 import *
28-
from .helpers import ListReference, generate_list_entries_path
72+
from .type_adapters import list_adapter, note_adapter
2973

3074

3175
def pydantic_model_dump(model: BaseModel, **kwargs):
@@ -518,7 +562,8 @@ def __list_entries() -> Iterable[TDataItem]:
518562

519563
@dlt.source(name="affinity")
520564
def source(
521-
list_refs: List[ListReference] = field(default_factory=list), dev_mode=False
565+
list_refs: List[ListReference] = dataclass_field(default_factory=list),
566+
dev_mode=False,
522567
) -> Sequence[DltResource]:
523568
"""
524569
list_refs - one or more references to lists and/or saved list views

dlt_source_affinity/helpers.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""Affinity CRM source helpers"""
22

3-
from dlt.sources.helpers import requests
43
from typing import NamedTuple
54

65

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# OpenAPI spec
22

3-
You can find the latest version of the spec [here](https://developer.affinity.co/).
3+
Find the latest version of the spec on the [Affinity developer portal](https://developer.affinity.co/).
44

55
## How to regenerate
66

77
1. Download the new spec from the link above.
8-
2. Put it into this folder.
9-
3. Run `generate-model` from project (requires direnv)
8+
1. Put it into this folder.
9+
1. Run `generate-model` from project (requires direnv)

dlt_source_affinity/model/v1.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1+
from datetime import datetime
12
from enum import IntEnum
23
from inspect import get_annotations
3-
from pydantic import BaseModel, Field, model_serializer
4-
from typing import ClassVar, List, Annotated, get_args
5-
from datetime import datetime
4+
from typing import Annotated, ClassVar, List, get_args
65

76
from dlt.common.libs.pydantic import DltConfig
7+
from pydantic import BaseModel, Field, model_serializer
88

9-
from .v2 import ChatMessage, PhoneCall, Email, Meeting
9+
from .v2 import ChatMessage, Email, Meeting, PhoneCall
1010

1111

1212
class NoteType(IntEnum):

dlt_source_affinity/rest_client.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,16 @@
22
from typing import Any
33

44
import dlt
5+
from dlt.sources.helpers.requests.session import Session
56
from dlt.sources.helpers.rest_client.auth import BearerTokenAuth, HttpBasicAuth
6-
from dlt.sources.helpers.rest_client.client import RESTClient, Response
7+
from dlt.sources.helpers.rest_client.client import Response, RESTClient
78
from dlt.sources.helpers.rest_client.paginators import (
89
JSONLinkPaginator,
910
JSONResponseCursorPaginator,
1011
)
1112

12-
from .type_adapters import error_adapter
1313
from .settings import API_BASE, V2_PREFIX
14-
from dlt.sources.helpers.requests.session import Session
15-
14+
from .type_adapters import error_adapter
1615

1716
# Share a session (and thus pool) between all rest clients
1817
session: Session = None

0 commit comments

Comments
 (0)