Skip to content

Commit

Permalink
fix: simple retrieve multiple with fetchXml
Browse files Browse the repository at this point in the history
  • Loading branch information
BetimBeja committed Dec 9, 2023
1 parent 2cf5a65 commit a7b9ef6
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 14 deletions.
36 changes: 36 additions & 0 deletions __tests__/WebApiMock.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,40 @@ describe('WebApiMock', () => {
const userData = mockGenerator.metadata.GetRow('systemuser', createdUser.id);
expect(userData.row['firstname']).toEqual('Betim');
});

it('Should retrieve multiple records using FetchXml', async () => {
const result = await mockGenerator.context.webAPI.retrieveMultipleRecords(
'systemuser',
'?fetchXml=' +
encodeURIComponent(`
<fetch>
<entity name="systemuser">
<attribute name="systemuserid" />
<attribute name="firstname" />
<attribute name="lastname" />
</entity>
</fetch>
`),
);

const betimBeja = result.entities.find((e) => e['systemuserid'] === '682d1eb3-0ba4-ed11-aad1-000d3add5311');
expect(betimBeja).toEqual({
systemuserid: '682d1eb3-0ba4-ed11-aad1-000d3add5311',
firstname: 'Betim',
lastname: 'Beja',
});
});

it('Should update record', async () => {
const updated = await mockGenerator.context.webAPI.updateRecord(
'systemuser',
'682d1eb3-0ba4-ed11-aad1-000d3add5311',
{
firstname: 'Betim2',
},
);

const userData = mockGenerator.metadata.GetRow('systemuser', updated.id);
expect(userData.row['firstname']).toEqual('Betim2');
});
});
35 changes: 33 additions & 2 deletions src/ComponentFramework-Mock-Generator/Metadata.db/Metadata.db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class MetadataDB {
for (let optionValue in optionsetAttribute.OptionSet.Options) {
const option = optionsetAttribute.OptionSet.Options[optionValue];
const optionId = this._newId();

this.OptionSetMetadataSQL.AddOptionMetadata({
OptionId: optionId,
OptionSetId,
Expand Down Expand Up @@ -117,7 +117,7 @@ export class MetadataDB {
OptionSetId,
OptionSetType: booleanAttribute.OptionSet.OptionSetType,
});

let OptionId = this._newId();
let option = booleanAttribute.OptionSet.FalseOption;
this.OptionSetMetadataSQL.AddOptionMetadata({
Expand Down Expand Up @@ -276,6 +276,22 @@ export class MetadataDB {
return this.mapAttributeFromAttributeDB(tableMetadata, resultDB[0]);
}

/**
* Get the metadata for a table
* @param entity The target table
*/
getTableMetadataByEntitySet(entitySetName: string) {
const tableMetadataDB = this.EntityMetadataSQL.SelectTableMetadataByEntitySet(entitySetName);
if (!tableMetadataDB || tableMetadataDB.length === 0) {
if (this._warnMissingInit) {
console.warn(`Missing init for entitySet ${entitySetName}`);
}
return;
}

return this.getTableMetadata(tableMetadataDB[0].LogicalName);
}

/**
* Get the metadata for a table
* @param entity The target table
Expand Down Expand Up @@ -820,4 +836,19 @@ export class MetadataDB {

this.db.exec(`UPDATE ${safeTableName} ${statements.join(' ')}`, params);
}

SelectUsingFetchXml(fetchXml: XMLDocument) {
var fetchNode = fetchXml.documentElement;
var entityNode = fetchNode.firstElementChild;
if(!entityNode){
throw new Error('Fetch does not contain the entity node');
}

var attributesX = entityNode.getElementsByTagName('attribute');
const attributes: string[] = [];
for (let i = 0; i < attributesX.length; i++) {
attributes.push(attributesX[i].getAttribute('name') as string);
}
return this.db.exec(`SELECT ${attributes.join(',')} FROM ${entityNode.getAttribute('name')}`);
}
}
67 changes: 55 additions & 12 deletions src/ComponentFramework-Mock/WebApi.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import type { SinonStub } from 'sinon';

import { stub } from 'sinon';
import { parseOData } from '@shko.online/dataverse-odata';
import { ODataQuery, parseOData } from '@shko.online/dataverse-odata';
import { MetadataDB } from '../ComponentFramework-Mock-Generator';
import { FormattingMock } from './Formatting.mock';
import { ShkoOnline } from '../ShkoOnline';
Expand Down Expand Up @@ -73,18 +73,61 @@ export class WebApiMock implements ComponentFramework.WebApi {
});
this.updateRecord = stub();
this.updateRecord.callsFake((entityType: string, id: string, data: ComponentFramework.WebApi.Entity) => {
return new Promise<ComponentFramework.LookupValue>((resolve) => {
resolve({
id,
name: 'Any',
entityType,
});
return new Promise<ComponentFramework.LookupValue>((resolve, reject) => {
setTimeout(() => {
const metadata = db.getTableMetadata(entityType);
if (!metadata) {
return reject({ message: `Entity ${entityType} does not exist.` });
}

metadata.Attributes?.forEach(attribute=>{
if(attribute.AttributeOf || attribute.AttributeType === AttributeType.Virtual){
return;
}

const key = attribute.AttributeType === AttributeType.Lookup ? `_${attribute.LogicalName}_value` : attribute.LogicalName;

if(key in data){
db.UpdateValue(data[key], entityType, key, id);
console.log('updated ' + key);
}
})

var result = db.GetRow(entityType, id);

resolve({
id,
name: result.row?.[metadata.PrimaryNameAttribute || 'name'],
entityType,
});
}, this._Delay);
});
});

this.retrieveMultipleRecords = stub();
this.retrieveMultipleRecords.callsFake((entityType: string, options?: string, maxPageSize?: number) => {
return new Promise<ComponentFramework.WebApi.RetrieveMultipleResponse>((resolve) => {
return new Promise<ComponentFramework.WebApi.RetrieveMultipleResponse>((resolve, reject) => {
const parsed = options ? parseOData(options) : ({} as ODataQuery);

if (parsed.error) {
reject(parsed.error);
}

var entityMetadata = db.getTableMetadata(entityType);

if (!entityMetadata) {
reject(`Table ${entityType} does not exist`);
}

if (parsed.fetchXml) {
var entities = db.SelectUsingFetchXml(parsed.fetchXml);
resolve({
entities,
nextLink: 'next'
});
return;
}

resolve({
entities: [],
nextLink: 'string',
Expand Down Expand Up @@ -126,20 +169,20 @@ export class WebApiMock implements ComponentFramework.WebApi {
}
} else if (attribute.AttributeType === AttributeType.Lookup) {
const key = `_${attribute.LogicalName}_value`;

const lookupValue = oldRow[key] as ComponentFramework.LookupValue;
if (key in result.row) {
result.row[key] = lookupValue && lookupValue.id ? lookupValue.id : null;
if (lookupValue && lookupValue.id) {
result.row[`${key}@Microsoft.Dynamics.CRM.lookuplogicalname`] =
lookupValue.entityType;
lookupValue.entityType;
if (lookupValue.name != null) {
result.row[`${key}@OData.Community.Display.V1.FormattedValue`] =
lookupValue.name;
lookupValue.name;
}
if (oldRow[`${attribute.LogicalName}navigation`]) {
result.row[`${key}@Microsoft.Dynamics.CRM.associatednavigationproperty`] =
oldRow[`${attribute.LogicalName}navigation`];
oldRow[`${attribute.LogicalName}navigation`];
}
}
}
Expand Down

0 comments on commit a7b9ef6

Please sign in to comment.