From 1ce25289d45fa6a95aa6507f5f7e9c9ccb4345ae Mon Sep 17 00:00:00 2001 From: Mohamed El-Sawy Date: Thu, 28 Sep 2023 21:54:54 +0300 Subject: [PATCH] List `TiplineMessage`'s by UID Add GraphQL type for `TiplineMessage` and list tipline messages by UID in `TeamType`. Reference: CV2-3681. --- app/graph/types/team_type.rb | 8 + app/graph/types/tipline_message_type.rb | 22 + lib/relay.idl | 80 ++++ public/relay.json | 453 ++++++++++++++++++ test/controllers/graphql_controller_9_test.rb | 44 ++ 5 files changed, 607 insertions(+) create mode 100644 app/graph/types/tipline_message_type.rb diff --git a/app/graph/types/team_type.rb b/app/graph/types/team_type.rb index 8403dd5335..073f9293ab 100644 --- a/app/graph/types/team_type.rb +++ b/app/graph/types/team_type.rb @@ -295,4 +295,12 @@ def shared_teams field :feeds, FeedType.connection_type, null: true field :tipline_newsletters, TiplineNewsletterType.connection_type, null: true field :tipline_resources, TiplineResourceType.connection_type, null: true + + field :tipline_messages, TiplineMessageType.connection_type, null: true do + argument :uid, GraphQL::Types::String, required: true + end + + def tipline_messages(uid:) + object.tipline_messages.where(uid: uid).last(100) + end end diff --git a/app/graph/types/tipline_message_type.rb b/app/graph/types/tipline_message_type.rb new file mode 100644 index 0000000000..4fcbd51939 --- /dev/null +++ b/app/graph/types/tipline_message_type.rb @@ -0,0 +1,22 @@ +class TiplineMessageType < DefaultObject + description "TiplineMessage type" + + implements GraphQL::Types::Relay::Node + + field :dbid, GraphQL::Types::Int, null: true + field :event, GraphQL::Types::String, null: true + field :direction, GraphQL::Types::String, null: true + field :language, GraphQL::Types::String, null: true + field :platform, GraphQL::Types::String, null: true + field :uid, GraphQL::Types::String, null: true + field :external_id, GraphQL::Types::String, null: true + field :payload, JsonStringType, null: true + field :team_id, GraphQL::Types::Int, null: true + field :state, GraphQL::Types::String, null: true + field :team, TeamType, null: true + field :sent_at, GraphQL::Types::String, null: true, camelize: false + + def sent_at + object.sent_at.to_i.to_s + end +end diff --git a/lib/relay.idl b/lib/relay.idl index 0f5887a61d..2303bb732a 100644 --- a/lib/relay.idl +++ b/lib/relay.idl @@ -12866,6 +12866,28 @@ type Team implements Node { last: Int status: [String] ): TeamUserConnection + tipline_messages( + """ + Returns the elements in the list that come after the specified cursor. + """ + after: String + + """ + Returns the elements in the list that come before the specified cursor. + """ + before: String + + """ + Returns the first _n_ elements from the list. + """ + first: Int + + """ + Returns the last _n_ elements from the list. + """ + last: Int + uid: String! + ): TiplineMessageConnection tipline_newsletters( """ Returns the elements in the list that come after the specified cursor. @@ -13142,6 +13164,64 @@ type TeamUserEdge { node: TeamUser } +""" +TiplineMessage type +""" +type TiplineMessage implements Node { + created_at: String + dbid: Int + direction: String + event: String + external_id: String + id: ID! + language: String + payload: JsonStringType + permissions: String + platform: String + sent_at: String + state: String + team: Team + team_id: Int + uid: String + updated_at: String +} + +""" +The connection type for TiplineMessage. +""" +type TiplineMessageConnection { + """ + A list of edges. + """ + edges: [TiplineMessageEdge] + + """ + A list of nodes. + """ + nodes: [TiplineMessage] + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + totalCount: Int +} + +""" +An edge in a connection. +""" +type TiplineMessageEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of the edge. + """ + node: TiplineMessage +} + """ TiplineNewsletter type """ diff --git a/public/relay.json b/public/relay.json index 5459ac477c..df8894a54b 100644 --- a/public/relay.json +++ b/public/relay.json @@ -54069,6 +54069,11 @@ "name": "TeamUser", "ofType": null }, + { + "kind": "OBJECT", + "name": "TiplineMessage", + "ofType": null + }, { "kind": "OBJECT", "name": "TiplineNewsletter", @@ -67302,6 +67307,83 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "tipline_messages", + "description": null, + "args": [ + { + "name": "after", + "description": "Returns the elements in the list that come after the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "before", + "description": "Returns the elements in the list that come before the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "first", + "description": "Returns the first _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "last", + "description": "Returns the last _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "uid", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "OBJECT", + "name": "TiplineMessageConnection", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "tipline_newsletters", "description": null, @@ -68815,6 +68897,377 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "OBJECT", + "name": "TiplineMessage", + "description": "TiplineMessage type", + "fields": [ + { + "name": "created_at", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "dbid", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "direction", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "event", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "external_id", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": null, + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "language", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "payload", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "JsonStringType", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "permissions", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "platform", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "sent_at", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "state", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "team", + "description": null, + "args": [ + + ], + "type": { + "kind": "OBJECT", + "name": "Team", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "team_id", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "uid", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "updated_at", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "TiplineMessageConnection", + "description": "The connection type for TiplineMessage.", + "fields": [ + { + "name": "edges", + "description": "A list of edges.", + "args": [ + + ], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "TiplineMessageEdge", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "nodes", + "description": "A list of nodes.", + "args": [ + + ], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "TiplineMessage", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "pageInfo", + "description": "Information to aid in pagination.", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "PageInfo", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "totalCount", + "description": null, + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "TiplineMessageEdge", + "description": "An edge in a connection.", + "fields": [ + { + "name": "cursor", + "description": "A cursor for use in pagination.", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "node", + "description": "The item at the end of the edge.", + "args": [ + + ], + "type": { + "kind": "OBJECT", + "name": "TiplineMessage", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, { "kind": "OBJECT", "name": "TiplineNewsletter", diff --git a/test/controllers/graphql_controller_9_test.rb b/test/controllers/graphql_controller_9_test.rb index cc17d51750..c3edeaa351 100644 --- a/test/controllers/graphql_controller_9_test.rb +++ b/test/controllers/graphql_controller_9_test.rb @@ -365,6 +365,50 @@ def setup assert_equal 'test', t.reload.get_outgoing_urls_utm_code end + test "should get tipline messages by uid" do + t = create_team slug: 'test', private: true + u = create_user + create_team_user user: u, team: t, role: 'admin' + authenticate_with_user(u) + uid = random_string + uid2 = random_string + uid3 = random_string + tp1_uid = create_tipline_message team_id: t.id, uid: uid, state: 'sent' + tp2_uid = create_tipline_message team_id: t.id, uid: uid, state: 'delivered' + tp1_uid2 = create_tipline_message team_id: t.id, uid: uid2, state: 'sent' + tp2_uid2 = create_tipline_message team_id: t.id, uid: uid2, state: 'delivered' + + query = 'query read { team(slug: "test") { tipline_messages(uid:"'+ uid +'") { edges { node { dbid, event, direction, language, platform, uid, external_id, payload, team_id, state, team { dbid}, sent_at } } } } }' + post :create, params: { query: query } + assert_response :success + edges = JSON.parse(@response.body)['data']['team']['tipline_messages']['edges'] + assert_equal [tp1_uid.id, tp2_uid.id], edges.collect{ |e| e['node']['dbid'] }.sort + + query = 'query read { team(slug: "test") { tipline_messages(uid:"'+ uid2 +'") { edges { node { dbid } } } } }' + post :create, params: { query: query } + assert_response :success + edges = JSON.parse(@response.body)['data']['team']['tipline_messages']['edges'] + assert_equal [tp1_uid2.id, tp2_uid2.id], edges.collect{ |e| e['node']['dbid'] }.sort + + query = 'query read { team(slug: "test") { tipline_messages(uid:"'+ uid3 +'") { edges { node { dbid } } } } }' + post :create, params: { query: query } + assert_response :success + edges = JSON.parse(@response.body)['data']['team']['tipline_messages']['edges'] + assert_empty edges + end + + test "non members should not read tipline messages" do + t = create_team slug: 'test', private: true + uid = random_string + tp1_uid = create_tipline_message team_id: t.id, uid: uid, state: 'sent' + authenticate_with_user + create_team slug: 'team', name: 'Team', private: true + query = 'query read { team(slug: "test") { name, tipline_messages(uid:"'+ uid +'") { edges { node { dbid } } } } }' + post :create, params: { query: query } + assert_response 200 + assert_equal "Not Found", JSON.parse(@response.body)['errors'][0]['message'] + end + protected def assert_error_message(expected)