# Backlog Client TS

[GitLab repository](https://gitlab.com/booksakura/backlog-client-ts)

## What is Backlog Client TS?

Backlog Client TS is a JavaScript (TypeScript) client library for using the Backlog API v2 (https://developer.nulab.com/ja/docs/backlog). 

This library allows you to easily interact with the Backlog API v2.

## Documentation

* [API Docs](https://booksakura.gitlab.io/backlog-client-ts/)

## Installation

### JSR

Install using the [JSR](https://jsr.io/@booksakura/backlog-client-ts) package manager:

```bash
# npm
npx jsr add @booksakura/backlog-client-ts
# deno
deno add @booksakura/backlog-client-ts
# bun
bunx jsr add @booksakura/backlog-client-ts
```

### NPM

Install using the [npm](https://www.npmjs.com/package/@booksakura/backlog-client-ts) package manager:

```bash
npm install --save @booksakura/backlog-client-ts
```

### Browser

#### CDN

```html
<!-- UNPKG -->
<script src="https://unpkg.com/@booksakura/backlog-client-ts/dist/umd/backlog-client-ts.min.js"></script>
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@booksakura/backlog-client-ts/dist/umd/backlog-client-ts.min.js"></script>
```

#### Manual Build

```bash
# UMD build
cd backlog-client-ts/source
npm run build:umd
```

After building, include the script in your HTML:

```html
<script src="path/to/dist/umd/backlog-client-ts.min.js"></script>
```

## Usage

### Node.js

Load the library as follows:

```js
const backlogclient = require("@booksakura/backlog-client-ts");

// or 
const { BacklogConfig, BacklogApiClient } = require("@booksakura/backlog-client-ts");
```

Or in TypeScript:

```ts
import * as backlogclient from "@booksakura/backlog-client-ts";

// or
import {BacklogConfig, BacklogApiClient} from "@booksakura/backlog-client-ts";
```

#### Client Configuration and Usage Example

```js
const config = new backlogclient.BacklogConfig({
  host: "****.backlog.com", // ****.backlog.com or ****.backlog.jp or 'your enterprise host'
  apiKey: "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", // API key
});

const client = new backlogclient.BacklogApiClient({
  config: config
});

// get space (callback style)
client.getSpace().then(data => {
  const space = data.data();
  console.log('success:', space);
}).catch((err) => {
  console.log('error:', err.message);
});

// get project list 
async function fetchProjects() {
  try {
    const project_list = await client.getProjectList();
    project_list.data().forEach((project, _) => {
      console.log('success:', project);
    });
  } catch (error) {
    console.log('error:', error.message);
  }
}

fetchProjects();
```

#### Functional Style Usage Example

```js
const { BacklogConfig, getSpace } = require("@booksakura/backlog-client-ts");

const config = new BacklogConfig({
  host: "****.backlog.com",
  apiKey: "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
});

async function fetchSpace() {
  const space = await getSpace(config);
  console.log(space.data());
}

fetchSpace();
```

### Browser

After loading the UMD build in your HTML, use it as follows:

```js
const config = new BacklogClientTs.BacklogConfig({
  host: "****.backlog.com",
  apiKey: "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
});

const client = new BacklogClientTs.BacklogApiClient({
  config: config
});

async function fetchSpace() {
  const space = await client.getSpace();
  console.log(space.data());
}

fetchSpace();
```


## Example

### Download File

```js
// Node.js
const { BacklogConfig, getSpaceLogo } = require("@booksakura/backlog-client-ts");
const fs = require("node:fs");

const date = (await getSpaceLogo(config)).data();
const buffer = Buffer.from(await date.arrayBuffer());

fs.writeFileSync("spacelogo.png", buffer);

// browser
const date = (await client.getSpaceLogo()).data();
const blob = await date.blob();

const object_url = URL.createObjectURL(blob);
const element = window.document.querySelector("#logo");
element.src = object_url;
```

### Uoload File

```js
// Node.js
const { BacklogConfig, postAttachmentFile } = require("@booksakura/backlog-client-ts");
const fs = require("node:fs");
const { Blob } = require("node:buffer");

const buffer = fs.readFileSync("logo.png");
const blob = new Blob([buffer]);

const date = (
  await postAttachmentFile(config, {
    contents: blob,
    filename: "logo.png",
  })
).data();
console.log(date); // { id: 1234567890, name: 'logo.png', size: 12345 }

// text file
const contents = "AttachmentFile Sample Text";
const date = (
  await postAttachmentFile(config, {
    contents: contents,
    filename: "sample.txt",
  })
).data();
console.log(date); // { id: 1234567890, name: 'sample.txt', size: 26 }

// browser
// <input type="file" id="upload_file" >
const element = window.document.querySelector("#upload_file");
const file = element.files[0];

const date = (
  await client.postAttachmentFile({
    contents: file,
    // filename omitted, file.name is used.
  })
).data();
console.log(date); // { id: 1234567890, name: 'abc.png', size: 12345 }
```

### Aborting Requests

```js
try {
  const space = await getSpace(config, {
    requestOptions: {
      signal: AbortSignal.timeout(1000 * 5), // timeout 5 sec.
    },
  });
  console.log(space.data());
} catch (error) {
  if (error instanceof BacklogApiAbortError) {
    console.log("timeout", error);
  }
}
```

### Get Response Header

```js
const space = await getSpace(config);

const response = space.getResponse();
// Get Response Header
console.log(response.getHeader("date"));
// Rate Limit
console.log(response.getRateLimitLimit());
console.log(response.getRateLimitRemaining());
console.log(response.getRateLimitReset());

```

### Using Metadata

To use metadata, you need to call `loadBacklogMetadata()` beforehand.

Note that if you use `BacklogApiClient`, `loadBacklogMetadata()` is implicitly called.

```js
const { loadBacklogMetadata, getSpace } = require("@booksakura/backlog-client-ts");

// Call once at the beginning
loadBacklogMetadata();

const space = await getSpace(config);

// Get API specifications
const spec = space.getSpec();
console.log(spec.info);

// Validate response data
const validator = space.validator();
const validation_result = await validator.validate();
console.log(validation_result.isValid);
console.log(validation_result.errors);
```

### Custom Fetch

```js
const { BacklogConfig, getOwnUser, utils: { bindBacklogContainer, resolveBacklogContainer } } = require("@booksakura/backlog-client-ts");

class CustomFetcher {
    /**
     * @param {URL} url
     * @param {object} options
     * @returns {Promise<BacklogHttpResponse>}
     */
    async fetch(url, options) {
        console.log("srat-log", url, options);
        const fetch_response = await fetch(url, options);
        const response = resolveBacklogContainer("BacklogHttpResponse", {
            response: fetch_response,
        });
        return response;
    }
}
bindBacklogContainer("BacklogHttpFetcher", CustomFetcher);

async function fetchUserData() {
    const config = new BacklogConfig(
        {
          host: "****.backlog.com",
          apiKey: "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
        }
    );
    const date = (await getOwnUser(config)).data();
    console.log(date);
}

fetchUserData();
```

## TODO

- [ ] add Unit Tests
- [ ] OAuth 2.0 Support
