Skip to content

Commit c3cb5b5

Browse files
authored
Implemented the apps package for creating and managing App instances (#2)
* Implemented the apps package for creating and managing App instances * Improved documentation and API * Renamed context file to internal.go * Updated AppService test * Fixed typo in comment * Renamed package to app * Making the Cred field required in Conf for New() function; Panicking when calling Del() multiple times * Renamed helper function
1 parent b8731e4 commit c3cb5b5

File tree

3 files changed

+475
-0
lines changed

3 files changed

+475
-0
lines changed

app/app.go

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
// Package app is the entry point to the Firebase Admin SDK. It provides functionality for initializing and managing
2+
// App instances, which serve as central entities that provide access to various other Firebase services exposed from
3+
// the SDK.
4+
package app
5+
6+
import (
7+
"errors"
8+
"fmt"
9+
"sync"
10+
11+
"github.com/firebase/firebase-admin-go/credentials"
12+
"github.com/firebase/firebase-admin-go/internal"
13+
)
14+
15+
const defaultName string = "[DEFAULT]"
16+
17+
// apps holds all Firebase Apps initialized using this SDK.
18+
var apps = make(map[string]App)
19+
20+
// mutex guards access to package-level state variables (apps in particular)
21+
var mutex = &sync.Mutex{}
22+
23+
// An App holds configuration and state common to all Firebase services that are exposed from the SDK.
24+
//
25+
// Client code should initialize an App with a valid authentication credential, and then use it to access
26+
// Firebase services.
27+
type App interface {
28+
// Name returns the name of this App.
29+
Name() string
30+
31+
// Credential returns the credential used to initialize this App.
32+
Credential() credentials.Credential
33+
34+
// Auth returns an instance of the auth.Auth service.
35+
//
36+
// Multiple calls to Auth may return the same value. Auth panics if the App is already deleted.
37+
// Auth() auth.Auth
38+
39+
// Del gracefully terminates this App.
40+
//
41+
// Del stops any services associated with this App, and releases all allocated resources. Trying to obtain a
42+
// Firebase service from the App after a call to Del, or calling Del multiple times on an App will panic.
43+
Del()
44+
}
45+
46+
// Conf represents the configuration used to initialize an App.
47+
type Conf struct {
48+
Name string
49+
Cred credentials.Credential
50+
}
51+
52+
type appImpl struct {
53+
Conf *internal.AppConf
54+
Mutex *sync.Mutex
55+
Services map[string]internal.AppService
56+
Deleted bool
57+
}
58+
59+
func (a *appImpl) Name() string {
60+
return a.Conf.Name
61+
}
62+
63+
func (a *appImpl) Credential() credentials.Credential {
64+
return a.Conf.Cred
65+
}
66+
67+
func (a *appImpl) Del() {
68+
mutex.Lock()
69+
defer mutex.Unlock()
70+
a.Mutex.Lock()
71+
defer a.Mutex.Unlock()
72+
73+
a.checkNotDeleted()
74+
if _, exists := apps[a.Name()]; exists {
75+
delete(apps, a.Name())
76+
}
77+
78+
for _, s := range a.Services {
79+
s.Del()
80+
}
81+
a.Services = nil
82+
a.Deleted = true
83+
}
84+
85+
func (a *appImpl) checkNotDeleted() {
86+
if !a.Deleted {
87+
return
88+
}
89+
var msg string
90+
if a.Name() == defaultName {
91+
msg = "Default app is deleted."
92+
} else {
93+
msg = fmt.Sprintf("App %q is deleted.", a.Name())
94+
}
95+
panic(msg)
96+
}
97+
98+
// service returns the AppService identified by the specified ID. If the AppService does not exist yet, the
99+
// provided function is invoked to initialize a new instance.
100+
func (a *appImpl) service(id string, fn func() internal.AppService) internal.AppService {
101+
a.Mutex.Lock()
102+
defer a.Mutex.Unlock()
103+
a.checkNotDeleted()
104+
105+
var s internal.AppService
106+
var ok bool
107+
if s, ok = a.Services[id]; !ok {
108+
s = fn()
109+
a.Services[id] = s
110+
}
111+
return s
112+
}
113+
114+
// New initializes a new Firebase App using the specified configuration. New returns an error if
115+
// the given configuration is invalid, or if an App by the same name already exists.
116+
func New(c *Conf) (App, error) {
117+
mutex.Lock()
118+
defer mutex.Unlock()
119+
120+
if c == nil {
121+
return nil, errors.New("configuration must not be nil")
122+
} else if c.Cred == nil {
123+
return nil, errors.New("configuration must contain a valid Credential")
124+
}
125+
126+
var name string
127+
if c.Name == "" {
128+
name = defaultName
129+
} else {
130+
name = c.Name
131+
}
132+
133+
if _, exists := apps[name]; exists {
134+
var msg string
135+
if name == defaultName {
136+
msg = "The default Firebase app already exists. This means you called apps.New() multiple " +
137+
"times. If you want to initialize multiple apps, specify a unique name for each app " +
138+
"instance via the apps.Conf argument passed into apps.New()."
139+
} else {
140+
msg = fmt.Sprintf("Firebase app named %q already exists. This means you called apps.New() "+
141+
"multiple times with the same name argument. Make sure to provide a unique name in the "+
142+
"apps.Conf each time you call apps.New().", name)
143+
}
144+
return nil, errors.New(msg)
145+
}
146+
147+
a := &appImpl{
148+
Conf: &internal.AppConf{Name: name, Cred: c.Cred},
149+
Mutex: &sync.Mutex{},
150+
Services: make(map[string]internal.AppService),
151+
}
152+
apps[name] = a
153+
return a, nil
154+
}
155+
156+
// Default returns the default Firebase App. If the default App is not initialized, Default returns an error.
157+
func Default() (App, error) {
158+
return Get(defaultName)
159+
}
160+
161+
// Get returns the Firebase App identified by the specified name. If the specified App is not initialized, Get
162+
// returns an error.
163+
func Get(name string) (App, error) {
164+
mutex.Lock()
165+
defer mutex.Unlock()
166+
if app, ok := apps[name]; ok {
167+
return app, nil
168+
}
169+
170+
var msg string
171+
if name == defaultName {
172+
msg = "The default Firebase app does not exist. Make sure to initialize the SDK by calling app.New()."
173+
} else {
174+
msg = fmt.Sprintf("Firebase app named '%s' does not exist. Make sure to initialize the SDK by "+
175+
" calling app.New() with your app name in the app.Options argument.", name)
176+
}
177+
return nil, errors.New(msg)
178+
}

0 commit comments

Comments
 (0)