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

[Feature Request] Access properties using Maps (ferry_generator) #577

Open
codakkk opened this issue Feb 4, 2024 · 3 comments
Open

[Feature Request] Access properties using Maps (ferry_generator) #577

codakkk opened this issue Feb 4, 2024 · 3 comments

Comments

@codakkk
Copy link

codakkk commented Feb 4, 2024

Accessing generated code members via a Map should be feasible. For example, in my current project, I'm utilizing 'proto_grid' for table management. This tool enables column filtering using various criteria such as "Greater than", "Contains", "Equals to", etc. Consequently, for each column that is available, I find myself manually scripting a filter like so:

Future<PlutoInfinityScrollRowsResponse> _fetch(
    PlutoInfinityScrollRowsRequest request,
  ) async {
    final filterBuilder = GVehiclesBoolExpBuilder();

    if (request.lastRow != null) {
      filterBuilder.id.G_gt = int.tryParse(request.lastRow?.cells['id']?.value);
    }

    if (request.filterRows.isNotEmpty) {
      final filters = FilterHelper.convertRowsToMap(request.filterRows);

      if (filters.containsKey('name')) {
          filterBuilder.name.G_eq = v[PlutoFilterTypeEquals.name];
          final containsValue = v[PlutoFilterTypeContains.name];
          final startsWithValue = v[PlutoFilterTypeStartsWith.name];
          final endsWithValue = v[PlutoFilterTypeEndsWith.name];
    
          if (containsValue != null) {
            filterBuilder.name.G_ilike = '%$containsValue%';
          } else if (startsWithValue != null) {
            filterBuilder.name.G_ilike = '$startsWithValue%';
          } else if (endsWithValue != null) {
            filterBuilder.name.G_ilike = '%$endsWithValue';
          }
    
          filterBuilder.name.G_gt = v[PlutoFilterTypeGreaterThan.name];
          filterBuilder.name.G_gte = v[PlutoFilterTypeGreaterThanOrEqualTo.name];
          filterBuilder.name.G_lt = v[PlutoFilterTypeLessThan.name];
          filterBuilder.name.G_lte = v[PlutoFilterTypeLessThanOrEqualTo.name];
       }
       
       if(filters.containsKey('companyOwner')) {
           // Same code as before but with filterBuilder.companyOwner
       }
    }
    
    final req = gqlClient.request(GVehicleListReq(
      (b) => b
        ..requestId = 'VehicleListReq'
        ..vars.where = filterBuilder
        ..vars.limit = 20
        ..fetchPolicy = FetchPolicy.NetworkOnly,
    ));

    final result = await req.first;
    final vehicles = result.data?.vehicles ?? BuiltList();

    return PlutoInfinityScrollRowsResponse(
      isLast: vehicles.length < 20,
      rows: vehicles.map(_createRowFromData).toList(),
    );
  }

Hence, the feature request aims to provide a Map<String, TComparisonExpBuilder> or a similar construct (like overriding the [] operator with a set of if-else statements) to enable this kind of access:

      for (final filter in filters) {
        filterBuilder[filter].G_eq = filters[filter][PlutoFilterTypeEquals.name];
        filterBuilder[filter].G_gt = filters[filter][PlutoFilterTypeGreaterThan.name];
        // etc.
      }

I understand this approach might lead to some type errors, but I am willing to consider a dynamic type and perform manual casting, like this:

    for (final filter in filters) {
        filterBuilder.get<GStringComparisonExpBuilder>(filter).G_eq = filters[filter][PlutoFilterTypeEquals.name];
        filterBuilder.get<GStringComparisonExpBuilder>(filter).G_gt = filters[filter][PlutoFilterTypeGreaterThan.name];
        // etc.
      }

An alternative, albeit less elegant, solution I've considered is the following, which aims to avoid manually setting all the fields:

extension GStringComparisonExpBuilderX on GStringComparisonExpBuilder {
  void buildFromMap(List<Map<String, String>>? map) {
    if (map == null) {
      return;
    }
    for (final v in map) {
      G_eq = v[PlutoFilterTypeEquals.name];
      final containsValue = v[PlutoFilterTypeContains.name];
      final startsWithValue = v[PlutoFilterTypeStartsWith.name];
      final endsWithValue = v[PlutoFilterTypeEndsWith.name];

      if (containsValue != null) {
        G_ilike = '%$containsValue%';
      } else if (startsWithValue != null) {
        G_ilike = '$startsWithValue%';
      } else if (endsWithValue != null) {
        G_ilike = '%$endsWithValue';
      }

      G_gt = v[PlutoFilterTypeGreaterThan.name];
      G_gte = v[PlutoFilterTypeGreaterThanOrEqualTo.name];
      G_lt = v[PlutoFilterTypeLessThan.name];
      G_lte = v[PlutoFilterTypeLessThanOrEqualTo.name];
    }
  }
}

However, this method still requires manual verification of each filter's existence, similar to the first example. I think it's possible to better use code generation for those use-cases.
Thank you!

@knaeckeKami
Copy link
Collaborator

knaeckeKami commented Feb 4, 2024

I don't really understand what you are trying to do here.

Specifically:

  • what does your schema/inputs look like?
  • what does ` FilterHelper.convertRowsToMap(request.filterRows);? do?
  • what does v[] do?
  • why does GVehiclesBoolExp.fromJson() not work?
  • would using JsonOperationRequest() help?

@codakkk
Copy link
Author

codakkk commented Feb 6, 2024

  • what does your schema/inputs look like?

The schema is really big and I don't think this is important here.
I'm trying to access to generated code members using [] operator. In my case instead of accessing name like this: filterBuilder.name.G_ilike, access it like this: filterBuilder['name'].G_ilike

what does ` FilterHelper.convertRowsToMap(request.filterRows);? do?

This just converts from ProtoRow to a Map<String, List<Map<String, String>>>. An example result is the following: {all: [{Contains: abc}, {Contains: 123}]}

what does v[] do?

Just access the value in the previous example (for example: abc or 123).

why does GVehiclesBoolExp.fromJson() not work?

Didn't know it even existed, there's no mention on the examples or in the official documentation. Can you provide me an example? Thanks!

would using JsonOperationRequest() help?

Well didn't know about this either. Can you provide an example?

Thank you for your answer, anyway

@knaeckeKami
Copy link
Collaborator

knaeckeKami commented Feb 6, 2024

ferry uses built_value for serialization. All generated request- data and variable classes can be serialized and deserialized from and to json.
see https://pub.dev/packages/built_value

JsonOperationRequest

is just a request that takes a raw graphql document, takes in json variables and returns json.
it is meant as an escape hatch when the generated code is inconvenient.

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

No branches or pull requests

2 participants