GithubHelp home page GithubHelp logo

Comments (13)

SandroMaglione avatar SandroMaglione commented on September 28, 2024 1

Hi @Biplab-Dutta

Some suggestions on your implementation.

If getHeader can fail then remember to wrap it in TaskEither, otherwise use Task instead of a Future, something like the following:

final cachedHeader = await _(Task(() async => _headerCache.getHeader(fullPathToMarkdownFile)).toTaskEither());

When you use the .Do constructor you can get and use response like the following:

final response = await _(TaskEither<Failure, Response<String>>.tryCatch(
  () => _dio.getUri<String>(
    requestUri,
    options: Options(
      headers: <String, String>{
        'If-None-Match': cachedHeader?.eTag ?? '',
      },
    ),
  ),
  (error, stackTrace) => ApiFailure(
    'Error on network request',
    errorObject: error,
    stackTrace: stackTrace,
  ),
));

You can then expand the logic without using map or flatMap:

return TaskEither.Do(
  (_) async {
    final requestUri =
        await _(_uriParser(fullPathToMarkdownFile).toTaskEither());

    final cachedHeader = await _headerCache.getHeader(fullPathToMarkdownFile);

    final response = await _(TaskEither<Failure, Response<String>>.tryCatch(
      () => _dio.getUri<String>(
        requestUri,
        options: Options(
          headers: <String, String>{
            'If-None-Match': cachedHeader?.eTag ?? '',
          },
        ),
      ),
      (error, stackTrace) => ApiFailure(
        'Error on network request',
        errorObject: error,
        stackTrace: stackTrace,
      ),
    ));

    if (response.statusCode == 304) {
      // ...
    } else if (response.statusCode == 200) {
      // You can run async code here using other TaskEither(s)
    } else {
      // ...
    }
  },
);

Let me know if this is clear, feel free to ask other questions otherwise

from fpdart.

Biplab-Dutta avatar Biplab-Dutta commented on September 28, 2024 1

Closing the issue as I have successfully migrated my project to fpdart 🥳
Thank you very much @SandroMaglione

from fpdart.

Biplab-Dutta avatar Biplab-Dutta commented on September 28, 2024

Thanks for responding. I will do as you have suggested in the evening. If everything works, I'm gonna close this issue. If I happen to encounter any issue, I shall post my question here again. Until then let's have this issue opened.
Thanks.

from fpdart.

Biplab-Dutta avatar Biplab-Dutta commented on September 28, 2024

Hi @SandroMaglione. I've a question.

TaskEither<Failure, RemoteResponse<String>> getDetail(
  int id,
  String fullPathToMarkdownFile,
) {
  return TaskEither.Do(
    (_) async {
      final requestUri =
          await _(_uriParser(fullPathToMarkdownFile).toTaskEither());

      final cachedHeader = await _(
        Task(
          () => _headerCache.getHeader(fullPathToMarkdownFile),
        ).toTaskEither(),
      );

      final response = await _(
        TaskEither<Failure, Response<String>>.tryCatch(
          () => _dio.getUri<String>(
            requestUri,
            options: Options(
              headers: <String, String>{
                'If-None-Match': cachedHeader?.eTag ?? '',
              },
            ),
          ),
          (e, stackTrace) {
            if (e is DioException && e.isNoConnectionError) {
              // How do I return RemoteResponse type object as this is my requirement?
            }
            return ApiFailure(
              'Error on network request',
              errorObject: e,
              stackTrace: stackTrace,
            );
          },
        ),
      );

      if (response.statusCode == 200) {
        final header = GithubHeader.parse(
          id,
          response,
          fullPathToMarkdownFile,
        );

        await _(Task(() => _headerCache.saveHeader(header)).toTaskEither());
        final html = response.data!;
        return RemoteResponse.withNewData(html);
      }
      return const RemoteResponse.notModified();
    },
  );
}

The question has been commented down in the code. I couldn't figure out a way of doing it. Any help shall be appreciated. Thanks.

from fpdart.

SandroMaglione avatar SandroMaglione commented on September 28, 2024

In this case you should use mapLeft to change the error type of TaskEither:

final response = await _(
  TaskEither<Failure, Response<String>>.tryCatch(
    () => _dio.getUri<String>(
      requestUri,
      options: Options(
        headers: <String, String>{
          'If-None-Match': cachedHeader?.eTag ?? '',
        },
      ),
    ),
    (e, stackTrace) {
      return ApiFailure(
        'Error on network request',
        errorObject: e,
        stackTrace: stackTrace,
      );
    },
  ).mapLeft((apiFailure) {
    if (apiFailure.errorObject is DioException && apiFailure.errorObject.isNoConnectionError) {
      /// return [RemoteResponse]
    } else {
      return apiFailure;
    }
  }),
);

from fpdart.

Biplab-Dutta avatar Biplab-Dutta commented on September 28, 2024

Unfortunately, it didn't work. Maybe looking at the entire source code for this file might help.

typedef _RemoteMarkdown = TaskEither<Failure, RemoteResponse<String>>;

abstract class DetailRemoteService {
  DetailRemoteService({
    Dio? dio,
    HeaderCache? headerCache,
  })  : _dio = dio ?? Dio(),
        _headerCache = headerCache ?? GithubHeaderCache();

  final Dio _dio;
  final HeaderCache _headerCache;

  @protected
  _RemoteMarkdown getDetail(
    int id,
    String fullPathToMarkdownFile,
  ) {
    return TaskEither.Do(
      (_) async {
        final requestUri =
            await _(_uriParser(fullPathToMarkdownFile).toTaskEither());

        final cachedHeader = await _(
          Task(
            () => _headerCache.getHeader(fullPathToMarkdownFile),
          ).toTaskEither(),
        );

        final response = await _(
          TaskEither<Failure, Response<String>>.tryCatch(
            () => _dio.getUri<String>(
              requestUri,
              options: Options(
                headers: <String, String>{
                  'If-None-Match': cachedHeader?.eTag ?? '',
                },
              ),
            ),
            (e, stackTrace) {
              return ApiFailure(
                'Error on network request',
                errorObject: e,
                stackTrace: stackTrace,
              );
            },
          ).mapLeft(
            (failure) {
              final error = failure.errorObject;
              if (error is DioException && error.isNoConnectionError) {
                return const RemoteResponse.noConnection(); // The return type 'RemoteResponse<dynamic>' isn't a 'Failure', as required by the closure's context.
              }
              return failure;
            },
          ),
        );

        if (response.statusCode == 200) {
          final header = GithubHeader.parse(
            id,
            response,
            fullPathToMarkdownFile,
          );

          await _(Task(() => _headerCache.saveHeader(header)).toTaskEither());
          final html = response.data!;
          return RemoteResponse.withNewData(html);
        }
        return const RemoteResponse.notModified();
      },
    );
  }
}

IOEither<Failure, Uri> _uriParser(String uri) {
  return IOEither.tryCatch(
    () => Uri.parse(uri),
    (e, stackTrace) => UriParserFailure(
      'Invalid Uri string',
      errorObject: e,
      stackTrace: stackTrace,
    ),
  );
}

I have commented the error message that I am receiving.

from fpdart.

SandroMaglione avatar SandroMaglione commented on September 28, 2024

That's because _RemoteMarkdown is defined as TaskEither<Failure, Response<String>>, therefore the context (i.e. the .Do constructor) expects the error type to be of type Failure.

The error is saying that RemoteResponse is not of type Failure, so it is not valid as an error type. How is RemoteResponse defined? Is it an instance of Failure?

from fpdart.

Biplab-Dutta avatar Biplab-Dutta commented on September 28, 2024

RemoteResponse is a union type. It doesn't extend Failure and I don't want to as well in this case. In the old imperative code, I had this logic where if DioException is caught and if it's no connection error, I would return RemoteResponse.noConnection() instead of throwing. To have a look at my old imperative code, check this out.
https://github.com/Biplab-Dutta/now-in-dart-flutter/blob/master/lib/features/detail/core/data/detail_remote_service.dart

You would want to check this repository too (imperative one) to understand what I am trying to achieve here.
https://github.com/Biplab-Dutta/now-in-dart-flutter/blob/master/lib/features/detail/dart_detail/data/dart_detail_repository.dart

from fpdart.

SandroMaglione avatar SandroMaglione commented on September 28, 2024

Got it. You are trying to recover from an error. In this case you should use orElse:

final response = await _(
  TaskEither<Failure, Response<String>>.tryCatch(
    () => _dio.getUri<String>(
      requestUri,
      options: Options(
        headers: <String, String>{
          'If-None-Match': cachedHeader?.eTag ?? '',
        },
      ),
    ),
    (e, stackTrace) {
      return ApiFailure(
        'Error on network request',
        errorObject: e,
        stackTrace: stackTrace,
      );
    },
  ).orElse(
    (failure) {
      final error = failure.errorObject;
      if (error is DioException && error.isNoConnectionError) {
        return TaskEither.right(const RemoteResponse.noConnection()); 
      }
      
      return TaskEither.left(failure);
    },
  ),
);

Using orElse you get the ApiFailure (failure) and you can return a right value (RemoteResponse) or otherwise return again the Failure.

from fpdart.

Biplab-Dutta avatar Biplab-Dutta commented on September 28, 2024

@SandroMaglione Unfortunately, it doesn't seem to work.

TaskEither<Failure, RemoteResponse<String>> getDetail(
  int id,
  String fullPathToMarkdownFile,
) {
  return TaskEither.Do(   // Here, the right is RemoteResponse<String>
    (_) async {
      final requestUri =
          await _(_uriParser(fullPathToMarkdownFile).toTaskEither());

      final cachedHeader = await _(
        Task(
          () => _headerCache.getHeader(fullPathToMarkdownFile),
        ).toTaskEither(),
      );

      final response = await _(
        TaskEither<Failure, Response<String>>.tryCatch(  // Here, the right is Response<String>
          () => _dio.getUri<String>(
            requestUri,
            options: Options(
              headers: <String, String>{
                'If-None-Match': cachedHeader?.eTag ?? '',
              },
            ),
          ),
          (e, stackTrace) {
            return ApiFailure(
              'Error on network request',
              errorObject: e,
              stackTrace: stackTrace,
            );
          },
        ).orElse(
          (failure) {
            final error = failure.errorObject;
            if (error is DioException && error.isNoConnectionError) {
              return TaskEither.of(const RemoteResponse.noConnection());  // The argument type 'RemoteResponse<dynamic>' can't be assigned to the parameter type 'Response<String>'.
            }
            return TaskEither.left(failure);
          },
        ),
      );

      if (response.statusCode == 200) {
        final header = GithubHeader.parse(
          id,
          response,
          fullPathToMarkdownFile,
        );

        await _(Task(() => _headerCache.saveHeader(header)).toTaskEither());
        final html = response.data!;
        return RemoteResponse.withNewData(html);
      }
      return const RemoteResponse.notModified();
    },
  );
}

My method has the return type of TaskEither<Failure, RemoteResponse<String>>. Then the inner TaskEither.tryCatch has the return type of TaskEither<Failure, Response<String>>.

RemoteResponse<T> is a union type (by freezed).
Response<T> is an object from dio package.

Maybe you got confused there by similar object names. Sorry if that was the case.

from fpdart.

SandroMaglione avatar SandroMaglione commented on September 28, 2024

Inside a .Do constructor the left and right types returned by calling _ must always be the same, returning Response<string> won't work.

In this case you need to use flatMap first to convert Response to RemoteResponse, and then call orElse:

final response = await _(
  TaskEither<Failure, Response<String>>.tryCatch(
    () => _dio.getUri<String>(
      requestUri,
      options: Options(
        headers: <String, String>{
          'If-None-Match': cachedHeader?.eTag ?? '',
        },
      ),
    ),
    (e, stackTrace) {
      return ApiFailure(
        'Error on network request',
        errorObject: e,
        stackTrace: stackTrace,
      );
    },
  ).flatMap((response) {
    return TaskEither<Failure, RemoteResponse<String>>(() async {
      if (response.statusCode == 200) {
        final header = GithubHeader.parse(
          id,
          response,
          fullPathToMarkdownFile,
        );

        await _headerCache.saveHeader(header);
        
        final html = response.data!;
        return RemoteResponse.withNewData(html);
      }

      return const RemoteResponse.notModified();
    });
  }).orElse(
    (failure) {
      final error = failure.errorObject;
      if (error is DioException && error.isNoConnectionError) {
        return TaskEither.of(const RemoteResponse.noConnection());
      }
      return TaskEither.left(failure);
    },
  ),
);

from fpdart.

Biplab-Dutta avatar Biplab-Dutta commented on September 28, 2024

Thanks. Using flatMap was the right call here. I made small changes to your last suggestion and I am not getting any compile time errors.

_RemoteMarkdown getDetail(
  int id,
  String fullPathToMarkdownFile,
) {
  return TaskEither.Do(
    (_) async {
      final requestUri =
          await _(_uriParser(fullPathToMarkdownFile).toTaskEither());

      final cachedHeader = await _(
        Task(
          () => _headerCache.getHeader(fullPathToMarkdownFile),
        ).toTaskEither(),
      );

      return _(
        TaskEither<Failure, Response<String>>.tryCatch(
          () => _dio.getUri<String>(
            requestUri,
            options: Options(
              headers: <String, String>{
                'If-None-Match': cachedHeader?.eTag ?? '',
              },
            ),
          ),
          (e, stackTrace) {
            return ApiFailure(
              'Error on network request',
              errorObject: e,
              stackTrace: stackTrace,
            );
          },
        ).flatMap(
          (response) {
            return TaskEither<Failure, RemoteResponse<String>>(
              () async {
                if (response.statusCode == 200) {
                  final header = GithubHeader.parse(
                    id,
                    response,
                    fullPathToMarkdownFile,
                  );

                  await _(
                    Task(
                      () => _headerCache.saveHeader(header),
                    ).toTaskEither(),
                  );

                  final html = response.data!;
                  return right(RemoteResponse.withNewData(html));
                }

                return right(const RemoteResponse.notModified());
              },
            );
          },
        ).orElse(
          (failure) {
            final error = failure.errorObject;
            if (error is DioException && error.isNoConnectionError) {
              return TaskEither.right(const RemoteResponse.noConnection());
            }
            return TaskEither.left(failure);
          },
        ),
      );
    },
  );
}

from fpdart.

Biplab-Dutta avatar Biplab-Dutta commented on September 28, 2024

Is it okay if I keep this issue opened until I am done migrating my entire project to fpdart? I shall close it once I am done with migration.

from fpdart.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.