feat(session): carry the bot service_language on the Session wire
Thread the Telegram bot's service language (en/ru) from the session mint response through the gateway into the FlatBuffers Session, so the UI knows which bot the player signed in through. handleTelegramAuth refreshes the account's service language onto the response before minting (it was set after the fetched copy). Empty for a non-Telegram login.
This commit is contained in:
@@ -16,10 +16,11 @@ import (
|
|||||||
|
|
||||||
// sessionResponse is the credential returned by every auth endpoint.
|
// sessionResponse is the credential returned by every auth endpoint.
|
||||||
type sessionResponse struct {
|
type sessionResponse struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
UserID string `json:"user_id"`
|
UserID string `json:"user_id"`
|
||||||
IsGuest bool `json:"is_guest"`
|
IsGuest bool `json:"is_guest"`
|
||||||
DisplayName string `json:"display_name"`
|
DisplayName string `json:"display_name"`
|
||||||
|
ServiceLanguage string `json:"service_language"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// okResponse is a simple success acknowledgement.
|
// okResponse is a simple success acknowledgement.
|
||||||
@@ -159,10 +160,11 @@ type errorBody struct {
|
|||||||
// sessionResponseFor builds the credential payload for a minted session.
|
// sessionResponseFor builds the credential payload for a minted session.
|
||||||
func sessionResponseFor(token string, acc account.Account) sessionResponse {
|
func sessionResponseFor(token string, acc account.Account) sessionResponse {
|
||||||
return sessionResponse{
|
return sessionResponse{
|
||||||
Token: token,
|
Token: token,
|
||||||
UserID: acc.ID.String(),
|
UserID: acc.ID.String(),
|
||||||
IsGuest: acc.IsGuest,
|
IsGuest: acc.IsGuest,
|
||||||
DisplayName: acc.DisplayName,
|
DisplayName: acc.DisplayName,
|
||||||
|
ServiceLanguage: acc.ServiceLanguage,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ func (s *Server) handleTelegramAuth(c *gin.Context) {
|
|||||||
s.abortErr(c, err)
|
s.abortErr(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
acc.ServiceLanguage = req.ServiceLanguage // reflect this login's bot in the session response
|
||||||
s.mintSession(c, acc)
|
s.mintSession(c, acc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ import (
|
|||||||
|
|
||||||
// SessionResp is the credential minted by an auth operation.
|
// SessionResp is the credential minted by an auth operation.
|
||||||
type SessionResp struct {
|
type SessionResp struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
UserID string `json:"user_id"`
|
UserID string `json:"user_id"`
|
||||||
IsGuest bool `json:"is_guest"`
|
IsGuest bool `json:"is_guest"`
|
||||||
DisplayName string `json:"display_name"`
|
DisplayName string `json:"display_name"`
|
||||||
|
ServiceLanguage string `json:"service_language"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProfileResp is an account's own profile.
|
// ProfileResp is an account's own profile.
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ func encodeSession(s backendclient.SessionResp, supportedLangs []string) []byte
|
|||||||
token := b.CreateString(s.Token)
|
token := b.CreateString(s.Token)
|
||||||
uid := b.CreateString(s.UserID)
|
uid := b.CreateString(s.UserID)
|
||||||
name := b.CreateString(s.DisplayName)
|
name := b.CreateString(s.DisplayName)
|
||||||
|
svcLang := b.CreateString(s.ServiceLanguage)
|
||||||
langs := buildSupportedLanguagesVector(b, supportedLangs)
|
langs := buildSupportedLanguagesVector(b, supportedLangs)
|
||||||
fb.SessionStart(b)
|
fb.SessionStart(b)
|
||||||
fb.SessionAddToken(b, token)
|
fb.SessionAddToken(b, token)
|
||||||
@@ -43,6 +44,7 @@ func encodeSession(s backendclient.SessionResp, supportedLangs []string) []byte
|
|||||||
fb.SessionAddIsGuest(b, s.IsGuest)
|
fb.SessionAddIsGuest(b, s.IsGuest)
|
||||||
fb.SessionAddDisplayName(b, name)
|
fb.SessionAddDisplayName(b, name)
|
||||||
fb.SessionAddSupportedLanguages(b, langs)
|
fb.SessionAddSupportedLanguages(b, langs)
|
||||||
|
fb.SessionAddServiceLanguage(b, svcLang)
|
||||||
b.Finish(fb.SessionEnd(b))
|
b.Finish(fb.SessionEnd(b))
|
||||||
return b.FinishedBytes()
|
return b.FinishedBytes()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ func TestTelegramAuthForwardsSeedFields(t *testing.T) {
|
|||||||
t.Errorf("unexpected path %q", r.URL.Path)
|
t.Errorf("unexpected path %q", r.URL.Path)
|
||||||
}
|
}
|
||||||
_ = json.NewDecoder(r.Body).Decode(&gotBody)
|
_ = json.NewDecoder(r.Body).Decode(&gotBody)
|
||||||
_, _ = w.Write([]byte(`{"token":"tok-tg","user_id":"u-tg","is_guest":false,"display_name":"Иван"}`))
|
_, _ = w.Write([]byte(`{"token":"tok-tg","user_id":"u-tg","is_guest":false,"display_name":"Иван","service_language":"ru"}`))
|
||||||
})
|
})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
@@ -67,6 +67,11 @@ func TestTelegramAuthForwardsSeedFields(t *testing.T) {
|
|||||||
if got := sessionLanguages(sess); len(got) != 1 || got[0] != "ru" {
|
if got := sessionLanguages(sess); len(got) != 1 || got[0] != "ru" {
|
||||||
t.Errorf("session supported_languages = %v, want [ru]", got)
|
t.Errorf("session supported_languages = %v, want [ru]", got)
|
||||||
}
|
}
|
||||||
|
// The bot's service language rides the Session so the UI can build a share link to
|
||||||
|
// the same bot.
|
||||||
|
if got := string(sess.ServiceLanguage()); got != "ru" {
|
||||||
|
t.Errorf("session service_language = %q, want ru", got)
|
||||||
|
}
|
||||||
// The validated launch fields are forwarded so the backend can seed a new account;
|
// The validated launch fields are forwarded so the backend can seed a new account;
|
||||||
// service_language is recorded to route the account's later out-of-app push.
|
// service_language is recorded to route the account's later out-of-app push.
|
||||||
if gotBody["external_id"] != "42" || gotBody["language_code"] != "ru" || gotBody["first_name"] != "Иван" || gotBody["service_language"] != "ru" {
|
if gotBody["external_id"] != "42" || gotBody["language_code"] != "ru" || gotBody["first_name"] != "Иван" || gotBody["service_language"] != "ru" {
|
||||||
|
|||||||
@@ -123,6 +123,10 @@ table Session {
|
|||||||
is_guest:bool;
|
is_guest:bool;
|
||||||
display_name:string;
|
display_name:string;
|
||||||
supported_languages:[string];
|
supported_languages:[string];
|
||||||
|
// service_language is the language tag (en/ru) of the Telegram bot the session was
|
||||||
|
// minted through; empty for a non-Telegram login. The UI uses it to build a share
|
||||||
|
// link that points at the same bot.
|
||||||
|
service_language:string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ack is a simple success acknowledgement (e.g. an email-code request).
|
// Ack is a simple success acknowledgement (e.g. an email-code request).
|
||||||
|
|||||||
@@ -94,8 +94,16 @@ func (rcv *Session) SupportedLanguagesLength() int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rcv *Session) ServiceLanguage() []byte {
|
||||||
|
o := flatbuffers.UOffsetT(rcv._tab.Offset(14))
|
||||||
|
if o != 0 {
|
||||||
|
return rcv._tab.ByteVector(o + rcv._tab.Pos)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func SessionStart(builder *flatbuffers.Builder) {
|
func SessionStart(builder *flatbuffers.Builder) {
|
||||||
builder.StartObject(5)
|
builder.StartObject(6)
|
||||||
}
|
}
|
||||||
func SessionAddToken(builder *flatbuffers.Builder, token flatbuffers.UOffsetT) {
|
func SessionAddToken(builder *flatbuffers.Builder, token flatbuffers.UOffsetT) {
|
||||||
builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(token), 0)
|
builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(token), 0)
|
||||||
@@ -115,6 +123,9 @@ func SessionAddSupportedLanguages(builder *flatbuffers.Builder, supportedLanguag
|
|||||||
func SessionStartSupportedLanguagesVector(builder *flatbuffers.Builder, numElems int) flatbuffers.UOffsetT {
|
func SessionStartSupportedLanguagesVector(builder *flatbuffers.Builder, numElems int) flatbuffers.UOffsetT {
|
||||||
return builder.StartVector(4, numElems, 4)
|
return builder.StartVector(4, numElems, 4)
|
||||||
}
|
}
|
||||||
|
func SessionAddServiceLanguage(builder *flatbuffers.Builder, serviceLanguage flatbuffers.UOffsetT) {
|
||||||
|
builder.PrependUOffsetTSlot(5, flatbuffers.UOffsetT(serviceLanguage), 0)
|
||||||
|
}
|
||||||
func SessionEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
func SessionEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||||
return builder.EndObject()
|
return builder.EndObject()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,8 +58,15 @@ supportedLanguagesLength():number {
|
|||||||
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
|
return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serviceLanguage():string|null
|
||||||
|
serviceLanguage(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
|
||||||
|
serviceLanguage(optionalEncoding?:any):string|Uint8Array|null {
|
||||||
|
const offset = this.bb!.__offset(this.bb_pos, 14);
|
||||||
|
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
|
||||||
|
}
|
||||||
|
|
||||||
static startSession(builder:flatbuffers.Builder) {
|
static startSession(builder:flatbuffers.Builder) {
|
||||||
builder.startObject(5);
|
builder.startObject(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
static addToken(builder:flatbuffers.Builder, tokenOffset:flatbuffers.Offset) {
|
static addToken(builder:flatbuffers.Builder, tokenOffset:flatbuffers.Offset) {
|
||||||
@@ -94,18 +101,23 @@ static startSupportedLanguagesVector(builder:flatbuffers.Builder, numElems:numbe
|
|||||||
builder.startVector(4, numElems, 4);
|
builder.startVector(4, numElems, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static addServiceLanguage(builder:flatbuffers.Builder, serviceLanguageOffset:flatbuffers.Offset) {
|
||||||
|
builder.addFieldOffset(5, serviceLanguageOffset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static endSession(builder:flatbuffers.Builder):flatbuffers.Offset {
|
static endSession(builder:flatbuffers.Builder):flatbuffers.Offset {
|
||||||
const offset = builder.endObject();
|
const offset = builder.endObject();
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createSession(builder:flatbuffers.Builder, tokenOffset:flatbuffers.Offset, userIdOffset:flatbuffers.Offset, isGuest:boolean, displayNameOffset:flatbuffers.Offset, supportedLanguagesOffset:flatbuffers.Offset):flatbuffers.Offset {
|
static createSession(builder:flatbuffers.Builder, tokenOffset:flatbuffers.Offset, userIdOffset:flatbuffers.Offset, isGuest:boolean, displayNameOffset:flatbuffers.Offset, supportedLanguagesOffset:flatbuffers.Offset, serviceLanguageOffset:flatbuffers.Offset):flatbuffers.Offset {
|
||||||
Session.startSession(builder);
|
Session.startSession(builder);
|
||||||
Session.addToken(builder, tokenOffset);
|
Session.addToken(builder, tokenOffset);
|
||||||
Session.addUserId(builder, userIdOffset);
|
Session.addUserId(builder, userIdOffset);
|
||||||
Session.addIsGuest(builder, isGuest);
|
Session.addIsGuest(builder, isGuest);
|
||||||
Session.addDisplayName(builder, displayNameOffset);
|
Session.addDisplayName(builder, displayNameOffset);
|
||||||
Session.addSupportedLanguages(builder, supportedLanguagesOffset);
|
Session.addSupportedLanguages(builder, supportedLanguagesOffset);
|
||||||
|
Session.addServiceLanguage(builder, serviceLanguageOffset);
|
||||||
return Session.endSession(builder);
|
return Session.endSession(builder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ describe('codec', () => {
|
|||||||
isGuest: true,
|
isGuest: true,
|
||||||
displayName: 'Me',
|
displayName: 'Me',
|
||||||
supportedLanguages: ['en', 'ru'],
|
supportedLanguages: ['en', 'ru'],
|
||||||
|
serviceLanguage: '',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -311,7 +312,7 @@ describe('codec', () => {
|
|||||||
b.finish(fb.LinkResult.endLinkResult(b));
|
b.finish(fb.LinkResult.endLinkResult(b));
|
||||||
const r = decodeLinkResult(b.asUint8Array());
|
const r = decodeLinkResult(b.asUint8Array());
|
||||||
expect(r.status).toBe('merged');
|
expect(r.status).toBe('merged');
|
||||||
expect(r.session).toEqual({ token: 'tok-9', userId: 'a-1', isGuest: false, displayName: 'Kaya', supportedLanguages: [] });
|
expect(r.session).toEqual({ token: 'tok-9', userId: 'a-1', isGuest: false, displayName: 'Kaya', supportedLanguages: [], serviceLanguage: '' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('decodes an Invitation with inviter and invitees', () => {
|
it('decodes an Invitation with inviter and invitees', () => {
|
||||||
|
|||||||
@@ -292,6 +292,7 @@ function sessionFromTable(t: fb.Session): Session {
|
|||||||
isGuest: t.isGuest(),
|
isGuest: t.isGuest(),
|
||||||
displayName: s(t.displayName()),
|
displayName: s(t.displayName()),
|
||||||
supportedLanguages,
|
supportedLanguages,
|
||||||
|
serviceLanguage: s(t.serviceLanguage()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export const SESSION: Session = {
|
|||||||
displayName: 'You',
|
displayName: 'You',
|
||||||
// Both languages by default, so the mock-driven UI offers every variant.
|
// Both languages by default, so the mock-driven UI offers every variant.
|
||||||
supportedLanguages: ['en', 'ru'],
|
supportedLanguages: ['en', 'ru'],
|
||||||
|
serviceLanguage: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PROFILE: Profile = {
|
export const PROFILE: Profile = {
|
||||||
|
|||||||
@@ -234,6 +234,9 @@ export interface Session {
|
|||||||
// variants these languages support (en -> English; ru -> Russian + Эрудит). Empty
|
// variants these languages support (en -> English; ru -> Russian + Эрудит). Empty
|
||||||
// means ungated (all variants).
|
// means ungated (all variants).
|
||||||
supportedLanguages: string[];
|
supportedLanguages: string[];
|
||||||
|
// serviceLanguage is the en/ru tag of the Telegram bot this session was minted
|
||||||
|
// through (empty for a non-Telegram login); used to share a link to the same bot.
|
||||||
|
serviceLanguage: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LinkResult is the outcome of an account link/merge step. status is
|
// LinkResult is the outcome of an account link/merge step. status is
|
||||||
|
|||||||
Reference in New Issue
Block a user