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

feat(params): adds optional param types #599

Merged
merged 31 commits into from
Jan 30, 2020
Merged

feat(params): adds optional param types #599

merged 31 commits into from
Jan 30, 2020

Conversation

steffnay
Copy link
Contributor

@steffnay steffnay commented Dec 26, 2019

Adds ability to optionally provide query parameter types when performing queries.

Fixes #400
🦕

@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Dec 26, 2019
@codecov
Copy link

codecov bot commented Dec 27, 2019

Codecov Report

Merging #599 into master will not change coverage.
The diff coverage is 45.02%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master     #599   +/-   ##
=======================================
  Coverage   94.74%   94.74%           
=======================================
  Files           7        7           
  Lines        6218     6218           
  Branches      358      358           
=======================================
  Hits         5891     5891           
  Misses        326      326           
  Partials        1        1
Impacted Files Coverage Δ
src/index.ts 100% <100%> (ø) ⬆️
src/routine.ts 19.58% <19.58%> (ø) ⬆️
src/dataset.ts 93.7% <72.54%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7c650f5...1f160a2. Read the comment docs.

@steffnay steffnay changed the title Param types feat(params): Adds optional param types Dec 27, 2019
@steffnay steffnay changed the title feat(params): Adds optional param types feat(params): adds optional param types Dec 27, 2019
Copy link
Contributor

@bcoe bcoe left a comment

Choose a reason for hiding this comment

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

This is looking pretty slick 😄

Biggest thing that initially jumped out at me is I think we could potentially use IStandardSqlDataType rather than any perhaps? and I'd dig into whether there's a way to get a string representation of this type so that we don't duplicate the list of types.

Might be worth asking @alexander-fenster if this is possible.

src/bigquery.ts Outdated
type: 'ARRAY',
arrayType: BigQuery.getType_(value[0]),
};
if (value.length === 0 || value[0] === null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

could someone provide undefined, or null is a special case I'm guessing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As far as I know, undefined shouldn't be an option, just null

src/bigquery.ts Show resolved Hide resolved
@alexander-fenster
Copy link
Contributor

I don't know much about this library (it's not a GAPIC), but I generally agree that we should avoid using any. It might make sense to have this one merged and then have a separate PR to get rid of all explicitly used any types in the code since I'm guessing most of the types should present in .d.ts.

src/bigquery.ts Outdated
@@ -42,6 +42,7 @@ import {
} from './table';
import {GoogleErrorBody} from '@google-cloud/common/build/src/util';
import bigquery from './types';
import {type} from 'os';
Copy link
Contributor

Choose a reason for hiding this comment

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

Was this supposed to come in?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope

src/bigquery.ts Outdated
@@ -94,6 +95,8 @@ export type Query = JobRequest<bigquery.IJobConfigurationQuery> & {
// tslint:disable-next-line no-any
params?: any[] | {[param: string]: any};
dryRun?: boolean;
// tslint:disable-next-line: no-any
types?: any[] | {[type: string]: any};
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this truly any? It looks like there's an array of types below that define what this can look like.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, I can change this.

src/bigquery.ts Outdated Show resolved Hide resolved
src/bigquery.ts Outdated
};
}),
};
} else if (is.boolean(providedType)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it valid/expected for a user to provide a boolean as a type?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, it's a BigQuery type that can be detected so they should be able to provide it as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

In this code path, it looks like the "providedType" would be true or false, which would end up returning {type: false}, as opposed to {type: 'BOOL'}.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, I see. Fixed.

src/bigquery.ts Outdated Show resolved Hide resolved
src/bigquery.ts Outdated Show resolved Hide resolved
src/bigquery.ts Outdated Show resolved Hide resolved
@stephenplusplus
Copy link
Contributor

Sorry for the delay @steffnay.

I believe we'll want a new describe block just to test getTypeDescriptorFromProvidedType_. That would handle all of the checks for if it throws when expected, recurses itself as intended, etc. Tests for other describes can then remain isolated to only verify behavior that happens in their respective functions.

I wrote up a working example of how you can verify that a sibling method is being called with the correct arguments, and that the value it returns is being used properly. This template should be repeatable for the different code paths, but let me know if you get stuck or have more questions about how to apply this elsewhere.

This is a test that would go under createQueryJob -> SQL parameters -> positional.

it('should convert value and type to query parameter', done => {
  const fakeQueryParameter = {fake: 'query parameter'};

  bq.createJob = (reqOpts: JobOptions) => {
    const queryParameters = reqOpts.configuration!.query!.queryParameters;
    assert.deepStrictEqual(queryParameters, [fakeQueryParameter]);
    done();
  };

  sandbox
    .stub(BigQuery, 'valueToQueryParameter_')
    .callsFake((value, type) => {
      assert.strictEqual(value, POSITIONAL_PARAMS[0]);
      assert.strictEqual(type, POSITIONAL_TYPES[0]);
      return fakeQueryParameter;
    });

  bq.createQueryJob({
    query: QUERY_STRING,
    params: POSITIONAL_PARAMS,
    types: POSITIONAL_TYPES,
  });
});

I hope this is helpful!

src/bigquery.ts Outdated
};
}),
};
} else if (is.boolean(providedType)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

In this code path, it looks like the "providedType" would be true or false, which would end up returning {type: false}, as opposed to {type: 'BOOL'}.

test/bigquery.ts Outdated Show resolved Hide resolved
test/bigquery.ts Outdated Show resolved Hide resolved
test/bigquery.ts Outdated Show resolved Hide resolved
test/bigquery.ts Outdated Show resolved Hide resolved
test/bigquery.ts Outdated Show resolved Hide resolved
@steffnay steffnay marked this pull request as ready for review January 23, 2020 22:42
@steffnay steffnay added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jan 23, 2020
@kokoro-team kokoro-team removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jan 23, 2020
test/bigquery.ts Outdated
types: INVALID_TYPES,
});
}, /Invalid type provided./);
done();
Copy link
Contributor

Choose a reason for hiding this comment

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

This done() and the ones that follow in this block can be removed. It's only needed when we want to hold the test from exiting prematurely while we wait for a callback to be executed. In this case, the assert.throws() will run sync and then exit the test block with a failure or a success.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm really curious about this because these tests that check for errors keep timing out if I don't call done(). Is there something else I should be doing to fix that?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you just need to remove the done argument from the test function's argument list:

- it('should ...', done => {
+ it('should ...', () => {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, got it.

test/bigquery.ts Outdated Show resolved Hide resolved
test/bigquery.ts Outdated Show resolved Hide resolved
.callsFake(value_ => {
assert.strictEqual(value_, value);
setImmediate(done);
return {
Copy link
Contributor

Choose a reason for hiding this comment

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

We need to follow this through to verify this object we return is used as we expect.

test/bigquery.ts Outdated Show resolved Hide resolved
test/bigquery.ts Outdated Show resolved Hide resolved
src/bigquery.ts Outdated Show resolved Hide resolved
src/bigquery.ts Outdated Show resolved Hide resolved
src/bigquery.ts Outdated Show resolved Hide resolved
src/bigquery.ts Outdated
@@ -807,12 +873,18 @@ export class BigQuery extends common.Service {
* @returns {object} A properly-formed `queryParameter` object.
*/
// tslint:disable-next-line no-any
static valueToQueryParameter_(value: any) {
static valueToQueryParameter_(value: any, providedType: any = undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is providedType really any? I'm surprised we can't further constrain this to {}. Side note - for undefined, you don't need to set a default value. You can say providedType?: {}

Copy link
Contributor Author

@steffnay steffnay Jan 27, 2020

Choose a reason for hiding this comment

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

providedType can be a string, object, or array, so I don't understand how to make providedType?: {} work this way. I don't know how I need to refactor providedType[0] on line 899 to work with this change.

Copy link
Contributor

Choose a reason for hiding this comment

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

you could do something along the lines of:

providedType: string|{[key: string]: string}|string[]

☝️ | is used to allow for multiple types.

Copy link
Contributor Author

@steffnay steffnay Jan 28, 2020

Choose a reason for hiding this comment

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

@bcoe The array type can also be populated with string, array, or object. Similarly, the object can have values that are arrays. How do I specify that without using any?

Copy link
Contributor

Choose a reason for hiding this comment

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

@steffnay you'd potentially probably want to start breaking it out into interfaces, but you'd do something along the lines of:

{[key: string]: string|string[]}

☝️ but as it gets complex like this, you'd probably want to start describing an interface. The advantage of putting in this work is that folks using the API have more safety, and it's easier to figure out what to pass to the API.

src/bigquery.ts Outdated Show resolved Hide resolved
src/bigquery.ts Outdated Show resolved Hide resolved
system-test/bigquery.ts Outdated Show resolved Hide resolved
system-test/bigquery.ts Outdated Show resolved Hide resolved
test/bigquery.ts Show resolved Hide resolved
test/bigquery.ts Outdated Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes This human has signed the Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Named query params with an empty array cannot be parsed
7 participants