# xlPort library

This library is a wrapper for xlport - the API we are using to export and import data to and from Excel.

It provides the `XlPortService` which can be used by any other module. The service provides the following methods:

- `exportToFile` - exports data to Excel
- `importFromFile` - imports data from Excel
- `replaceBlankTables` - replaces blank tables from an Excel import with blank arrays
- `replaceBlankStringsWithNull` - Helper function to replace blank strings with null values (as Excel does not allow null values as formula results)

## Usage

### Export

Here is a typical controller method to export data to Excel:

```typescript
@Get('export/:id')
async export(@Res({ passthrough: true }) response: FastifyReply, @Param('id') id: string) {
  
  const data = await this.getData(id)

  const request: Export.TemplateIdBody = {
    // NOTE: The template must be uploaded to xlport's templates folder
    templateId: 'project/template.xlsx',
    data,
  }

  // NOTE: Change this to mimeTypes.xlsm in case template contains macros
  const returnType = mimeTypes.xlsx
  const filename = `${timeStamp()} ${submission.name}`
  
  void response.type(returnType)
  void response.header('Content-Disposition', `attachment; filename=${filename}.{returnType === mimeTypes.xlsx ? 'xlsx' : 'xlsm'}`)
  return this.xlPort.exportToFile(request)
}
```

### Import

```typescript
@Post('/import/:id')
@UseGuards(UploadAndLogGuard)
@UseGuards(AuthGuard)
async importSurveyForParticipant(
  @Param('id') id: string,
  @Req() req: FastifyRequestWithFileAndId,
  @FileStream() file: MultipartFile,
  @UserParam() user: User,
): Promise<SubmissionImportResult> {
  
  if (!file) {
    throw new BadRequestException('No attachment was provided')
  }

  const start = Date.now()
  const xlData = await this.xlPort.importFromFile(await file.toBuffer())

  if (xlData.status !== 'success') {
    this.logger.warn(xlData.message)
    throw new Error(`Error in reading Excel data: ${JSON.stringify(xlData.message)}`)
  }

  // NOTE: You need to provide a decoder for your data
  const dataParsed = decoder.safeParse(
    this.xlPort.replaceBlankTables(this.xlPort.replaceBlankStringsWithNull(xlData.data.tables)),
  )
  
  if (!dataParsed.success) {
    this.logger.warn(dataParsed.error.format())
    throw new Error(`Error in parsing Excel data: ${JSON.stringify(dataParsed.error.flatten())}`)
  }
  this.logger.log(dataParsed.data)

  const result = await this.handleImport({
    data: dataParsed.data,
    user,
  })

  if (result.type === 'error') {
    this.logger.error(
      `Error importing data for (${id}) in ${file.filename}: ${JSON.stringify(result.error)}`,
    )
    return result
  }

  return result
}
```