Repository: paustint/soql-parser-js (ts)

Issue:
Subquery relationship names should not use SObject
<!--
  Please include the following classification.
  If your classification is not added, you can modify the list below
-->
- [ ] Feature
- [x] Bug Request

<!--If this is a bug, please include the steps to reproduce the bug-->
## Description
the SObject field on the results does not make sense to use for subqueries, as it is really the child relationship name.

There should be a new field on Query to indicate the name, since we really don't know the actual SObject name


Pull request description:
Subquery relationship names should not use SObject #35
Added differentiated sobject name for subqueries since it is actually the relationship name
Updated docs
Updated unit tests
Modified object prefixes to be an array and added support for multiple prefixes

resolves #35

Patch:
diff --git a/lib/SoqlComposer.ts b/lib/SoqlComposer.ts
index c669931..3f5b9da 100644
--- a/lib/SoqlComposer.ts
+++ b/lib/SoqlComposer.ts
@@ -62,7 +62,7 @@ export class Compose {
     fields.forEach((field, i) => {
       if (field.match(this.subqueryFieldRegex)) {
         const subquery = query.subqueries.find(
-          subquery => subquery.sObject === field.replace(this.subqueryFieldReplaceRegex, '')
+          subquery => subquery.sObjectRelationshipName === field.replace(this.subqueryFieldReplaceRegex, '')
         );
         if (subquery) {
           fields[i] = `(${this.parseQuery(subquery)})`;
@@ -70,7 +70,13 @@ export class Compose {
       }
     });
     output += ` ${fields.join(', ').trim()} FROM`;
-    output += ` ${utils.get(query.sObjectPrefix, '.')}${query.sObject}${utils.get(query.sObjectAlias, '', ' ')}`;
+    if (query.sObjectRelationshipName) {
+      const sObjectPrefix = query.sObjectPrefix || [];
+      sObjectPrefix.push(query.sObjectRelationshipName);
+      output += ` ${sObjectPrefix.join('.')}${utils.get(query.sObjectAlias, '', ' ')}`;
+    } else {
+      output += ` ${query.sObject}${utils.get(query.sObjectAlias, '', ' ')}`;
+    }
     this.log(output);
 
     if (query.where) {
diff --git a/lib/SoqlListener.ts b/lib/SoqlListener.ts
index 239524f..aefff9c 100644
--- a/lib/SoqlListener.ts
+++ b/lib/SoqlListener.ts
@@ -844,11 +844,20 @@ export class Listener implements SOQLListener {
     if (this.config.logging) {
       console.log('enterObject_spec:', ctx.text);
     }
-    this.getSoqlQuery().sObject = ctx.getChild(0).text;
-    if (this.config.includeSubqueryAsField && this.context.isSubQuery) {
+
+    if (!this.context.isSubQuery) {
+      this.getSoqlQuery().sObject = ctx.getChild(0).text;
+    } else {
+      this.getSoqlQuery().sObjectRelationshipName = ctx.getChild(0).text;
       if (ctx.getChild(0).text.includes('.')) {
-        this.getSoqlQuery().sObject = ctx.getChild(1).text;
-        this.getSoqlQuery().sObjectPrefix = ctx.getChild(0).text.replace('.', '');
+        this.getSoqlQuery().sObjectRelationshipName = ctx.getChild(1).text;
+        const prefixList: string[] = [];
+        for (let i = 0; i < ctx.getChild(0).childCount; i++) {
+          if (ctx.getChild(0).getChild(i) instanceof Parser.Object_nameContext) {
+            prefixList.push(ctx.getChild(0).getChild(i).text);
+          }
+        }
+        this.getSoqlQuery().sObjectPrefix = prefixList;
         this.soqlQuery.fields.push({
           subqueryObjName: ctx.getChild(1).text,
         });
diff --git a/lib/models/SoqlQuery.model.ts b/lib/models/SoqlQuery.model.ts
index eb8fc2d..eb05f13 100644
--- a/lib/models/SoqlQuery.model.ts
+++ b/lib/models/SoqlQuery.model.ts
@@ -9,9 +9,10 @@ export type UpdateClause = 'TRACKING' | 'VIEWSTAT';
 export interface Query {
   fields: Field[];
   subqueries: Query[];
-  sObject: string;
+  sObject?: string;
   sObjectAlias?: string;
-  sObjectPrefix?: string;
+  sObjectPrefix?: string[];
+  sObjectRelationshipName?: string;
   where?: WhereClause;
   limit?: number;
   offset?: number;


Test patch:
diff --git a/debug/test.js b/debug/test.js
index bd9c49b..7c3b81d 100644
--- a/debug/test.js
+++ b/debug/test.js
@@ -1,7 +1,7 @@
 var soqlParserJs = require('../dist');
 
 const query = `
-SELECT Company, toLabel(Status) FROM Lead WHERE toLabel(Status) = 'le Draft'
+SELECT Account.Name, (SELECT Contact.LastName FROM Account.Contact.Foo.Bars) FROM Account
 `;
 
 const parsedQuery = soqlParserJs.parseQuery(query, { logging: true });
diff --git a/test/TestCases.ts b/test/TestCases.ts
index 74b6d79..a06c981 100644
--- a/test/TestCases.ts
+++ b/test/TestCases.ts
@@ -306,7 +306,7 @@ export const testCases: TestCase[] = [
             },
           ],
           subqueries: [],
-          sObject: 'Contacts',
+          sObjectRelationshipName: 'Contacts',
         },
       ],
       sObject: 'Account',
@@ -334,8 +334,8 @@ export const testCases: TestCase[] = [
             },
           ],
           subqueries: [],
-          sObject: 'Contacts',
-          sObjectPrefix: 'Account',
+          sObjectRelationshipName: 'Contacts',
+          sObjectPrefix: ['Account'],
         },
       ],
       sObject: 'Account',
@@ -362,7 +362,7 @@ export const testCases: TestCase[] = [
             },
           ],
           subqueries: [],
-          sObject: 'Contacts',
+          sObjectRelationshipName: 'Contacts',
           where: {
             left: {
               field: 'CreatedBy.Alias',
@@ -430,7 +430,7 @@ export const testCases: TestCase[] = [
             },
           ],
           subqueries: [],
-          sObject: 'Line_Items__r',
+          sObjectRelationshipName: 'Line_Items__r',
         },
       ],
       sObject: 'Merchandise__c',
@@ -565,7 +565,7 @@ export const testCases: TestCase[] = [
             },
           ],
           subqueries: [],
-          sObject: 'Notes',
+          sObjectRelationshipName: 'Notes',
         },
       ],
       sObject: 'Account',
@@ -609,7 +609,7 @@ export const testCases: TestCase[] = [
             },
           ],
           subqueries: [],
-          sObject: 'OpportunityLineItems',
+          sObjectRelationshipName: 'OpportunityLineItems',
         },
       ],
       sObject: 'Opportunity',
@@ -1007,7 +1007,7 @@ export const testCases: TestCase[] = [
             },
           ],
           subqueries: [],
-          sObject: 'ChildAccounts',
+          sObjectRelationshipName: 'ChildAccounts',
           sObjectAlias: 'a2',
         },
         {
@@ -1018,7 +1018,7 @@ export const testCases: TestCase[] = [
             },
           ],
           subqueries: [],
-          sObject: 'ChildAccounts1',
+          sObjectRelationshipName: 'ChildAccounts1',
           sObjectAlias: 'a1',
         },
       ],
@@ -1440,5 +1440,34 @@ export const testCases: TestCase[] = [
       },
     },
   },
+  {
+    testCase: 50,
+    soql: 'SELECT Account.Name, (SELECT Contact.LastName FROM Account.Contact.Foo.Bars) FROM Account',
+    output: {
+      fields: [
+        {
+          text: 'Account.Name',
+          relationshipFields: ['Account', 'Name'],
+        },
+        {
+          subqueryObjName: 'Bars',
+        },
+      ],
+      subqueries: [
+        {
+          fields: [
+            {
+              text: 'Contact.LastName',
+              relationshipFields: ['Contact', 'LastName'],
+            },
+          ],
+          subqueries: [],
+          sObjectRelationshipName: 'Bars',
+          sObjectPrefix: ['Account', 'Contact', 'Foo'],
+        },
+      ],
+      sObject: 'Account',
+    },
+  },
 ];
 export default testCases;