From 2d44f879f76ec11c9b74f3ad5a92fef912441a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=E1=BB=8Bnh=20Duy=20H=C6=B0ng?= <57101685+HUNG-rushb@users.noreply.github.com> Date: Mon, 11 Dec 2023 03:00:42 +0700 Subject: [PATCH] Update Contest --- src/Type_Definitions/Contest_Contest.js | 70 ++++++++------- src/Type_Definitions/User_User.js | 3 + src/prisma/ERD.svg | 2 +- src/prisma/schema.prisma | 61 +++++++------ src/resolvers/Mutation/contest.js | 77 +++++++++++------ src/resolvers/Mutation/user.js | 2 + src/resolvers/Query/CONTEST.js | 110 +++++++++++++----------- src/resolvers/Type/ContestPrize___.js | 20 +++++ src/resolvers/Type/Contest___.js | 19 +++- src/resolvers/Type/User___.js | 21 +++++ src/resolvers/Type/_Type.js | 2 + 11 files changed, 251 insertions(+), 136 deletions(-) create mode 100644 src/resolvers/Type/ContestPrize___.js diff --git a/src/Type_Definitions/Contest_Contest.js b/src/Type_Definitions/Contest_Contest.js index e83bb92..0d9abed 100644 --- a/src/Type_Definitions/Contest_Contest.js +++ b/src/Type_Definitions/Contest_Contest.js @@ -5,11 +5,12 @@ const contestDefs = gql` allContests: [Contest]! contestInfo(data: ContestInfoInput!): Contest! contestPosts( - data: ContestPostsInpout - limit: Int + contestId: String + userId: String after: String ): PostConnection! - getTopContestPosts(contestId: String, top: Int!): [Post]! + # getTopContestPosts(contestId: String, top: Int!): [Post]! + getTopContestPosts(data: ContestInfoInput!): [Post]! # getContestPrizes # getPrizes # getContestPostScore @@ -27,11 +28,12 @@ const contestDefs = gql` # _______________________________________________________ extend type Mutation { - createPrize(data: CreatePrizeInput): Prize! + # createPrize(data: CreatePrizeInput): Prize! # createPrizeSet createContest(data: CreateContestInput!): Contest! deleteContest(data: DeleteContestInput!): Contest! joinContest(data: JoinContestInput!): Contest! + endContest(data: EndContestInput!): Contest! # submitPostToContest(data: SubmitPostToContestInput): Contest_Score # deletePostToContest(data: SubmitPostToContestInput): Contest_Score # endContest @@ -44,20 +46,18 @@ const contestDefs = gql` # contestId: ID! # } - input CreatePrizeInput { - name: String! - prizeImageURL: String! - } + # input CreatePrizeInput { + # name: String! + # prizeImageURL: String! + # } input CreateContestInput { name: String! contestImageURL: String! description: String! - startDate: Int! - endDate: Int - - contestPrizeList: [CreateContestPrizeInput]! + startDate: String! + endDate: String } input CreateContestPrizeInput { @@ -75,44 +75,50 @@ const contestDefs = gql` userId: ID! } + input EndContestInput { + contestId: ID! + } + type Contest { id: ID! name: String! contestImageURL: String! description: String! - startDate: Int! - endDate: Int + startDate: String! + endDate: String + isFinished: Boolean! - joinedUserList: [User]! - scores: [Contest_Score]! + joinedUserIds: [User]! contestPrizeList: [Contest_Prize]! } - type Contest_Score { - id: ID! + # type Contest_Score { + # id: ID! - contest: Contest! - user: User! - post: Post! - score: Int - } + # contest: Contest! + # user: User! + # post: Post! + # score: Int + # } type Contest_Prize { id: ID! - contest: Contest! - prize: Prize! - user: User + + contestId: Contest! + userId: User! + title: String! type: String! - } - - type Prize { - id: ID! - name: String! prizeImageURL: String! - contestPrizeList: [Contest_Prize] } + + # type Prize { + # id: ID! + # name: String! + # prizeImageURL: String! + # contestPrizeList: [Contest_Prize] + # } `; export default contestDefs; diff --git a/src/Type_Definitions/User_User.js b/src/Type_Definitions/User_User.js index 5b962b5..8f22a28 100644 --- a/src/Type_Definitions/User_User.js +++ b/src/Type_Definitions/User_User.js @@ -150,6 +150,9 @@ const userDefs = gql` biography: String userEndorsements: [Endorsement] interestCategories: [Category] + + joinedContestIds: [Contest]! + contestPrizeList: [Contest_Prize]! } ${categoryDefs} diff --git a/src/prisma/ERD.svg b/src/prisma/ERD.svg index 9847671..f29ab8f 100644 --- a/src/prisma/ERD.svg +++ b/src/prisma/ERD.svg @@ -1 +1 @@ -TypeNotiPOST_CREATEDPOST_CREATEDPOST_LIKEDPOST_LIKEDViewStatusPUBLICPUBLICONLY_FOLLOWERSONLY_FOLLOWERSPRIVATEPRIVATEUserStringz_id🗝️StringemailStringphoneNumberStringhashPasswordStringnameStringprofileImageURLStringbackgroundImageURLIntageStringbirthdayIntisAdminDateTimecreatedAtDateTimeupdatedAtStringbiographyStringnotiIdsStringchatIDsStringendoseredIdsStringinterestCategoryIdsStringjoinedContestIdsNotificationStringz_id🗝️TypeNotitypeStringpostIdStringpostTitleStringpostImageDateTimecreatedAtStringuserIdsStringuserTriggerIdEndorsementStringz_id🗝️StringownerIdStringendorserIdsStringskillIdSkillStringz_id🗝️StringnameReportStringz_id🗝️BooleanisFinishedStringreasonStringpostIdStringuserIdStringuserReportedDateTimecreatedAtDateTimeupdatedAtFollowerStringz_id🗝️StringuserFollowerStringuserIdFollowingStringz_id🗝️StringuserFollowingStringuserIdLevelStringz_id🗝️IntcurrentXPIntcurrentLevelStringuserIdImageStringz_id🗝️StringurlStringhashDateTimecreatedAtDateTimeupdatedAtStringpostIdImageInfoStringz_id🗝️StringcameraStringlensStringapertureStringfocalLengthStringshutterSpeedStringISOStringtakenWhenStringcopyRightStringimageIdCategoryStringz_id🗝️StringnameDateTimecreatedAtDateTimeupdatedAtStringpostsStringstoriesStringinterestUserIdsAlbumStringz_id🗝️StringnameDateTimecreatedAtDateTimeupdatedAtStringuserIdStringpostsTagStringz_id🗝️StringnameDateTimecreatedAtDateTimeupdatedAtPostStringz_id🗝️StringtitleStringcaptionDateTimecreatedAtDateTimeupdatedAtViewStatuspostViewStatusIntpointsStringcontestIdStringuserIdStringcategoryIdStringalbumIdStringtagStringuserLikedPostStringreportedUserIdsStoryStringz_id🗝️StringtitleViewStatusstoryViewStatusDateTimecreatedAtDateTimeupdatedAtIntpointsStringcontentStringimagesStringtagStringcategoryIdStringuserIdStringuserLikedStoryStringreportedUserIdsCommentStringz_id🗝️StringcontentDateTimecreatedAtStringuserIdStringpostIdStringstoryIdStringparentIdIntvotesStringupVoteUserlistStringdownVoteUserlistContestStringz_id🗝️StringnameStringcontestImageURLStringdescriptionDateTimestartDateDateTimeendDateStringjoinedUserIdsDateTimecreatedAtDateTimeupdatedAtContest_PrizeStringz_id🗝️StringcontestIdStringprizeIdStringuserIdStringtitleStringtypePrizeStringz_id🗝️StringnameStringprizeImageURLContest_ScoreStringz_id🗝️StringcontestIdStringuserIdStringpostIdIntscoreDateTimecreatedAtDateTimeupdatedAtChatStringz_id🗝️DateTimecreatedAtDateTimeupdatedAtDateTimelastMessageAtStringuserIDsMessageStringz_id🗝️StringmessageDateTimecreatedAtBooleanisImageStringuserIdStringchatIdlevelalbumspostsstoriescommentsfollowersfollowingsmessagesuser_to_notificationnotificationsTriggereduser_to_chatendoseredListendorsementsinterestCategoriessubmissionscontestPrizeListjoinedContestsenum:typenotification_to_usernotification_to_usertriggerownerendorserskillendorsementsfollower_to_userfollowing_to_userlevel_to_userimage_to_postimageInfoIdimageInfo_to_imagecategory_to_postcategory_to_storycategory_to_useralbum_to_useralbum_to_postenum:postViewStatusimagecmtspost_to_userpost_to_categorypost_to_albumsubmissionsenum:storyViewStatuscommentsstory_to_categorystory_to_usercmt_to_usercmt_to_postcmt_to_storyparentchildjoinedUserListscorescontestPrizeListcontestprizeusercontestPrizeListcontestuserpostmessageschat_to_usermessage_to_usermessage_to_chat \ No newline at end of file +TypeNotiPOST_CREATEDPOST_CREATEDPOST_LIKEDPOST_LIKEDViewStatusPUBLICPUBLICONLY_FOLLOWERSONLY_FOLLOWERSPRIVATEPRIVATEUserStringz_id🗝️StringemailStringphoneNumberStringhashPasswordStringnameStringprofileImageURLStringbackgroundImageURLIntageStringbirthdayIntisAdminDateTimecreatedAtDateTimeupdatedAtStringbiographyStringnotiIdsStringchatIDsStringendoseredIdsStringinterestCategoryIdsStringjoinedContestIdsNotificationStringz_id🗝️TypeNotitypeStringpostIdStringpostTitleStringpostImageDateTimecreatedAtStringuserIdsStringuserTriggerIdEndorsementStringz_id🗝️StringownerIdStringendorserIdsStringskillIdSkillStringz_id🗝️StringnameReportStringz_id🗝️BooleanisFinishedStringreasonStringpostIdStringuserIdStringuserReportedDateTimecreatedAtDateTimeupdatedAtFollowerStringz_id🗝️StringuserFollowerStringuserIdFollowingStringz_id🗝️StringuserFollowingStringuserIdLevelStringz_id🗝️IntcurrentXPIntcurrentLevelStringuserIdImageStringz_id🗝️StringurlStringhashDateTimecreatedAtDateTimeupdatedAtStringpostIdImageInfoStringz_id🗝️StringcameraStringlensStringapertureStringfocalLengthStringshutterSpeedStringISOStringtakenWhenStringcopyRightStringimageIdCategoryStringz_id🗝️StringnameDateTimecreatedAtDateTimeupdatedAtStringpostsStringstoriesStringinterestUserIdsAlbumStringz_id🗝️StringnameDateTimecreatedAtDateTimeupdatedAtStringuserIdStringpostsTagStringz_id🗝️StringnameDateTimecreatedAtDateTimeupdatedAtPostStringz_id🗝️StringtitleStringcaptionDateTimecreatedAtDateTimeupdatedAtViewStatuspostViewStatusIntpointsStringcontestIdStringuserIdStringcategoryIdStringalbumIdStringtagStringuserLikedPostStringreportedUserIdsStoryStringz_id🗝️StringtitleViewStatusstoryViewStatusDateTimecreatedAtDateTimeupdatedAtIntpointsStringcontentStringimagesStringtagStringcategoryIdStringuserIdStringuserLikedStoryStringreportedUserIdsCommentStringz_id🗝️StringcontentDateTimecreatedAtStringuserIdStringpostIdStringstoryIdStringparentIdIntvotesStringupVoteUserlistStringdownVoteUserlistContestStringz_id🗝️StringnameStringcontestImageURLStringdescriptionDateTimestartDateDateTimeendDateBooleanisFinishedStringjoinedUserIdsDateTimecreatedAtDateTimeupdatedAtContest_PrizeStringz_id🗝️StringcontestIdStringuserIdStringtitleStringtypeStringprizeImageURLChatStringz_id🗝️DateTimecreatedAtDateTimeupdatedAtDateTimelastMessageAtStringuserIDsMessageStringz_id🗝️StringmessageDateTimecreatedAtBooleanisImageStringuserIdStringchatIdlevelalbumspostsstoriescommentsfollowersfollowingsmessagesuser_to_notificationnotificationsTriggereduser_to_chatendoseredListendorsementsinterestCategoriescontestPrizeListjoinedContestsenum:typenotification_to_usernotification_to_usertriggerownerendorserskillendorsementsfollower_to_userfollowing_to_userlevel_to_userimage_to_postimageInfoIdimageInfo_to_imagecategory_to_postcategory_to_storycategory_to_useralbum_to_useralbum_to_postenum:postViewStatusimagecmtspost_to_userpost_to_categorypost_to_albumenum:storyViewStatuscommentsstory_to_categorystory_to_usercmt_to_usercmt_to_postcmt_to_storyparentchildjoinedUserListcontestPrizeListprize_to_contestusermessageschat_to_usermessage_to_usermessage_to_chat \ No newline at end of file diff --git a/src/prisma/schema.prisma b/src/prisma/schema.prisma index 67db270..c59892e 100644 --- a/src/prisma/schema.prisma +++ b/src/prisma/schema.prisma @@ -68,7 +68,7 @@ model User { interestCategoryIds String[] @db.ObjectId interestCategories Category[] @relation(name: "interest", fields: [interestCategoryIds], references: [id]) - submissions Contest_Score[] + // submissions Contest_Score[] contestPrizeList Contest_Prize[] joinedContestIds String[] @db.ObjectId @@ -251,7 +251,7 @@ model Post { userLikedPost String[] reportedUserIds String[] - submissions Contest_Score[] + // submissions Contest_Score[] } model Story { @@ -308,8 +308,10 @@ model Contest { contestImageURL String description String - startDate DateTime - endDate DateTime? + startDate DateTime @default(now()) + endDate DateTime + + isFinished Boolean joinedUserIds String[] @db.ObjectId joinedUserList User[] @relation(fields: [joinedUserIds],references: [id]) @@ -317,52 +319,55 @@ model Contest { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - scores Contest_Score[] + // scores Contest_Score[] contestPrizeList Contest_Prize[] } model Contest_Prize { id String @id @default(auto()) @map("_id") @db.ObjectId - contestId String @db.ObjectId - contest Contest @relation(fields: [contestId],references: [id]) + // contestId String @db.ObjectId + // contest Contest @relation(fields: [contestId],references: [id]) - prizeId String @db.ObjectId - prize Prize @relation(fields: [prizeId], references: [id]) + // prizeId String @db.ObjectId + // prize Prize @relation(fields: [prizeId], references: [id]) + + contestId String @db.ObjectId + prize_to_contest Contest @relation(fields: [contestId], references: [id]) userId String? @db.ObjectId user User? @relation(fields: [userId], references: [id]) title String type String - + prizeImageURL String } -model Prize { - id String @id @default(auto()) @map("_id") @db.ObjectId - name String - prizeImageURL String +// model Prize { +// id String @id @default(auto()) @map("_id") @db.ObjectId +// name String +// prizeImageURL String - contestPrizeList Contest_Prize[] -} +// contestPrizeList Contest_Prize[] +// } -model Contest_Score { - id String @id @default(auto()) @map("_id") @db.ObjectId +// model Contest_Score { +// id String @id @default(auto()) @map("_id") @db.ObjectId - contestId String @db.ObjectId - contest Contest @relation(fields: [contestId], references: [id]) +// contestId String @db.ObjectId +// contest Contest @relation(fields: [contestId], references: [id]) - userId String @db.ObjectId - user User @relation(fields: [userId], references: [id]) +// userId String @db.ObjectId +// user User @relation(fields: [userId], references: [id]) - postId String? @db.ObjectId - post Post? @relation(fields: [postId], references: [id]) +// postId String? @db.ObjectId +// post Post? @relation(fields: [postId], references: [id]) - score Int? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt +// score Int? +// createdAt DateTime @default(now()) +// updatedAt DateTime @updatedAt -} +// } // Chat Application model Chat { diff --git a/src/resolvers/Mutation/contest.js b/src/resolvers/Mutation/contest.js index b03f116..58dc994 100644 --- a/src/resolvers/Mutation/contest.js +++ b/src/resolvers/Mutation/contest.js @@ -7,15 +7,15 @@ const contestMutation = { * @param {*} info * @returns */ - createPrize: async (parent, args, info) => { - const { name, prizeImageURL } = args.data; - return prisma.prize.create({ - data: { - name: name, - prizeImageURL: prizeImageURL, - }, - }); - }, + // createPrize: async (parent, args, info) => { + // const { name, prizeImageURL } = args.data; + // return prisma.prize.create({ + // data: { + // name: name, + // prizeImageURL: prizeImageURL, + // }, + // }); + // }, /** * @param {*} parent @@ -26,30 +26,59 @@ const contestMutation = { */ createContest: async (parent, args, info) => { const { data } = args; - const { contestPrizeList } = data; + const result = await prisma.contest.create({ data: { name: data.name, contestImageURL: data.contestImageURL, description: data.description, + isFinished: false, startDate: new Date(data.startDate), - endDate: data.endDate ? new Date(data.endDate) : null, - contestPrizeList: { - create: contestPrizeList.map((each) => { - return { - prize: { - connect: { - id: each.prizeId, - }, - }, - type: each.type, - title: each.title, - }; - }), + endDate: new Date(data.endDate), + }, + }); + + return result; + }, + // !!!!!!!!!!!!!!!!!!!!!!!!!!!! + endContest: async (parent, args, info) => { + const { contestId } = args.data; + + const result = await prisma.contest.update({ + where: { + id: contestId, + }, + data: { + isFinished: true, + }, + }); + + const users = await prisma.post.findMany({ + where: { + contestId: contestId, + }, + orderBy: [ + { points: 'desc' }, + { + createdAt: 'desc', + }, + ], + take: 3, + include: { + userId: { + id: true, }, }, }); + console.log({ users }); + + // await prisma.contest_Prize.create({ + // data: { + // contestId: contestId, + // }, + // }); + return result; }, @@ -77,7 +106,7 @@ const contestMutation = { id: args.data.contestId, }, data: { - userJoined: { + joinedUserIds: { push: args.data.userId, }, }, diff --git a/src/resolvers/Mutation/user.js b/src/resolvers/Mutation/user.js index 4299be8..095fd9a 100644 --- a/src/resolvers/Mutation/user.js +++ b/src/resolvers/Mutation/user.js @@ -16,6 +16,7 @@ const userMutation = { isAdmin: 0, age: 18, notiIds: [], + contestPrizeList: [], level: { create: { currentXP: 0, @@ -161,6 +162,7 @@ const userMutation = { interestCategories: true, }, }); + if (!user) { throw Error('User is not existed'); } diff --git a/src/resolvers/Query/CONTEST.js b/src/resolvers/Query/CONTEST.js index 3cccf3c..0f9a049 100644 --- a/src/resolvers/Query/CONTEST.js +++ b/src/resolvers/Query/CONTEST.js @@ -20,53 +20,52 @@ const contestQuery = { * @param {*} info */ contestPosts: async (parent, args, info) => { - const { contestId } = args.data || {}; - const { after, limit } = args; + const { contestId, userId: currentUserId, after } = args; + console.log(args); + let nodes, a; - const [contestScores, count] = await Promise.all([ - prisma.contest_Score.findMany({ - take: limit || DEFAULT_LIMIT, - ...(after && { - skip: 1, - }), - where: { - id: contestId, - }, - include: { - post: true, + a = await prisma.post.findMany({ + where: { + contestId: contestId, + }, + orderBy: [ + { points: 'desc' }, + { + createdAt: 'desc', }, - ...(after && { - cursor: { - id: after, - }, - }), - }), - prisma.contest_Score.count(), - ]); + ], + }); + + a = _.sortBy(a, ({ userId }) => (userId === currentUserId ? 0 : 1)); + console.log({ a }); - const result = contestScores.map((each) => each.post); - console.log('Result', result); - console.log('count', count); + if (!after) { + nodes = a.slice(0, 4).map((post) => ({ + node: post, + cursor: post.id, + })); + + console.log({ nodes }); + } else { + const index = a.findIndex((post) => post.id === after); + nodes = a.slice(index + 1, index + 3).map((post) => ({ + node: post, + cursor: post.id, + })); + + console.log({ nodes }); + } - const sortedResult = result.sort( - (before, after) => after.votes - before.votes, - ); const hasNextPage = - sortedResult.length !== 0 && - sortedResult.length < count && - sortedResult.length === limit; - console.log('hasNextPage', hasNextPage); + nodes.length === 0 + ? false + : nodes.slice(-1)[0].cursor !== a.slice(-1)[0].id; - const nodes = sortedResult.map((each) => ({ - node: each, - cursor: each.id, - })); return { edges: nodes, pageInfo: { hasNextPage, hasPreviousPage: after ? true : false, - // startCursor, startCursor: nodes.length === 0 ? '' : nodes[0].cursor, endCursor: nodes.length === 0 ? '' : nodes.slice(-1)[0].cursor, }, @@ -79,27 +78,40 @@ const contestQuery = { * @param {*} info */ getTopContestPosts: async (parent, args, info) => { - const { contestId, top } = args; + // const { contestId, top } = args; + + // const result = await prisma.contest_Score.findMany({ + // where: { + // contestId: contestId, + // }, + // take: top, + // include: { + // post: true, + // }, + // orderBy: [ + // { score: 'desc' }, + // { + // createdAt: 'asc', + // }, + // ], + // }); + + // if (result.length === 0) return []; + + // return result.map((each) => each.post); - const result = await prisma.contest_Score.findMany({ + return await prisma.post.findMany({ where: { - contestId: contestId, - }, - take: top, - include: { - post: true, + contestId: args.data.contestId, }, orderBy: [ - { score: 'desc' }, + { points: 'desc' }, { - createdAt: 'asc', + createdAt: 'desc', }, ], + take: 5, }); - - if (result.length === 0) return []; - - return result.map((each) => each.post); }, }; diff --git a/src/resolvers/Type/ContestPrize___.js b/src/resolvers/Type/ContestPrize___.js new file mode 100644 index 0000000..05f169c --- /dev/null +++ b/src/resolvers/Type/ContestPrize___.js @@ -0,0 +1,20 @@ +import { prisma } from '../../prisma/database.js'; + +const Contest_Prize = { + contestId: async (parent, args, info) => { + return await prisma.contest.findMany({ + where: { + id: parent.contestId, + }, + }); + }, + userId: async (parent, args, info) => { + return await prisma.user.findMany({ + where: { + id: parent.userId, + }, + }); + }, +}; + +export default Contest_Prize; diff --git a/src/resolvers/Type/Contest___.js b/src/resolvers/Type/Contest___.js index 1deb81e..d285bb9 100644 --- a/src/resolvers/Type/Contest___.js +++ b/src/resolvers/Type/Contest___.js @@ -1,5 +1,20 @@ -// import { prisma } from '../../prisma/database.js'; +import { prisma } from '../../prisma/database.js'; -const Contest = {}; +const Contest = { + joinedUserIds: async (parent, args, info) => { + return await prisma.user.findMany({ + where: { + id: { in: parent.joinedUserIds }, + }, + }); + }, + contestPrizeList: async (parent, args, info) => { + return await prisma.contest_Prize.findMany({ + where: { + contestId: parent.id, + }, + }); + }, +}; export default Contest; diff --git a/src/resolvers/Type/User___.js b/src/resolvers/Type/User___.js index 43fb7ab..e74c348 100644 --- a/src/resolvers/Type/User___.js +++ b/src/resolvers/Type/User___.js @@ -29,6 +29,27 @@ const User = { }, }); }, + notiIds: async (parent, args, info) => { + return await prisma.album.findMany({ + where: { + userIds: { has: parent.id }, + }, + }); + }, + joinedContestIds: async (parent, args, info) => { + return await prisma.contest.findMany({ + where: { + id: { in: parent.joinedContestIds }, + }, + }); + }, + contestPrizeList: async (parent, args, info) => { + return await prisma.contest_prize.findMany({ + where: { + user: parent.id, + }, + }); + }, chatIDs: async (parent, args, info) => { return await prisma.chat.findMany({ where: { diff --git a/src/resolvers/Type/_Type.js b/src/resolvers/Type/_Type.js index 3c41b4c..aacce98 100644 --- a/src/resolvers/Type/_Type.js +++ b/src/resolvers/Type/_Type.js @@ -12,6 +12,7 @@ import Follower from './Follower___.js'; import Following from './Following___.js'; import Report from './Report___.js'; import Contest from './Contest___.js'; +import Contest_Prize from './ContestPrize___.js'; import Chat from './Chat___.js'; import Message from './Message___.js'; import Skill from './Skill___.js'; @@ -33,6 +34,7 @@ const Type = { Following, Report, Contest, + Contest_Prize, Chat, Message, Endorsement,