119 lines
5.0 KiB
Scala
119 lines
5.0 KiB
Scala
package com.twitter.tweetypie.additionalfields
|
|
|
|
import com.twitter.tweetypie.thriftscala.Tweet
|
|
import com.twitter.scrooge.TFieldBlob
|
|
import com.twitter.scrooge.ThriftStructField
|
|
|
|
object AdditionalFields {
|
|
type FieldId = Short
|
|
|
|
/** additional fields really start at 100, be we are ignoring conversation id for now */
|
|
val StartAdditionalId = 101
|
|
|
|
/** all known [[Tweet]] field IDs */
|
|
val CompiledFieldIds: Seq[FieldId] = Tweet.metaData.fields.map(_.id)
|
|
|
|
/** all known [[Tweet]] fields in the "additional-field" range (excludes id) */
|
|
val CompiledAdditionalFieldMetaDatas: Seq[ThriftStructField[Tweet]] =
|
|
Tweet.metaData.fields.filter(f => isAdditionalFieldId(f.id))
|
|
|
|
val CompiledAdditionalFieldsMap: Map[Short, ThriftStructField[Tweet]] =
|
|
CompiledAdditionalFieldMetaDatas.map(field => (field.id, field)).toMap
|
|
|
|
/** all known [[Tweet]] field IDs in the "additional-field" range */
|
|
val CompiledAdditionalFieldIds: Seq[FieldId] =
|
|
CompiledAdditionalFieldsMap.keys.toSeq
|
|
|
|
/** all [[Tweet]] field IDs which should be rejected when set as additional
|
|
* fields on via PostTweetRequest.additionalFields or RetweetRequest.additionalFields */
|
|
val RejectedFieldIds: Seq[FieldId] = Seq(
|
|
// Should be provided via PostTweetRequest.conversationControl field. go/convocontrolsbackend
|
|
Tweet.ConversationControlField.id,
|
|
// This field should only be set based on whether the client sets the right community
|
|
// tweet annotation.
|
|
Tweet.CommunitiesField.id,
|
|
// This field should not be set by clients and should opt for
|
|
// [[PostTweetRequest.ExclusiveTweetControlOptions]].
|
|
// The exclusiveTweetControl field requires the userId to be set
|
|
// and we shouldn't trust the client to provide the right one.
|
|
Tweet.ExclusiveTweetControlField.id,
|
|
// This field should not be set by clients and should opt for
|
|
// [[PostTweetRequest.TrustedFriendsControlOptions]].
|
|
// The trustedFriendsControl field requires the trustedFriendsListId to be
|
|
// set and we shouldn't trust the client to provide the right one.
|
|
Tweet.TrustedFriendsControlField.id,
|
|
// This field should not be set by clients and should opt for
|
|
// [[PostTweetRequest.CollabControlOptions]].
|
|
// The collabControl field requires a list of Collaborators to be
|
|
// set and we shouldn't trust the client to provide the right one.
|
|
Tweet.CollabControlField.id
|
|
)
|
|
|
|
def isAdditionalFieldId(fieldId: FieldId): Boolean =
|
|
fieldId >= StartAdditionalId
|
|
|
|
/**
|
|
* Provides a list of all additional field IDs on the tweet, which include all
|
|
* the compiled additional fields and all the provided passthrough fields. This includes
|
|
* compiled additional fields where the value is None.
|
|
*/
|
|
def allAdditionalFieldIds(tweet: Tweet): Seq[FieldId] =
|
|
CompiledAdditionalFieldIds ++ tweet._passthroughFields.keys
|
|
|
|
/**
|
|
* Provides a list of all field IDs that have a value on the tweet which are not known compiled
|
|
* additional fields (excludes [[Tweet.id]]).
|
|
*/
|
|
def unsettableAdditionalFieldIds(tweet: Tweet): Seq[FieldId] =
|
|
CompiledFieldIds
|
|
.filter { id =>
|
|
!isAdditionalFieldId(id) && id != Tweet.IdField.id && tweet.getFieldBlob(id).isDefined
|
|
} ++
|
|
tweet._passthroughFields.keys
|
|
|
|
/**
|
|
* Provides a list of all field IDs that have a value on the tweet which are explicitly disallowed
|
|
* from being set via PostTweetRequest.additionalFields and RetweetRequest.additionalFields
|
|
*/
|
|
def rejectedAdditionalFieldIds(tweet: Tweet): Seq[FieldId] =
|
|
RejectedFieldIds
|
|
.filter { id => tweet.getFieldBlob(id).isDefined }
|
|
|
|
def unsettableAdditionalFieldIdsErrorMessage(unsettableFieldIds: Seq[FieldId]): String =
|
|
s"request may not contain fields: [${unsettableFieldIds.sorted.mkString(", ")}]"
|
|
|
|
/**
|
|
* Provides a list of all additional field IDs that have a value on the tweet,
|
|
* compiled and passthrough (excludes Tweet.id).
|
|
*/
|
|
def nonEmptyAdditionalFieldIds(tweet: Tweet): Seq[FieldId] =
|
|
CompiledAdditionalFieldMetaDatas.collect {
|
|
case f if f.getValue(tweet) != None => f.id
|
|
} ++ tweet._passthroughFields.keys
|
|
|
|
def additionalFields(tweet: Tweet): Seq[TFieldBlob] =
|
|
(tweet.getFieldBlobs(CompiledAdditionalFieldIds) ++ tweet._passthroughFields).values.toSeq
|
|
|
|
/**
|
|
* Merge base tweet with additional fields.
|
|
* Non-additional fields in the additional tweet are ignored.
|
|
* @param base: a tweet that contains basic fields
|
|
* @param additional: a tweet object that carries additional fields
|
|
*/
|
|
def setAdditionalFields(base: Tweet, additional: Tweet): Tweet =
|
|
setAdditionalFields(base, additionalFields(additional))
|
|
|
|
def setAdditionalFields(base: Tweet, additional: Option[Tweet]): Tweet =
|
|
additional.map(setAdditionalFields(base, _)).getOrElse(base)
|
|
|
|
def setAdditionalFields(base: Tweet, additional: Traversable[TFieldBlob]): Tweet =
|
|
additional.foldLeft(base) { case (t, f) => t.setField(f) }
|
|
|
|
/**
|
|
* Unsets the specified fields on the given tweet.
|
|
*/
|
|
def unsetFields(tweet: Tweet, fieldIds: Iterable[FieldId]): Tweet = {
|
|
tweet.unsetFields(fieldIds.toSet)
|
|
}
|
|
}
|