Promote development → master (initial production release: pre-release line + Stage 18) #104
@@ -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