restqiita (qiita_repository.dart)


import 'dart:convert';

import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

import 'models/item.dart';
import 'models/tag.dart';
import 'models/user.dart';

class QiitaRepository {
  final clientID = 'XXX'; // 登録したアプリケーションの ClientID を設定する
  final clientSecret = 'YYY'; // 登録したアプリケーションの ClientSecret を設定する
  final keyAccessToken = 'qiita/accessToken';

  String createAuthorizeUrl(String state) {
    final scope = 'read_qiita';
    return 'https://qiita.com/api/v2/oauth/authorize?client_id=$clientID&scope=$scope&state=$state';
  }

  Future<String> createAccessTokenFromCallbackUri(
      Uri uri,
      String expectedState,
      ) async {
    final String? state = uri.queryParameters['state'];
    final String? code = uri.queryParameters['code'];
    if (expectedState != state) {
      throw Exception('The state is different from expectedState.');
    }

    final response = await http.post(
      Uri.parse('https://qiita.com/api/v2/access_tokens'),
      headers: {
        'content-type': 'application/json',
      },
      body: jsonEncode({
        'client_id': clientID,
        'client_secret': clientSecret,
        'code': code,
      }),
    );
    final body = jsonDecode(response.body);
    final accessToken = body['token'];

    return accessToken;
  }

  Future<void> revokeSavedAccessToken() async {
    final accessToken = await getAccessToken();
    final response = await http.delete(
      Uri.parse('https://qiita.com/api/v2/access_tokens/$accessToken'),
      headers: {
        'Authorization': 'Bearer $accessToken',
      },
    );

    if (response.statusCode != 204) {
      throw Exception('Failed to revoke the access token');
    }
  }

  Future<void> saveAccessToken(String accessToken) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.setString(keyAccessToken, accessToken);
  }

  Future<String?> getAccessToken() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    return prefs.getString(keyAccessToken);
  }

  Future<void> deleteAccessToken() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.remove(keyAccessToken);
  }

  Future<bool> accessTokenIsSaved() async {
    final accessToken = await getAccessToken();
    return accessToken != null;
  }

  Future<List<Item>> getItemList({int page = 1, QiitaItemsQuery? query}) async {
    final accessToken = await getAccessToken();
    String url = 'https://qiita.com/api/v2/items?page=$page';
    if (query != null) {
      url += '&query=${query.buildString()}';
    }

    final response = await http.get(
      Uri.parse(url),
      headers: {
        'Authorization': 'Bearer $accessToken',
      },
    );
    final body = jsonDecode(response.body);
    final itemList = (body as List<dynamic>).map((item) {
      return Item(
        id: item['id'],
        title: item['title'],
        renderedBody: item['rendered_body'],
        likesCount: item['likes_count'],
        createdAt: DateTime.parse(item['created_at']),
        tags: (item['tags'] as List<dynamic>).map((tag) {
          return Tag(
            name: tag['name'],
            versions: (tag['versions'] as List<dynamic>)
                .map((v) => v as String)
                .toList(),
          );
        }).toList(),
        user: _mapToUser(item['user']),
      );
    }).toList();

    return itemList;
  }

  Future<User> getAuthenticatedUser() async {
    final accessToken = await getAccessToken();
    final response = await http.get(
      Uri.parse('https://qiita.com/api/v2/authenticated_user'),
      headers: {
        'Authorization': 'Bearer $accessToken',
      },
    );
    final body = jsonDecode(response.body);
    final user = _mapToUser(body);

    return user;
  }

  User _mapToUser(Map<String, dynamic> map) {
    return User(
      id: map['id'],
      name: map['name'],
      description: map['description'],
      profileImageUrl: map['profile_image_url'],
      itemsCount: map['items_count'],
      followersCount: map['followers_count'],
    );
  }
}

class QiitaItemsQuery {
  String? userID;

  QiitaItemsQuery userIdEquals(String id) {
    userID = id;
    return this;
  }

  String buildString() {
    List<String> queries = [];

    if (userID != null) {
      queries.add('user:$userID');
    }

    return queries.join(' ');
  }
}