<commit_before>part of slack_reminder.server_app;

class SlackOAuthClient {
  static const String SCOPE = 'post';

  final Uri authorizationEndpoint;
  final Uri tokenEndpoint;
  final Uri callbackUrl;
  final Uri authTestUrl;
  final String stateSalt;
  final String clientID;
  final String clientSecret;

  SlackOAuthClient(
      this.authorizationEndpoint,
      this.tokenEndpoint,
      this.callbackUrl,
      this.authTestUrl,
      this.stateSalt,
      this.clientID,
      this.clientSecret
      );

  Uri getAuthRedirectUri(String state) {
    return new Uri.https(this.authorizationEndpoint.authority, this.authorizationEndpoint.path, {
      'client_id': this.clientID,
      'redirect_uri': this.callbackUrl.toString(),
      'scope': SlackOAuthClient.SCOPE,
      'state': state,
    });
  }

  String createState(String original) {
    return Crypt.sha256(original, salt: "${this.stateSalt}:${new DateTime.now().millisecondsSinceEpoch}");
  }

  Future<Map<String, String>> getAccessTokenFromCallbackRequest(Request request) async {
    var completer = new Completer();

    try {
      if (!request.uri.hasQuery) {
        throw new Exception('response has no query');
      }
      Map<String, String> params = request.uri.queryParameters;
      if (!params.containsKey('code') || !params.containsKey('state')) {
        throw new Exception('response params invalid');
      }
      if (!request.session.containsKey('state')) {
        throw new Exception('missing state in session');
      }
      if (request.session['state'] != params['state']) {
        throw new Exception('invalid state');
      }

      //clear state
      request.session.remove('state');

      String code = params['code'];

      Uri tokenUrl = new Uri.https(this.tokenEndpoint.authority, this.tokenEndpoint.path, {
        'client_id': this.clientID,
        'client_secret': this.clientSecret,
        'code': code,
        'redirect_url': this.callbackUrl.toString(),
      });

      //request access token
      http.Response response = await http.get(tokenUrl);

      //parse response
      Map<String, dynamic> data = JSON.decode(response.body);

      if (!data.containsKey('ok') || !data['ok']) {
        throw new Exception('ok is not true');
      }
      if (!data.containsKey('access_token') || !data.containsKey('scope')) {
        throw new Exception('access_token/scope does not exists in response');
      }

      completer.complete({<commit_msg>fix param key url to uri<commit_after>part of slack_reminder.server_app;

class SlackOAuthClient {
  static const String SCOPE = 'post';

  final Uri authorizationEndpoint;
  final Uri tokenEndpoint;
  final Uri callbackUrl;
  final Uri authTestUrl;
  final String stateSalt;
  final String clientID;
  final String clientSecret;

  SlackOAuthClient(
      this.authorizationEndpoint,
      this.tokenEndpoint,
      this.callbackUrl,
      this.authTestUrl,
      this.stateSalt,
      this.clientID,
      this.clientSecret
      );

  Uri getAuthRedirectUri(String state) {
    return new Uri.https(this.authorizationEndpoint.authority, this.authorizationEndpoint.path, {
      'client_id': this.clientID,
      'redirect_uri': this.callbackUrl.toString(),
      'scope': SlackOAuthClient.SCOPE,
      'state': state,
    });
  }

  String createState(String original) {
    return Crypt.sha256(original, salt: "${this.stateSalt}:${new DateTime.now().millisecondsSinceEpoch}");
  }

  Future<Map<String, String>> getAccessTokenFromCallbackRequest(Request request) async {
    var completer = new Completer();

    try {
      if (!request.uri.hasQuery) {
        throw new Exception('response has no query');
      }
      Map<String, String> params = request.uri.queryParameters;
      if (!params.containsKey('code') || !params.containsKey('state')) {
        throw new Exception('response params invalid');
      }
      if (!request.session.containsKey('state')) {
        throw new Exception('missing state in session');
      }
      if (request.session['state'] != params['state']) {
        throw new Exception('invalid state');
      }

      //clear state
      request.session.remove('state');

      String code = params['code'];

      Uri tokenUrl = new Uri.https(this.tokenEndpoint.authority, this.tokenEndpoint.path, {
        'client_id': this.clientID,
        'client_secret': this.clientSecret,
        'code': code,
        'redirect_uri': this.callbackUrl.toString(),
      });

      //request access token
      http.Response response = await http.get(tokenUrl);

      //parse response
      Map<String, dynamic> data = JSON.decode(response.body);

      if (!data.containsKey('ok') || !data['ok']) {
        throw new Exception('ok is not true');
      }
      if (!data.containsKey('access_token') || !data.containsKey('scope')) {
        throw new Exception('access_token/scope does not exists in response');
      }

      completer.complete({
{'fraction_non_alphanumeric': 0.12310184247823446, 'fraction_numerical': 0.0012148208139299453, 'mean_word_length': 3.0826446280991737, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '120004', 'n_tokens_mistral': 1432, 'n_tokens_neox': 1373, 'n_words': 324}