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

⚡ use qs_dart for query string encoding #592

Merged
merged 12 commits into from
Apr 4, 2024
Merged

Conversation

techouse
Copy link
Collaborator

@techouse techouse commented Apr 3, 2024

@diegotori noticed that a Map<String, dynamic> like this

{
  'filters': {
    r'$or': [
      {
        'date': {
          r'$eq': '2020-01-01',
        }
      },
      {
        'date': {
          r'$eq': '2020-01-02',
        }
      }
    ],
    'author': {
      'name': {
        r'$eq': 'Kai doe',
      },
    }
  }
}

did not get encoded properly to

filters[$or][][date][$eq]=2020-01-01&filters[$or][][date][$eq]=2020-01-02&filters[author][name][$eq]=Kai%20doe

but became this

filters[$or][]={date: {$eq: 2020-01-01}}&filters[$or][]={date: {$eq: 2020-01-02}}&filters[author][name][$eq]=Kai doe

This PR fixes this by using an external library qs_dart which is a Dart port of qs that I've been painstakingly working on over the past couple of weeks. 🤓

This now means that the output of Chopper, while still backwards compatible, is now qs compatible.

The only caveat is null and empty String handling in query strings, which is a bit different in qs and therefore also qs_dart.

Please check the various test case changes to see these null and empty String differences.


Addresses #584
Supersedes #591

CC / @JEuler @Guldem @diegotori

@techouse techouse added the enhancement New feature or request label Apr 3, 2024
Copy link

codecov bot commented Apr 3, 2024

Codecov Report

Attention: Patch coverage is 84.61538% with 2 lines in your changes are missing coverage. Please review.

Project coverage is 93.52%. Comparing base (dd61ec1) to head (34d1d06).

❗ Current head 34d1d06 differs from pull request most recent head 7a6c67a. Consider uploading reports for the commit 7a6c67a to get more accurate results

Files Patch % Lines
chopper/lib/src/list_format.dart 0.00% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #592      +/-   ##
===========================================
- Coverage    94.19%   93.52%   -0.68%     
===========================================
  Files           11       12       +1     
  Lines          482      463      -19     
===========================================
- Hits           454      433      -21     
- Misses          28       30       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@techouse techouse marked this pull request as ready for review April 3, 2024 00:53
@techouse techouse changed the title ⚡ use qs for query string encoding ⚡ use qs_dart for query string encoding Apr 3, 2024
@techouse techouse modified the milestones: 7.4.0, 8.0.0 Apr 3, 2024
@techouse
Copy link
Collaborator Author

techouse commented Apr 3, 2024

I propose this goes into v8.0.0 since it slightly changes the way null and empty Strings are encoded.

CC / @JEuler @Guldem @diegotori

@Guldem
Copy link
Contributor

Guldem commented Apr 3, 2024

I propose this goes into v8.0.0 since it slightly changes the way null and empty Strings are encoded.

CC / @JEuler @Guldem @diegotori

Makes sense. Introduce all changes at once 😉

LGTM! Also want someone to have a look at qs_dart?

@techouse
Copy link
Collaborator Author

techouse commented Apr 3, 2024

LGTM! Also want someone to have a look at qs_dart?

Please :) I converted all the tests of qs, however, porting the JavaScript code was quite the ordeal since it's not typed and written in such a legacy way that made me cry a few times 🙈

@Guldem
Copy link
Contributor

Guldem commented Apr 3, 2024

LGTM! Also want someone to have a look at qs_dart?

Please :) I converted all the tests of qs, however, porting the JavaScript code was quite the ordeal since it's not typed and written in such a legacy way that made me cry a few times 🙈

Have looked globally at the code of qs and qs_dart and the tests which seemed alright. Since the test cases cover a lot I'm sure its working or needs small fixes. Great job porting it! ❤️

@diegotori
Copy link
Contributor

@techouse I still need to add the encoding tests that evaluate Strapi query map encoding into this library's tests.

Also, please consider adding an option for indexed bracketed arrays, unless qs_dart already does that by default, then we're already good.

@techouse
Copy link
Collaborator Author

techouse commented Apr 3, 2024

Also, please consider adding an option for indexed bracketed arrays, unless qs_dart already does that by default, then we're already good.

qs_dart indeed defaults to ListFormat.indices, however, Chopper does not.

I'm working on deprecating the current tags (while still keeping them) and adding a new ones which will give the user an option to encode with any of these:

  • brackets (current default)
  • repeat
  • indices
  • comma

@diegotori
Copy link
Contributor

diegotori commented Apr 3, 2024

@techouse I think we should also target this for 7.4.0, so that it's available immediately.

Otherwise, the changes related to the interceptors might get in the way if we have to upgrade to 8.0.0.

Not to mention, it gives you time to deprecate useBrackets and replace it with ListFormat.

@Guldem
Copy link
Contributor

Guldem commented Apr 3, 2024

@techouse I think we should also target this for 7.4.0, so that it's available immediately.
Otherwise, the changes related to the interceptors might get in the way if we have to upgrade to 8.0.0.
Not to mention, it gives you time to deprecate useBrackets and replace it with ListFormat.

Hmm, fair point.

@Guldem are you onboard with this?

That's fine! This is needed, the interceptors not necessarily 😄

chopper/lib/src/utils.dart Outdated Show resolved Hide resolved
@techouse
Copy link
Collaborator Author

techouse commented Apr 4, 2024

I've added a new annotation listFormat

/// List format to use when encoding lists
///
/// - [ListFormat.repeat] (default) hxxp://path/to/script?foo=123&foo=456&foo=789
/// - [ListFormat.brackets] hxxp://path/to/script?foo[]=123&foo[]=456&foo[]=789
/// - [ListFormat.indices] hxxp://path/to/script?foo[0]=123&foo[1]=456&foo[2]=789
/// - [ListFormat.comma] hxxp://path/to/script?foo=123,456,789
final ListFormat? listFormat;

and have deprecated useBrackets

/// Use brackets [ ] to when encoding
///
/// - lists
/// hxxp://path/to/script?foo[]=123&foo[]=456&foo[]=789
///
/// - maps
/// hxxp://path/to/script?user[name]=john&user[surname]=doe&user[age]=21
@Deprecated('Use listFormat instead')
final bool? useBrackets;

In line with the above I have also modified mapToQuery

String mapToQuery(
  Map<String, dynamic> map, {
  ListFormat? listFormat,
  @Deprecated('Use listFormat instead') bool? useBrackets,
  bool? includeNullQueryVars,
}) {
  listFormat ??= useBrackets == true ? ListFormat.brackets : ListFormat.repeat;

  return QS.encode(
    map,
    EncodeOptions(
      listFormat: listFormat,
      allowDots: listFormat == ListFormat.repeat,
      encodeDotInKeys: listFormat == ListFormat.repeat,
      encodeValuesOnly: listFormat == ListFormat.repeat,
      skipNulls: includeNullQueryVars != true,
      strictNullHandling: false,
      serializeDate: (DateTime date) => date.toUtc().toIso8601String(),
    ),
  );
}

and added a tonne of tests 🤪

CC / @JEuler @Guldem @diegotori

@techouse
Copy link
Collaborator Author

techouse commented Apr 4, 2024

@diegotori could you please test this PR in your problematic project/app and report back any findings?

dependency_overrides:
  chopper:
    git:
      url: https://github.com/lejard-h/chopper.git
      ref: fix/issue-584-qs
      path: chopper
  chopper_generator:
    git:
      url: https://github.com/lejard-h/chopper.git
      ref: fix/issue-584-qs
      path: chopper_generator

@diegotori
Copy link
Contributor

dependency_overrides:
  chopper:
    git:
      url: https://github.com/lejard-h/chopper.git
      ref: fix/issue-584-qs
      path: chopper
  chopper_generator:
    git:
      url: https://github.com/lejard-h/chopper.git
      ref: fix/issue-584-qs
      path: chopper_generator

I dunno if I have the capacity to do that currently. However, once you release it and we start the process of migrating to using this new version, if there are any issues, we'll be sure to raise them.

So far, the tests that were added to both this library and qs_dart are reflective of the issues that we ran into when trying to create query strings from maps in chopper.

Since qs_dart is literally a 1:1 port of JavaScript's qs library (which Strapi uses), then I'm highly confident that this new version will work flawlessly.

@techouse
Copy link
Collaborator Author

techouse commented Apr 4, 2024

Since qs_dart is literally a 1:1 port of JavaScript's qs library (which Strapi uses), then I'm highly confident that this new version will work flawlessly.

I tried my best to port everything that could be ported, however, I could not make it a 1:1 port because of some core differences between JavaScript and Dart, i.e. Object prototypes, sparse arrays, etc.

@diegotori
Copy link
Contributor

Since qs_dart is literally a 1:1 port of JavaScript's qs library (which Strapi uses), then I'm highly confident that this new version will work flawlessly.

I tried my best to port everything that could be ported, however, I could not make it a 1:1 port because of some core differences between JavaScript and Dart, i.e. Object prototypes, sparse arrays, etc.

No worries. So far, it's spitting out Strapi-compatible query strings, and that's what really matters for us.

That way, we won't have to do backend hackery to get around the previous limitations.

Standby for another set of tests that you'll need to add so that it covers ones that I found.

Copy link
Contributor

@diegotori diegotori left a comment

Choose a reason for hiding this comment

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

LGTM.

@techouse
Copy link
Collaborator Author

techouse commented Apr 4, 2024

I identified another bug in qs_dart. I forgot to serialize any object by calling .toString() on it.

EDIT: Fixed

@techouse techouse merged commit 84471e4 into develop Apr 4, 2024
4 checks passed
@techouse techouse deleted the fix/issue-584-qs branch April 4, 2024 19:32
@techouse techouse mentioned this pull request Apr 4, 2024
10 tasks
techouse added a commit that referenced this pull request Apr 4, 2024
# chopper

## 7.4.0

- #592

# chopper_generator

## 7.4.0

- #592
@techouse techouse mentioned this pull request Apr 4, 2024
techouse added a commit that referenced this pull request Apr 5, 2024
* 🔖 release v7.4.0

# chopper

## 7.4.0

- #592

# chopper_generator

## 7.4.0

- #592

---------

Signed-off-by: dependabot[bot] <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

QueryMap encoding does not bracket collections of maps or other collections
4 participants