Статьи

Создание собственных мобильных приложений на базе облачных технологий с помощью Parse и Codename One

Пару недель назад я создал простое приложение для социальных сетей в рамках вебинара Codename One . Это приложение предоставило функциональность, аналогичную приложению Facebook.

  1. Отправить и принять запросы в друзья
  2. Опубликовать новости
  3. Просмотр новостей, опубликованных вами и вашими друзьями

Для сервера я использовал REST-интерфейс PHP / MySQL и написал очень тонкий клиент в Codename One. И сервер, и клиентский проект находятся на GitHub, поэтому вы можете просмотреть его и установить самостоятельно на свой сервер.

Некоторые скриншоты приложения, работающего в симуляторе Codename One:

социально-приложение-скриншоты

Я решил портировать это приложение на Parse.com в качестве подтверждения концепции.

Вот 3-минутный скринкаст приложения от Parse.com:

Что такое Parse.com?

Parse.com похож на серверную часть в коробке. Вы получаете легко масштабируемый REST-сервер и базу данных NoSQL без необходимости управлять им самостоятельно. Это позволяет вам сосредоточиться на одном клиентском приложении Codename — сделать его красивым и красивым. Не беспокойтесь о масштабировании, исправлениях серверного программного обеспечения или любом другом шуме. Просто приложение.

Мне особенно нравится тот факт, что Parse.com бесплатен для приложений, которые получают небольшой объем трафика (до 30 запросов в секунду). По мере роста вашего приложения вы просто платите за добавленную пропускную способность.

С высоты птичьего полета процесс портирования

Я начал с полнофункционального приложения. Единственной частью приложения Codename One, которое требовало изменений, был класс SocialClient , в котором происходило все взаимодействие с сервером.

На стороне сервера процесс был примерно:

  1. Зарегистрировать аккаунт на Parse.com
  2. Создать новый идентификатор приложения
  3. Настройте мою модель данных
  4. Реализуйте тонкий REST-интерфейс для моих данных, используя облачные функции Parse.

Клиентский API

Полный API для этого класса выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Registers new user
public void register(String username, String password) throws IOException;
 
// Logs in as user
public void login(String username, String password) throws IOException;
 
// Logs current user out
public void logout() throws IOException;
 
// Gets list of friends of current user
public List<Map> getFriends() throws IOException;
 
// Finds users with username matching query string
public List<Map> findUsers(String query) throws IOException;
 
// Finds pending friend requests for current user
public List<Map> getPendingFriendRequests() throws IOException;
 
// Sends a friend request to given user
public void sendFriendRequest(String username) throws IOException;
 
// Accepts a friend request from a given user
public void acceptFriendRequest(String username) throws IOException;
 
// Decline a friend request from a given user
public void declineFriendRequest(String username) throws IOException;
 
// Gets the profile of a given user
public Map getProfile(String username) throws IOException;
 
// Updates the profile of a given user with specified values
public void updateProfile(Map profile) throws IOException;
 
// Posts a news item
public long post(Map post) throws IOException;
 
// Gets the news feed for the given user
public List<Map> getFeed(Date olderThan) throws IOException;
 
// Gets the username of the current user
public String getUsername();

Для взаимодействия с серверной частью PHP / MySQL этот API был реализован непосредственно поверх ConnectionRequest и NetworkManager для отправки запросов HTTP GET и POST непосредственно на сервер. JSON использовался для передачи ответа от сервера клиенту, и он был преобразован в `Map`s и` List`s.

Примечание: вы заметите, что этот API не использует строгую типизацию Java … Я просто использую списки и карты. Это было для гибкости, пока я дорабатывал API. В какой-то момент в реальном приложении я, вероятно, реорганизовал бы использование некоторых пользовательских типов Java.

Для бэк-энда Parse мы могли бы просто написать тонкий REST-клиент поверх ConnectionRequest , но есть более простой способ, благодаря Chidiebere Okwudire parse4cn1 cn1lib, который оборачивает Parse REST API, предоставляя Java-API, очень похожий на официальный Parse Java API .

Создание приложения Parse

  1. Войти в Parse.com
  2. Создать новое приложение

Создание модели данных

После того, как ваше приложение было создано, вам нужно создать модель данных. В Parse.com это делается путем определения набора классов. Это аналогично созданию таблиц в базе данных SQL. Чтобы мотивировать это упражнение, давайте взглянем на схему базы данных MySQL в предыдущей версии. Ниже приведен PHP (со встроенным кодом SQL), который использовался для создания базы данных:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
class conf_Installer {
  function update_1() {
    $q[] = "create table users (
      username varchar(32) primary key not null,
      password varchar(64)
      )";
    $q[] = "create table sessions (
      username varchar(32),
      token_id varchar(64) primary key not null,
      expires INT(11)
      )";
 
    $q[] = "create table friends (
      user1 varchar(32),
      user2 varchar(32),
      primary key (user1, user2 )
      )";
 
    $q[] = "create table friend_requests (
      sender varchar(32),
      receiver varchar(32),
      primary key (sender, receiver)
      )";
 
    $q[] = "create table profiles (
      username varchar(32) primary key not null,
      screen_name varchar(100),
      avatar varchar(100),
      avatar_mimetype varchar(100)
      )";
 
    $q[] = "create table posts (
      post_id int(11) not null auto_increment primary key,
      username varchar(32),
      date_posted INT(11),
      photo varchar(255),
      photo_mimetype varchar(100),
      comment text
      )";
 
    df_q($q);
  }
}
?>

Для модели данных Parse мой первый инстинкт был просто создать класс для каждой таблицы. Однако я обнаружил, что сопоставление «один к одному» не было идеальным. Частично это связано с тем, что Parse уже предоставляет некоторые функциональные возможности из коробки, а также то, что база данных Parse не реляционная, как MySQL.

Примечание: Parse поддерживает отношения, но они работают немного иначе, чем в реляционной базе данных, такой как MySQL. Мы рассмотрим различия более подробно позже.

В итоге я остановился на следующих классах для своего приложения:

  1. User — для учетных записей пользователей. Я свел profile таблицы profile в этот отдельный класс, чтобы упростить модель.
  2. Post — для новостей, публикуемых пользователями.

Шаг за шагом: создание модели данных

  1. В разделе «Данные» вкладки «Core» нажмите «Добавить класс»:

    c0310d16-4d66-11e5-8e21-509e44e4f3bb

  2. Выберите «Пользователь» в диалоговом окне и нажмите «Создать класс»:

    d83039dc-4d66-11e5-8c00-7d19b3ba56e3

  3. Создать столбец с именем экрана . Нажмите кнопку «+ Col» в верхнем меню.

    e530ac48-4d66-11e5-8e78-eb23f5ac216a

    Затем выберите type = «String» и name = «screen_name»:

    fa79f3e8-4d66-11e5-8d80-f76b0bd0ca10

  4. Создайте колонку аватара типа «Файл»:

    0d569200-4d67-11e5-89dc-4cb5397bfaf8

  5. Добавьте отношения «друзья» и «pendingFriendRequests». , Добавьте их как столбцы типа «Отношение»:

    1f6f2ff6-4d67-11e5-94ef-99c25a29fb5e

Создание класса «Пост»

  1. Создайте новый класс с именем «Post»:

    4bad51a6-4d67-11e5-982d-171387d2cb83

  2. Добавьте колонку «Комментарий».

    59b01482-4d67-11e5-9f3e-5597adb48fc4

  3. Добавьте столбец «фото» с типом «Файл»:

    6d7ea848-4d67-11e5-875b-e041168d5671

  4. Добавьте столбец «postsBy» как тип «Указатель» к классу «_User»:

    7fb2cc74-4d67-11e5-9fb7-0a439670a154

Загрузка файлов

Я также закончил тем, что создал класс Upload но это было чисто для того, чтобы помочь с загрузкой файлов, и не является частью концептуального дизайна.

Разве мы не пропустили много столов? !!

Как мы сократили базу данных с 6 таблиц до 2 классов? Что ж:

  1. Таблица sessions больше не нужна в Parse, потому что Parse заботится обо всех аспектах регистрации пользователей, входа в систему и управления сеансами.
  2. Как упоминалось ранее, я просто сложил данные profiles непосредственно в класс User . Это было сделано для простоты и для минимизации количества запросов данных для получения данных профиля.
  3. Таблицы friends и friend_requests были объединенными таблицами, предназначенными для связи пользователей друг с другом. В Parse мы обрабатываем это, добавляя столбец типа Relation к нашим классам. Например, класс User имеет отношение friends отношение pendingFriendRequests которые обеспечивают эквивалентную функциональность для таблиц friends и friend_requests .

связи

Как я упоминал выше, отношения обрабатываются немного иначе в Parse, чем в реляционной базе данных. Parse предоставляет два типа столбцов для «указания» на другие записи в базе данных:

  1. Указатель — тип, который вы можете использовать для добавления ссылки на отдельную запись в этом столбце. Например, если вы хотите отследить, был ли пользователь родительским для другого пользователя, вы можете добавить столбец с именем «parent» в класс «User» с типом «Pointer».
  2. Отношение — тип, используемый для хранения ссылок на несколько записей в этом столбце. Например, если вы хотите отслеживать все дочерние элементы пользователя, вы можете добавить в класс «Пользователь» столбец с именем «children» с типом «Relation».

В нашей модели данных нам нужно было отследить две взаимосвязи между записями пользователей:

  1. Будь они друзьями
  2. Есть ли ожидающий запрос на дружбу от одного к другому.

Поэтому я добавил столбцы с именами «friends» и «pendingFriendRequests» в класс «User», оба типа «Relation».

Важно: отношения только односторонние. Например, если вы добавите «Стив» в отношение друзей «Дуг», то это автоматически не добавит «Дуг» в отношение друзей «Стив». Для отношений «pendingFriendRequests» это то, что мы хотим в любом случае, но для отношений «друзья» мы хотели, чтобы они были двусторонними, поэтому нам нужно добавить Стива к Дугу и Дуга к Стиву, когда они становятся друзьями.

Доступ к базе данных из кодового имени один

Теперь, когда наша база данных настроена, давайте попробуем подключиться к ней из нашего приложения Codename one.

Установка библиотеки parse4cn1

Первое, что нам нужно сделать, это загрузить и установить библиотеку parse4cn1 . Вы можете скачать его здесь .

Скопируйте файл parse4cn1.cn1lib в каталог lib вашего проекта, затем выберите «Обновить» библиотеки (т. parse4cn1.cn1lib Щелкните правой кнопкой мыши проект> «Codename One»> «Обновить Libs»).

Вам также необходимо установить библиотеку CN1JSON, от которой зависит библиотека parse4cn1.

Инициализация API Parse4CN1

Прежде чем делать что-либо еще, нам нужно инициализировать Parse API, вызвав Parse.initialize() . Я помещаю этот метод в конструктор для моего клиентского класса:

1
2
3
public SocialClientParse() {
    Parse.initialize("<APP ID>", "<CLIENT KEY>");
}

Идентификатор приложения и ключ клиента можно найти на вкладке «Ключи» при входе в Parse.com.

Предупреждение. Обязательно используйте «Ключ клиента», а не «Ключ API REST» или «Мастер-ключ» при подключении к Parse с клиентского устройства, как, скорее всего, в случае приложений Codename One. Ключ REST API и мастер-ключ предоставляют полные разрешения для вашей базы данных и не должны быть встроены в ваше приложение из соображений безопасности. Эти ключи предназначены для использования в безопасных настройках, таких как серверное приложение, которое подключается к вашему приложению для разбора.

Вход в систему

Реализация метода входа в систему для нашего REST-клиента заключается в следующем.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
ParseUser user;
String token;
...
 
public void login(String username, String password) throws IOException {
    try {
        user = ParseUser.create(username, password);
        user.login();
        token = user.getSessionToken();
    } catch (ParseException ex) {
        Log.e(ex);
        throw new IOException(ex.getMessage());
    }
 
}

и выход из системы:

01
02
03
04
05
06
07
08
09
10
public void logout() throws IOException {
    try {
        user.logout();
        user = null;
        token = null;
    } catch (ParseException ex) {
        Log.e(ex);
        throw new IOException(ex.getMessage());
    }
}

Регистрация пользователя

Регистрация пользователя очень похожа на вход в систему. Он просто использует метод ParseUser.signUp() вместо ParseUser.login() .

01
02
03
04
05
06
07
08
09
10
11
12
public void register(String username, String password) throws IOException {
    try {
        ParseUser user = ParseUser.create(username, password);
        user.put("screen_name", username);
        user.signUp();
 
 
    } catch (ParseException ex) {
        Log.e(ex);
        throw new IOException(ex.getMessage());
    }
}

Остальная часть REST API

Parse API обеспечивает поддержку CRUD (Create-Read-Update-Delete) непосредственно от клиента к источнику данных. Для безопасности он поддерживает ACL как на уровне класса, так и на уровне объекта. Поэтому, если вы правильно настроите свои ACL, вы можете взаимодействовать с базой данных непосредственно из клиентского приложения Codename one. Вы можете увидеть примеры API на вики parse4cn1 .

Вот несколько причин, почему вы не должны делать это

  1. Предоставление клиенту прямого доступа к базе данных делает приложение очень трудным для защиты . Любой серверный инженер, достойный похвалы, знает, что ВЫ НЕ МОЖЕТЕ ДОВЕРЯТЬ КЛИЕНТУ Если вы хотите, чтобы действия были доступны для некоторых пользователей, но не для других — и они используют одно и то же клиентское приложение, вам нужно быть очень осторожным с ACL, которые вы используете в своей базе данных.
  2. Для некоторых операций может потребоваться несколько запросов к базе данных, что может замедлить работу приложения. Лучше просто отправить один запрос серверу разбора и позволить серверному коду обрабатывать несколько запросов.

Облачный код

Parse позволяет вам реализовывать серверные веб-сервисы REST, известные как «облачный код». Поскольку этот код выполняется на стороне сервера, вы можете разрешить им работать с главным ключом, поэтому вам не нужно полагаться на ACL для ограничения доступа к записям и классам. Вы можете использовать свою собственную логику, чтобы решить, кто что может делать. Эта модель более точно соответствует вашему собственному серверу и обеспечивает больший контроль. И это позволяет вам заблокировать вашу базу данных, чтобы вам не нужно было предоставлять прямой доступ клиентам.

В облачном коде используется API Parse Javascript, который эквивалентен API REST и Java. Чтобы использовать их, вам нужно установить инструменты командной строки parse .

Создание проекта Local Cloud Code

Он позволяет вам создавать локальную версию приложения Parse, поскольку это касается разработки облачного кода. Вот вывод команды parse new для настройки проекта локальной разработки:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Steves-iMac:social-network-parse shannah$ parse new
Please log in to Parse using your email and password.
Email: myemail@example.com
Password (will be hidden):
Would you like to create a new app, or add Cloud Code to an existing app?
Type "(n)ew" or "(e)xisting": e
1:  Social Demo
Select an App to add to config: 1
Awesome! Now it's time to setup some Cloud Code for the app: "Social Demo",
Next we will create a directory to hold your Cloud Code.
Please enter the name to use for this directory,
or hit ENTER to use "Social Demo" as the directory name.
 
Directory Name: social-demo-parse-2
Your Cloud Code has been created at /Users/shannah/cn1_files/incubator/social-network-parse/social-demo-parse-2.
Next, you might want to deploy this code with "parse deploy".
This includes a "Hello world" cloud function, so once you deploy
you can test that it works, with:
 
curl -X POST \
 -H "X-Parse-Application-Id: xxxxxxxxxxxxxxxxxx" \
 -H "X-Parse-REST-API-Key: xxxxxxxxxxxxxxxxxxxx \
 -H "Content-Type: application/json" \
 -d '{}' \
 https://api.parse.com/1/functions/hello

Это создает основу для моего проекта приложения в каталоге «social-demo-parse-2». Структура каталогов:

1
2
3
4
5
6
./cloud
./cloud/main.js
./config
./config/global.json
./public
./public/index.html

Здесь ./cloud/main.js файл ./cloud/main.js , в который будет включен весь облачный код. Он запускает вас с хорошим примером «привет» функции, которая может быть вызвана через REST API. Его содержание таково:

1
2
3
4
5
// Use Parse.Cloud.define to define as many cloud functions as you want.
// For example:
Parse.Cloud.define("hello", function(request, response) {
  response.success("Hello world!");
});

Это простая функция, которая просто возвращает строку «Hello world!». Вы можете вызвать эту функцию прямо из приложения Codename One, просто вызвав:

1
2
String result = (String)ParseCloud.callFunction("hello", null);
System.out.println(result); // Hello world!

Написание конечных точек для REST API

Теперь нам просто нужно создать конечные точки для всех соответствующих функций нашего клиента. Давайте начнем с вставки заполнителей для наших конечных точек API:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Parse.Cloud.define("send_friend_request", function(request, response) {
   ...
});
 
Parse.Cloud.define("accept_friend_request", function(request, response) {
    ...
});
 
Parse.Cloud.define("decline_friend_request", function(request, response) {
 
});
 
Parse.Cloud.define("get_pending_friend_requests", function(request, response) {
 
});
 
Parse.Cloud.define("get_friends", function(request, response) {
 
});
 
Parse.Cloud.define("post", function(request, response) {
 
});
 
Parse.Cloud.define("update_profile", function(request, response) {
 
});
 
Parse.Cloud.define("get_profile", function(request, response) {
 
});
 
Parse.Cloud.define("get_feed", function(request, response) {
 
});
 
Parse.Cloud.define("find_users", function(request, response) {
 
});

Чтобы оставаться совместимым с PHP / MySQL REST API в предыдущей версии, эти конечные точки всегда будут возвращать объект JSON в следующей форме:

1
2
3
4
5
//For errors:
{code : 500, message : "Some error message", ...}
 
// For successes
{code : 200, ... }

Таким образом, клиент всегда может проверить свойство «code», чтобы узнать, было ли действие успешным.

Для успешных операций может быть три типа возвращаемых значений:

  1. Нет возвращаемого значения. Например, accept_friend_request , send_friend_request .
  2. Возвращает список объектов. Например, find_users , get_friends и т. Д.
  3. Возвращает один объект. Например, get_profile

Написание кодового имени One Web Service Client

Итак, на стороне клиента (в нашем приложении Codename One) я создал 3 служебных оболочки для этих случаев:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
    * Calls cloud code function with void return type.
    * @param funcName The name of the function to call.
    * @param params Parameters passed to the function.  Accepts null.
    */
   private void callFunc(String funcName, Map params) throws IOException {
       try {
           JSONObject response = (JSONObject)ParseCloud.callFunction(funcName, params);
           int code = response.getInt("code");
           if (code != 200) {
               throw new IOException(response.getString("message"));
           }
       } catch (Throwable ex) {
           Log.e(ex);
           ex.printStackTrace();
           throw new IOException(ex.getMessage());
       }
   }
 
   /**
    * Calls a cloud code function whose result will be a list of objects.
    * @param funcName The name of the cloud code function to call.
    * @param params Parameters passed to the cloud code. Accepts null.
    * @param listKey The JSON key in the response object that contains the list of objects that
    *  were returned by the cloud code.
    */
   private List<Map> getList(String funcName, Map params, String listKey) throws IOException {
       try {
           JSONObject response = (JSONObject)ParseCloud.callFunction(funcName, params);
           System.out.println(response);
           int code = response.getInt("code");
           if (code != 200) {
               throw new IOException(response.getString("message"));
           } else {
               ArrayList<Map> out = new ArrayList<Map>();
               JSONArray posts = response.getJSONArray(listKey);
               int len = posts.length();
               for (int i=0; i<len; i++) {
                   JSONObject row = posts.getJSONObject(i);
                   out.add(toMap(row));
               }
               return out;
           }
       } catch (Throwable ex) {
           Log.e(ex);
           ex.printStackTrace();
           throw new IOException(ex.getMessage());
       }
   }
 
 
   /**
    * Calls cloud code function that returns a single object.
    * @param funcName The name of the cloud code function to call.
    * @param params Parameters passed to the cloud code function.  Accepts null.
    * @param mapKey The JSON key of the response object that contains the object that
    * was returned from the cloud code function.
    */
   private Map getMap(String funcName, Map params, String mapKey) throws IOException {
       try {
           JSONObject response = (JSONObject)ParseCloud.callFunction(funcName, params);
           int code = response.getInt("code");
           if (code != 200) {
               throw new IOException(response.getString("message"));
           } else {
 
               JSONObject row = response.getJSONObject(mapKey);
               return toMap(row);
           }
       } catch (Throwable ex) {
           Log.e(ex);
           ex.printStackTrace();
           throw new IOException(ex.getMessage());
       }
   }

Для удобства я также создал тонкие обертки вокруг этих служебных функций, чтобы можно было передавать объекты массива для параметров вместо карты.

Код клиента и сервера для get_feed

Теперь, когда у нас есть основа для серверной и клиентской сторон REST API, давайте еще немного уточним, как именно будет выглядеть код на стороне сервера и на стороне клиента. Возьмите, например, метод getFeed() , который предназначен для возврата списка сообщений, которые должны появляться в ленте новостей текущего пользователя.

Код сервера будет выглядеть так:

1
2
3
4
5
6
7
8
Parse.Cloud.define("get_feed", function(request, response) {
    // ... some logic to retrieve the posts from the database
    if (success) {
        response.success({code : 200, posts : [...]});
    } else {
        response.success({code : 500, message : 'Some error message'});
    }
});

Это исключает всю логику и в значительной степени упрощается, но ключ здесь в том, что он возвращает объект JSON с помощью обратного вызова response.success() . Если ошибок не было, сообщения содержатся в массиве под ключом posts полученного объекта JSON. Таким образом, клиент веб-службы будет использовать наш getList() утилиты getList() следующим образом:

1
2
3
public List<Map> getFeed(Date olderThan) throws IOException {
       return getList("get_feed", "posts");
    }

Отправка, получение и принятие запросов друзей

Напомним из нашего дизайна базы данных, что запросы друзей и друзей поддерживаются через отношения «friends» и «pendingFriendRequests» в таблице users. Для отправки запроса на добавление в друзья пользователю необходимо добавить текущего пользователя в отношение pendingFriendRequests этого пользователя. Принятие запроса на добавление в друзья от пользователя включает добавление текущего пользователя в отношение «друзья» этого пользователя, добавление этого пользователя в отношение «друзья» текущего пользователя и удаление этого пользователя из отношения «pendingFriendRequests» текущего пользователя. Код для send_friend_request выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
Parse.Cloud.define("send_friend_request", function(request, response) {
    Parse.Cloud.useMasterKey();
 
    (new Parse.Query(Parse.User)).equalTo("username", request.params.username).each(function(friend) {
        friend.relation("pendingFriendRequests").add(Parse.User.current());
        friend.save().then(function(result) {
            response.success({code: 200, message: "Successfully sent friend request"});
        });
    });
 
});

Некоторые вещи, чтобы отметить здесь:

  1. Parse.Cloud.useMasterKey() дает нам карт-бланш на API разбора. У нас нет никаких ограничений безопасности. Без этого на весь доступ будут распространяться ограничения безопасности для текущего вошедшего в систему пользователя.
  2. (new Parse.Query(Parse.User)).equalTo("username", request.params.username).each(function(friend) { Создает запрос в классе User для всех пользователей с именем пользователя, равным « Параметр username »передается как часть запроса. Затем метод each() перебирает результаты с предоставленным обратным вызовом.
  3. friend.relation("pendingFriendRequests").add(Parse.User.current()); Добавляет текущего пользователя в отношение pendingFriendRequests найденного пользователя.
  4. friend.save().then(function(result) { Мы сохраняем объект пользователя. save() возвращает обещание, так что вызов then() даст нам возможность отложить то, что будет дальше, до завершения сохранения. Если вы Вы не знакомы с обещаниями, это просто симпатичный синтаксис для обратного вызова.
  5. response.success({code: 200, message: "Successfully sent friend request"}); В конечном итоге это возвращает ответ клиенту в виде объекта JSON.

Код для получения списка ожидающих запросов на добавление в друзья выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
Parse.Cloud.define("get_pending_friend_requests", function(request, response) {
    Parse.Cloud.useMasterKey();
 
    var out = [];
    var user = Parse.User.current();
    user.relation("pendingFriendRequests").query().each(function(friend) {
        out.push({
            sender : friend.get("username"),
            receiver : user.get("username"),
            avatar : friend.get("avatar") ? friend.get("avatar").url() : null,
            screen_name : friend.get("screen_name")
 
        });
    })
    .then(function(result) {
        response.success({code: 200, requests: out});
    });
 
});

И принимать ожидающие запросы на добавление в друзья:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Parse.Cloud.define("accept_friend_request", function(request, response) {
    Parse.Cloud.useMasterKey();
 
    var currentUser = Parse.User.current();
    var pendingRequests = currentUser.relation("pendingFriendRequests");
 
    pendingRequests.query().equalTo("username", request.params.username).each(function(friend) {
        currentUser.relation("friends").add(friend);
        pendingRequests.remove(friend);
        currentUser.save().then(function(result) {
            friend.relation("friends").add(currentUser);
            return friend.save();
 
        }, function(error) {
            response.success({code : 500, message : error});
        }).then(function(result) {
            response.success({code: 200, message: "Friend request accepted"});
        }, function(error) {
            response.success({code : 500, message : error});
        });
    });
 
});

Полный исходный код облака

Вы можете просмотреть полный исходный код этого облачного кода здесь . Каждый метод следует примерно по одной схеме:

  1. Получить некоторые данные из базы данных
  2. Переберите результаты, чтобы получить ответ JSON.

Некоторые запросы, особенно запросы, включающие сложные отношения, такие как в get_feed были немного сложны для get_feed , но в итоге я был впечатлен гибкостью Parse в возможности поддерживать довольно сложные запросы. Я не буду вдаваться в подробности здесь, но отмечу, что документация Parse является исключительной, и, похоже, она имеет довольно большую базу пользователей, судя по количеству вопросов и ответов, связанных с анализом, которые уже доступны онлайн на их форумах и в другом месте. Мне едва приходилось тратить больше 5 минут на поиск в Google, чтобы найти ответ на мои вопросы, когда я застрял.

Создайте приложение самостоятельно

Полный проект, как клиентский проект Codename One, так и облачный код синтаксического анализа , размещены на Github, чтобы вы могли загрузить и собрать проект самостоятельно.

Установите приложение на Android

Я разместил Android-сборку этого приложения, чтобы вы могли установить его прямо на свой телефон, если хотите: