DBIO-GraphQL

 view release on metacpan or  search on metacpan

lib/DBIO/GraphQL.pm  view on Meta::CPAN

    fields => {
      nodes       => { type => GraphQL::Type::List->new(of => $gql_type) },
      total       => { type => $Int     },
      nextCursor  => { type => $String  },
      hasNextPage => { type => $Boolean },
    },
  );
}

# _encode_cursor / _decode_cursor
#
# Cursors are base64(val1:val2:...), stateless, no extra dependencies.
# Colons inside values are percent-encoded so the separator is unambiguous.
sub _encode_cursor {
  my (@vals) = @_;
  my $raw = join ':', map { (my $v = $_) =~ s/%/%25/g; $v =~ s/:/%3A/g; $v } @vals;
  return encode_base64($raw, '');   # no newline
}

sub _decode_cursor {
  my ($cursor) = @_;
  my $raw  = decode_base64($cursor);
  my @vals = map { (my $v = $_) =~ s/%3A/:/gi; $v =~ s/%25/%/g; $v }
             split /:/, $raw;
  return @vals;
}

# _apply_pagination($rs, $args, $source)
#
# Applies ordering, then either offset (page) or cursor (cursor) pagination.
# Returns ($nodes, $next_cursor, $has_next).
#
# Precedence: cursor pagination wins if both are supplied.
sub _apply_pagination {
  my ($rs, $args, $source) = @_;

  my @pk_cols = $source->primary_columns;

  my $order_by = $args->{orderBy};
  if ($order_by && $order_by->{field}) {
    my $field = $order_by->{field};
    unless ($source->has_column($field)) {
      die "DBIO::GraphQL: unknown order column '$field' on source '"
        . $source->source_name . "'\n";
    }
    my $dir = lc($order_by->{direction} // 'ASC');
    $rs = $rs->search(undef, {
      order_by => { "-$dir" => $field }
    });
  }
  else {
    # Default: stable order by PK
    $rs = $rs->search(undef, { order_by => [ map { { -asc => $_ } } @pk_cols ] });
  }

  if (my $cursor_args = $args->{cursor}) {
    my $first = $cursor_args->{first} // 10;
    my $after = $cursor_args->{after};

    if ($after) {
      my @decoded = _decode_cursor($after);
      if (@pk_cols == 1) {
        $rs = $rs->search({ $pk_cols[0] => { '>' => $decoded[0] } });
      }
      else {
        my %after_cond = map { $pk_cols[$_] => { '>' => $decoded[$_] } }
                             0 .. $#pk_cols;
        $rs = $rs->search(\%after_cond);
      }
    }

    my @rows    = $rs->search(undef, { rows => $first + 1 })->all;
    my $has_next = @rows > $first;
    @rows = @rows[0 .. $first - 1] if $has_next;

    my $next_cursor;
    if ($has_next && @rows) {
      my $last_row = $rows[-1];
      $next_cursor = _encode_cursor(map { $last_row->get_column($_) } @pk_cols);
    }

    return (
      [ map { { $_->get_columns } } @rows ],
      $next_cursor,
      $has_next ? 1 : 0,
    );
  }

  if (my $page_args = $args->{page}) {
    my $skip = $page_args->{skip} // 0;
    my $take = $page_args->{take} // 10;
    my @rows = $rs->search(undef, { rows => $take, offset => $skip })->all;
    return (
      [ map { { $_->get_columns } } @rows ],
      undef,
      0,
    );
  }

  my @rows = $rs->all;
  return (
    [ map { { $_->get_columns } } @rows ],
    undef,
    0,
  );
}


1;

__END__

=pod

=encoding UTF-8

=head1 NAME

DBIO::GraphQL - Auto-generate a GraphQL schema from a DBIO schema

=head1 VERSION

version 0.900001

=head1 SYNOPSIS



( run in 0.583 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )