markdown-to-confluence 0.1.13__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
md2conf/__main__.py CHANGED
@@ -1,139 +1,211 @@
1
- import argparse
2
- import logging
3
- import os.path
4
- import sys
5
- from pathlib import Path
6
- from typing import Optional
7
-
8
- import requests
9
-
10
- from .api import ConfluenceAPI
11
- from .application import Application
12
- from .converter import ConfluenceDocumentOptions
13
- from .processor import Processor
14
- from .properties import ConfluenceProperties
15
-
16
-
17
- class Arguments(argparse.Namespace):
18
- mdpath: Path
19
- domain: str
20
- path: str
21
- username: str
22
- apikey: str
23
- space: str
24
- loglevel: str
25
- ignore_invalid_url: bool
26
- generated_by: Optional[str]
27
-
28
-
29
- def main() -> None:
30
- parser = argparse.ArgumentParser()
31
- parser.prog = os.path.basename(os.path.dirname(__file__))
32
- parser.add_argument(
33
- "mdpath", help="Path to Markdown file or directory to convert and publish."
34
- )
35
- parser.add_argument("-d", "--domain", help="Confluence organization domain.")
36
- parser.add_argument(
37
- "-p", "--path", help="Base path for Confluence (default: '/wiki/')."
38
- )
39
- parser.add_argument("-u", "--username", help="Confluence user name.")
40
- parser.add_argument(
41
- "-a",
42
- "--apikey",
43
- help="Confluence API key. Refer to documentation how to obtain one.",
44
- )
45
- parser.add_argument(
46
- "-s",
47
- "--space",
48
- help="Confluence space key for pages to be published. If omitted, will default to user space.",
49
- )
50
- parser.add_argument(
51
- "-l",
52
- "--loglevel",
53
- choices=[
54
- logging.getLevelName(level).lower()
55
- for level in (
56
- logging.DEBUG,
57
- logging.INFO,
58
- logging.WARN,
59
- logging.ERROR,
60
- logging.CRITICAL,
61
- )
62
- ],
63
- default=logging.getLevelName(logging.INFO),
64
- help="Use this option to set the log verbosity.",
65
- )
66
- parser.add_argument(
67
- "-r",
68
- dest="root_page",
69
- help="Root Confluence page to create new pages. If omitted, will raise exception when creating new pages.",
70
- )
71
- parser.add_argument(
72
- "--generated-by",
73
- default="This page has been generated with a tool.",
74
- help="Add prompt to pages (default: 'This page has been generated with a tool.').",
75
- )
76
- parser.add_argument(
77
- "--no-generated-by",
78
- dest="generated_by",
79
- action="store_const",
80
- const=None,
81
- help="Do not add 'generated by a tool' prompt to pages.",
82
- )
83
- parser.add_argument(
84
- "--ignore-invalid-url",
85
- action="store_true",
86
- default=False,
87
- help="Emit a warning but otherwise ignore relative URLs that point to ill-specified locations.",
88
- )
89
- parser.add_argument(
90
- "--local",
91
- action="store_true",
92
- default=False,
93
- help="Write XHTML-based Confluence Storage Format files locally without invoking Confluence API.",
94
- )
95
-
96
- args = Arguments()
97
- parser.parse_args(namespace=args)
98
-
99
- # NOTE: If we switch to modern type aware CLI tool like typer
100
- # the following line won't be necessary
101
- args.mdpath = Path(args.mdpath)
102
-
103
- logging.basicConfig(
104
- level=getattr(logging, args.loglevel.upper(), logging.INFO),
105
- format="%(asctime)s - %(levelname)s - %(funcName)s [%(lineno)d] - %(message)s",
106
- )
107
-
108
- options = ConfluenceDocumentOptions(
109
- ignore_invalid_url=args.ignore_invalid_url,
110
- generated_by=args.generated_by,
111
- root_page_id=args.root_page,
112
- )
113
- properties = ConfluenceProperties(
114
- args.domain, args.path, args.username, args.apikey, args.space
115
- )
116
- if args.local:
117
- Processor(options, properties).process(args.mdpath)
118
- else:
119
- try:
120
- with ConfluenceAPI(properties) as api:
121
- Application(
122
- api,
123
- options,
124
- ).synchronize(args.mdpath)
125
- except requests.exceptions.HTTPError as err:
126
- logging.error(err)
127
-
128
- # print details for a response with JSON body
129
- if err.response is not None:
130
- try:
131
- logging.error(err.response.json())
132
- except requests.exceptions.JSONDecodeError:
133
- pass
134
-
135
- sys.exit(1)
136
-
137
-
138
- if __name__ == "__main__":
139
- main()
1
+ import argparse
2
+ import logging
3
+ import os.path
4
+ import sys
5
+ import typing
6
+ from pathlib import Path
7
+ from typing import Any, Literal, Optional, Sequence, Union
8
+
9
+ import requests
10
+
11
+ from . import __version__
12
+ from .api import ConfluenceAPI
13
+ from .application import Application
14
+ from .converter import ConfluenceDocumentOptions
15
+ from .processor import Processor
16
+ from .properties import ConfluenceProperties
17
+
18
+
19
+ class Arguments(argparse.Namespace):
20
+ mdpath: Path
21
+ domain: str
22
+ path: str
23
+ username: str
24
+ apikey: str
25
+ space: str
26
+ loglevel: str
27
+ ignore_invalid_url: bool
28
+ heading_anchors: bool
29
+ root_page: Optional[str]
30
+ generated_by: Optional[str]
31
+ render_mermaid: bool
32
+ diagram_output_format: Literal["png", "svg"]
33
+ webui_links: bool
34
+
35
+
36
+ class KwargsAppendAction(argparse.Action):
37
+ """Append key-value pairs to a dictionary"""
38
+
39
+ def __call__(
40
+ self,
41
+ parser: argparse.ArgumentParser,
42
+ namespace: argparse.Namespace,
43
+ values: Union[None, str, Sequence[Any]],
44
+ option_string: Optional[str] = None,
45
+ ) -> None:
46
+ try:
47
+ d = dict(map(lambda x: x.split("="), typing.cast(Sequence[str], values)))
48
+ except ValueError:
49
+ raise argparse.ArgumentError(
50
+ self,
51
+ f'Could not parse argument "{values}". It should follow the format: k1=v1 k2=v2 ...',
52
+ )
53
+ setattr(namespace, self.dest, d)
54
+
55
+
56
+ def main() -> None:
57
+ parser = argparse.ArgumentParser()
58
+ parser.prog = os.path.basename(os.path.dirname(__file__))
59
+ parser.add_argument("--version", action="version", version=__version__)
60
+ parser.add_argument(
61
+ "mdpath", help="Path to Markdown file or directory to convert and publish."
62
+ )
63
+ parser.add_argument("-d", "--domain", help="Confluence organization domain.")
64
+ parser.add_argument(
65
+ "-p", "--path", help="Base path for Confluence (default: '/wiki/')."
66
+ )
67
+ parser.add_argument("-u", "--username", help="Confluence user name.")
68
+ parser.add_argument(
69
+ "-a",
70
+ "--apikey",
71
+ help="Confluence API key. Refer to documentation how to obtain one.",
72
+ )
73
+ parser.add_argument(
74
+ "-s",
75
+ "--space",
76
+ help="Confluence space key for pages to be published. If omitted, will default to user space.",
77
+ )
78
+ parser.add_argument(
79
+ "-l",
80
+ "--loglevel",
81
+ choices=[
82
+ logging.getLevelName(level).lower()
83
+ for level in (
84
+ logging.DEBUG,
85
+ logging.INFO,
86
+ logging.WARN,
87
+ logging.ERROR,
88
+ logging.CRITICAL,
89
+ )
90
+ ],
91
+ default=logging.getLevelName(logging.INFO),
92
+ help="Use this option to set the log verbosity.",
93
+ )
94
+ parser.add_argument(
95
+ "-r",
96
+ dest="root_page",
97
+ help="Root Confluence page to create new pages. If omitted, will raise exception when creating new pages.",
98
+ )
99
+ parser.add_argument(
100
+ "--generated-by",
101
+ default="This page has been generated with a tool.",
102
+ help="Add prompt to pages (default: 'This page has been generated with a tool.').",
103
+ )
104
+ parser.add_argument(
105
+ "--no-generated-by",
106
+ dest="generated_by",
107
+ action="store_const",
108
+ const=None,
109
+ help="Do not add 'generated by a tool' prompt to pages.",
110
+ )
111
+ parser.add_argument(
112
+ "--render-mermaid",
113
+ dest="render_mermaid",
114
+ action="store_true",
115
+ default=True,
116
+ help="Render Mermaid diagrams as image files and add as attachments.",
117
+ )
118
+ parser.add_argument(
119
+ "--no-render-mermaid",
120
+ dest="render_mermaid",
121
+ action="store_false",
122
+ help="Inline Mermaid diagram in Confluence page.",
123
+ )
124
+ parser.add_argument(
125
+ "--render-mermaid-format",
126
+ dest="diagram_output_format",
127
+ choices=["png", "svg"],
128
+ default="png",
129
+ help="Format for rendering Mermaid diagrams (default: 'png').",
130
+ )
131
+ parser.add_argument(
132
+ "--heading-anchors",
133
+ action="store_true",
134
+ default=False,
135
+ help="Place an anchor at each section heading with GitHub-style same-page identifiers.",
136
+ )
137
+ parser.add_argument(
138
+ "--ignore-invalid-url",
139
+ action="store_true",
140
+ default=False,
141
+ help="Emit a warning but otherwise ignore relative URLs that point to ill-specified locations.",
142
+ )
143
+ parser.add_argument(
144
+ "--local",
145
+ action="store_true",
146
+ default=False,
147
+ help="Write XHTML-based Confluence Storage Format files locally without invoking Confluence API.",
148
+ )
149
+ parser.add_argument(
150
+ "--headers",
151
+ nargs="*",
152
+ required=False,
153
+ action=KwargsAppendAction,
154
+ metavar="KEY=VALUE",
155
+ help="Apply custom headers to all Confluence API requests.",
156
+ )
157
+ parser.add_argument(
158
+ "--webui-links",
159
+ action="store_true",
160
+ default=False,
161
+ help="Enable Confluence Web UI links.",
162
+ )
163
+
164
+ args = Arguments()
165
+ parser.parse_args(namespace=args)
166
+
167
+ # NOTE: If we switch to modern type aware CLI tool like typer
168
+ # the following line won't be necessary
169
+ args.mdpath = Path(args.mdpath)
170
+
171
+ logging.basicConfig(
172
+ level=getattr(logging, args.loglevel.upper(), logging.INFO),
173
+ format="%(asctime)s - %(levelname)s - %(funcName)s [%(lineno)d] - %(message)s",
174
+ )
175
+
176
+ options = ConfluenceDocumentOptions(
177
+ heading_anchors=args.heading_anchors,
178
+ ignore_invalid_url=args.ignore_invalid_url,
179
+ generated_by=args.generated_by,
180
+ root_page_id=args.root_page,
181
+ render_mermaid=args.render_mermaid,
182
+ diagram_output_format=args.diagram_output_format,
183
+ webui_links=args.webui_links,
184
+ )
185
+ properties = ConfluenceProperties(
186
+ args.domain, args.path, args.username, args.apikey, args.space, args.headers
187
+ )
188
+ if args.local:
189
+ Processor(options, properties).process(args.mdpath)
190
+ else:
191
+ try:
192
+ with ConfluenceAPI(properties) as api:
193
+ Application(
194
+ api,
195
+ options,
196
+ ).synchronize(args.mdpath)
197
+ except requests.exceptions.HTTPError as err:
198
+ logging.error(err)
199
+
200
+ # print details for a response with JSON body
201
+ if err.response is not None:
202
+ try:
203
+ logging.error(err.response.json())
204
+ except requests.exceptions.JSONDecodeError:
205
+ pass
206
+
207
+ sys.exit(1)
208
+
209
+
210
+ if __name__ == "__main__":
211
+ main()