Skip to content

Commit ecab24b

Browse files
committed
v4: several breaking changes for changeset APIs to be consistent with...
...the OSM API's new json endpoints.
1 parent d08c923 commit ecab24b

File tree

10 files changed

+164
-118
lines changed

10 files changed

+164
-118
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## 4.0.0 (----------)
11+
12+
- 💥 BREAKING CHANGE: `uploadChangeset` now returns a object instead of a single changeset ID. This is because:
13+
1. the function supports chunking uploads into multiple changesets if it exceeds the limit of 10,000 features per changeset.
14+
2. For each feature that was created, the response now includes a mapping between the temporary ID used by the uploader, and the permanent ID allocated by the server.
15+
- 💥 BREAKING CHANGE: The type defintions for `Changeset` have been changed to mark several properties as optional. (see [#14](https://github.com/osmlab/osm-api-js/issues/14))
16+
- 💥 BREAKING CHANGE: `Changeset.created_at`, `Changeset.closed_at`, and `ChangesetComment.date` are now a `string`, not a `Date`. This makes it more consistent with the XML format, and easier to serialise to JSON.
17+
- 💥 BREAKING CHANGE: `ChangesetComment.uid` is now a `number`, not a `string`. This matches the behaviour of OSM's new json API.
18+
- 💥 BREAKING CHANGE: `Changeset.discussion` has been renamed to `Changeset.comments`. This matches the behaviour of OSM's new json API.
19+
1020
## 3.3.0 (2025-09-18)
1121

1222
- [uploadChangeset] add an `onProgress` callback, so that apps can show a progress bar while uploading

examples/uploadChangeset.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,17 @@ await uploadChangeset(
2626

2727
Response:
2828

29-
```json
30-
12345
29+
```jsonc
30+
{
31+
// 12345 is the changeset number
32+
"12345": {
33+
// the contents of this object is the diff result.
34+
// - for created features, this object allows you to map the temporary ID used by the uploader, to the permananet ID that the server allocated to this feature.
35+
// - for updated & deleted features, it includes the new version number
36+
},
37+
}
3138
```
3239

33-
(changeset number)
34-
3540
## Detailed Examples
3641

3742
### Updating existing features

src/__tests__/__snapshots__/e2e.test.ts.snap

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,9 @@ exports[`end to end tests > getCapabilities 1`] = `
109109
exports[`end to end tests > getChangeset (no discussion) 1`] = `
110110
{
111111
"changes_count": 10,
112-
"closed_at": 2021-12-16T09:29:15.000Z,
112+
"closed_at": "2021-12-16T09:29:15Z",
113113
"comments_count": 2,
114-
"created_at": 2021-12-16T09:29:14.000Z,
115-
"discussion": undefined,
114+
"created_at": "2021-12-16T09:29:14Z",
116115
"id": 227200,
117116
"max_lat": -36.8801278,
118117
"max_lon": 174.7400986,
@@ -136,27 +135,27 @@ exports[`end to end tests > getChangeset (no discussion) 1`] = `
136135
exports[`end to end tests > getChangeset (with discussion) 1`] = `
137136
{
138137
"changes_count": 10,
139-
"closed_at": 2021-12-16T09:29:15.000Z,
140-
"comments_count": 2,
141-
"created_at": 2021-12-16T09:29:14.000Z,
142-
"discussion": [
138+
"closed_at": "2021-12-16T09:29:15Z",
139+
"comments": [
143140
{
144-
"date": 2024-06-30T08:59:15.000Z,
141+
"date": "2024-06-30T08:59:15Z",
145142
"id": 736,
146143
"text": "­",
147-
"uid": "12248",
144+
"uid": 12248,
148145
"user": "kylenz_testing",
149146
"visible": true,
150147
},
151148
{
152-
"date": 2024-06-30T08:59:55.000Z,
149+
"date": "2024-06-30T08:59:55Z",
153150
"id": 737,
154151
"text": "sup bro",
155-
"uid": "12248",
152+
"uid": 12248,
156153
"user": "kylenz_testing",
157154
"visible": true,
158155
},
159156
],
157+
"comments_count": 2,
158+
"created_at": "2021-12-16T09:29:14Z",
160159
"id": 227200,
161160
"max_lat": -36.8801278,
162161
"max_lon": 174.7400986,
@@ -533,10 +532,9 @@ exports[`end to end tests > listChangesets 1`] = `
533532
[
534533
{
535534
"changes_count": 2,
536-
"closed_at": 2022-09-10T11:47:14.000Z,
535+
"closed_at": "2022-09-10T11:47:14Z",
537536
"comments_count": 0,
538-
"created_at": 2022-09-10T11:47:13.000Z,
539-
"discussion": undefined,
537+
"created_at": "2022-09-10T11:47:13Z",
540538
"id": 243638,
541539
"max_lat": -36.8804862,
542540
"max_lon": 174.739748,
@@ -553,10 +551,9 @@ exports[`end to end tests > listChangesets 1`] = `
553551
},
554552
{
555553
"changes_count": 10,
556-
"closed_at": 2022-09-10T11:30:13.000Z,
554+
"closed_at": "2022-09-10T11:30:13Z",
557555
"comments_count": 0,
558-
"created_at": 2022-09-10T11:30:12.000Z,
559-
"discussion": undefined,
556+
"created_at": "2022-09-10T11:30:12Z",
560557
"id": 243637,
561558
"max_lat": -36.8804809,
562559
"max_lon": 174.7397571,
@@ -576,10 +573,9 @@ exports[`end to end tests > listChangesets 1`] = `
576573
},
577574
{
578575
"changes_count": 10,
579-
"closed_at": 2021-12-16T09:29:15.000Z,
576+
"closed_at": "2021-12-16T09:29:15Z",
580577
"comments_count": 2,
581-
"created_at": 2021-12-16T09:29:14.000Z,
582-
"discussion": undefined,
578+
"created_at": "2021-12-16T09:29:14Z",
583579
"id": 227200,
584580
"max_lat": -36.8801278,
585581
"max_lon": 174.7400986,
@@ -600,10 +596,9 @@ exports[`end to end tests > listChangesets 1`] = `
600596
},
601597
{
602598
"changes_count": 5,
603-
"closed_at": 2021-12-16T08:43:07.000Z,
599+
"closed_at": "2021-12-16T08:43:07Z",
604600
"comments_count": 0,
605-
"created_at": 2021-12-16T08:43:06.000Z,
606-
"discussion": undefined,
601+
"created_at": "2021-12-16T08:43:06Z",
607602
"id": 227184,
608603
"max_lat": -36.8804809,
609604
"max_lon": 174.7397571,

src/api/_rawResponse.d.ts

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,5 @@
11
import type { Feature, FeatureCollection, Point } from "geojson";
2-
import type {
3-
Changeset,
4-
ChangesetComment,
5-
OsmFeatureType,
6-
OsmNote,
7-
} from "../types";
8-
9-
/** @internal */
10-
export type RawChangeset = Omit<
11-
Changeset,
12-
"discussion" | "created_at" | "closed_at"
13-
> & {
14-
created_at: string;
15-
closed_at?: string;
16-
comments?: (Omit<ChangesetComment, "date" | "uid"> & {
17-
/** ISO Date */
18-
date: string;
19-
uid: number;
20-
})[];
21-
};
2+
import type { OsmFeatureType, OsmNote } from "../types";
223

234
/** @internal */
245
export type RawNotesSearch = FeatureCollection<

src/api/changesets/__tests__/uploadChangeset.test.ts

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ describe("uploadChangeset", () => {
236236
{ comment: "added a building" },
237237
{ create: [], modify: [], delete: [] }
238238
);
239-
expect(output).toBe(1);
239+
expect(output).toStrictEqual({ 1: { diffResult: {} } });
240240

241241
expect(osmFetch).toHaveBeenCalledTimes(3);
242242
expect(osmFetch).toHaveBeenNthCalledWith(
@@ -277,7 +277,56 @@ describe("uploadChangeset", () => {
277277
);
278278
}
279279

280-
expect(output).toBe(1);
280+
expect(output).toStrictEqual({
281+
// create nodes first.
282+
1: {
283+
diffResult: {
284+
node: {
285+
"-10": { newId: 2, newVersion: 1 },
286+
"-11": { newId: 1, newVersion: 1 },
287+
"-2": { newId: 5, newVersion: 1 },
288+
"-7": { newId: 6, newVersion: 1 },
289+
"-8": { newId: 4, newVersion: 1 },
290+
"-9": { newId: 3, newVersion: 1 },
291+
},
292+
},
293+
},
294+
// create nodes then ways then relations next
295+
2: {
296+
diffResult: {
297+
node: {
298+
"-100": { newId: 10, newVersion: 1 },
299+
"-4": { newId: 9, newVersion: 1 },
300+
"-5": { newId: 8, newVersion: 1 },
301+
"-6": { newId: 7, newVersion: 1 },
302+
},
303+
relation: { "-300000": { newId: 1, newVersion: 1 } },
304+
way: { "-3": { newId: 1, newVersion: 1 } },
305+
},
306+
},
307+
// modify and delete next (any order)
308+
3: {
309+
diffResult: {
310+
node: {
311+
1: { newId: 1, newVersion: 2 },
312+
2: { newId: 2, newVersion: 3 },
313+
},
314+
relation: {
315+
2: { newId: 2, newVersion: 22 },
316+
3: { newId: 3, newVersion: 2 },
317+
4: { newId: 4, newVersion: 2 },
318+
},
319+
way: { 2: { newId: 2, newVersion: 2 } },
320+
},
321+
},
322+
// delete last
323+
4: {
324+
diffResult: {
325+
relation: { 1: { newId: 1, newVersion: 11 } },
326+
way: { 1: { newId: 1, newVersion: 3 } },
327+
},
328+
},
329+
});
281330
});
282331

283332
it("splits changesets into chunks and suports a custom tag function", async () => {
@@ -311,7 +360,9 @@ describe("uploadChangeset", () => {
311360
);
312361
}
313362

314-
expect(output).toBe(1);
363+
// don't need to assert the output, since it's the same
364+
// as the previous test case.
365+
expect(Object.keys(output).map(Number)).toStrictEqual([1, 2, 3, 4]);
315366
});
316367

317368
// end of tests

src/api/changesets/createChangesetComment.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import type { Changeset } from "../../types";
22
import { type FetchOptions, osmFetch } from "../_osmFetch";
3-
import type { RawChangeset } from "../_rawResponse";
4-
import { mapRawChangeset } from "./getChangesets";
53

64
/** Add a comment to a changeset. The changeset must be closed. */
75
export async function createChangesetComment(
86
changesetId: number,
97
commentText: string,
108
options?: FetchOptions
119
): Promise<Changeset> {
12-
const result = await osmFetch<{ changeset: RawChangeset }>(
10+
const result = await osmFetch<{ changeset: Changeset }>(
1311
`/0.6/changeset/${changesetId}/comment.json`,
1412
undefined,
1513
{
@@ -22,5 +20,5 @@ export async function createChangesetComment(
2220
},
2321
}
2422
);
25-
return mapRawChangeset(result.changeset);
23+
return result.changeset;
2624
}

src/api/changesets/getChangesets.ts

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,5 @@
11
import type { BBox, Changeset } from "../../types";
22
import { type FetchOptions, osmFetch } from "../_osmFetch";
3-
import type { RawChangeset } from "../_rawResponse";
4-
5-
/** @internal */
6-
export const mapRawChangeset = ({
7-
comments,
8-
...raw
9-
}: RawChangeset): Changeset => ({
10-
...raw,
11-
created_at: new Date(raw.created_at),
12-
closed_at: raw.closed_at ? new Date(raw.closed_at) : undefined!,
13-
14-
discussion: comments?.map((comment) => ({
15-
...comment,
16-
date: new Date(comment.date),
17-
uid: `${comment.uid}`,
18-
})),
19-
});
203

214
// does not extend BasicFilters for historical reasons
225
export type ListChangesetOptions = {
@@ -57,7 +40,7 @@ export async function listChangesets(
5740
): Promise<Changeset[]> {
5841
const { only, ...otherQueries } = query;
5942

60-
const raw = await osmFetch<{ changesets: RawChangeset[] }>(
43+
const raw = await osmFetch<{ changesets: Changeset[] }>(
6144
"/0.6/changesets.json",
6245
{
6346
...(only && { [only]: true }),
@@ -66,7 +49,7 @@ export async function listChangesets(
6649
options
6750
);
6851

69-
return raw.changesets.map(mapRawChangeset);
52+
return raw.changesets;
7053
}
7154

7255
/** get a single changeset */
@@ -76,11 +59,11 @@ export async function getChangeset(
7659
includeDiscussion = true,
7760
options?: FetchOptions
7861
): Promise<Changeset> {
79-
const raw = await osmFetch<{ changeset: RawChangeset }>(
62+
const raw = await osmFetch<{ changeset: Changeset }>(
8063
`/0.6/changeset/${id}.json`,
8164
includeDiscussion ? { include_discussion: 1 } : {},
8265
options
8366
);
8467

85-
return mapRawChangeset(raw.changeset);
68+
return raw.changeset;
8669
}

0 commit comments

Comments
 (0)