Skip to content
This repository was archived by the owner on Mar 17, 2025. It is now read-only.

Commit 96f7d8e

Browse files
committed
Merge pull request #42 from ed7coyne/refactor
Refactored Firebase library
2 parents 4cae3f8 + caa70e9 commit 96f7d8e

File tree

4 files changed

+245
-132
lines changed

4 files changed

+245
-132
lines changed

Diff for: Firebase.cpp

+104-73
Original file line numberDiff line numberDiff line change
@@ -15,114 +15,145 @@
1515
//
1616
#include "Firebase.h"
1717

18-
const char* firebaseFingerprint = "7A 54 06 9B DC 7A 25 B3 86 8D 66 53 48 2C 0B 96 42 C7 B3 0A";
19-
const uint16_t firebasePort = 443;
18+
namespace {
19+
const char* kFirebaseFingerprint = "7A 54 06 9B DC 7A 25 B3 86 8D 66 53 48 2C 0B 96 42 C7 B3 0A";
20+
const uint16_t kFirebasePort = 443;
2021

21-
Firebase::Firebase(const String& host) : _host(host) {
22-
_http.setReuse(true);
22+
String makeFirebaseURL(const String& path, const String& auth) {
23+
String url;
24+
if (path[0] != '/') {
25+
url = "/";
26+
}
27+
url += path + ".json";
28+
if (auth.length() > 0) {
29+
url += "?auth=" + auth;
30+
}
31+
return url;
32+
}
33+
34+
} // namespace
35+
36+
Firebase::Firebase(const String& host) : host_(host) {
37+
http_.setReuse(true);
2338
}
2439

2540
Firebase& Firebase::auth(const String& auth) {
26-
_auth = auth;
41+
auth_ = auth;
2742
return *this;
2843
}
2944

30-
String Firebase::get(const String& path) {
31-
sendRequest("GET", path);
32-
return readBody();
45+
FirebaseGet Firebase::get(const String& path) {
46+
return FirebaseGet(host_, auth_, path, &http_);
3347
}
3448

35-
String Firebase::push(const String& path, const String& value) {
36-
sendRequest("POST", path, value);
37-
return readBody();
49+
FirebasePush Firebase::push(const String& path, const String& value) {
50+
return FirebasePush(host_, auth_, path, value, &http_);
3851
}
3952

40-
void Firebase::remove(const String& path) {
41-
sendRequest("DELETE", path);
53+
FirebaseRemove Firebase::remove(const String& path) {
54+
return FirebaseRemove(host_, auth_, path, &http_);
4255
}
4356

44-
String Firebase::set(const String& path, const String& value) {
45-
sendRequest("PUT", path, value);
46-
return readBody();
57+
FirebaseStream Firebase::stream(const String& path) {
58+
// TODO: create new client dedicated to stream.
59+
return FirebaseStream(host_, auth_, path, &http_);
4760
}
4861

49-
Firebase& Firebase::stream(const String& path) {
50-
_error.reset();
51-
String url = makeURL(path);
52-
const char* headers[] = {"Location"};
53-
_http.setReuse(true);
54-
_http.begin(_host.c_str(), firebasePort, url.c_str(), true, firebaseFingerprint);
55-
_http.collectHeaders(headers, 1);
56-
_http.addHeader("Accept", "text/event-stream");
57-
int statusCode = _http.sendRequest("GET", (uint8_t*)NULL, 0);
58-
String location;
59-
// TODO(proppy): Add a max redirect check
60-
while (statusCode == 307) {
61-
location = _http.header("Location");
62-
_http.setReuse(false);
63-
_http.end();
64-
_http.setReuse(true);
65-
_http.begin(location, firebaseFingerprint);
66-
statusCode = _http.sendRequest("GET", (uint8_t*)NULL, 0);
62+
// FirebaseCall
63+
FirebaseCall::FirebaseCall(const String& host, const String& auth,
64+
const char* method, const String& path,
65+
const String& data, HTTPClient* http) : http_(http) {
66+
String url = makeFirebaseURL(path, auth);
67+
http_->setReuse(true);
68+
http_->begin(host, kFirebasePort, url, true, kFirebaseFingerprint);
69+
70+
bool followRedirect = false;
71+
if (method == "STREAM") {
72+
method = "GET";
73+
http_->addHeader("Accept", "text/event-stream");
74+
followRedirect = true;
6775
}
68-
if (statusCode != 200) {
69-
_error.set(statusCode,
70-
"stream " + location + ": "
71-
+ HTTPClient::errorToString(statusCode));
76+
77+
if (followRedirect) {
78+
const char* headers[] = {"Location"};
79+
http_->collectHeaders(headers, 1);
7280
}
73-
return *this;
74-
}
81+
82+
int status = http_->sendRequest(method, (uint8_t*)data.c_str(), data.length());
7583

76-
String Firebase::makeURL(const String& path) {
77-
String url;
78-
if (path[0] != '/') {
79-
url = "/";
84+
// TODO: Add a max redirect check
85+
if (followRedirect) {
86+
while (status == HTTP_CODE_TEMPORARY_REDIRECT) {
87+
String location = http_->header("Location");
88+
http_->setReuse(false);
89+
http_->end();
90+
http_->setReuse(true);
91+
http_->begin(location, kFirebaseFingerprint);
92+
status = http_->sendRequest("GET", (uint8_t*)NULL, 0);
93+
}
8094
}
81-
url += path + ".json";
82-
if (_auth.length() > 0) {
83-
url += "?auth=" + _auth;
95+
96+
if (status != 200) {
97+
error_ = FirebaseError(status, String(method) + " " + url + ": " + HTTPClient::errorToString(status));
98+
}
99+
100+
// if not streaming.
101+
if (!followRedirect) {
102+
response_ = http_->getString();
84103
}
85-
return url;
86104
}
87105

88-
void Firebase::sendRequest(const char* method, const String& path, const String& value) {
89-
String url = makeURL(path);
90-
_http.begin(_host.c_str(), firebasePort, url.c_str(), true, firebaseFingerprint);
91-
int statusCode = _http.sendRequest(method, (uint8_t*)value.c_str(), value.length());
92-
_error.reset();
93-
if (statusCode < 0) {
94-
_error.set(statusCode,
95-
String(method) + " " + url + ": "
96-
+ HTTPClient::errorToString(statusCode));
97-
}
106+
// FirebaseGet
107+
FirebaseGet::FirebaseGet(const String& host, const String& auth,
108+
const String& path,
109+
HTTPClient* http)
110+
: FirebaseCall(host, auth, "GET", path, "", http) {
111+
112+
if (!error()) {
113+
// TODO: parse json
114+
json_ = response();
115+
}
98116
}
99117

100-
String Firebase::readBody() {
101-
if (_error.code() != 0) {
102-
return "";
118+
// FirebasePush
119+
FirebasePush::FirebasePush(const String& host, const String& auth,
120+
const String& path, const String& value,
121+
HTTPClient* http)
122+
: FirebaseCall(host, auth, "POST", path, value, http) {
123+
if (!error()) {
124+
// TODO: parse name
125+
name_ = response();
103126
}
104-
// no _http.end() because of connection reuse.
105-
return _http.getString();
106127
}
107128

108-
bool Firebase::connected() {
109-
return _http.connected();
129+
// FirebasePush
130+
FirebaseRemove::FirebaseRemove(const String& host, const String& auth,
131+
const String& path,
132+
HTTPClient* http)
133+
: FirebaseCall(host, auth, "DELETE", path, "", http) {
134+
}
135+
136+
// FirebaseStream
137+
FirebaseStream::FirebaseStream(const String& host, const String& auth,
138+
const String& path,
139+
HTTPClient* http)
140+
: FirebaseCall(host, auth, "STREAM", path, "", http) {
110141
}
111142

112-
bool Firebase::available() {
113-
return _http.getStreamPtr()->available();
143+
bool FirebaseStream::available() {
144+
return http_->getStreamPtr()->available();
114145
}
115146

116-
Firebase::Event Firebase::read(String& event) {
117-
auto client = _http.getStreamPtr();
118-
Event type;;
147+
FirebaseStream::Event FirebaseStream::read(String& event) {
148+
auto client = http_->getStreamPtr();
149+
Event type;
119150
String typeStr = client->readStringUntil('\n').substring(7);
120151
if (typeStr == "put") {
121-
type = Firebase::Event::PUT;
152+
type = Event::PUT;
122153
} else if (typeStr == "patch") {
123-
type = Firebase::Event::PATCH;
154+
type = Event::PATCH;
124155
} else {
125-
type = Firebase::Event::UNKNOWN;
156+
type = Event::UNKNOWN;
126157
}
127158
event = client->readStringUntil('\n').substring(6);
128159
client->readStringUntil('\n'); // consume separator

Diff for: Firebase.h

+108-35
Original file line numberDiff line numberDiff line change
@@ -25,56 +25,129 @@
2525
#include <WiFiClientSecure.h>
2626
#include <ESP8266HTTPClient.h>
2727

28-
// FirebaseError represents a Firebase API error with a code and a
29-
// message.
28+
class FirebaseGet;
29+
class FirebasePush;
30+
class FirebaseRemove;
31+
class FirebaseStream;
32+
33+
// Primary client to the Firebase backend.
34+
class Firebase {
35+
public:
36+
Firebase(const String& host);
37+
Firebase& auth(const String& auth);
38+
39+
// Fetch result at "path".
40+
FirebaseGet get(const String& path);
41+
42+
// Add new value to list at "path", will return key for the new item.
43+
FirebasePush push(const String& path, const String& value);
44+
45+
// Deletes value at "path" from firebase.
46+
FirebaseRemove remove(const String& path);
47+
48+
// Starts a stream of events that affect object at "path".
49+
// TODO: fix FirebaseStream lifecycle
50+
// https://github.com/esp8266/Arduino/issues/500
51+
FirebaseStream stream(const String& path);
52+
53+
private:
54+
HTTPClient http_;
55+
String host_;
56+
String auth_;
57+
};
58+
3059
class FirebaseError {
3160
public:
32-
operator bool() const { return _code < 0; }
33-
int code() const { return _code; }
34-
const String& message() const { return _message; }
35-
void reset() { set(0, ""); }
36-
void set(int code, const String& message) {
37-
_code = code;
38-
_message = message;
61+
FirebaseError() {}
62+
FirebaseError(int code, const String& message) : code_(code), message_(message) {
63+
}
64+
operator bool() const { return code_ != 0; }
65+
int code() const { return code_; }
66+
const String& message() const { return message_; }
67+
private:
68+
int code_ = 0;
69+
String message_ = "";
70+
};
71+
72+
class FirebaseCall {
73+
public:
74+
FirebaseCall() {}
75+
FirebaseCall(const String& host, const String& auth,
76+
const char* method, const String& path,
77+
const String& data = "",
78+
HTTPClient* http = NULL);
79+
const FirebaseError& error() const {
80+
return error_;
81+
}
82+
const String& response() {
83+
return response_;
3984
}
85+
protected:
86+
HTTPClient* http_;
87+
FirebaseError error_;
88+
String response_;
89+
};
90+
91+
class FirebaseGet : public FirebaseCall {
92+
public:
93+
FirebaseGet() {}
94+
FirebaseGet(const String& host, const String& auth,
95+
const String& path, HTTPClient* http = NULL);
96+
97+
const String& json() const {
98+
return json_;
99+
}
100+
40101
private:
41-
int _code = 0;
42-
String _message = "";
102+
String json_;
43103
};
44104

45-
// Firebase is the connection to firebase.
46-
class Firebase {
105+
class FirebasePush : public FirebaseCall {
47106
public:
48-
Firebase(const String& host);
49-
Firebase& auth(const String& auth);
50-
const FirebaseError& error() const {
51-
return _error;
107+
FirebasePush() {}
108+
FirebasePush(const String& host, const String& auth,
109+
const String& path, const String& value, HTTPClient* http = NULL);
110+
111+
const String& name() const {
112+
return name_;
52113
}
53-
String get(const String& path);
54-
// write a new JSON `value` to the given `path`.
55-
// Note: A String `value` must include double quotes to be valid json.
56-
String set(const String& path, const String& value);
57-
// append a new JSON `value` to the given `path`.
58-
// Note: A String `value` must include double quotes to be valid json.
59-
String push(const String& path, const String& value);
60-
void remove(const String& path);
61-
bool connected();
62-
Firebase& stream(const String& path);
114+
115+
private:
116+
String name_;
117+
};
118+
119+
class FirebaseRemove : public FirebaseCall {
120+
public:
121+
FirebaseRemove() {}
122+
FirebaseRemove(const String& host, const String& auth,
123+
const String& path, HTTPClient* http = NULL);
124+
};
125+
126+
127+
class FirebaseStream : public FirebaseCall {
128+
public:
129+
FirebaseStream() {}
130+
FirebaseStream(const String& host, const String& auth,
131+
const String& path, HTTPClient* http = NULL);
132+
133+
// True if there is an event available.
63134
bool available();
135+
136+
// event type.
64137
enum Event {
65138
UNKNOWN,
66139
PUT,
67140
PATCH
68141
};
69-
Event read(String& event);
70-
private:
71-
String makeURL(const String& path);
72-
void sendRequest(const char* method, const String& path, const String& value = "");
73-
String readBody();
74142

75-
HTTPClient _http;
76-
String _host;
77-
String _auth;
143+
// Read next event in stream.
144+
Event read(String& event);
145+
146+
const FirebaseError& error() const {
147+
return _error;
148+
}
149+
150+
private:
78151
FirebaseError _error;
79152
};
80153

0 commit comments

Comments
 (0)