Skip to content

Commit 94be7a4

Browse files
authored
Google Cloud Storage API (#27)
* Changed the app.Auth() to accept a context * Added GCS API * Fixing GCS tests for Travis environment * Fixing GCS tests for Travis environment * Fixing failing tests
1 parent a5b0a27 commit 94be7a4

File tree

10 files changed

+382
-66
lines changed

10 files changed

+382
-66
lines changed

auth/auth.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"strings"
2626

2727
"firebase.google.com/go/internal"
28+
"golang.org/x/net/context"
2829
)
2930

3031
const firebaseAudience = "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
@@ -73,7 +74,7 @@ type signer interface {
7374
//
7475
// This function can only be invoked from within the SDK. Client applications should access the
7576
// the Auth service through firebase.App.
76-
func NewClient(c *internal.AuthConfig) (*Client, error) {
77+
func NewClient(ctx context.Context, c *internal.AuthConfig) (*Client, error) {
7778
var (
7879
err error
7980
email string
@@ -99,13 +100,13 @@ func NewClient(c *internal.AuthConfig) (*Client, error) {
99100
if email != "" && pk != nil {
100101
snr = serviceAcctSigner{email: email, pk: pk}
101102
} else {
102-
snr, err = newSigner(c.Ctx)
103+
snr, err = newSigner(ctx)
103104
if err != nil {
104105
return nil, err
105106
}
106107
}
107108

108-
ks, err := newHTTPKeySource(c.Ctx, googleCertURL, c.Opts...)
109+
ks, err := newHTTPKeySource(ctx, googleCertURL, c.Opts...)
109110
if err != nil {
110111
return nil, err
111112
}

auth/auth_test.go

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,16 @@ func TestMain(m *testing.M) {
6060
log.Fatalln(err)
6161
}
6262
} else {
63-
opt := option.WithCredentialsFile("../testdata/service_account.json")
64-
creds, err = transport.Creds(context.Background(), opt)
63+
ctx = context.Background()
64+
creds, err = transport.Creds(ctx, option.WithCredentialsFile("../testdata/service_account.json"))
6565
if err != nil {
6666
log.Fatalln(err)
6767
}
6868

6969
ks = &fileKeySource{FilePath: "../testdata/public_certs.json"}
7070
}
7171

72-
client, err = NewClient(&internal.AuthConfig{
73-
Ctx: ctx,
72+
client, err = NewClient(ctx, &internal.AuthConfig{
7473
Creds: creds,
7574
ProjectID: "mock-project-id",
7675
})
@@ -87,11 +86,8 @@ func TestNewClientInvalidCredentials(t *testing.T) {
8786
creds := &google.DefaultCredentials{
8887
JSON: []byte("foo"),
8988
}
90-
conf := &internal.AuthConfig{
91-
Ctx: context.Background(),
92-
Creds: creds,
93-
}
94-
if c, err := NewClient(conf); c != nil || err == nil {
89+
conf := &internal.AuthConfig{Creds: creds}
90+
if c, err := NewClient(context.Background(), conf); c != nil || err == nil {
9591
t.Errorf("NewCient() = (%v,%v); want = (nil, error)", c, err)
9692
}
9793
}
@@ -106,11 +102,8 @@ func TestNewClientInvalidPrivateKey(t *testing.T) {
106102
t.Fatal(err)
107103
}
108104
creds := &google.DefaultCredentials{JSON: b}
109-
conf := &internal.AuthConfig{
110-
Ctx: context.Background(),
111-
Creds: creds,
112-
}
113-
if c, err := NewClient(conf); c != nil || err == nil {
105+
conf := &internal.AuthConfig{Creds: creds}
106+
if c, err := NewClient(context.Background(), conf); c != nil || err == nil {
114107
t.Errorf("NewCient() = (%v,%v); want = (nil, error)", c, err)
115108
}
116109
}
@@ -165,7 +158,7 @@ func TestCustomTokenError(t *testing.T) {
165158
}
166159

167160
func TestCustomTokenInvalidCredential(t *testing.T) {
168-
s, err := NewClient(&internal.AuthConfig{Ctx: context.Background()})
161+
s, err := NewClient(context.Background(), &internal.AuthConfig{})
169162
if err != nil {
170163
t.Fatal(err)
171164
}
@@ -232,7 +225,7 @@ func TestVerifyIDTokenError(t *testing.T) {
232225
}
233226

234227
func TestNoProjectID(t *testing.T) {
235-
c, err := NewClient(&internal.AuthConfig{Ctx: context.Background()})
228+
c, err := NewClient(context.Background(), &internal.AuthConfig{})
236229
if err != nil {
237230
t.Fatal(err)
238231
}

firebase.go

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package firebase
2020
import (
2121
"firebase.google.com/go/auth"
2222
"firebase.google.com/go/internal"
23+
"firebase.google.com/go/storage"
2324

2425
"os"
2526

@@ -30,6 +31,7 @@ import (
3031
)
3132

3233
var firebaseScopes = []string{
34+
"https://www.googleapis.com/auth/devstorage.full_control",
3335
"https://www.googleapis.com/auth/firebase",
3436
"https://www.googleapis.com/auth/userinfo.email",
3537
}
@@ -39,26 +41,35 @@ const Version = "1.0.2"
3941

4042
// An App holds configuration and state common to all Firebase services that are exposed from the SDK.
4143
type App struct {
42-
ctx context.Context
43-
creds *google.DefaultCredentials
44-
projectID string
45-
opts []option.ClientOption
44+
creds *google.DefaultCredentials
45+
projectID string
46+
storageBucket string
47+
opts []option.ClientOption
4648
}
4749

4850
// Config represents the configuration used to initialize an App.
4951
type Config struct {
50-
ProjectID string
52+
ProjectID string
53+
StorageBucket string
5154
}
5255

5356
// Auth returns an instance of auth.Client.
54-
func (a *App) Auth() (*auth.Client, error) {
57+
func (a *App) Auth(ctx context.Context) (*auth.Client, error) {
5558
conf := &internal.AuthConfig{
56-
Ctx: a.ctx,
5759
Creds: a.creds,
5860
ProjectID: a.projectID,
5961
Opts: a.opts,
6062
}
61-
return auth.NewClient(conf)
63+
return auth.NewClient(ctx, conf)
64+
}
65+
66+
// Storage returns a new instance of storage.Client.
67+
func (a *App) Storage(ctx context.Context) (*storage.Client, error) {
68+
conf := &internal.StorageConfig{
69+
Opts: a.opts,
70+
Bucket: a.storageBucket,
71+
}
72+
return storage.NewClient(ctx, conf)
6273
}
6374

6475
// NewApp creates a new App from the provided config and client options.
@@ -75,8 +86,12 @@ func NewApp(ctx context.Context, config *Config, opts ...option.ClientOption) (*
7586
return nil, err
7687
}
7788

89+
if config == nil {
90+
config = &Config{}
91+
}
92+
7893
var pid string
79-
if config != nil && config.ProjectID != "" {
94+
if config.ProjectID != "" {
8095
pid = config.ProjectID
8196
} else if creds.ProjectID != "" {
8297
pid = creds.ProjectID
@@ -85,9 +100,9 @@ func NewApp(ctx context.Context, config *Config, opts ...option.ClientOption) (*
85100
}
86101

87102
return &App{
88-
ctx: ctx,
89-
creds: creds,
90-
projectID: pid,
91-
opts: o,
103+
creds: creds,
104+
projectID: pid,
105+
storageBucket: config.StorageBucket,
106+
opts: o,
92107
}, nil
93108
}

firebase_test.go

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,18 @@ func TestServiceAcctFile(t *testing.T) {
4040
if err != nil {
4141
t.Fatal(err)
4242
}
43-
verifyServiceAcct(t, app)
43+
44+
if app.projectID != "mock-project-id" {
45+
t.Errorf("Project ID: %q; want: %q", app.projectID, "mock-project-id")
46+
}
47+
if len(app.opts) != 2 {
48+
t.Errorf("Client opts: %d; want: 2", len(app.opts))
49+
}
50+
if app.creds == nil {
51+
t.Error("Credentials: nil; want creds")
52+
} else if len(app.creds.JSON) == 0 {
53+
t.Error("JSON: empty; want; non-empty")
54+
}
4455
}
4556

4657
func TestClientOptions(t *testing.T) {
@@ -90,8 +101,10 @@ func TestRefreshTokenFile(t *testing.T) {
90101
if len(app.opts) != 2 {
91102
t.Errorf("Client opts: %d; want: 2", len(app.opts))
92103
}
93-
if app.ctx == nil {
94-
t.Error("Context: nil; want: ctx")
104+
if app.creds == nil {
105+
t.Error("Credentials: nil; want creds")
106+
} else if len(app.creds.JSON) == 0 {
107+
t.Error("JSON: empty; want; non-empty")
95108
}
96109
}
97110

@@ -107,8 +120,10 @@ func TestRefreshTokenFileWithConfig(t *testing.T) {
107120
if len(app.opts) != 2 {
108121
t.Errorf("Client opts: %d; want: 2", len(app.opts))
109122
}
110-
if app.ctx == nil {
111-
t.Error("Context: nil; want: ctx")
123+
if app.creds == nil {
124+
t.Error("Credentials: nil; want creds")
125+
} else if len(app.creds.JSON) == 0 {
126+
t.Error("JSON: empty; want; non-empty")
112127
}
113128
}
114129

@@ -128,11 +143,10 @@ func TestRefreshTokenWithEnvVar(t *testing.T) {
128143
if app.projectID != "mock-project-id" {
129144
t.Errorf("Project ID: %q; want: mock-project-id", app.projectID)
130145
}
131-
if len(app.opts) != 2 {
132-
t.Errorf("Client opts: %d; want: 2", len(app.opts))
133-
}
134-
if app.ctx == nil {
135-
t.Error("Context: nil; want: ctx")
146+
if app.creds == nil {
147+
t.Error("Credentials: nil; want creds")
148+
} else if len(app.creds.JSON) == 0 {
149+
t.Error("JSON: empty; want; non-empty")
136150
}
137151
}
138152

@@ -145,17 +159,18 @@ func TestAppDefault(t *testing.T) {
145159
}
146160
defer os.Setenv(varName, current)
147161

148-
ctx := context.Background()
149-
app, err := NewApp(ctx, nil)
162+
app, err := NewApp(context.Background(), nil)
150163
if err != nil {
151164
t.Fatal(err)
152165
}
153166

154167
if len(app.opts) != 1 {
155168
t.Errorf("Client opts: %d; want: 1", len(app.opts))
156169
}
157-
if app.ctx == nil {
158-
t.Error("Context: nil; want: ctx")
170+
if app.creds == nil {
171+
t.Error("Credentials: nil; want creds")
172+
} else if len(app.creds.JSON) == 0 {
173+
t.Error("JSON: empty; want; non-empty")
159174
}
160175
}
161176

@@ -190,16 +205,29 @@ func TestInvalidCredentialFile(t *testing.T) {
190205
}
191206

192207
func TestAuth(t *testing.T) {
193-
app, err := NewApp(context.Background(), nil, option.WithCredentialsFile("testdata/service_account.json"))
208+
ctx := context.Background()
209+
app, err := NewApp(ctx, nil, option.WithCredentialsFile("testdata/service_account.json"))
194210
if err != nil {
195211
t.Fatal(err)
196212
}
197213

198-
if c, err := app.Auth(); c == nil || err != nil {
214+
if c, err := app.Auth(ctx); c == nil || err != nil {
199215
t.Errorf("Auth() = (%v, %v); want (auth, nil)", c, err)
200216
}
201217
}
202218

219+
func TestStorage(t *testing.T) {
220+
ctx := context.Background()
221+
app, err := NewApp(ctx, nil, option.WithCredentialsFile("testdata/service_account.json"))
222+
if err != nil {
223+
t.Fatal(err)
224+
}
225+
226+
if c, err := app.Storage(ctx); c == nil || err != nil {
227+
t.Errorf("Storage() = (%v, %v); want (auth, nil)", c, err)
228+
}
229+
}
230+
203231
func TestCustomTokenSource(t *testing.T) {
204232
ctx := context.Background()
205233
ts := &testTokenSource{AccessToken: "mock-token-from-custom"}
@@ -257,19 +285,6 @@ func (t *testTokenSource) Token() (*oauth2.Token, error) {
257285
}, nil
258286
}
259287

260-
func verifyServiceAcct(t *testing.T, app *App) {
261-
// TODO: Compare creds JSON
262-
if app.projectID != "mock-project-id" {
263-
t.Errorf("Project ID: %q; want: %q", app.projectID, "mock-project-id")
264-
}
265-
if len(app.opts) != 2 {
266-
t.Errorf("Client opts: %d; want: 2", len(app.opts))
267-
}
268-
if app.ctx == nil {
269-
t.Error("Context: nil; want: ctx")
270-
}
271-
}
272-
273288
// mockServiceAcct generates a service account configuration with the provided URL as the
274289
// token_url value.
275290
func mockServiceAcct(tokenURL string) ([]byte, error) {

integration/auth/auth_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,13 @@ func TestMain(m *testing.M) {
4343
os.Exit(0)
4444
}
4545

46-
app, err := internal.NewTestApp(context.Background())
46+
ctx := context.Background()
47+
app, err := internal.NewTestApp(ctx)
4748
if err != nil {
4849
log.Fatalln(err)
4950
}
5051

51-
client, err = app.Auth()
52+
client, err = app.Auth(ctx)
5253
if err != nil {
5354
log.Fatalln(err)
5455
}
@@ -139,5 +140,8 @@ func postRequest(url string, req []byte) ([]byte, error) {
139140
}
140141

141142
defer resp.Body.Close()
143+
if resp.StatusCode != 200 {
144+
return nil, fmt.Errorf("unexpected http status code: %d", resp.StatusCode)
145+
}
142146
return ioutil.ReadAll(resp.Body)
143147
}

integration/internal/internal.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package internal
1717

1818
import (
19+
"encoding/json"
1920
"go/build"
2021
"io/ioutil"
2122
"path/filepath"
@@ -42,7 +43,14 @@ func Resource(name string) string {
4243
// in the testdata directory. This file is used to initialize the newly created
4344
// App instance.
4445
func NewTestApp(ctx context.Context) (*firebase.App, error) {
45-
return firebase.NewApp(ctx, nil, option.WithCredentialsFile(Resource(certPath)))
46+
pid, err := ProjectID()
47+
if err != nil {
48+
return nil, err
49+
}
50+
config := &firebase.Config{
51+
StorageBucket: pid + ".appspot.com",
52+
}
53+
return firebase.NewApp(ctx, config, option.WithCredentialsFile(Resource(certPath)))
4654
}
4755

4856
// APIKey fetches a Firebase API key for integration tests.
@@ -56,3 +64,18 @@ func APIKey() (string, error) {
5664
}
5765
return strings.TrimSpace(string(b)), nil
5866
}
67+
68+
// ProjectID fetches a Google Cloud project ID for integration tests.
69+
func ProjectID() (string, error) {
70+
b, err := ioutil.ReadFile(Resource(certPath))
71+
if err != nil {
72+
return "", err
73+
}
74+
var serviceAccount struct {
75+
ProjectID string `json:"project_id"`
76+
}
77+
if err := json.Unmarshal(b, &serviceAccount); err != nil {
78+
return "", err
79+
}
80+
return serviceAccount.ProjectID, nil
81+
}

0 commit comments

Comments
 (0)