Skip to content

Commit

Permalink
Merge pull request #5 from tewen/project-types-patch-vercel
Browse files Browse the repository at this point in the history
Version 0.5.0
  • Loading branch information
tewen authored Oct 8, 2023
2 parents 19ba53d + 93f48fc commit 3cb9aa6
Show file tree
Hide file tree
Showing 13 changed files with 401 additions and 33 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,27 @@ console.log(formattedDate); // DateTime(2020, 12, 31)

You can read more about the Accounting API [here](https://developer.xero.com/documentation/api/accounting/overview).

### Attachments

You can read more about the Attachments API [here](https://developer.xero.com/documentation/api/attachments/overview).

#### Methods

##### createInvoiceAttachment(client:XeroClient, tenantId:string, { invoiceId:string, fileName:string, contents:Buffer })

Creates an attachment for an invoice.

```TypeScript
import { createInvoiceAttachment } from 'xero-hero';

const invoice = await getMyInvoice();
const attachment = await createInvoiceAttachment(xero, tenantId, {
invoiceId: invoice.invoiceId,
fileName: 'my-attachment.pdf',
contents: fs.readFileSync('my-attachment.pdf'),
});
```

### Contacts

You can read more about the Contacts API [here](https://developer.xero.com/documentation/api/contacts/overview).
Expand Down
46 changes: 38 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "xero-hero",
"version": "0.4.0",
"version": "0.5.0",
"description": "",
"files": [
"dist",
Expand Down Expand Up @@ -45,17 +45,20 @@
"@types/jest": "^29.5.5",
"@types/luxon": "^3.3.2",
"@types/qs": "^6.9.8",
"@types/uuid": "^9.0.5",
"eslint-config-ethang": "^11.0.0",
"eslint-plugin-jest": "^27.4.0",
"jest": "^29.7.0",
"luxon": "^3.4.3",
"ts-jest": "^29.1.1",
"tsup": "^7.2.0",
"typescript": "^5.2.2",
"uuid": "^9.0.1",
"xero-node": "^4.36.0"
},
"dependencies": {
"deep-cuts": "^2.8.0",
"qs": "^6.11.2"
"qs": "^6.11.2",
"tranquil-stream": "^0.1.0"
}
}
36 changes: 36 additions & 0 deletions src/accounting/attachments/__tests__/requests.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Duplex } from 'node:stream';

import { v4 as uuid } from 'uuid';
import type { XeroClient } from 'xero-node';

import { createInvoiceAttachment } from '../../../';

const getMockXeroClient = (): any => {
return {
accountingApi: {
createInvoiceAttachmentByFileName: jest.fn(),
},
};
};

describe('attachments/requests', () => {
describe('createInvoiceAttachment()', () => {
it('should call the createInvoiceAttachmentByFileName() method with the relevant arguments ', () => {
const client = getMockXeroClient() as unknown as XeroClient;
const tenantId = uuid();
const invoiceId = uuid();
const filename = 'test.pdf';
const contents = Buffer.from('Test Content Here');

createInvoiceAttachment(client, tenantId, {
contents,
filename,
invoiceId,
});

expect(
client.accountingApi.createInvoiceAttachmentByFileName,
).toHaveBeenCalledWith(tenantId, invoiceId, filename, expect.any(Duplex));
});
});
});
1 change: 1 addition & 0 deletions src/accounting/attachments/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { createInvoiceAttachment } from './requests';
27 changes: 27 additions & 0 deletions src/accounting/attachments/requests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { ReadStream } from 'node:fs';
import type http from 'node:http';

import { bufferToStream } from 'tranquil-stream';
import type { Attachments, XeroClient } from 'xero-node';

type ICreateInvoiceAttachmentParameters = {
contents: Buffer;
filename: string;
invoiceId: string;
};

export const createInvoiceAttachment = async (
client: XeroClient,
tenantId: string,
{ invoiceId, filename, contents }: ICreateInvoiceAttachmentParameters,
): Promise<{
body: Attachments;
response: http.IncomingMessage;
}> => {
return client.accountingApi.createInvoiceAttachmentByFileName(
tenantId,
invoiceId,
filename,
bufferToStream(contents) as unknown as ReadStream,
);
};
14 changes: 7 additions & 7 deletions src/accounting/contacts/__tests__/links.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,36 @@ describe('contacts/links', () => {
it('returns a null indicator if passed undefined', () => {
// @ts-expect-error - This is an invalid type for the function.
expect(getContactLink()).toBe(
'https://go.xero.com/app/!kR4N6/contacts/contact/null-or-empty-contact-id',
'https://go.xero.com/Contacts/View.aspx?contactID=null-or-empty-contact-id',
);
});

it('returns a null indicator if passed null', () => {
// @ts-expect-error - This is an invalid type for the function.
expect(getContactLink(null)).toBe(
'https://go.xero.com/app/!kR4N6/contacts/contact/null-or-empty-contact-id',
'https://go.xero.com/Contacts/View.aspx?contactID=null-or-empty-contact-id',
);
});

it('returns a null indicator if passed an empty string', () => {
expect(getContactLink('')).toBe(
'https://go.xero.com/app/!kR4N6/contacts/contact/null-or-empty-contact-id',
'https://go.xero.com/Contacts/View.aspx?contactID=null-or-empty-contact-id',
);
});

it('returns the correct URL for an invoice object with an invoiceID property', () => {
it('returns the correct URL for an contact object with an contactID property', () => {
const contact = new Contact();
/* eslint-disable functional/immutable-data */
contact.contactID = '25OR6TO4';
/* eslint-enable functional/immutable-data */
expect(getContactLink(contact)).toBe(
'https://go.xero.com/app/!kR4N6/contacts/contact/25OR6TO4',
'https://go.xero.com/Contacts/View.aspx?contactID=25OR6TO4',
);
});

it('returns the correct URL for a string invoice ID', () => {
expect(getContactLink('kooolaid-9086-t728')).toBe(
'https://go.xero.com/app/!kR4N6/contacts/contact/kooolaid-9086-t728',
'https://go.xero.com/Contacts/View.aspx?contactID=kooolaid-9086-t728',
);
});

Expand All @@ -45,7 +45,7 @@ describe('contacts/links', () => {
contactID: '25OR6TO4',
};
expect(getContactLink(contact)).toBe(
'https://go.xero.com/app/!kR4N6/contacts/contact/25OR6TO4',
'https://go.xero.com/Contacts/View.aspx?contactID=25OR6TO4',
);
});
});
Expand Down
12 changes: 7 additions & 5 deletions src/accounting/contacts/links.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import qs from 'qs';
import type { Contact } from 'xero-node';

import { hasProperty } from '../../utils/properties';

export const getContactLink = (contact: Contact | string): string => {
return `https://go.xero.com/app/!kR4N6/contacts/contact/${
(hasProperty(contact, 'contactID')
? (contact as Contact).contactID
: contact) || 'null-or-empty-contact-id'
}`;
return `https://go.xero.com/Contacts/View.aspx?${qs.stringify({
contactID:
(hasProperty(contact, 'contactID')
? (contact as Contact).contactID
: contact) || 'null-or-empty-contact-id',
})}`;
};
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Common
export { dateInWhereFormat } from './common';
// Attachments
export { createInvoiceAttachment } from './accounting/attachments';
// Contacts
export { getContactLink } from './accounting/contacts';
// Invoices
Expand Down
11 changes: 5 additions & 6 deletions src/projects/__tests__/timeEntries.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { TimeEntry } from 'xero-node/dist/gen/model/projects/timeEntry';

import { hoursFromTimeEntries } from '../../';
import type { TimeEntry } from '../shimTypes';

describe('projects/timeEntries', () => {
describe('hoursFromTimeEntries()', () => {
Expand Down Expand Up @@ -39,9 +38,9 @@ describe('projects/timeEntries', () => {

it('should play nice with a non-default denominator for rounding', () => {
/* eslint-disable functional/immutable-data */
const timeEntryA = new TimeEntry();
const timeEntryA = {} as unknown as TimeEntry;
timeEntryA.duration = 60;
const timeEntryB = new TimeEntry();
const timeEntryB = {} as unknown as TimeEntry;
timeEntryB.duration = 23;
/* eslint-enable functional/immutable-data */
const timeEntries = [timeEntryA, timeEntryB];
Expand All @@ -65,8 +64,8 @@ describe('projects/timeEntries', () => {

it('should sub in 0 when a Time Entry is missing a duration', () => {
/* eslint-disable functional/immutable-data */
const timeEntryA = new TimeEntry();
const timeEntryB = new TimeEntry();
const timeEntryA = {} as unknown as TimeEntry;
const timeEntryB = {} as unknown as TimeEntry;
timeEntryB.duration = 23;
/* eslint-enable functional/immutable-data */
const timeEntries = [timeEntryA, timeEntryB];
Expand Down
7 changes: 3 additions & 4 deletions src/projects/generators.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { Amount } from 'xero-node/dist/gen/model/projects/amount';
import { CurrencyCode } from 'xero-node/dist/gen/model/projects/currencyCode';
import type { Amount, CurrencyCode } from './shimTypes';

export const generateProjectAmountUSD = (value: number): Amount => {
const amount = new Amount();
const amount = {} as Amount;
/* eslint-disable functional/immutable-data */
amount.currency = CurrencyCode.USD;
amount.currency = 'USD' as unknown as CurrencyCode;
amount.value = value;
/* eslint-enable functional/immutable-data */
return amount;
Expand Down
Loading

0 comments on commit 3cb9aa6

Please sign in to comment.