Skip to content

Commit 65692c9

Browse files
committed
In progress
1 parent d2adf4e commit 65692c9

File tree

7 files changed

+249
-4
lines changed

7 files changed

+249
-4
lines changed

packages/storage/exp/index.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,27 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { _registerComponent, registerVersion } from '@firebase/app-exp';
18+
import {
19+
_registerComponent,
20+
registerVersion,
21+
_getProvider
22+
} from '@firebase/app-exp';
1923

2024
import { XhrIoPool } from '../src/implementation/xhriopool';
2125
import { StorageService } from '../src/service';
2226
import {
2327
Component,
2428
ComponentType,
25-
ComponentContainer
29+
ComponentContainer,
30+
Provider
2631
} from '@firebase/component';
2732

2833
import { name, version } from '../package.json';
34+
import { FirebaseApp } from '@firebase/app-types-exp';
2935

3036
export { ref } from '../src/service';
3137
export {
38+
uploadBytes,
3239
uploadBytesResumable,
3340
uploadString,
3441
getMetadata,
@@ -44,6 +51,16 @@ export {
4451
*/
4552
const STORAGE_TYPE = 'storage-exp';
4653

54+
export function getStorage(app: FirebaseApp): StorageService {
55+
// Dependencies
56+
const storageProvider: Provider<'storage-exp'> = _getProvider(
57+
app,
58+
STORAGE_TYPE
59+
);
60+
const storageInstance = storageProvider.getImmediate();
61+
return storageInstance;
62+
}
63+
4764
function factory(container: ComponentContainer, url?: string): StorageService {
4865
// Dependencies
4966
const app = container.getProvider('app-exp').getImmediate();

packages/storage/src/implementation/requests.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,36 @@ export function metadataForUpload_(
279279
return metadataClone;
280280
}
281281

282+
export function simpleUpload(
283+
service: StorageService,
284+
location: Location,
285+
mappings: MetadataUtils.Mappings,
286+
blob: FbsBlob,
287+
metadata?: Metadata | null
288+
): RequestInfo<Metadata> {
289+
const urlPart = location.bucketOnlyServerUrl();
290+
const headers: { [prop: string]: string } = {
291+
'Content-Type': 'application/octet-stream'
292+
};
293+
294+
const metadata_ = metadataForUpload_(location, blob, metadata);
295+
const urlParams: UrlParams = { name: metadata_['fullPath']! };
296+
const url = UrlUtils.makeUrl(urlPart);
297+
const method = 'POST';
298+
const timeout = service.maxUploadRetryTime;
299+
const requestInfo = new RequestInfo(
300+
url,
301+
method,
302+
metadataHandler(service, mappings),
303+
timeout
304+
);
305+
requestInfo.urlParams = urlParams;
306+
requestInfo.headers = headers;
307+
requestInfo.body = blob.uploadData();
308+
requestInfo.errorHandler = sharedErrorHandler(location);
309+
return requestInfo;
310+
}
311+
282312
export function multipartUpload(
283313
service: StorageService,
284314
location: Location,

packages/storage/src/reference.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import { ListOptions, ListResult } from './list';
3737
import { UploadTask } from './task';
3838
import { invalidRootOperation, noDownloadURL } from './implementation/error';
3939
import { validateNumber } from './implementation/type';
40+
import { UploadTaskNonResumableSnapshot } from './tasksnapshot';
41+
import * as fbsRequests from './implementation/requests';
4042

4143
/**
4244
* Provides methods to interact with a bucket in the Firebase Storage service.
@@ -118,7 +120,37 @@ export class Reference {
118120
}
119121

120122
/**
121-
* Uploads a blob to this object's location.
123+
* Uploads data to this object's location.
124+
* The upload is not resumable.
125+
* @public
126+
* @param ref - Storage Reference where data should be uploaded.
127+
* @param data - The data to upload.
128+
* @param metadata - Metadata for the newly uploaded string.
129+
* @returns An UploadTask that lets you control and
130+
* observe the upload.
131+
*/
132+
export async function uploadBytes(
133+
ref: Reference,
134+
data: Blob | Uint8Array | ArrayBuffer,
135+
metadata: Metadata | null = null
136+
): Promise<UploadTaskNonResumableSnapshot> {
137+
ref._throwIfRoot('uploadBytes');
138+
const authToken = await ref.storage.getAuthToken();
139+
const requestInfo = fbsRequests.multipartUpload(
140+
ref.storage,
141+
ref._location,
142+
getMappings(),
143+
new FbsBlob(data),
144+
metadata
145+
);
146+
const multipartRequest = ref.storage.makeRequest(requestInfo, authToken);
147+
const finalMetadata = metadata || (await multipartRequest.getPromise());
148+
return new UploadTaskNonResumableSnapshot(finalMetadata, ref);
149+
}
150+
151+
/**
152+
* Uploads data to this object's location.
153+
* The upload is resumable and exposes progress updates.
122154
* @public
123155
* @param ref - Storage Reference where data should be uploaded.
124156
* @param data - The data to upload.

packages/storage/src/tasksnapshot.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,6 @@ export class UploadTaskSnapshot {
2929
readonly ref: Reference
3030
) {}
3131
}
32+
export class UploadTaskNonResumableSnapshot {
33+
constructor(readonly metadata: Metadata, readonly ref: Reference) {}
34+
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
// eslint-disable-next-line import/no-extraneous-dependencies
19+
import { initializeApp } from '@firebase/app-exp';
20+
import { signInAnonymously, getAuth } from '@firebase/auth-exp';
21+
22+
// See https://github.com/typescript-eslint/typescript-eslint/issues/363
23+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
24+
// import * as storage from '@firebase/storage-types';
25+
26+
// import { expect } from 'chai';
27+
import { ref, uploadBytes, getStorage } from '../../exp/index';
28+
import { StorageService } from '../../src/service';
29+
30+
// eslint-disable-next-line @typescript-eslint/no-require-imports
31+
const PROJECT_CONFIG = require('../../../../config/project.json');
32+
33+
export const PROJECT_ID = PROJECT_CONFIG.projectId;
34+
export const STORAGE_BUCKET = PROJECT_CONFIG.storageBucket;
35+
export const API_KEY = PROJECT_CONFIG.apiKey;
36+
37+
let appCount = 0;
38+
39+
export async function withTestInstance(): Promise<StorageService> {
40+
const app = initializeApp(
41+
{ apiKey: API_KEY, projectId: PROJECT_ID, storageBucket: STORAGE_BUCKET },
42+
'test-app-' + appCount++
43+
);
44+
console.log('signing in');
45+
try {
46+
await signInAnonymously(getAuth(app));
47+
} catch (e) {
48+
console.log(e);
49+
}
50+
// await signInAnonymously(getAuth(app));
51+
console.log('signed in');
52+
return getStorage(app);
53+
}
54+
55+
describe.only('FirebaseStorage', () => {
56+
it('can upload bytes', async () => {
57+
const storage = await withTestInstance();
58+
const reference = ref(storage, 'public/bytes');
59+
console.log(reference.fullPath);
60+
await uploadBytes(reference, new Uint8Array([0, 1, 3]));
61+
console.log('done');
62+
});
63+
64+
// it('can upload string', () => {
65+
// return withTestInstance(async storage => {
66+
// const ref = storage.ref('public/string');
67+
// await ref.putString('foo');
68+
// });
69+
// });
70+
71+
// it('validates operations on root', () => {
72+
// return withTestInstance(async storage => {
73+
// const ref = storage.ref('');
74+
// try {
75+
// // eslint-disable-next-line @typescript-eslint/no-floating-promises
76+
// ref.putString('foo');
77+
// expect.fail();
78+
// } catch (e) {
79+
// expect(e.message).to.satisfy((v: string) =>
80+
// v.match(
81+
// /The operation 'putString' cannot be performed on a root reference/
82+
// )
83+
// );
84+
// }
85+
// });
86+
// });
87+
88+
// it('can delete object ', () => {
89+
// return withTestInstance(async storage => {
90+
// const ref = storage.ref('public/delete');
91+
// await ref.putString('foo');
92+
93+
// // getDownloadURL() succeeds for an existing object
94+
// await ref.getDownloadURL();
95+
96+
// await ref.delete();
97+
// try {
98+
// // getDownloadURL() fails for a deleted object
99+
// await ref.getDownloadURL();
100+
// expect.fail();
101+
// } catch (e) {
102+
// expect(e.message).to.satisfy((v: string) =>
103+
// v.match(/Object 'public\/delete' does not exist/)
104+
// );
105+
// }
106+
// });
107+
// });
108+
109+
// it('can get download URL', () => {
110+
// return withTestInstance(async storage => {
111+
// const ref = storage.ref('public/downloadurl');
112+
// await ref.put(new Uint8Array([0, 1, 3]));
113+
// const url = await ref.getDownloadURL();
114+
// expect(url).to.satisfy((v: string) =>
115+
// v.match(
116+
// /https:\/\/firebasestorage\.googleapis\.com\/v0\/b\/.*\/o\/public%2Fdownloadurl/
117+
// )
118+
// );
119+
// });
120+
// });
121+
122+
// it('can get metadata', () => {
123+
// return withTestInstance(async storage => {
124+
// const ref = storage.ref('public/getmetadata');
125+
// await ref.put(new Uint8Array([0, 1, 3]));
126+
// const metadata = await ref.getMetadata();
127+
// expect(metadata.name).to.equal('getmetadata');
128+
// });
129+
// });
130+
131+
// it('can update metadata', () => {
132+
// return withTestInstance(async storage => {
133+
// const ref = storage.ref('public/updatemetadata');
134+
// await ref.put(new Uint8Array([0, 1, 3]));
135+
// const metadata = await ref.updateMetadata({
136+
// customMetadata: { foo: 'bar' }
137+
// });
138+
// expect(metadata.customMetadata).to.deep.equal({ foo: 'bar' });
139+
// });
140+
// });
141+
142+
// it('can list files', () => {
143+
// return withTestInstance(async storage => {
144+
// await storage.ref('public/list/a').putString('');
145+
// await storage.ref('public/list/b').putString('');
146+
// await storage.ref('public/list/c/d').putString('');
147+
// const listResult = await storage.ref('public/list').listAll();
148+
// expect(listResult.items.map(v => v.name)).to.have.members(['a', 'b']);
149+
// expect(listResult.prefixes.map(v => v.name)).to.have.members(['c']);
150+
// });
151+
// });
152+
});

packages/storage/test/unit/reference.exp.test.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import {
2727
list,
2828
getMetadata,
2929
updateMetadata,
30-
getDownloadURL
30+
getDownloadURL,
31+
uploadBytes
3132
} from '../../src/reference';
3233
import { StorageService, ref } from '../../src/service';
3334
import * as testShared from './testshared';
@@ -255,6 +256,16 @@ describe('Firebase Storage > Reference', () => {
255256
});
256257
});
257258

259+
describe('uploadBytes', () => {
260+
it('Uses metadata.contentType', async () => {
261+
// Regression test for b/30989476
262+
const snapshot = await uploadBytes(child, new Blob(), {
263+
contentType: 'lol/wut'
264+
} as Metadata);
265+
expect(snapshot.metadata!.contentType).to.equal('lol/wut');
266+
});
267+
});
268+
258269
describe('Argument verification', () => {
259270
describe('list', () => {
260271
it('throws on invalid maxResults', async () => {

0 commit comments

Comments
 (0)