Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Mar 18, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions packages/core/src/apex/ApexClassFetcher.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Connection } from '@salesforce/core';
import chunkCollection from "../queryHelper/ChunkCollection";
import QueryHelper from '../queryHelper/QueryHelper';

export default class ApexClassFetcher {
Expand All @@ -11,9 +12,17 @@ export default class ApexClassFetcher {
* @returns
*/
public async fetchApexClassByName(classNames: string[]): Promise<{ Id: string; Name: string }[]> {
let collection = classNames.map((name) => `'${name}'`).toString(); // transform into formatted string for query
let query = `SELECT ID, Name FROM ApexClass WHERE Name IN (${collection})`;
let result: {Id: string; Name: string}[] = [];

return QueryHelper.query<{ Id: string; Name: string }>(query, this.conn, false);
const chunks = chunkCollection(classNames);
for (const chunk of chunks) {
const formattedChunk = chunk.map(elem => `'${elem}'`).toString(); // transform into formatted string for query
const query = `SELECT ID, Name FROM ApexClass WHERE Name IN (${formattedChunk})`;

const records = await QueryHelper.query<{ Id: string; Name: string }>(query, this.conn, false);
result = result.concat(records);
}

return result;
}
}
}
15 changes: 12 additions & 3 deletions packages/core/src/apex/ApexTriggerFetcher.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Connection } from '@salesforce/core';
import chunkCollection from '../queryHelper/ChunkCollection';
import QueryHelper from '../queryHelper/QueryHelper';

export default class ApexTriggerFetcher {
Expand All @@ -11,9 +12,17 @@ export default class ApexTriggerFetcher {
* @returns
*/
public async fetchApexTriggerByName(triggerNames: string[]): Promise<{ Id: string; Name: string }[]> {
let collection = triggerNames.map((name) => `'${name}'`).toString(); // transform into formatted string for query
let query = `SELECT ID, Name FROM ApexTrigger WHERE Name IN (${collection})`;
let result: {Id: string, Name: string}[] = [];

return QueryHelper.query<{ Id: string; Name: string }>(query, this.conn, false);
const chunks = chunkCollection(triggerNames);
for (const chunk of chunks) {
const formattedChunk = chunk.map(elem => `'${elem}'`).toString(); // transform into formatted string for query
const query = `SELECT ID, Name FROM ApexTrigger WHERE Name IN (${formattedChunk})`;

const records = await QueryHelper.query<{ Id: string; Name: string }>(query, this.conn, false);
result = result.concat(records);
}

return result;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Connection } from '@salesforce/core';
import chunkCollection from '../../queryHelper/ChunkCollection';
import QueryHelper from '../../queryHelper/QueryHelper';

export default class ApexCodeCoverageAggregateFetcher {
Expand All @@ -9,17 +10,33 @@ export default class ApexCodeCoverageAggregateFetcher {
* @param listOfApexClassOrTriggerId
* @returns
*/
public async fetchACCAById(listOfApexClassOrTriggerId: string[]) {
let collection = listOfApexClassOrTriggerId
.map((ApexClassOrTriggerId) => `'${ApexClassOrTriggerId}'`)
.toString();
let query = `SELECT ApexClassorTriggerId, NumLinesCovered, NumLinesUncovered, Coverage FROM ApexCodeCoverageAggregate WHERE ApexClassorTriggerId IN (${collection})`;

return QueryHelper.query<{
public async fetchACCAById(listOfApexClassOrTriggerId: string[]): Promise<{
ApexClassOrTriggerId: string;
NumLinesCovered: number;
NumLinesUncovered: number;
Coverage: any;
}[]> {
let result: {
ApexClassOrTriggerId: string;
NumLinesCovered: number;
NumLinesUncovered: number;
Coverage: any;
}>(query, this.conn, true);
}[] = [];

const chunks = chunkCollection(listOfApexClassOrTriggerId);
for (const chunk of chunks) {
const formattedChunk = chunk.map(elem => `'${elem}'`).toString();
let query = `SELECT ApexClassorTriggerId, NumLinesCovered, NumLinesUncovered, Coverage FROM ApexCodeCoverageAggregate WHERE ApexClassorTriggerId IN (${formattedChunk})`;

const records = await QueryHelper.query<{
ApexClassOrTriggerId: string;
NumLinesCovered: number;
NumLinesUncovered: number;
Coverage: any;
}>(query, this.conn, true);
result = result.concat(records);
}

return result;
}
}
38 changes: 38 additions & 0 deletions packages/core/src/queryHelper/ChunkCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@


/**
* Split values in SOQL WHERE clause into chunks to avoid exceeding max. URI length (16,000 chars) or max. WHERE clause length (4000 chars)
* @param collection values in SOQL WHERE clause
* @param chunkSize default is 4000
* @param offset offset to account for keywords, fields, operators and literals in the query. Default is 1000
*/
export default function chunkCollection(collection: string[], chunkSize: number = 4000, offset: number = 1000): string[][] {
const result: string[][] = [];
chunkSize = chunkSize - offset;

let chunk: string[] = [];
let numberOfCharsInChunk: number = 0;
for (const elem of collection) {
if (elem.length + 2 > chunkSize) {
throw new Error(`Single value cannot exceed chunk size limit of ${chunkSize}`);
}

const commasAndQuotes = 2*(chunk.length+1) + chunk.length;
if (numberOfCharsInChunk + elem.length + commasAndQuotes <= chunkSize) {
chunk.push(elem);
numberOfCharsInChunk += elem.length;
} else {
result.push(chunk);

// Create new chunk
chunk = [];
numberOfCharsInChunk = 0;
chunk.push(elem);
numberOfCharsInChunk += elem.length;
}
}

result.push(chunk);

return result;
}
30 changes: 30 additions & 0 deletions packages/core/tests/queryHelper/ChunkCollection.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { expect } from '@jest/globals';
import chunkCollection from '../../src/queryHelper/ChunkCollection';

describe('Given a collection', () => {

it('should return a single chunk for a collection less than 3000 chars', () => {
const collection = ["ApexClassA", "ApexClassB", "ApexClassC"];
const result = chunkCollection(collection);
expect(result.length).toBe(1);
expect(result).toEqual([
["ApexClassA", "ApexClassB", "ApexClassC"]
]);
});


it('should return N chunks for a collection exceeding the chunk size', () => {
const collection = ["ApexClassA", "ApexClassB", "ApexClassC", "ApexClassD"];
const result = chunkCollection(collection, 1050, 1000);
expect(result.length).toBe(2);
expect(result).toEqual([
["ApexClassA", "ApexClassB", "ApexClassC"],
["ApexClassD"]
]);
});

it('should throw an error if single element in collection exceeds chunk size', () => {
const collection = ["ApexClassWithAnExceedinglyLongNameGreaterThanTheChunkSize"];
expect(() => {chunkCollection(collection, 1050, 1000)}).toThrow();
});
});