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

Filtering output #125

Merged
merged 17 commits into from
Nov 18, 2023
Merged

Filtering output #125

merged 17 commits into from
Nov 18, 2023

Conversation

Bugs5382
Copy link
Contributor

@Bugs5382 Bugs5382 commented Oct 29, 2023

Registering a "directive" that can check a user's role could modify the "output" of string types if the directive "apply policy" returned 'false'. If the 'valueOverride' value has a string or a function that returns a string, that is what will go in it's place. Otherwise it will make the field null regardless of the graphql type. This will override any graphql errors that might say that something was missing.

app.register(mercuriusAuth, {
    authContext: hasPermissionContext,
    applyPolicy: hasFilterPolicy,
    outputPolicyErrors: {
      enabled: false,
      valueOverride: 'foo'
    },
    authDirective: 'filterData'
  })

Will Close #124

Shane Froebel added 4 commits October 26, 2023 22:07
basic tests are in there are designed correctly // replacement "output" is not yet in there
initial unit tests pass // need to ensure that we are "replacing" only on String scalar types. we are going to actually return an error at this point
@jonnydgreen
Copy link
Collaborator

Great work so far! Give me a shout when it's ready for review! :)

Shane Froebel added 4 commits October 30, 2023 21:01
make sure registration errors out correctly if not done correctly
how to use, but also some examples
@Bugs5382
Copy link
Contributor Author

@jonnydgreen So.. 2 issues..

I can't seem to generate unit tests for this line 59 or line 108-109. Ideas?

Also the refresh.js unit test fails all 4. Not sure why.

Also added some documentation in the mix. I will need some proof reading by a 2nd person after I re-read it tomorrow. It will form the basis of the example I place in the example folder once done.

@jonnydgreen
Copy link
Collaborator

this line 59

If you define a policy that returns an error instance that should do the trick!

line 108-109

If you define a field resolver this should also do the trick :)

Also the refresh.js unit test fails all 4. Not sure why.

Do you have any error logs?

@Bugs5382
Copy link
Contributor Author

Bugs5382 commented Nov 1, 2023

Do you have any error logs?

TAP version 13
# Subtest: polling interval with a new schema should trigger refresh of schema policy build
    1..4
    ok 1 - expect truthy value
    ok 2 - should be equivalent
    not ok 3 - should be equivalent
      ---
      diff: |
        --- expected
        +++ actual
        @@ -3,22 +3,7 @@
             "me": Object {
               "id": "u1",
               "name": "John",
        -      "lastName": null,
        +      "lastName": "Doe",
             },
           },
        -  "errors": Array [
        -    Object {
        -      "message": "Failed auth policy check on lastName",
        -      "locations": Array [
        -        Object {
        -          "line": 5,
        -          "column": 7,
        -        },
        -      ],
        -      "path": Array [
        -        "me",
        -        "lastName",
        -      ],
        -    },
        -  ],
         }
      at:
        line: 169
        column: 7
        file: refresh.js
        type: Test
      stack: |
        Test.<anonymous> (refresh.js:169:7)
      source: |2
      
            t.same(JSON.parse(res.body), {
        ------^
              data: {
                me: {
      ...
    
    # test count(3) != plan(4)
    # failed 1 of 3 tests
not ok 1 - polling interval with a new schema should trigger refresh of schema policy build # time=-49307724.042ms

I am certain that this was passing prior to me starting...

@Bugs5382
Copy link
Contributor Author

Bugs5382 commented Nov 1, 2023

@jonnydgreen Ok. Throwing up the 'white' flag. Not sure how to generate an error that would trigger or the resolve. Also with the refresh not sure. I think it's also causing another line not to "pass" check with 100% coverage:

Suites:   ​1 failed​, ​16 passed​, ​17 of 17 completed​
Asserts:  ​​​4 failed​, ​217 passed​, ​2 todo​, ​of 223​
​Time:​   ​4s​ERROR: Coverage for lines (98.89%) does not meet global threshold (100%)
ERROR: Coverage for branches (98.59%) does not meet global threshold (100%)
ERROR: Coverage for statements (98.9%) does not meet global threshold (100%)
-----------------------|---------|----------|---------|---------|-------------------
File                   | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------------------|---------|----------|---------|---------|-------------------
All files              |    98.9 |    98.59 |     100 |   98.89 |                   
 mercurius-js-auth     |     100 |    91.66 |     100 |     100 |                   
  index.js             |     100 |    91.66 |     100 |     100 | 25                
 mercurius-js-auth/lib |   98.82 |       99 |     100 |    98.8 |                   
  auth.js              |    96.7 |    96.66 |     100 |   96.59 | 58,107-108        
  errors.js            |     100 |      100 |     100 |     100 |                   
  filter-schema.js     |     100 |      100 |     100 |     100 |                   
  prune-schema.js      |     100 |      100 |     100 |     100 |                   
  symbols.js           |     100 |      100 |     100 |     100 |                   
  validation.js        |     100 |      100 |     100 |     100 |                   
-----------------------|---------|----------|---------|---------|-------------------

@Bugs5382 Bugs5382 marked this pull request as ready for review November 1, 2023 10:32
@jonnydgreen
Copy link
Collaborator

jonnydgreen commented Nov 2, 2023

Thanks for the update @Bugs5382, fantastic work! I'm currently away but will be back on Sunday where I'll take a closer look and see what I can find/replicate :)

@jonnydgreen
Copy link
Collaborator

jonnydgreen commented Nov 6, 2023

Hi @Bugs5382 !

line 108-109

For this one, if you define a schema of the form, this should do the trick :)

directive @filterData (disallow: String!) on FIELD_DEFINITION

type Message {
  message: String!
  notes: String
}

type Query {
  publicMessages: @filterData (disallow: "no-public-messages")
}

The reason why this is picked up the coverage is because the field publicMessages is a resolver and therefore the code that checks if the field.resolver is a function is hit.

line 59

For this one, the following policy should do the trick

async function hasFilterPolicyReturnError (authDirectiveAST, parent, args, context, info) {
  const notNeeded = authDirectiveAST.arguments.find(arg => arg.name.value === 'disallow').value.value

  const policyPassed = !context.auth.permission.includes(notNeeded)
  if (!policyPassed) {
    // This is the key bit
    return new MER_AUTH_ERR_FAILED_POLICY_CHECK(info.fieldName)
  }
}

fixes unit testing

Co-authored-by: Jonny Green <[email protected]>
@Bugs5382
Copy link
Contributor Author

Bugs5382 commented Nov 7, 2023

I'll get the other two inserted and updated soon!

@Bugs5382
Copy link
Contributor Author

Bugs5382 commented Nov 7, 2023

Ok. So after you pointed that it is on a "resolver", I made it error out in the auth.js file:

  [kWrapFieldResolverReplace] (schemaTypeField, fieldPolicy, fieldReplace) {
    // Overwrite field resolver
    const fieldName = schemaTypeField.name
    if (typeof schemaTypeField.resolve === 'function') {
      throw new MER_AUTH_ERR_USAGE_ERROR('Replacement can not happen on a resolver. Only a field.')
    } else {
      schemaTypeField.resolve = this[kMakeProtectedResolverReplace](fieldPolicy, (parent) => parent[fieldName], fieldReplace)
    }
  }

and updated the test to check for the error output. I am not sure doing filtering on the resolver/function does any good. I didn't want to have to check for string. Unless you think it's "needed". I never envisoneded it that way @jonnydgreen .

100% coverage coming!

removed resolver filtering // fixed refresh test again // 100% code coverage!
@Bugs5382
Copy link
Contributor Author

Bugs5382 commented Nov 7, 2023

Suites:   ​17 passed​, ​17 of 17 completed​
Asserts:  ​​​225 passed​, ​2 todo​, ​of 227​
​Time:​   ​5s​
-----------------------|---------|----------|---------|---------|-------------------
File                   | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------------------|---------|----------|---------|---------|-------------------
All files              |     100 |      100 |     100 |     100 |                   
 mercurius-js-auth     |     100 |      100 |     100 |     100 |                   
  index.js             |     100 |      100 |     100 |     100 |                   
 mercurius-js-auth/lib |     100 |      100 |     100 |     100 |                   
  auth.js              |     100 |      100 |     100 |     100 |                   
  errors.js            |     100 |      100 |     100 |     100 |                   
  filter-schema.js     |     100 |      100 |     100 |     100 |                   
  prune-schema.js      |     100 |      100 |     100 |     100 |                   
  symbols.js           |     100 |      100 |     100 |     100 |                   
  validation.js        |     100 |      100 |     100 |     100 |                   
-----------------------|---------|----------|---------|---------|-------------------

> [email protected] typescript
> tsd

🥳 🎉

@jonnydgreen
Copy link
Collaborator

Fantastic work! I'll take a detailed look tomorrow evening!

Copy link
Collaborator

@jonnydgreen jonnydgreen left a comment

Choose a reason for hiding this comment

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

Functionally looks great, just some comments around testing and docs! 🚀 Could we add an example as well? :)

@jonnydgreen jonnydgreen requested a review from mcollina November 10, 2023 09:12
errors were still coming over for null values // unit tests updated and fixed code that does not send an error back
Shane Froebel added 2 commits November 10, 2023 06:58
if outputPolicyErrors.enabled was 'true,' then no errors would show on a failure other than schema ones if they excited after a replacement
@Bugs5382
Copy link
Contributor Author

Functionally looks great, just some comments around testing and docs! 🚀 Could we add an example as well? :)

Example uploaded. Unit testing and come code fixed to solve them. 👍

@Bugs5382 Bugs5382 requested a review from jonnydgreen November 10, 2023 12:07
Copy link
Collaborator

@jonnydgreen jonnydgreen left a comment

Choose a reason for hiding this comment

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

LGTM!

@Bugs5382
Copy link
Contributor Author

@jonnydgreen Thank you!!! 🥇

@jonnydgreen
Copy link
Collaborator

@jonnydgreen Thank you!!! 🥇

You're welcome and thank you to you too! If it's okay, I'll get this deployed this week as I'd like to include this with another change!

@mcollina I'm thinking of including this as part of v5 (semver major to remove Node.js v14+16 support and add Node.js v20 support), wdyt?

@Bugs5382
Copy link
Contributor Author

@jonnydgreen Ya. Not a problem.

I been coding with v20, and I found no issues. Version 14 is not on my internal software plan.

@jonnydgreen jonnydgreen merged commit b02fd01 into mercurius-js:main Nov 18, 2023
@Bugs5382 Bugs5382 deleted the filtering-output branch November 19, 2023 03:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Directive filterSchema: true -- directive to prevent "message"
2 participants