Skip to content

Commit

Permalink
Merge pull request #6972 from TerriaJS/api-table-columntraits
Browse files Browse the repository at this point in the history
Add `ApiColumnTraits` (`responseDataPath`) to `ApiTableCatalogItem`
  • Loading branch information
nf-s authored Nov 30, 2023
2 parents 179805f + 7ff3fa0 commit 57a5e8c
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#### next release (8.3.10)

- Added `apiColumns` to `ApiTableCatalogItem` - this can now be used to specify `responseDataPath` per table column.
- [The next improvement]

#### 8.3.9 - 2023-11-24
Expand Down
17 changes: 15 additions & 2 deletions lib/Models/Catalog/CatalogItems/ApiTableCatalogItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,21 @@ export class ApiTableCatalogItem extends AutoRefreshingMixin(
this.apiResponses.forEach((response) => {
this.columns.forEach((col, mappingIdx) => {
if (!isDefined(col.name)) return;
// Append the new value to the correct column
columnMajorTable[mappingIdx].push(`${response[col.name] ?? ""}`);

// If ApiColumnTraits has a responseDataPath, use that to get the value
const dataPath = this.apiColumns.find(
(c) => c.name === col.name
)?.responseDataPath;

if (dataPath) {
columnMajorTable[mappingIdx].push(
`${getResponseDataPath(response, dataPath) ?? ""}`
);
}
// Otherwise, use column name as the path
else {
columnMajorTable[mappingIdx].push(`${response[col.name] ?? ""}`);
}
});
});

Expand Down
2 changes: 1 addition & 1 deletion lib/Traits/TraitsClasses/ApiRequestTraits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default class ApiRequestTraits extends mixTraits(UrlTraits) {
name: "Response data path",
type: "string",
description:
"Path to relevent data in JSON response. eg: `some.user.name`, `some.users[0].name` or `some.users[].name`"
"Path to relevant data in JSON response. eg: `some.user.name`, `some.users[0].name` or `some.users[].name`"
})
responseDataPath?: string;
}
52 changes: 48 additions & 4 deletions lib/Traits/TraitsClasses/ApiTableCatalogItemTraits.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import objectArrayTrait from "../Decorators/objectArrayTrait";
import primitiveArrayTrait from "../Decorators/primitiveArrayTrait";
import primitiveTrait from "../Decorators/primitiveTrait";
import ModelTraits from "../ModelTraits";
import mixTraits from "../mixTraits";
import ApiRequestTraits from "./ApiRequestTraits";
import ApiRequestTraits, { QueryParamTraits } from "./ApiRequestTraits";
import AutoRefreshingTraits from "./AutoRefreshingTraits";
import CatalogMemberTraits from "./CatalogMemberTraits";
import LegendOwnerTraits from "./LegendOwnerTraits";
import TableTraits from "./Table/TableTraits";
import primitiveArrayTrait from "../Decorators/primitiveArrayTrait";

export class ApiTableRequestTraits extends mixTraits(ApiRequestTraits) {
@primitiveTrait({
Expand All @@ -29,12 +30,28 @@ export class ApiTableRequestTraits extends mixTraits(ApiRequestTraits) {
columnMajorColumnNames?: string[] = ["value"];
}

export class ApiTableColumnTraits extends ModelTraits {
@primitiveTrait({
name: "Name",
type: "string",
description: "Column name. This must match name in `columns`."
})
name?: string;

@primitiveTrait({
name: "Response data path",
type: "string",
description:
"Path to relevant data in JSON response. eg: `some.user.name`, `some.users[0].name` or `some.users[].name`. For data to be parsed correctly, it must return a primitive value (string, number, boolean)."
})
responseDataPath?: string;
}

export default class ApiTableCatalogItemTraits extends mixTraits(
TableTraits,
CatalogMemberTraits,
LegendOwnerTraits,
AutoRefreshingTraits,
ApiRequestTraits
AutoRefreshingTraits
) {
@objectArrayTrait({
name: "APIs",
Expand All @@ -45,6 +62,15 @@ export default class ApiTableCatalogItemTraits extends mixTraits(
})
apis: ApiTableRequestTraits[] = [];

@objectArrayTrait({
name: "API Columns",
type: ApiTableColumnTraits,
description:
'ApiTableCatalogItem specific column configuration. Note: you **must** define which columns to use from API response in the `columns` `TableColumnTraits` - for example `[{name:"some-key-in-api-response", ...}]`. This object only adds additional properties',
idProperty: "name"
})
apiColumns: ApiTableColumnTraits[] = [];

@primitiveTrait({
name: "Id key",
type: "string",
Expand All @@ -59,4 +85,22 @@ export default class ApiTableCatalogItemTraits extends mixTraits(
"When true, new data received through APIs will be appended to existing data. If false, new data will replace existing data."
})
shouldAppendNewData?: boolean = true;

@objectArrayTrait({
name: "Common query parameters",
type: QueryParamTraits,
description:
"Common query parameters to supply to all APIs. These are merged into the query parameters for each API.",
idProperty: "name"
})
queryParameters: QueryParamTraits[] = [];

@objectArrayTrait({
name: "Common query parameters for updates",
type: QueryParamTraits,
description:
"Common query parameters to supply to all APIs on subsequent calls after the first call. These are merged into the update query parameters for each API.",
idProperty: "name"
})
updateQueryParameters: QueryParamTraits[] = [];
}
90 changes: 90 additions & 0 deletions test/Models/Catalog/CatalogItems/ApiTableCatalogItemSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,4 +248,94 @@ describe("ApiTableCatalogItem", function () {
expect(valueColumn).toEqual(["value", "8", "9", "7"]);
});
});

describe("supports apiColumns", function () {
beforeEach(function () {
jasmine.Ajax.stubRequest(
"build/TerriaJS/data/regionMapping.json"
).andReturn({ responseText: regionMapping });

jasmine.Ajax.stubRequest(
proxyCatalogItemUrl(apiCatalogItem, "https://terria.io/position.json")
).andReturn({
responseText: positionApiResponse
});

runInAction(() => {
updateModelFromJson(apiCatalogItem, CommonStrata.definition, {
idKey: "id",
apis: [
{
url: "https://terria.io/position.json"
}
],
columns: [
{
name: "id"
},
{
name: "some embedded value"
},
{
name: "some other embedded value"
},
{
name: "some fake embedded value"
}
],

apiColumns: [
{
name: "some embedded value",
responseDataPath: "some.embedded.0"
},
{
name: "some other embedded value",
responseDataPath: "some.embedded.1"
},
{
name: "some fake embedded value",
responseDataPath: "some.embedded.path.that[].does.not.exist.1"
}
]
});
});
});

it("uses responseDataPath", async function () {
await apiCatalogItem.loadMapItems();
const table = apiCatalogItem.dataColumnMajor;
expect(table).toBeDefined();
let definedTable: string[][] = table!;
const embeddedColumn = definedTable.find(
([name]) => name === "some embedded value"
);
expect(embeddedColumn).toEqual([
"some embedded value",
"first_element 1",
"first_element 2",
"first_element 3"
]);

const otherEmbeddedColumn = definedTable.find(
([name]) => name === "some other embedded value"
);
expect(otherEmbeddedColumn).toEqual([
"some other embedded value",
"second_element 1",
"second_element 2",
"second_element 3"
]);

const fakeEmbeddedColumn = definedTable.find(
([name]) => name === "some fake embedded value"
);
expect(fakeEmbeddedColumn).toEqual([
"some fake embedded value",
"",
"",
""
]);
});
});
});
9 changes: 6 additions & 3 deletions wwwroot/test/JSON-api/position_api_response.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@
{
"id": 1,
"latitude": -37.25,
"longitude": 145.25
"longitude": 145.25,
"some": { "embedded": ["first_element 1", "second_element 1"] }
},
{
"id": 2,
"latitude": -37,
"longitude": 145.5
"longitude": 145.5,
"some": { "embedded": ["first_element 2", "second_element 2"] }
},
{
"id": 3,
"latitude": -37.1,
"longitude": 145.7
"longitude": 145.7,
"some": { "embedded": ["first_element 3", "second_element 3"] }
}
]

0 comments on commit 57a5e8c

Please sign in to comment.