# API Spec v1

Last Edited Time: Jan 1, 2020 3:07 AM

The ArchMLP API is responsible for handling *data transfer* between the various components in the system architecture. All API endpoints are tagged with begin with `/api/v1` to indicate that these are `v1` endpoints.

## Overview

| Endpoint              | Type     | Params                                                  | Description                                 |
| --------------------- | -------- | ------------------------------------------------------- | ------------------------------------------- |
| `/api/v1/uploadData`  | **POST** | `File` object                                           | Upload CSV file to the server.              |
| `/api/v1/setDataName` | **POST** | `String` \[length <= 31 && contains only alphanumerics] | Send the name of the dataset to the server. |

## Document Upload

### POST: `/api/v1/uploadData`

| Status Code       | Type        | Message                                                   |
| ----------------- | ----------- | --------------------------------------------------------- |
| 200 (OK)          | **Success** | File successfully uploaded!                               |
| 400 (Bad Request) | **Error**   | File upload only supports the following filetypes - /csv/ |
| 400 (Bad Request) | **Error**   | File required!                                            |

#### Usage

```javascript
export const upload = file => {
  const formData = new FormData();
  formData.append('file', file);
  return fetch('/api/v1/uploadData', {
    // Your POST endpoint
    method: 'POST',
    body: formData // This is your file object
  })
    .then(
      response => response.json() // if the response is a JSON object
    )
    .then(
      success => console.log(success) // Handle the success response object
    )
    .catch(
      error => console.log(error) // Handle the error response object
    );
};
```

The above function takes a file object `file` as a parameter. This endpoint expects a `File` object in the body. If using the [`fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), leave out the `Content-Type` header (let the `fetch` API set it for you). In the above code, the `/api/v1/uploadData` is pinged with a `POST` request containing our `File` object in the body. We then chain promises to receive the JSON response and then log the success message or the error message to the console.

#### Implementation Details

```javascript
// POST route for uploading new file using upload (multer middleware)
app.post('/api/v1/uploadData', upload, (req, res, next) => {
  logger.info('User requested /api/v1/uploadData');
  try {
    if (req.fileValidationError) {
      logger.error(req.fileValidationError);
      res.status(400).send({ error: req.fileValidationError });
      next(req.fileValidationError);
    } else {
      logger.info(`File ${req.file.originalname} uploaded successfuly.`);
      file = req.file;
      logger.info(`File object: ${file}`);
      res.status(200).send({ success: 'File successfully uploaded!' });
    }
  } catch (err) {
    logger.error('File missing from request.');
    res.status(400).send({ error: 'File required!' });
  }
});
```

The `uploadData` endpoint is responsible for uploading CSV files into the server. It uses the `upload` middleware, which is configured using [multer](https://www.npmjs.com/package/multer). The endpoint attempts to see if an uploaded file was successfully validated by the middleware by checking `req.fileValidationError`. The `fileValidationProperty` property is added to the `req` object through `multer`. If the upload is successful we send a `200` status code, otherwise a `400` status code.

Multer is used as middleware to handle file upload and validation.

```javascript
// multer config
const UPLOAD_PATH = 'uploads';
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    const dir = `${UPLOAD_PATH}/`;
    mkdirp(dir, err => cb(err, dir));
  },
  filename: (req, file, cb) => {
    // Remove extension, add time stamp, readd extension
    cb(null, `${file.originalname.slice(0, -4)}-${Date.now()}.csv`);
  }
});

const upload = multer({
  storage,
  fileFilter: (req, file, cb) => {
    const filetypes = /csv/;
    const mimetype = filetypes.test(file.mimetype);
    const extname = filetypes.test(
      path.extname(file.originalname).toLowerCase()
    );
    logger.info("Attempting to validate file")
    // Successful validation
    if (mimetype && extname) {
      logger.info("File validation successful.")
      return cb(null, true);
    }
    // File validation error
    logger.error("File validation failed.")
    req.fileValidationError = `File upload only supports the following filetypes - ${filetypes}`;
    return cb(null, false, req.fileValidationError);
  }
}).single('file');
```

The `multer` middleware is configured to accept a single CSV file per request. It checks that the extension of the file and mimetype are both consistent with that of CSV files. If not, the server will return an error. If a file is successfully accepted, it is renamed to `{original-name}-{time-stamp}.csv`, i.e if you upload `data.csv`, the file will be renamed to `data-1577692525912.csv`, where the timestamp is `1577692525912`. We append the timestamp for logging purposes. Our configuration currently stores all uploaded CSV files in `uploads/`.

### POST: `/api/v1/setDataName`

| Status Code       | Type        | Message                                                |
| ----------------- | ----------- | ------------------------------------------------------ |
| 200 (OK)          | **Success** | Dataset name has been received!                        |
| 400 (Bad Request) | **Error**   | Dataset name can only contain alphanumeric characters. |
| 400 (Bad Request) | **Error**   | Dataset name can contain at-most 31 characters.        |
| 400 (Bad Request) | **Error**   | Failed to receive dataset name.                        |

#### Usage

```javascript
export const setName = dataName => {
  return fetch('/api/v1/setDataName', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name: dataName })
  })
    .then(
      response => response.json() // The response is a JSON object
    )
    .then(
      success => console.log(success) // Handle the success response object
    )
    .catch(
      error => console.log(error) // Handle the error response object
    );
};
```

The above function takesa string `dataName` as a parameter, which gets passed to the endpoint in the body of a `POST` request as a JSON string via [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). The endpoint send a JSON response containing a success or an error message.

#### Implementation Details

```javascript
// POST route for setting dataset name
app.post('/api/v1/setDataName', (req, res) => {
  logger.info('User requested /api/v1/setDataName');
  if (req.body.name) {
    logger.info(`Dataset name ${req.body.name} received.`);
    if (req.body.name.match(/^[a-z0-9]+$/i) && req.body.name.length <= 31) {
      dataName = req.body.name;
      logger.info(`Dataset name ${dataName} validation successful.`);
      res.status(200).send({ success: 'Dataset name has been received!' });
    } else if (req.body.name.match(/^[a-z0-9]+$/i) === null) {
      logger.error(`Dataset name ${dataName} has non-alphanumeric characters.`);
      res.status(400).send({
        error: 'Dataset name can only contain alphanumeric characters.'
      });
    } else if (req.body.name.length > 31) {
      logger.error(`Dataset name ${dataName} has length greater than 31.`);
      res
        .status(400)
        .send({ error: 'Dataset name can contain at-most 31 characters.' });
    }
  } else {
    logger.error('Dataset name not received');
    res.status(400).send({ error: 'Failed to receive dataset name.' });
  }
});
```

The `setDataName` endpoint allows a user to set the name that they would like the dataset to have. The restrictions imposed on this endpoint ensure that 1) a name is received, 2) the name contains only alphanumerics, and 3) the name contains at most 31 characters. Dataset names are validated in the client as well as within the server. The alphanumeric restriction is imposed via matching the string `name` in the the request body to the following regex `/^[a-z0-9]+$/i`, which is broken down [here](https://stackoverflow.com/questions/388996/regex-for-javascript-to-allow-only-alphanumeric). The length restriction is validated by checking the length of the string. All validation rules on the dataset name are meant to make it simple to create SQL tables using the name provided by the user.

See [server/server.js](https://github.com/AumitLeon/archMLP/tree/0778ea4480d2b38df6529b7fe710581efd86150b/server/server.js) for the full server implementation and [server/server.test.js](https://github.com/AumitLeon/archMLP/tree/0778ea4480d2b38df6529b7fe710581efd86150b/server/server.test.js) for tests.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://aumitleon1.gitbook.io/archmlp/api-specification/api-spec-v1.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
