Skip to content

Commit

Permalink
feat: update options structure with name definition
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeSiu committed Apr 23, 2024
1 parent 75e6be3 commit 0f653b2
Show file tree
Hide file tree
Showing 6 changed files with 352 additions and 235 deletions.
49 changes: 25 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,6 @@ To use `xeno-canto-api-ts` in your Node.js project, you need to import it as fol
import * as XenoCanto from "xeno-canto-api-ts";
```

or

```ts
const XenoCanto = require('xeno-canto-api-ts');
```

### Simple Search

You can pass a string query to the `search` method like this:
Expand All @@ -44,7 +38,7 @@ const result = await XenoCanto.search("Owl");
or

```ts
XenoCanto.search("Owl").then((result) => {
XenoCanto.search({query: "Owl"}).then((result) => {
// Do something with result
});
```
Expand All @@ -68,15 +62,15 @@ console.log(result.xcResponse.recordings[0].file) // The first recording result'
You can pass a `XCQueryOption` object to the `search` method like this:

```ts
// Create query and options
const query = "Eagle";
const options = {
// Create options
const options: XenoCanto.XCQueryOption = {
query: "Eagle",
grp: "birds",
cnt: "United States",
// ...
} as XenoCanto.XCQueryOption;
};

const result = await XenoCanto.search(query, options);
const result = await XenoCanto.search(options);
```

* Some of the `XCQueryOption` properties accepts operators such as `=`, `>`, `<` or `-`. For example, the recording length property `len` can accept `10`, `">120"` or `"=19.8"`.
Expand All @@ -87,17 +81,17 @@ const result = await XenoCanto.search(query, options);
For results that have multiple pages, you can pass the `page` parameter to the `search` method:

```ts
// Create query and options
const query = "Eagle";
const options = {
// Create options
const options: XenoCanto.XCQueryOption = {
query: "Eagle",
grp: "birds",
cnt: "United States",
// ...
} as XenoCanto.XCQueryOption;
};

// Begin initial search
console.log("Fetching page 1...");
const result = await XenoCanto.search(query, options);
const result = await XenoCanto.search(options);

// Print first recording data from response
console.log(result.xcResponse.recordings[0]);
Expand All @@ -109,10 +103,10 @@ await new Promise((resolve) => setTimeout(resolve, 1000));
const numPages = result.xcResponse.numPages;
if (numPages > 1) {
// Loop through rest of the pages
for (let page = 2; page < numPages; page++) {
for (let currentPage = 2; currentPage < numPages; currentPage++) {
// Begin next search
console.log(`Fetching page ${page}/${numPages}...`);
const result = await XenoCanto.search(query, options, page); // Here we pass the original query and options with a new page
const result = await XenoCanto.search({...options, page: currentPage}); // Here we pass the original query and options with a new page

// Print first recording data from response
console.log(result.xcResponse.recordings[0]);
Expand All @@ -132,16 +126,19 @@ The wrapper also provides additional options by passing a `AdditionalWrapperOpti
For development purpose, the Base URL can be changed as follows:

```ts
// Create query and options
const query = "Owl";
const options = {} as XenoCanto.XCQueryOption;
// Create options
const options: XenoCanto.XCQueryOption = {
query: "Owl"
};
const additionalOptions = {
baseUrl: "https://run.mocky.io/v3/9f08db9a-cfba-4b1d-8c4a-765932f6cf3b?query=", // A fake JSON server URL
baseUrl: "https://run.mocky.io/v3/9f08db9a-cfba-4b1d-8c4a-765932f6cf3b", // A fake JSON server URL
} as XenoCanto.AdditionalWrapperOption;

const result = await XenoCanto.search(query, options, 1, additionalOptions);
const result = await XenoCanto.search(options, additionalOptions);
```

### Other Usages

#### Custom Data Fetching

If you wish to implement your own data retrieval methods instead of using the Fetch API, you can utilize the `convertJsonToXCResponse` method by passing the JSON response:
Expand All @@ -150,6 +147,10 @@ If you wish to implement your own data retrieval methods instead of using the Fe
const xcResponse = XenoCanto.convertJsonToXCResponse(json);
```

#### Query Parameters Names

To get the query parameters names / response JSON key names of the Xeno Canto API, you can use the `XCQueryNameDefinition`, `XCResponseNameDefinition` and `XCRecordingNameDefinition` enum, for example, `XCQueryNameDefinition.rec `will return the string `rec`.

## Limitation

Due to the API limitation, only English queries are supported, and the query should based on scientific or common names. You may refer to the [IOC World Bird List - Multilingual Version](https://www.worldbirdnames.org/new/ioc-lists/master-list-2/) for looking up and mapping the corresponding names.
Expand Down
14 changes: 4 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
// Import the types and utility functions
import { AdditionalWrapperOption, XCQueryOption, XCResponse } from "./types";
import { convertJsonToXCResponse, constructQueryUrl } from "./utils/utils";
import { constructQueryUrl, convertJsonToXCResponse } from "./utils/utils";

// Define the base URL for the API
export const BASE_URL = "https://www.xeno-canto.org/api/2/recordings";

/**
* Searches for a query via Fetch API and returns the response and XC response.
*
* @param {string} query - The query to search for.
* @param {XCQueryOption} [options] - Options for the search query.
* @param {number} [page] - The page parameter is optional and is only needed if the results from a given search don't fit in a single page. If specified, page must be an integer between 1 and XCResponse.numPages.
* @param {AdditionalWrapperOption} [additionalOptions] - Additional options for this wrapper.
* @return {Promise<{ url: URL, response: Response; xcResponse: XCResponse }>} A promise that resolves to an object containing the query URL, the response from fetch and a XCResponse object.
*/
async function search(
query: string,
options?: XCQueryOption,
page?: number,
options: XCQueryOption,
additionalOptions?: AdditionalWrapperOption,
): Promise<{ url: URL; rawResponse: Response; xcResponse: XCResponse }> {
// If query is empty and options is not provided, throw an error instantly instead of trying to fetch
if (!query.trim() && !options) {
if (!options.query.trim()) {
return Promise.reject(
new Error(
"Please ensure that the 'query' parameter is not empty or that the 'options' parameter is provided",
Expand All @@ -32,9 +28,7 @@ async function search(
// Create the query URL
const url = constructQueryUrl(
additionalOptions?.baseUrl ?? BASE_URL,
query,
options,
page,
);

// Fetch the response and parse the JSON
Expand Down Expand Up @@ -62,6 +56,6 @@ async function search(
}
}

export { search };
export * from "./types";
export * from "./utils";
export { search };
Loading

0 comments on commit 0f653b2

Please sign in to comment.