Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add header input type to support multi headers #8

Merged
merged 16 commits into from
Feb 27, 2024
4 changes: 2 additions & 2 deletions package-lock.json

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

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vacasaoss/ts-force-project",
"version": "3.4.4",
"version": "4.0.0",
"description": "",
"scripts": {
"postinstall": "cd ts-force && npm install && npm run build && cd ../ts-force-gen && npm install",
Expand All @@ -25,4 +25,4 @@
"url": "https://github.com/ChuckJonas/ts-force/issues"
},
"homepage": "https://github.com/ChuckJonas/ts-force#readme"
}
}
4 changes: 2 additions & 2 deletions ts-force-gen/package-lock.json

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

4 changes: 2 additions & 2 deletions ts-force-gen/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vacasaoss/ts-force-gen",
"version": "3.4.4",
"version": "4.0.0",
"description": "Code generation for ts-force",
"main": "build/index.js",
"typings": "build/index.d.ts",
Expand Down Expand Up @@ -63,4 +63,4 @@
"ts-morph": "^2.1.0",
"tslib": "^2.0.3"
}
}
}
4 changes: 2 additions & 2 deletions ts-force/package-lock.json

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

4 changes: 2 additions & 2 deletions ts-force/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vacasaoss/ts-force",
"version": "3.4.4",
"version": "4.0.0",
"description": "a typescript client for connecting with salesforce APIs",
"main": "build/index.js",
"typings": "build/index.d.ts",
Expand Down Expand Up @@ -57,4 +57,4 @@
"reflect-metadata": "^0.1.10",
"tslib": "^1.6.0"
}
}
}
8 changes: 8 additions & 0 deletions ts-force/src/rest/restHeader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type RequestHeadersInput = {
'Sforce-Auto-Assign'?: boolean;
'If-Modified-Since'?: Date;
'If-Unmodified-Since'?: Date;
};
export type GeneralRequestHeadersInput = RequestHeadersInput | Record<string, string>;

export const ConditionalRequestHeaders = ['If-Match', 'If-None-Match', 'If-Modified-Since', 'If-Unmodified-Since'];
40 changes: 28 additions & 12 deletions ts-force/src/rest/restObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ import {
import { Rest } from './rest';
import { getSFieldProps, SalesforceFieldType, SFieldProperties } from './sObjectDecorators';
import { SObject } from './sObject';
import { CompositeError, StandardRestError } from './errors';
import { CompositeError, ConditionalError, StandardRestError } from './errors';
import { FieldProps } from '..';
import { CalendarDate, calendarToString, getCalendarDate } from '../utils/calendarDate';
import { AxiosRequestHeaders, AxiosResponse } from 'axios';
import { STATUS_CODES } from 'http';
import { ConditionalRequestHeaders, GeneralRequestHeadersInput } from './restHeader';

export interface DMLResponse {
id: string;
Expand Down Expand Up @@ -229,27 +228,30 @@ export abstract class RestObject extends SObject {
* @returns {Promise<void>}
* @memberof RestObject
*/
public async update(opts?: {
refresh?: boolean;
sendAllFields?: boolean;
ifModifiedSince?: Date;
ifUnmodifiedSince?: Date;
}): Promise<this> {
public async update(opts?: { refresh?: boolean; sendAllFields?: boolean; headers?: GeneralRequestHeadersInput }): Promise<this> {
opts = opts || {};
if (this.id == null) {
throw new Error('Must have Id to update!');
}
if (opts.refresh === true) {
return this.updateComposite(opts.sendAllFields);
} else {
const headers = {};
if (opts.ifModifiedSince) headers['If-Modified-Since'] = opts.ifModifiedSince.toUTCString()
if (opts.ifUnmodifiedSince) headers['If-Unmodified-Since'] = opts.ifUnmodifiedSince.toUTCString()
const headers = this.convertValuesToStrings(opts.headers);
const response = await this._client.request.patch(
`${this.attributes.url}/${this.id}`,
this.toJson({ dmlMode: opts.sendAllFields ? 'update' : 'update_modified_only' }),
{ headers }
);

// Check if it contains Conditional Request Headers and deal with errors
const containsConditionalHeader = Object.keys(headers).some((key) => ConditionalRequestHeaders.includes(key));
if (containsConditionalHeader && (response.status === 304 || response.status === 412)) {
const problematicHeaders = Object.keys(headers)
.filter((key) => ConditionalRequestHeaders.includes(key))
.join(', ');
throw new ConditionalError(`Conditions not met. Headers: ${problematicHeaders}`);
}

this._modified.clear();
}
return this;
Expand Down Expand Up @@ -497,4 +499,18 @@ export abstract class RestObject extends SObject {
throw e;
}
}

private convertValuesToStrings(headerInput: GeneralRequestHeadersInput) {
const headers = {};
for (const key in headerInput) {
const value = headerInput[key];
if (value instanceof Date) {
headerInput[key] = (value as Date).toUTCString();
} else if (typeof value === 'boolean') {
headerInput[key] = value.toString();
}
headers[key] = headerInput[key];
}
return headers;
}
}
33 changes: 12 additions & 21 deletions ts-force/src/test/rest/restObjects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,37 +175,28 @@ describe('Generated Classes', () => {
await acc.delete();
})

it('handle ifModifiedSince', async () => {
it('handle multiple headers', async () => {
const modifiedDate = new Date();
let acc = new Account({
name: 'account'
});
await acc.insert();
//@ts-expect-error
acc._client.request.interceptors.request.use((config) => {
if (config.method === 'patch')
expect(config.headers["If-Modified-Since"]).to.equal(modifiedDate.toUTCString());
if (config.method === 'patch') expect(config.headers['Sforce-Auto-Assign']).to.equal('true');
if (config.method === 'patch') expect(config.headers['Sforce-Mru']).to.equal('updateMru=true');
if (config.method === 'patch') expect(config.headers['If-Modified-Since']).to.equal(new Date().toUTCString());
return config
});
acc.name = "account2";
await acc.update({ refresh: false, ifModifiedSince: new Date()});
await acc.delete();
})

it('handle ifUmodifiedSince', async () => {
const modifiedDate = new Date();
let acc = new Account({
name: 'account'
await acc.update({
refresh: false,
headers: {
'Sforce-Auto-Assign': true,
'If-Modified-Since': new Date(),
'Sforce-Mru': 'updateMru=true',
},
});
await acc.insert();
//@ts-expect-error
acc._client.request.interceptors.request.use((config) => {
if (config.method === 'patch')
expect(config.headers["If-Unmodified-Since"]).to.equal(modifiedDate.toUTCString());
return config
});
acc.name = "account2";
await acc.update({ refresh: false, ifUnmodifiedSince: new Date()});
await acc.delete();
})

Expand Down Expand Up @@ -489,4 +480,4 @@ describe('Generated Classes', () => {
await c.delete();
});

});
});
Loading