Skip to content

W-16448626 feat: sosl#1025

Merged
WillieRuemmele merged 15 commits intomainfrom
wr/sosl
Aug 13, 2024
Merged

W-16448626 feat: sosl#1025
WillieRuemmele merged 15 commits intomainfrom
wr/sosl

Conversation

@WillieRuemmele
Copy link
Copy Markdown
Contributor

What does this PR do?

adds data search command to preform SOSL queries

What issues does this PR fix or reference?

@W-16448626@

@WillieRuemmele WillieRuemmele marked this pull request as ready for review August 8, 2024 21:52
@WillieRuemmele WillieRuemmele requested a review from a team as a code owner August 8, 2024 21:52
WillieRuemmele and others added 2 commits August 9, 2024 09:44
* fix: write the help for new command

* fix: add info about --result-format behavior
@mshanemc mshanemc changed the title Wr/sosl feat: sosl Aug 12, 2024
if (flags['result-format'] !== 'json') this.spinner.start(messages.getMessage('queryRunningMessage'));
const queryResult = await conn.search(queryString);
if (!this.jsonEnabled()) {
// once we overrode result-format summary, undefined became a flag option, but the default is human
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll make a note to ask Mike about this when he gets back

Copy link
Copy Markdown
Contributor

@mshanemc mshanemc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

most are comment/questions, some of which might need changes depending on the answer.

definitely fix

  • that empty string thing
  • ut using plugins-core's stubs

// --file will be present if flags.query isn't. Oclif exactlyOne isn't quite that clever
const queryString = flags.query ?? fs.readFileSync(flags.file as string, 'utf8');
const conn = flags['target-org'].getConnection(flags['api-version']);
if (flags['result-format'] !== 'json') this.spinner.start(messages.getMessage('queryRunningMessage'));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it the result format or the json-enabled-ness that matters for spinners?

ex: Imagine I want --result-format csv --json because I'm a CI script. I want to get that 0/1 and file location as json output BUT I want a csv file written. I wouldn't want a spinner.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is carried from data query, but this.spinner should be respecting the --json flag

import { SearchResult } from '@jsforce/jsforce-node';

export abstract class SearchReporter {
public types: string[];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this iterates the records to collect unique types, then each reporter iterates the records again to filter one type at a time.

Why not have a Map of <type, Record[]>. Then you already have them organized (and once we can use Map.groupBy it'll be even cleaner!)

public types: string[];
public ux = new Ux();
protected constructor(public result: SearchResult) {
this.types = [...new Set(this.result.searchRecords.map((row) => row.attributes?.type ?? ''))];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the important of the empty string? When does it come back empty? Leave a comment for the next person who reads this and wonders.

this.types.map((type) => {
const filtered = this.result.searchRecords.filter((t) => t.attributes?.type === type);
// remove 'attributes' property from result and csv output
filtered.map((r) => delete r.attributes);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both the filter and the removal of attributes seem common to both classes. Probably a shared fn?

Or, if you do move to the Map idea, then the value could be filtered to remove attributes via kit/omit .map((r) => omit(r, 'attributes'));

import { JsonSearchReporter } from './reporters/search/reporter.js';
import { CsvSearchReporter } from './reporters/search/csvSearchReporter.js';

export const displaySearchResults = (queryResult: SearchResult, resultFormat: 'human' | 'json' | 'csv'): void => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you wanted to do this without classes, this would be a function that decides which reporter function(ux, results) to call.

then you wouldn't need all the new and display() etc both here and in the tests

});

it('will write two csv files', () => {
const logStub = $$.SANDBOX.stub(Ux.prototype, 'log');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should use the ux tubs from sf-plugins-core.


it('will write two csv files', () => {
const logStub = $$.SANDBOX.stub(Ux.prototype, 'log');
const writeFileStub = $$.SANDBOX.stub(fs, 'writeFileSync');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idea: rather than the fs stubbing, I think this would be better to write to a temp dir (and then use mocha-snap to snapshot and commit them).

Then we'd detect accidental changes (ex: to.include would pass if you accidentally included the attributes stuff)

});

it('will write two tables', () => {
const tableStub = $$.SANDBOX.stub(Ux.prototype, 'table');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the sf-plugins-core ux stub has table on it already

const cols = Object.keys(filtered[0]).join(',');
const body = filtered.map((r) => Object.values(r).join(',')).join(os.EOL);

this.ux.log(`Written to ${type}.csv`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't the empty string be really bad here?


# flags.file.summary

File that contains the SOQL query.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SOSL ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
File that contains the SOQL query.
File that contains the SOSL query.

@@ -0,0 +1,31 @@
# summary

Execute a SOSL query.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably for Juliet...I think we should link to the SOSL syntax docs

@mshanemc
Copy link
Copy Markdown
Contributor

QA notes:

./bin/run.js data search -o platformers -q "find 'Shane'"
✅ shows lots of tables with IDs in them.
✅ --json looks good

🎨 I don't think the word Results on each table is adding much.

./bin/run.js data search -o platformers -q "find {Shane}" --json --result-format csv
👎🏻 I guess --json overrides the --result-format? I wonder if we could validate that better to make it clearer that the command isn't gonna do what they might think it does?

data search -o platformers -q "find {Shane}" --result-format csv
✅ got lots of CSVs
🎨 I think I might prefer these in a folder? I could go either way on that

"find {Shane} returning RecordType"
✅ shows the server error nicely

"find {Shane} returning ApexClass(name, Status), User(name, email)"
✅ tables look good with fields in them
using --result-format csv produces good csv (I also added ID tot the returning fields on each object)

✅ query with file throws good error
✅ query from a file works fine

* refactor: use oclif custom flag

* refactor: exclusive json

* style: remove the word "results" from table heads

* test: no "Results" in test assertions
@WillieRuemmele
Copy link
Copy Markdown
Contributor Author

✅ : no --json and -r json =>

 ➜  ../../oss/plugin-data/bin/run.js data search --file query.txt  -r json --json
{
  "name": "Error",
  "message": "The following error occurred:\n  \u001b[2m--json=true cannot also be provided when using --result-format\u001b[22m\nSee more help with --help",
  "exitCode": 2,
  "context": "DataSearchCommand",
  "stack": "Error: The following error occurred:\n  \u001b[2m--json=true cannot also be provided when using --result-formats```

@WillieRuemmele WillieRuemmele merged commit 833fc4e into main Aug 13, 2024
@WillieRuemmele WillieRuemmele deleted the wr/sosl branch August 13, 2024 17:15
@iowillhoit iowillhoit changed the title feat: sosl W-16448626 feat: sosl Jan 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants