toc_doc 1.1.0 → 1.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 31301e5ab8d81e4a073f6a86da51671dd7d41673eff44b48373659831b971010
4
- data.tar.gz: e308514b6009f7b703d8861e739d963ea0048ed930778329bd453b1f28f1e82c
3
+ metadata.gz: a6c39bc2d41f1be6ae2b12b23074de967614fc1c6067d217316c7cb8bb9f0c31
4
+ data.tar.gz: 6c3a8732a25916bb75ebb18c7f53a79ff272a0c8b646477160a8381890404dcf
5
5
  SHA512:
6
- metadata.gz: c8936171d0c8228e3a1390ef3d164ab5ca08cccecb656087cc983d61c859eaed3153d63715e4fd6f4bcd3f6b62cea4c85c1a6410669925ef3c73185f550eb28b
7
- data.tar.gz: 23ad7e64604e97d9c18a0b99b8917af0b97974f2c67d1dc842d16070650a3e97ef79e0902d96f4dd4f8e71f84bdc30d09811aa16fb61342bbdcacb6e7979b8fc
6
+ metadata.gz: dde234124b736d0149ac1f45bb6ab654487fb4e0c91bffb6c1b2462f51fa50a94cb59c5e9d4c4c5ef688eb12b9a8f8f321a0b5384b7d4bebe375506f81001b87
7
+ data.tar.gz: 1fd5632c3e5c73f89ae8db6f844269d5674141332ea9fd1956691695c3ae7242a7d7f706b3b0742f361df6b7a6a8ed931dc25f6d4b2c01426c6cc0ee379dacde
data/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.3.0] - 2026-03-15
4
+
5
+ ### Added
6
+
7
+ - **`TocDoc::Speciality`** — new `Resource`-based model representing a speciality returned by the autocomplete endpoint; exposes `#value`, `#slug`, and `#name` via dot-notation
8
+ - **`TocDoc::Profile`** — new `Resource`-based model for search profile results; `Profile.build(attrs)` factory returns a `Profile::Practitioner` or `Profile::Organization` based on the `owner_type` field; provides `#practitioner?` and `#organization?` predicates
9
+ - **`TocDoc::Profile::Practitioner`** and **`TocDoc::Profile::Organization`** — typed profile subclasses
10
+ - **`TocDoc::Search`** — new service class for the autocomplete endpoint (`/api/searchbar/autocomplete.json`); `Search.where(query:, type: nil, **options)` fetches results and returns a `Search::Result`, or a filtered array when `type:` is one of `'profile'`, `'practitioner'`, `'organization'`, or `'speciality'`
11
+ - **`TocDoc::Search::Result`** — envelope returned by `Search.where`; exposes `#profiles` (typed via `Profile.build`) and `#specialities`; `#filter_by_type` narrows to a specific kind
12
+ - **`TocDoc.search`** — top-level shortcut delegating to `TocDoc::Search.where`
13
+
14
+ ## [1.2.0] - 2026-03-08
15
+
16
+ ### Added
17
+
18
+ - **`TocDoc::Availability::Collection`** — new `Enumerable` collection class returned by `TocDoc::Availability.where`; provides `#total`, `#next_slot`, `#each`, `#raw_availabilities`, `#to_h`, and `#merge_page!`
19
+ - **`TocDoc::Availability.where`** — class-level query method replacing `Client#availabilities`; automatically follows a `next_slot` response key with a second request before returning the collection
20
+ - **`TocDoc.availabilities`** — top-level shortcut delegating to `TocDoc::Availability.where`
21
+ - **Dependabot** — automated Bundler dependency updates via `.github/dependabot.yml`
22
+
23
+ ### Changed
24
+
25
+ - **Connection** — `#get` and `#paginate` are now public on `TocDoc::Connection`, allowing model classes to call them directly via `TocDoc.client`
26
+ - **`TocDoc::UriUtils`** — updated module-level example to reflect actual usage (`TocDoc::Availability` with `extend`, not the removed `Client::Availabilities`)
27
+
28
+ ### Removed
29
+
30
+ - **`TocDoc::Client::Availabilities`** — endpoint module removed; availability querying now lives in `TocDoc::Availability.where` and `TocDoc::Availability::Collection`
31
+ - **`TocDoc::Response::Availability`** — response wrapper model removed; replaced by `TocDoc::Availability::Collection`
32
+ - **`auto_paginate`** — configuration key, default, and all related logic removed from `TocDoc::Configurable` and `TocDoc::Default`
33
+
3
34
  ## [1.1.0] - 2026-03-06
4
35
 
5
36
  ### Changed
@@ -0,0 +1,31 @@
1
+ # Potential Endpoints
2
+
3
+ - Interesting JSONs [GET]
4
+ - [x] Availabilities
5
+ - https://www.doctolib.fr/availabilities.json?visit_motive_ids=7767829&agenda_ids=1101600&practice_ids=377272&telehealth=false&start_date=2026-03-06&limit=5
6
+ - Practitioner ?
7
+ - https://www.doctolib.fr/pharmacie/paris/pharmacie-faidherbe.json
8
+ - https://www.doctolib.fr/dentiste/bordeaux/mathilde-devun-lesparre-medoc.json
9
+ - https://www.doctolib.fr/a/b/mathilde-devun-lesparre-medoc.json
10
+ - https://www.doctolib.fr/profiles/mathilde-devun-lesparre-medoc.json
11
+ - Rassemblement practiciens / Place Practitioners collection
12
+ - https://www.doctolib.fr/profiles/pavillon-de-la-mutualite-bordeaux-rue-vital-carles.json
13
+ - Places
14
+ - https://www.doctolib.fr/patient_app/place_autocomplete.json?query=47300
15
+ - Booking context
16
+ - https://www.doctolib.fr/online_booking/api/slot_selection_funnel/v1/info.json?profile_slug=brigitte-devun-pujols&locale=fr
17
+ - https://www.doctolib.fr/online_booking/api/slot_selection_funnel/v1/info.json?profile_slug=926388&locale=fr
18
+ - Interesting NON JSONs
19
+ - City practitioners (❗️JSON-LD in a script tag of an HTML page - data-id="removable-json-ld")
20
+ - https://www.doctolib.fr/dentiste/bordeaux/
21
+ - https://www.doctolib.fr/medecin-generaliste/bordeaux/
22
+ - https://www.doctolib.fr/medecin-generaliste/villeneuve-sur-lot
23
+ - Non-interesting
24
+ - Legal links
25
+ - https://www.doctolib.fr/search/footer_legal_links.json
26
+ - FAQ
27
+ - https://www.doctolib.fr/search/footer_public_content.json?hub_search=false&display_faq=true&speciality_id=2&place_id=18733
28
+ - Social media links
29
+ - https://www.doctolib.fr/search/footer_social_media_links.json
30
+ - New Booking [POST]
31
+ - online_booking/draft/new.json
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # TocDoc
2
2
 
3
- A Ruby gem for interacting with the (unofficial) Doctolib API. A thin, Faraday-based client with modular resource endpoints, configurable defaults, optional auto-pagination, and a clean error hierarchy.
3
+ A Ruby gem for interacting with the (unofficial) Doctolib API. A thin, Faraday-based client with configurable defaults, model-driven resource querying, and a clean error hierarchy.
4
4
 
5
5
  [![Gem Version](https://badge.fury.io/rb/toc_doc.svg)](https://badge.fury.io/rb/toc_doc)
6
6
  [![CI](https://github.com/01max/toc_doc/actions/workflows/main.yml/badge.svg)](https://github.com/01max/toc_doc/actions)
@@ -27,6 +27,7 @@ A Ruby gem for interacting with the (unofficial) Doctolib API. A thin, Faraday-b
27
27
  - [ENV variables](#environment-variable-overrides)
28
28
  4. [Endpoints](#endpoints)
29
29
  - [Availabilities](#availabilities)
30
+ - [Search](#search)
30
31
  5. [Response objects](#response-objects)
31
32
  6. [Pagination](#pagination)
32
33
  7. [Error handling](#error-handling)
@@ -65,18 +66,17 @@ gem install toc_doc
65
66
  ```ruby
66
67
  require 'toc_doc'
67
68
 
68
- # Use the pre-configured module-level client …
69
- response = TocDoc.availabilities(
69
+ collection = TocDoc::Availability.where(
70
70
  visit_motive_ids: 7_767_829,
71
71
  agenda_ids: 1_101_600,
72
72
  practice_ids: 377_272,
73
73
  telehealth: false
74
74
  )
75
75
 
76
- response.total # => 5
77
- response.next_slot # => "2026-02-28T10:00:00.000+01:00"
76
+ collection.total # => 5
77
+ collection.next_slot # => "2026-02-28T10:00:00.000+01:00"
78
78
 
79
- response.availabilities.each do |avail|
79
+ collection.each do |avail|
80
80
  puts "#{avail.date}: #{avail.slots.map { |s| s.strftime('%H:%M') }.join(', ')}"
81
81
  end
82
82
  ```
@@ -95,7 +95,7 @@ TocDoc.configure do |config|
95
95
  config.per_page = 10
96
96
  end
97
97
 
98
- TocDoc.availabilities(visit_motive_ids: 123, agenda_ids: 456)
98
+ TocDoc::Availability.where(visit_motive_ids: 123, agenda_ids: 456)
99
99
  ```
100
100
 
101
101
  Calling `TocDoc.reset!` restores all options to their defaults.
@@ -103,14 +103,24 @@ Use `TocDoc.options` to inspect the current configuration hash.
103
103
 
104
104
  ### Per-client configuration
105
105
 
106
- Instantiate independent clients with different options:
106
+ Instantiate independent clients with different options and query via `TocDoc::Availability.where`:
107
107
 
108
108
  ```ruby
109
- de_client = TocDoc::Client.new(api_endpoint: 'https://www.doctolib.de')
110
- it_client = TocDoc::Client.new(api_endpoint: 'https://www.doctolib.it', per_page: 3)
109
+ # Germany
110
+ TocDoc.configure { |c| c.api_endpoint = 'https://www.doctolib.de'; c.per_page = 3 }
111
+ TocDoc::Availability.where(visit_motive_ids: 123, agenda_ids: 456)
112
+
113
+ # Reset and switch to Italy
114
+ TocDoc.reset!
115
+ TocDoc.configure { |c| c.api_endpoint = 'https://www.doctolib.it' }
116
+ TocDoc::Availability.where(visit_motive_ids: 789, agenda_ids: 101)
117
+ ```
118
+
119
+ Alternatively, use `TocDoc::Client` directly for lower-level access ().
111
120
 
112
- de_client.availabilities(visit_motive_ids: 123, agenda_ids: 456)
113
- it_client.availabilities(visit_motive_ids: 789, agenda_ids: 101)
121
+ ```ruby
122
+ client = TocDoc::Client.new(api_endpoint: 'https://www.doctolib.de', per_page: 5)
123
+ client.get('/availabilities.json', query: { visit_motive_ids: '123', agenda_ids: '456', start_date: Date.today.to_s, limit: 5 })
114
124
  ```
115
125
 
116
126
  ### All configuration options
@@ -118,10 +128,9 @@ it_client.availabilities(visit_motive_ids: 789, agenda_ids: 101)
118
128
  | Option | Default | Description |
119
129
  |---|---|---|
120
130
  | `api_endpoint` | `https://www.doctolib.fr` | Base URL. Change to `.de` / `.it` for other countries. |
121
- | `user_agent` | `TocDoc Ruby Gem 0.1.0` | `User-Agent` header sent with every request. |
131
+ | `user_agent` | `TocDoc Ruby Gem 1.3.0` | `User-Agent` header sent with every request. |
122
132
  | `default_media_type` | `application/json` | `Accept` and `Content-Type` headers. |
123
- | `per_page` | `5` | Default number of results returned per request, platform's max is currently at `15`. |
124
- | `auto_paginate` | `false` | When `true`, automatically fetches all pages and merges results. |
133
+ | `per_page` | `15` | Default number of availability dates per request (capped at `15`). |
125
134
  | `middleware` | Retry + RaiseError + JSON + adapter | Full Faraday middleware stack. Override to customise completely. |
126
135
  | `connection_options` | `{}` | Options passed directly to `Faraday.new`. |
127
136
 
@@ -135,7 +144,6 @@ All primary options can be set via environment variables before the gem is loade
135
144
  | `TOCDOC_USER_AGENT` | `user_agent` |
136
145
  | `TOCDOC_MEDIA_TYPE` | `default_media_type` |
137
146
  | `TOCDOC_PER_PAGE` | `per_page` |
138
- | `TOCDOC_AUTO_PAGINATE` | `auto_paginate` (`"true"` / anything else) |
139
147
  | `TOCDOC_RETRY_MAX` | Maximum Faraday retry attempts (default `3`) |
140
148
 
141
149
  ---
@@ -147,7 +155,7 @@ All primary options can be set via environment variables before the gem is loade
147
155
  Retrieve open appointment slots for a given visit motive and agenda.
148
156
 
149
157
  ```ruby
150
- client.availabilities(
158
+ TocDoc::Availability.where(
151
159
  visit_motive_ids: visit_motive_id, # Integer, String, or Array
152
160
  agenda_ids: agenda_id, # Integer, String, or Array
153
161
  start_date: Date.today, # Date or String (default: today)
@@ -158,18 +166,48 @@ client.availabilities(
158
166
  )
159
167
  ```
160
168
 
169
+ `TocDoc.availabilities(...)` is a module-level shortcut with the same signature.
170
+
161
171
  **Multiple IDs** are accepted as arrays; the gem serialises them with the
162
172
  dash-separated format Doctolib expects:
163
173
 
164
174
  ```ruby
165
- client.availabilities(
175
+ TocDoc::Availability.where(
166
176
  visit_motive_ids: [7_767_829, 7_767_830],
167
177
  agenda_ids: [1_101_600, 1_101_601]
168
178
  )
169
179
  # → GET /availabilities.json?visit_motive_ids=7767829-7767830&agenda_ids=1101600-1101601&…
170
180
  ```
171
181
 
172
- **Return value:** a `TocDoc::Response::Availability` (see [Response objects](#response-objects)).
182
+ **Return value:** a `TocDoc::Availability::Collection` (see [Response objects](#response-objects)).
183
+
184
+ ### Search
185
+
186
+ Query the Doctolib autocomplete endpoint to look up practitioners, organizations, and specialities.
187
+
188
+ ```ruby
189
+ result = TocDoc::Search.where(query: 'dentiste')
190
+ result.profiles # => [#<TocDoc::Profile::Practitioner ...>, ...]
191
+ result.specialities # => [#<TocDoc::Speciality ...>, ...]
192
+ ```
193
+
194
+ Pass `type:` to receive a filtered array directly:
195
+
196
+ ```ruby
197
+ # Only specialities
198
+ TocDoc::Search.where(query: 'cardio', type: 'speciality')
199
+ # => [#<TocDoc::Speciality name="Cardiologue">, ...]
200
+
201
+ # Only practitioners
202
+ TocDoc::Search.where(query: 'dupont', type: 'practitioner')
203
+ # => [#<TocDoc::Profile::Practitioner ...>, ...]
204
+ ```
205
+
206
+ Valid `type:` values: `'profile'` (all profiles), `'practitioner'`, `'organization'`, `'speciality'`.
207
+
208
+ `TocDoc.search(...)` is a module-level shortcut with the same signature.
209
+
210
+ **Return value:** a `TocDoc::Search::Result` when `type:` is omitted, or a filtered `Array` otherwise (see [Response objects](#response-objects)).
173
211
 
174
212
  ---
175
213
 
@@ -178,20 +216,22 @@ client.availabilities(
178
216
  All API responses are wrapped in lightweight Ruby objects that provide
179
217
  dot-notation access and a `#to_h` round-trip helper.
180
218
 
181
- ### `TocDoc::Response::Availability`
219
+ ### `TocDoc::Availability::Collection`
182
220
 
183
- Returned by `#availabilities`.
221
+ Returned by `TocDoc::Availability.where`; also accessible via the `TocDoc.availabilities` module-level shortcut.
222
+ Implements `Enumerable`, yielding `TocDoc::Availability` instances that have at least one slot.
184
223
 
185
224
  | Method | Type | Description |
186
225
  |---|---|---|
187
226
  | `#total` | `Integer` | Total number of available slots across all dates. |
188
227
  | `#next_slot` | `String \| nil` | ISO 8601 datetime of the nearest available slot. `nil` when none remain. |
189
- | `#availabilities` | `Array<TocDoc::Availability>` | One entry per date. |
190
- | `#to_h` | `Hash` | Plain-hash representation including expanded availability entries. |
228
+ | `#each` | — | Yields each `TocDoc::Availability` that has at least one slot (excludes empty-slot dates). |
229
+ | `#raw_availabilities` | `Array<TocDoc::Availability>` | All date entries, including those with no slots. |
230
+ | `#to_h` | `Hash` | Plain-hash representation (only dates with slots in the `availabilities` key). |
191
231
 
192
232
  ### `TocDoc::Availability`
193
233
 
194
- Each element of `Response::Availability#availabilities`.
234
+ Represents a single availability date entry. Each element yielded by the collection.
195
235
 
196
236
  | Method | Type | Description |
197
237
  |---|---|---|
@@ -202,15 +242,15 @@ Each element of `Response::Availability#availabilities`.
202
242
  **Example:**
203
243
 
204
244
  ```ruby
205
- response = TocDoc.availabilities(visit_motive_ids: 123, agenda_ids: 456)
245
+ collection = TocDoc::Availability.where(visit_motive_ids: 123, agenda_ids: 456)
206
246
 
207
- response.total # => 5
208
- response.next_slot # => "2026-02-28T10:00:00.000+01:00"
247
+ collection.total # => 5
248
+ collection.next_slot # => "2026-02-28T10:00:00.000+01:00"
209
249
 
210
- response.availabilities.first.date # => #<Date: 2026-02-28>
211
- response.availabilities.first.slots # => [#<DateTime: 2026-02-28T10:00:00+01:00>, ...]
250
+ collection.first.date # => #<Date: 2026-02-28>
251
+ collection.first.slots # => [#<DateTime: 2026-02-28T10:00:00+01:00>, ...]
212
252
 
213
- response.to_h
253
+ collection.to_h
214
254
  # => {
215
255
  # "total" => 5,
216
256
  # "next_slot" => "2026-02-28T10:00:00.000+01:00",
@@ -218,39 +258,85 @@ response.to_h
218
258
  # }
219
259
  ```
220
260
 
221
- ---
261
+ ### `TocDoc::Search::Result`
222
262
 
223
- ## Pagination
263
+ Returned by `TocDoc::Search.where` when `type:` is omitted.
264
+
265
+ | Method | Type | Description |
266
+ |---|---|---|
267
+ | `#profiles` | `Array<TocDoc::Profile::Practitioner, TocDoc::Profile::Organization>` | All profile results, typed via `Profile.build`. |
268
+ | `#specialities` | `Array<TocDoc::Speciality>` | All speciality results. |
269
+ | `#filter_by_type(type)` | `Array` | Narrows results to `'profile'`, `'practitioner'`, `'organization'`, or `'speciality'`. |
270
+
271
+ ### `TocDoc::Profile`
272
+
273
+ Represents a search profile result (practitioner or organization). Use `Profile.build(attrs)` to obtain the correctly typed subclass instance.
224
274
 
225
- The Doctolib availability endpoint is paginated by `start_date` and `limit`.
226
- TocDoc can manage this automatically.
275
+ | Method | Type | Description |
276
+ |---|---|---|
277
+ | `Profile.build(attrs)` | `Profile::Practitioner \| Profile::Organization` | Factory: returns `Practitioner` when `owner_type` is `"Account"`, `Organization` otherwise. |
278
+ | `#practitioner?` | `Boolean` | `true` when this is a `Profile::Practitioner`. |
279
+ | `#organization?` | `Boolean` | `true` when this is a `Profile::Organization`. |
280
+
281
+ `TocDoc::Profile::Practitioner` and `TocDoc::Profile::Organization` are thin subclasses that inherit dot-notation attribute access from `TocDoc::Resource`.
227
282
 
228
- ### Automatic pagination
283
+ ### `TocDoc::Speciality`
284
+
285
+ Represents a speciality returned by the autocomplete endpoint. Inherits dot-notation attribute access from `TocDoc::Resource`.
286
+
287
+ | Method | Type | Description |
288
+ |---|---|---|
289
+ | `#value` | `Integer` | Numeric speciality identifier. |
290
+ | `#slug` | `String` | URL-friendly identifier. |
291
+ | `#name` | `String` | Human-readable speciality name. |
229
292
 
230
- Set `auto_paginate: true` on the client (or at module level) to fetch all pages
231
- and have results merged into a single `Response::Availability` object:
293
+ **Example:**
232
294
 
233
295
  ```ruby
234
- client = TocDoc::Client.new(auto_paginate: true, per_page: 5)
296
+ result = TocDoc::Search.where(query: 'dermato')
235
297
 
236
- all_slots = client.availabilities(
237
- visit_motive_ids: 7_767_829,
238
- agenda_ids: 1_101_600,
239
- start_date: Date.today
240
- )
298
+ result.profiles.first.class # => TocDoc::Profile::Practitioner
299
+ result.profiles.first.practitioner? # => true
300
+ result.profiles.first.name # => "Dr. Jane Smith"
241
301
 
242
- all_slots.total # total across every page
243
- all_slots.availabilities # every date entry, concatenated
302
+ result.specialities.first.slug # => "dermatologue"
303
+ result.specialities.first.name # => "Dermatologue"
244
304
  ```
245
305
 
246
- Pagination stops automatically when the API returns `next_slot: null`.
306
+ ---
307
+
308
+ ## Pagination
309
+
310
+ The Doctolib availability endpoint is window-based: each request returns up to
311
+ `limit` dates starting from `start_date`.
312
+
313
+ ### Automatic next-slot resolution
314
+
315
+ `TocDoc::Availability.where` automatically follows `next_slot` once: if the
316
+ first API response contains a `next_slot` key (indicating no available slots in
317
+ the requested window), a second request is issued transparently from that date
318
+ before the collection is returned.
247
319
 
248
- ### Module-level toggle
320
+ ### Manual window advancement
321
+
322
+ To fetch additional date windows, call `TocDoc::Availability.where` again with a
323
+ later `start_date`:
249
324
 
250
325
  ```ruby
251
- TocDoc.configure { |c| c.auto_paginate = true }
326
+ first_page = TocDoc::Availability.where(
327
+ visit_motive_ids: 7_767_829,
328
+ agenda_ids: 1_101_600,
329
+ start_date: Date.today
330
+ )
252
331
 
253
- TocDoc.availabilities(visit_motive_ids: 123, agenda_ids: 456)
332
+ if first_page.any?
333
+ next_start = first_page.raw_availabilities.last.date + 1
334
+ next_page = TocDoc::Availability.where(
335
+ visit_motive_ids: 7_767_829,
336
+ agenda_ids: 1_101_600,
337
+ start_date: next_start
338
+ )
339
+ end
254
340
  ```
255
341
 
256
342
  ---
@@ -262,7 +348,7 @@ so you can rescue the whole hierarchy with a single clause:
262
348
 
263
349
  ```ruby
264
350
  begin
265
- TocDoc.availabilities(visit_motive_ids: 0, agenda_ids: 0)
351
+ TocDoc::Availability.where(visit_motive_ids: 0, agenda_ids: 0)
266
352
  rescue TocDoc::Error => e
267
353
  puts "Doctolib error: #{e.message}"
268
354
  end
@@ -319,13 +405,14 @@ bundle exec rake install
319
405
 
320
406
  ### Adding new endpoints
321
407
 
322
- 1. Create `lib/toc_doc/client/<resource>.rb` and define a module
323
- `TocDoc::Client::<Resource>` with your endpoint methods.
324
- 2. Call `get`/`post`/`paginate` (from `TocDoc::Connection`) to issue requests.
325
- 3. Create `lib/toc_doc/models/response/<resource>.rb` (and any model classes)
326
- inheriting from `TocDoc::Resource`.
327
- 4. Include the new module in `TocDoc::Client` (`lib/toc_doc/client.rb`).
328
- 5. Add corresponding specs under `spec/toc_doc/client/`.
408
+ 1. Create `lib/toc_doc/models/<resource>.rb` with a model class inheriting from
409
+ `TocDoc::Resource`. Add a class-level `.where` (or equivalent) query method
410
+ that calls `TocDoc.client.get` / `.post` to issue requests.
411
+ 2. If the endpoint is paginated, create
412
+ `lib/toc_doc/models/<resource>/collection.rb` with an `Enumerable` collection
413
+ class (see `TocDoc::Availability::Collection` for the pattern).
414
+ 3. Require the new files from `lib/toc_doc/models.rb`.
415
+ 4. Add specs under `spec/toc_doc/models/`.
329
416
 
330
417
  ### Generating documentation
331
418
 
data/TODO.md CHANGED
@@ -1,52 +1,53 @@
1
1
  # PLAN
2
2
 
3
- ### Extra Endpoints
4
- - [x] Identify additional endpoints
5
- - [ ] Prioritize implementation of resource modules for those endpoints
3
+ [POTENTIAL_ENDPOINTS][POTENTIAL_ENDPOINTS.md]
6
4
 
7
- ## 1.2
5
+ ## 1.4
6
+
7
+ - [ ] Profile
8
+ - slug : https://www.doctolib.fr/profiles/mathilde-devun-lesparre-medoc.json
9
+ - id : https://www.doctolib.fr/profiles/926388.json
10
+
11
+ ## 1.5
12
+
13
+ - [ ] Booking context
14
+ - https://www.doctolib.fr/online_booking/api/slot_selection_funnel/v1/info.json?profile_slug=926388
15
+
16
+ ## 1.6
8
17
 
9
18
  ### Better API usage
10
19
  - [ ] Rate limiting
11
20
  - [ ] Caching
12
21
  - [ ] Logging
13
22
 
14
- ## 1.4
23
+ ## 2.0
15
24
 
16
25
  ### Auth / User-based actions
17
26
  - [ ] Research auth scheme
18
27
  - [ ] Authentication module + headers
19
28
  - [ ] Auth specs
20
29
 
21
- # Potential Endpoints
22
-
23
- - Interesting JSONs [GET]
24
- - Practitioner ?
25
- - https://www.doctolib.fr/pharmacie/paris/pharmacie-faidherbe.json
26
- - https://www.doctolib.fr/dentiste/bordeaux/mathilde-devun-lesparre-medoc.json
27
- - https://www.doctolib.fr/a/b/mathilde-devun-lesparre-medoc.json
28
- - https://www.doctolib.fr/profiles/mathilde-devun-lesparre-medoc.json
29
- - Rassemblement practiciens / Place Practitioners collection
30
- - https://www.doctolib.fr/profiles/pavillon-de-la-mutualite-bordeaux-rue-vital-carles.json
31
- - Places
32
- - https://www.doctolib.fr/patient_app/place_autocomplete.json?query=47300
33
- - Interesting NON JSONs
34
- - City practitioners (❗️JSON-LD in a script tag of an HTML page - data-id="removable-json-ld")
35
- - https://www.doctolib.fr/dentiste/bordeaux/
36
- - https://www.doctolib.fr/medecin-generaliste/bordeaux/
37
- - https://www.doctolib.fr/medecin-generaliste/villeneuve-sur-lot
38
- - Non-interesting
39
- - Legal links
40
- - https://www.doctolib.fr/search/footer_legal_links.json
41
- - FAQ
42
- - https://www.doctolib.fr/search/footer_public_content.json?hub_search=false&display_faq=true&speciality_id=2&place_id=18733
43
- - Social media links
44
- - https://www.doctolib.fr/search/footer_social_media_links.json
45
- - New Booking [POST]
46
- - online_booking/draft/new.json
30
+ # ???
31
+
32
+ - [ ] Figure what is `organization_statuses` in the autocomplete endpoint and what to do with it.
47
33
 
48
34
  # DONE & RELEASED
49
35
 
36
+ ## 1.3
37
+
38
+ - [x] Search (autocomplete)
39
+ - [x] search profile : https://www.doctolib.fr/api/searchbar/autocomplete.json?search=devun
40
+ - [x] search specialty : https://www.doctolib.fr/api/searchbar/autocomplete.json?search=dentiste
41
+
42
+ ## 1.2
43
+
44
+ - [x] Rework Availability's client, model and collection architecture.
45
+
46
+ ## 1.1
47
+
48
+ ### Parse raw API data
49
+ - [x] Parse date / datetime
50
+
50
51
  ## 1.0
51
52
 
52
53
  ### 1 – Skeleton & Tooling
@@ -100,9 +101,4 @@
100
101
  - [x] on rubygem
101
102
  - [x] release on GH
102
103
  - [x] gem.coop/@maxime
103
- - [x] Add test coverage tool
104
-
105
- ## 1.1
106
-
107
- ### Parse raw API data
108
- - [x] Parse date / datetime
104
+ - [x] Add test coverage tool
@@ -3,7 +3,6 @@
3
3
  require 'toc_doc/core/configurable'
4
4
  require 'toc_doc/core/connection'
5
5
  require 'toc_doc/core/uri_utils'
6
- require 'toc_doc/client/availabilities'
7
6
 
8
7
  module TocDoc
9
8
  # The main entry-point for interacting with the Doctolib API.
@@ -16,18 +15,14 @@ module TocDoc
16
15
  # api_endpoint: 'https://www.doctolib.de',
17
16
  # per_page: 5
18
17
  # )
19
- # client.availabilities(visit_motive_ids: 123, agenda_ids: 456)
20
18
  #
21
19
  # @see TocDoc::Configurable
22
20
  # @see TocDoc::Connection
23
- # @see TocDoc::Client::Availabilities
24
21
  class Client
25
22
  include TocDoc::Configurable
26
23
  include TocDoc::Connection
27
24
  include TocDoc::UriUtils
28
25
 
29
- include TocDoc::Client::Availabilities
30
-
31
26
  # Creates a new client instance.
32
27
  #
33
28
  # Options are merged on top of the module-level {TocDoc::Default} values.
@@ -39,7 +34,6 @@ module TocDoc
39
34
  # @option options [String] :user_agent User-Agent header value
40
35
  # @option options [String] :default_media_type Accept / Content-Type header
41
36
  # @option options [Integer] :per_page Results per page
42
- # @option options [Boolean] :auto_paginate Follow pagination automatically
43
37
  # @option options [Faraday::RackBuilder] :middleware Custom Faraday middleware
44
38
  # @option options [Hash] :connection_options Additional Faraday options
45
39
  #
@@ -29,7 +29,6 @@ module TocDoc
29
29
  connection_options
30
30
  default_media_type
31
31
  per_page
32
- auto_paginate
33
32
  ].freeze
34
33
 
35
34
  # @!attribute [rw] api_endpoint
@@ -42,8 +41,6 @@ module TocDoc
42
41
  # @return [Hash] additional Faraday connection options
43
42
  # @!attribute [rw] default_media_type
44
43
  # @return [String] the Accept / Content-Type header value
45
- # @!attribute [rw] auto_paginate
46
- # @return [Boolean] whether to follow pagination automatically
47
44
  attr_accessor(*VALID_CONFIG_KEYS)
48
45
 
49
46
  # Set the number of results per page, clamped to
@@ -9,8 +9,9 @@ module TocDoc
9
9
  # (`get`, `post`, `put`, `patch`, `delete`, `head`), a memoised
10
10
  # Faraday connection, pagination support, and response tracking.
11
11
  #
12
- # All HTTP methods are **private** callers interact with them through
13
- # higher-level endpoint modules (e.g. {TocDoc::Client::Availabilities}).
12
+ # {#get} and {#paginate} are public so that model classes (e.g.
13
+ # {TocDoc::Availability}) can call them via `TocDoc.client`; all other
14
+ # HTTP verb methods remain private.
14
15
  #
15
16
  # @see TocDoc::Client
16
17
  module Connection
@@ -107,12 +108,11 @@ module TocDoc
107
108
 
108
109
  # Performs a paginated GET, accumulating results across pages.
109
110
  #
110
- # When {Configurable#auto_paginate} is disabled or no block is given,
111
- # behaves exactly like {#get}.
111
+ # Behaves exactly like {#get} when no block is given.
112
112
  #
113
- # When +auto_paginate+ is +true+ **and** a block is provided, the block is
114
- # yielded after every page fetch — including the first — with
115
- # +(accumulator, last_response)+. The block must:
113
+ # When a block is provided, it is yielded after every page fetch —
114
+ # including the first — with +(accumulator, last_response)+. The block
115
+ # must:
116
116
  #
117
117
  # 1. Detect whether it is a continuation call by comparing object identity:
118
118
  # `acc.equal?(last_response.body)` is `true` only on the first yield,
@@ -129,7 +129,7 @@ module TocDoc
129
129
  # @return [Object] the fully-accumulated response body
130
130
  def paginate(path, options = {}, &)
131
131
  data = get(path, options)
132
- return data unless block_given? && auto_paginate
132
+ return data unless block_given?
133
133
 
134
134
  loop do
135
135
  next_options = yield(data, last_response)
@@ -192,5 +192,7 @@ module TocDoc
192
192
 
193
193
  [query || {}, explicit_headers]
194
194
  end
195
+
196
+ public :get, :paginate
195
197
  end
196
198
  end
@@ -28,9 +28,6 @@ module TocDoc
28
28
  # @return [Integer] the hard upper limit for per_page
29
29
  MAX_PER_PAGE = 15
30
30
 
31
- # @return [Boolean] whether to auto-paginate by default
32
- AUTO_PAGINATE = false
33
-
34
31
  # @return [Integer] the default maximum number of retries
35
32
  MAX_RETRY = 3
36
33
 
@@ -45,7 +42,6 @@ module TocDoc
45
42
  user_agent: user_agent,
46
43
  default_media_type: default_media_type,
47
44
  per_page: per_page,
48
- auto_paginate: auto_paginate,
49
45
  middleware: middleware,
50
46
  connection_options: connection_options
51
47
  }
@@ -93,19 +89,6 @@ module TocDoc
93
89
  PER_PAGE
94
90
  end
95
91
 
96
- # Whether to follow pagination automatically.
97
- #
98
- # Falls back to the `TOCDOC_AUTO_PAGINATE` environment variable (set to
99
- # `"true"` to enable), then {AUTO_PAGINATE}.
100
- #
101
- # @return [Boolean]
102
- def auto_paginate
103
- env_val = ENV.fetch('TOCDOC_AUTO_PAGINATE', nil)
104
- return AUTO_PAGINATE if env_val.nil?
105
-
106
- env_val.casecmp('true').zero?
107
- end
108
-
109
92
  # The default Faraday middleware stack.
110
93
  #
111
94
  # Includes retry logic, error raising, JSON parsing, and the default