首页  »  AWS 教程

使用 Amazon Neptune 构建游戏好友推荐引擎

测试应用程序

在本模块,您将开始使用已经部署的应用程序。在开始之前,让我们快速回顾一下应用程序中使用的 AWS 组件:

让我们看看这些组件是如何协同工作的。接下来的步骤中,您将使用这些组件,通过不同的应用程序端点来使用应用程序的功能。

首先从 Registration(注册)端点开始,新用户通过该端点注册并创建他们的账户。注册完用户之后,您可以使用 FetchUser(获取用户)端点来查看该用户的详细信息。

其次是 Login(登录)端点,用户可以使用客户端(如 Web 应用程序或移动应用程序)在此进行身份验证并获取身份令牌。

第三,使用 FetchUserRecommendations(获取用户推荐)端点来查找您应该关注的用户。

最后,使用 FollowUser(关注用户)端点开始关注其他用户。

 完成时间

15 分钟


  • 我们首先要了解的流程是 Registration(注册)端点。在此端点中,新用户通过提供登录信息(如用户名和密码)来注册。此外,新用户在注册时还可以表明自己的一些兴趣爱好。这有助于在用户初期就提供更多相关的推荐内容。

    在我们了解这些端点的过程中,本指南会展示端点调用的相关代码片段。

    您可以通过向 /users 端点发送 POST 请求来使用注册端点。您可以在 application/app.js 文件大约中间位置找到该端点的逻辑:

    // Create user
    app.post('/users', wrapAsync(async (req, res) => {
      const validated = validateCreateUser(req.body)
      if (!validated.valid) {
        throw new Error(validated.message)
      }
      await createCognitoUser(req.body.username, req.body.password, req.body.email)
      const user = await createUser(req.body.username, req.body.interests)
      res.json(user)
    }))

    处理函数首先会验证请求中传入的有效负载正文 (req.body)。如果验证通过,处理程序会在 Amazon Cognito 中创建用户,然后使用 createUser 函数在 Neptune 数据库中创建用户。

    我们在 Amazon Cognito 模块中已经回顾了 createCognitoUser 函数,这里就不再赘述了。让我们看看 application/data/createUser.js 文件中的 createUser 函数。

    // application/data/createUser.js
    const { g } = require('./utils')
    
    const createUser = async (username, interests) => {
      const user = await g.addV('User').property('username', username).next()
      const edgePromises = interests.map((interest) => {
        return g.V()
          .has('Interest', 'interest', interest)
          .as('interest')
          .V(user.value.id)
          .addE('InterestedIn').to('interest')
          .next()
      })
      await Promise.all(edgePromises)
      return {
        username,
        interests
      }
    }
    
    module.exports = createUser

    createUser 函数接受用户名和兴趣数组两个参数。该函数在 Neptune 数据库中创建用户,然后遍历兴趣数组,添加从用户顶点到兴趣顶点的 InterestedIn(感兴趣)边。

    试试调用 Registration 端点来创建一个新用户。在终端中运行以下命令:

    curl -X POST ${BASE_URL}/users \
      -H 'Content-Type: application/json' \
      -d '{
    	"username": "thefriendlyuser",
    	"password": "Password1",
    	"email": "test@email.com",
    	"interests": ["Sports", "Nature", "Cooking"]
    }'

    您应当会在终端看到以下输出结果:

    {"username":"thefriendlyuser","interests":["Sports","Nature","Cooking"]}

    太棒了!您已经成功创建了用户。

    通过以下命令获取用户详情,检查用户是否创建成功,以及是否与其兴趣建立了连接:

    curl -X GET ${BASE_URL}/users/thefriendlyuser

    您应当会在终端看到以下输出结果:

    {"username":"thefriendlyuser","friends":[],"interests":["Cooking","Nature","Sports"]}

    太棒了!您可以看到用户的兴趣已经被保存了。您还可以看到该用户目前还没有好友。这个问题我们将在后续步骤中解决。 

    现在,让我们登录并获取身份令牌。

  • 第二个端点是 Login(登录)端点。用户将向该端点提交用户名和密码,以获取身份令牌,该令牌将用于后续请求的身份验证。

    为了处理身份验证和发放令牌,您的应用程序中设置了一个 /login 端点。处理函数位于 application/app.js 文件中,如下所示:

    // Login
    app.post('/login', wrapAsync(async (req, res) => {
      const idToken = await login(req.body.username, req.body.password)
      res.json({ idToken })
    }))

    它要求请求的有效负载正文中包含 username 和 password 属性。然后调用 auth.js 文件中的辅助函数 login。如果登录成功,它会返回该用户的身份令牌。

    让我们用您创建的用户来测试一下。在终端中运行以下命令:

    curl -X POST ${BASE_URL}/login \
      -H 'Content-Type: application/json' \
      -d '{
    	"username": "thefriendlyuser",
    	"password": "Password1"
    }'

    您应当会在终端看到以下输出结果:

    {"idToken":"eyJraWQiO…."}

    该身份令牌会在后续请求中用于对用户进行授权。通过复制引号内的令牌值并运行以下命令,将身份令牌的值保存在 shell 中:

    export ID_TOKEN=<idToken>
  • 目前您的新用户还没有任何好友。这对应用程序内的用户体验不太友好,所以我们希望帮助他们找到新的好友。为此,您可以生成一些推荐,让用户关注新的好友。

    您的应用程序中有一个 FetchUserRecommendations(获取用户推荐)端点。该端点的代码如下所示:

    // Fetch user recommendations
    app.get('/users/:username/recommendations', wrapAsync(async (req, res) => {
      const recommendations = await fetchUserRecommendations(req.params.username)
      res.json(recommendations)
    }))

    处理函数从 URL 路径中获取用户名,并将其传递给 fetchUserRecommendations 函数来生成推荐。

    fetchUserRecommendations 函数位于 application/data/fetchUserRecommendations.js 文件中,内容如下所示:

    const { g, local, values, desc, without, neq } = require('./utils')
    
    const fetchUserRecommendations = async (username) => {
      const friendsOfFriends = await g.V()
        .has('User', 'username', username).as('user')
        .out('Follows').aggregate('friends')
        .in_('Follows')
        .out('Follows').where(without('friends'))
        .where(neq('user'))
        .values('username')
        .groupCount()
        .order(local)
        .by(values, desc)
        .limit(local, 10)
        .next()
      const friendsWithInterests = await g.V()
        .has('User', 'username', username).as('user')
        .out('InterestedIn')
        .in_('InterestedIn')
        .out('Follows')
        .where(neq('user'))
        .values('username')
        .groupCount()
        .order(local)
        .by(values, desc)
        .limit(local, 10)
        .next()
      return {
        username,
        friendsOfFriends,
        friendsWithInterests
      }
    }
    
    module.exports = fetchUserRecommendations

    fetchUserRecommendations 函数接受一个用户名参数,然后执行您在前面模块中看到的两种推荐策略。该函数会查找该用户好友的好友,以及有共同兴趣的好友。并返回这两种策略的结果。

    让我们来试试这个端点。在终端中运行以下命令:

    curl -X GET ${BASE_URL}/users/thefriendlyuser/recommendations

    您应当会在终端看到以下输出结果:

    {"username":"thefriendlyuser","friendsOfFriends":[],"friendsWithInterests":[["thardy",21],["michaelunderwood",20],["paullaurie",19],["ocarrillo",16],["paulacruz",16],["bergjames",16],["petersonchristina",15],["annette32",15],["evanewing",14],["hortonamy",14]]}
    

    太棒了!您可以为该用户生成推荐了。

    注意,您收到了 friendsWithInterests(有共同兴趣的好友)的推荐结果,但没有 friendsOfFriends(好友的好友)的推荐。这是因为您的用户目前还没有任何好友!

    在下一步中,您将关注您的第一个好友。

  • 在此步骤中,您将关注第一个好友。

    关注用户的端点是 POST /users/:username/friends,其处理函数的代码在 application/app.js 文件中,如下所示:

    // Follow user
    app.post('/users/:username/friends', wrapAsync(async (req, res) => {
      const validated = validateFollowUser(req.body)
      if (!validated.valid) {
        throw new Error(validated.message)
      }
      const token = await verifyToken(req.header('Authorization'))
      if (token['cognito:username'] != req.params.username) {
        throw new Error('Unauthorized')
      }
      const friendship = await followUser({ follower: req.params.username, friend: req.body.username })
      res.json(friendship)
    }))

    这里将执行一些操作。首先验证请求的有效负载正文,确保其中包含 username 属性。其次,处理函数验证传入的 Authorization 标头,确保其与 URL 路径中的用户名值匹配。如果请求通过了验证和授权,就调用 followUser 函数添加关注关系。

    在上一步中,您看到了一些推荐给该用户的好友。我们选择其中评分最高的一位,并关注该用户。

    您可以使用以下命令关注该用户:

    curl -X POST ${BASE_URL}/users/thefriendlyuser/friends \
     -H 'Content-Type: application/json' \
      -H "Authorization: ${ID_TOKEN}" \
      -d '{
    	"username": "thardy"
    }'

    您应当会在终端看到以下输出结果:

    {"follower":"thefriendlyuser","friend":"thardy"}

    成功了!您关注了您的第一个好友!

    让我们再次获取您的用户详情,确保关注关系已存入数据库:

    curl -X GET ${BASE_URL}/users/thefriendlyuser

    您应当会在终端看到以下输出结果:

    {"username":"thefriendlyuser","friends":["thardy"],"interests":["Sports","Cooking","Nature"]}

    太棒了!现在,在用户的好友列表结果中出现了 thardy

    最后,让我们看看推荐更新了没有:

    {"username":"thefriendlyuser","friendsOfFriends":[["michaelunderwood",8],["paullaurie",7],["morenojason",6],["paulacruz",6],["warrenpaul",5],["pricelucas",5],["ocarrillo",5],["hlowe",5],["clynch",5],["douglasrichard",5]],"friendsWithInterests":[["thardy",24],["michaelunderwood",20],["paullaurie",19],["ocarrillo",16],["paulacruz",16],["bergjames",16],["petersonchristina",15],["annette32",15],["evanewing",14],["hortonamy",14]]}

    成功了!请注意,由于您的用户现在有了一些好友,该用户已经收到了 friendsOfFriends(好友的好友)的推荐。


总结

在本模块中,通过使用可用的端点,您了解了各个组件是如何协同工作的。首先,您在应用程序中注册了一个新用户,并通过获取该用户信息,确保所有详细信息都已保存。其次,您使用登录端点获取了用户的身份令牌,客户端可以使用该令牌对用户进行身份验证。第三,您获取了该用户的好友推荐。然后,您根据这些推荐关注了第一个用户。在关注该用户后,您验证了好友关系已被保存,推荐也相应地更新了。

在下一个模块中,您将清理在本教程中创建的资源。