1
1
import { default as fetch } from "unfetch" ;
2
+ import {
3
+ EVENT_READTHEDOCS_ADDONS_DATA_READY ,
4
+ ReadTheDocsEventData ,
5
+ } from "./events" ;
2
6
import {
3
7
CLIENT_VERSION ,
8
+ IS_TESTING ,
4
9
ADDONS_API_VERSION ,
5
10
ADDONS_API_ENDPOINT ,
6
- IS_TESTING ,
7
11
} from "./utils" ;
8
12
9
13
/**
10
- * Load Read the Docs configuration from API endpoint .
14
+ * Get the Read the Docs API version supported by user's integrations .
11
15
*
12
16
*/
13
- export function getReadTheDocsConfig ( sendUrlParam ) {
17
+ export function getMetadataAddonsAPIVersion ( ) {
18
+ const meta = document . querySelector (
19
+ "meta[name=readthedocs-addons-api-version]" ,
20
+ ) ;
21
+ if ( meta !== null ) {
22
+ return meta . getAttribute ( "content" ) ;
23
+ }
24
+ return undefined ;
25
+ }
26
+
27
+ /**
28
+ * Get the Addons API endpoint URL to hit.
29
+ *
30
+ * It uses META HTML tags to get project/version slugs and `sendUrlParam` to
31
+ * decide whether or not sending `url=`.
32
+ */
33
+ function _getApiUrl ( sendUrlParam , apiVersion ) {
14
34
const metaProject = document . querySelector (
15
35
"meta[name='readthedocs-project-slug']" ,
16
36
) ;
@@ -22,7 +42,7 @@ export function getReadTheDocsConfig(sendUrlParam) {
22
42
let versionSlug ;
23
43
let params = {
24
44
"client-version" : CLIENT_VERSION ,
25
- "api-version" : ADDONS_API_VERSION ,
45
+ "api-version" : apiVersion ,
26
46
} ;
27
47
28
48
if ( sendUrlParam ) {
@@ -44,13 +64,90 @@ export function getReadTheDocsConfig(sendUrlParam) {
44
64
url = "/_/readthedocs-addons.json" ;
45
65
}
46
66
47
- return fetch ( url , {
48
- method : "GET" ,
49
- } ) . then ( ( response ) => {
50
- if ( ! response . ok ) {
51
- console . debug ( "Error parsing configuration data" ) ;
52
- return undefined ;
67
+ return url ;
68
+ }
69
+
70
+ function getReadTheDocsUserConfig ( sendUrlParam ) {
71
+ // Create a Promise here to handle the user request in a different async task.
72
+ // This allows us to start executing our integration independently from the user one.
73
+ return new Promise ( ( resolve , reject ) => {
74
+ // Note we force the user to define the `<meta>` tag to be able to use Read the Docs data directly.
75
+ // This is to keep forward/backward compatibility without breaking integrations.
76
+ const metadataAddonsAPIVersion = getMetadataAddonsAPIVersion ( ) ;
77
+
78
+ if (
79
+ metadataAddonsAPIVersion !== undefined &&
80
+ metadataAddonsAPIVersion !== ADDONS_API_VERSION
81
+ ) {
82
+ // When the addons API version doesn't match the one defined via `<meta>` tag by the user,
83
+ // we perform another request to get the Read the Docs response in the structure
84
+ // that's supported by the user and dispatch a custom event letting them know
85
+ // this data is ready to be consumed under `event.detail.data()`.
86
+ const userApiUrl = _getApiUrl ( sendUrlParam , metadataAddonsAPIVersion ) ;
87
+
88
+ // TODO: revert this change and use the correct URL here
89
+ const url = "/_/readthedocs-addons.json" ;
90
+ fetch ( url , {
91
+ method : "GET" ,
92
+ } ) . then ( ( response ) => {
93
+ if ( ! response . ok ) {
94
+ return reject (
95
+ "Error hitting addons API endpoint for user api-version" ,
96
+ ) ;
97
+ }
98
+ // Return the data in the API version requested.
99
+ return resolve ( response . json ( ) ) ;
100
+ } ) ;
53
101
}
54
- return response . json ( ) ;
102
+
103
+ // If the API versions match, we return `undefined`.
104
+ return resolve ( undefined ) ;
105
+ } ) . catch ( ( error ) => {
106
+ console . error ( error ) ;
55
107
} ) ;
56
108
}
109
+
110
+ /**
111
+ * Load Read the Docs configuration from API endpoint.
112
+ *
113
+ */
114
+ export function getReadTheDocsConfig ( sendUrlParam ) {
115
+ return new Promise ( ( resolve , reject ) => {
116
+ let dataUser ;
117
+ const defaultApiUrl = _getApiUrl ( sendUrlParam , ADDONS_API_VERSION ) ;
118
+
119
+ fetch ( defaultApiUrl , {
120
+ method : "GET" ,
121
+ } )
122
+ . then ( ( response ) => {
123
+ if ( ! response . ok ) {
124
+ return reject ( "Error hitting addons API endpoint" ) ;
125
+ }
126
+ return response . json ( ) ;
127
+ } )
128
+ . then ( ( data ) => {
129
+ // Trigger a new task here to hit the API again in case the version
130
+ // request missmatchs the one the user expects.
131
+ getReadTheDocsUserConfig ( sendUrlParam ) . then ( ( dataUser ) => {
132
+ // Expose `dataUser` if available or the `data` already requested.
133
+ const dataEvent = dataUser !== undefined ? dataUser : data ;
134
+
135
+ // Trigger the addons data ready CustomEvent to with the data the user is expecting.
136
+ return dispatchEvent (
137
+ EVENT_READTHEDOCS_ADDONS_DATA_READY ,
138
+ document ,
139
+ new ReadTheDocsEventData ( dataEvent ) ,
140
+ ) ;
141
+ } ) ;
142
+
143
+ return resolve ( data ) ;
144
+ } ) ;
145
+ } ) . catch ( ( error ) => {
146
+ console . error ( error ) ;
147
+ } ) ;
148
+ }
149
+
150
+ function dispatchEvent ( eventName , element , data ) {
151
+ const event = new CustomEvent ( eventName , { detail : data } ) ;
152
+ element . dispatchEvent ( event ) ;
153
+ }
0 commit comments